pan-wizard 3.35.0 → 3.37.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.
Files changed (339) hide show
  1. package/CHANGELOG.md +3645 -0
  2. package/README.md +46 -52
  3. package/agents/pan-conductor.md +2 -3
  4. package/agents/pan-counterfactual.md +1 -2
  5. package/agents/pan-debugger.md +14 -3
  6. package/agents/pan-design-checker.md +1 -2
  7. package/agents/pan-designer.md +2 -3
  8. package/agents/pan-document_code.md +1 -2
  9. package/agents/pan-executor.md +25 -0
  10. package/agents/pan-hardener.md +4 -3
  11. package/agents/pan-integration-checker.md +1 -2
  12. package/agents/pan-knowledge.md +1 -2
  13. package/agents/pan-meta-reviewer.md +3 -3
  14. package/agents/pan-optimizer.md +4 -4
  15. package/agents/pan-phase-researcher.md +80 -475
  16. package/agents/pan-plan-checker.md +40 -3
  17. package/agents/pan-planner.md +21 -7
  18. package/agents/pan-previewer.md +3 -2
  19. package/agents/pan-project-researcher.md +72 -493
  20. package/agents/pan-release.md +1 -1
  21. package/agents/pan-reviewer.md +14 -3
  22. package/agents/pan-roadmapper.md +12 -7
  23. package/agents/pan-verifier.md +9 -4
  24. package/bin/install-lib.cjs +134 -14
  25. package/bin/install.js +59 -12
  26. package/commands/pan/army.md +1 -1
  27. package/commands/pan/autodev.md +76 -0
  28. package/commands/pan/cost.md +21 -1
  29. package/commands/pan/exec-phase.md +3 -4
  30. package/commands/pan/focus-auto.md +14 -7
  31. package/commands/pan/focus-design.md +4 -5
  32. package/commands/pan/focus-docs.md +24 -0
  33. package/commands/pan/learn.md +13 -6
  34. package/commands/pan/milestone-audit.md +8 -1
  35. package/commands/pan/milestone-done.md +10 -5
  36. package/commands/pan/milestone-new.md +2 -2
  37. package/commands/pan/new-project.md +15 -12
  38. package/commands/pan/plan-phase.md +15 -9
  39. package/commands/pan/preview.md +1 -1
  40. package/commands/pan/quick.md +1 -1
  41. package/commands/pan/review-deep.md +11 -5
  42. package/commands/pan/settings.md +6 -0
  43. package/commands/pan/verify-phase.md +6 -2
  44. package/docs/AGENTS.md +917 -0
  45. package/docs/ARCHITECTURE.md +1054 -0
  46. package/docs/CLI-REFERENCE.md +4582 -0
  47. package/docs/COMPARISON.md +116 -0
  48. package/docs/DEVELOPMENT.md +490 -0
  49. package/docs/ECOSYSTEM-REVIEW-2026-06.md +107 -0
  50. package/docs/ECOSYSTEM-REVIEW-2026-08.md +349 -0
  51. package/docs/EXAMPLES.md +584 -0
  52. package/docs/FAQ.md +254 -0
  53. package/docs/FIELD-HARVEST-2026-07.md +88 -0
  54. package/docs/FIELD-REPORT-army-2026-06.md +121 -0
  55. package/docs/HOOKS.md +638 -0
  56. package/docs/IMPROVEMENT-TODO.md +246 -0
  57. package/docs/INTERNALS.md +485 -0
  58. package/docs/MIGRATION-v2-to-v3.md +258 -0
  59. package/docs/SECURITY-HARDENING.md +230 -0
  60. package/docs/SKILLS-ENHANCEMENT-PLAN-V2.md +122 -0
  61. package/docs/SKILLS-FULL-TEXT.md +9685 -0
  62. package/docs/SKILLS-REFERENCE.md +1037 -0
  63. package/docs/TROUBLESHOOTING.md +1084 -0
  64. package/docs/USER-GUIDE.md +1437 -0
  65. package/docs/archive/SKILLS-ENHANCEMENT-PLAN.md +230 -0
  66. package/docs/audits/AUDIT-2026-08-e2e-ROADMAP.md +117 -0
  67. package/docs/audits/AUDIT-2026-08-e2e-VERIFICATION.md +693 -0
  68. package/docs/audits/AUDIT-2026-08-e2e.md +1546 -0
  69. package/docs/audits/OUTSTANDING-2026-08.md +275 -0
  70. package/docs/audits/STABILITY-TEST-2026-08.md +640 -0
  71. package/docs/branding/README-brand-draft.md +119 -0
  72. package/docs/branding/handoff/IMPLEMENTATION.md +205 -0
  73. package/docs/branding/handoff/README-brand-draft.md +119 -0
  74. package/docs/branding/handoff/image-prompts.md +46 -0
  75. package/docs/branding/image-prompts.md +46 -0
  76. package/docs/branding/nano-banana-prompts.txt +60 -0
  77. package/docs/context-monitor.md +13 -0
  78. package/docs/decisions/ADR-0001-strategic-feature-prioritization.md +83 -0
  79. package/docs/decisions/ADR-0002-copilot-cli-runtime.md +71 -0
  80. package/docs/decisions/ADR-0003-smart-execution.md +84 -0
  81. package/docs/decisions/ADR-0004-copilot-cli-interaction.md +89 -0
  82. package/docs/decisions/ADR-0005-command-naming.md +80 -0
  83. package/docs/decisions/ADR-0006-focus-commands.md +101 -0
  84. package/docs/decisions/ADR-0007-ship-v1.md +73 -0
  85. package/docs/decisions/ADR-0008-map-codebase-mermaid-togaf.md +80 -0
  86. package/docs/decisions/ADR-0009-production-deployment-checklist.md +68 -0
  87. package/docs/decisions/ADR-0010-standards-integration.md +79 -0
  88. package/docs/decisions/ADR-0011-help-commands-rewrite.md +64 -0
  89. package/docs/decisions/ADR-0012-contributors-attribution-index.md +42 -0
  90. package/docs/decisions/ADR-0013-strategic-feature-additions.md +53 -0
  91. package/docs/decisions/ADR-0014-internal-cleanup-code-quality.md +53 -0
  92. package/docs/decisions/ADR-0015-focus-auto-runner.md +296 -0
  93. package/docs/decisions/ADR-0016-enhanced-doc-sync.md +54 -0
  94. package/docs/decisions/ADR-0017-enhanced-focus-design.md +69 -0
  95. package/docs/decisions/ADR-0018-user-reality-testing.md +84 -0
  96. package/docs/decisions/ADR-0019-end-to-end-user-acceptance-testing.md +113 -0
  97. package/docs/decisions/ADR-0020-ai-drift-prevention.md +55 -0
  98. package/docs/decisions/ADR-0021-codebase-mapper-v2.md +76 -0
  99. package/docs/decisions/ADR-0022-lifecycle-completeness.md +63 -0
  100. package/docs/decisions/ADR-0023-opus-4-7-adoption.md +89 -0
  101. package/docs/decisions/ADR-0024-spec-b-v2-completion.md +137 -0
  102. package/docs/decisions/ADR-0025-behavioral-guardrails-layer.md +129 -0
  103. package/docs/decisions/ADR-0026-self-improvement-loop.md +171 -0
  104. package/docs/decisions/ADR-0027-doc-code-link-graph.md +164 -0
  105. package/docs/decisions/ADR-0028-unified-agents-skills-target.md +46 -0
  106. package/docs/decisions/ADR-0029-experiment-evaluator.md +76 -0
  107. package/docs/decisions/ADR-0030-planning-concurrency.md +37 -0
  108. package/docs/decisions/ADR-0031-focus-auto-backlog-source.md +47 -0
  109. package/docs/decisions/ADR-0032-squad-model.md +64 -0
  110. package/docs/decisions/ADR-0033-army-campaign.md +40 -0
  111. package/docs/decisions/ADR-0034-scheduled-campaigns.md +57 -0
  112. package/docs/decisions/ADR-0035-army-hud-dashboard.md +40 -0
  113. package/docs/decisions/ADR-0036-agentic-memory-architecture.md +106 -0
  114. package/docs/decisions/ADR-0037-pin-security-agents-to-opus.md +51 -0
  115. package/docs/decisions/ADR-0038-skill-aligned-decomposition.md +41 -0
  116. package/docs/decisions/ADR-0039-worktree-as-project-variant.md +37 -0
  117. package/docs/decisions/ADR-0040-memory-injection-defense.md +55 -0
  118. package/docs/decisions/ADR-0041-mcp-dual-era-bridge.md +47 -0
  119. package/docs/decisions/ADR-0042-design-parity-both-flows.md +45 -0
  120. package/docs/decisions/ADR-0043-addressable-planning-roots.md +56 -0
  121. package/docs/decisions/ADR-0044-bound-the-cached-context.md +104 -0
  122. package/docs/decisions/ADR-0045-agent-plugins-bundle.md +76 -0
  123. package/docs/decisions/ADR-0046-subagent-frontmatter-decisions.md +39 -0
  124. package/docs/decisions/ADR-0047-pan-harness.md +76 -0
  125. package/docs/decisions/ADR-0048-one-core-per-project.md +43 -0
  126. package/docs/decisions/ADR-0049-machine-verdicts-and-findings-ledger.md +116 -0
  127. package/docs/specs/ai_drift_prevention_featureai.md +286 -0
  128. package/docs/specs/codebase_mapper_v2_featureai.md +930 -0
  129. package/docs/specs/command_naming_optimization_featureai.md +930 -0
  130. package/docs/specs/comp_analysis_markdown_v1.md +149 -0
  131. package/docs/specs/continuous_autonomous_execution_featureai.md +304 -0
  132. package/docs/specs/contributors_attribution_index_featureai.md +476 -0
  133. package/docs/specs/copilot_cli_interaction_featureai.md +868 -0
  134. package/docs/specs/copilot_cli_runtime_featureai.md +647 -0
  135. package/docs/specs/custom-skills-assessment.md +461 -0
  136. package/docs/specs/deployment_audit_findings_featureai.md +243 -0
  137. package/docs/specs/doc_code_link_graph_featureai.md +253 -0
  138. package/docs/specs/documentation_alignment_cleanup_featureai.md +69 -0
  139. package/docs/specs/e2e_visual_testing_featureai.md +615 -0
  140. package/docs/specs/end_to_end_user_acceptance_testing_featureai.md +810 -0
  141. package/docs/specs/evidence-loop-plan.md +76 -0
  142. package/docs/specs/evidence_loop_featureai.md +497 -0
  143. package/docs/specs/focus-auto-optimize-category.md +449 -0
  144. package/docs/specs/focus-auto-optimize-superplan.md +188 -0
  145. package/docs/specs/focus_auto_runner_featureai.md +1102 -0
  146. package/docs/specs/focus_design_v2_featureai.md +197 -0
  147. package/docs/specs/focus_sync_v2_featureai.md +202 -0
  148. package/docs/specs/git_integration_overhaul_featureai.md +326 -0
  149. package/docs/specs/glm_zai_provider_featureai.md +200 -0
  150. package/docs/specs/googlecli_adoption_featureai.md +661 -0
  151. package/docs/specs/help_commands_rewrite_featureai.md +860 -0
  152. package/docs/specs/industry_analysis_strategic_additions_featureai.md +231 -0
  153. package/docs/specs/industry_comparison_and_feature_gaps_featureai.md +674 -0
  154. package/docs/specs/internal_cleanup_code_quality_featureai.md +486 -0
  155. package/docs/specs/investigations/README.md +31 -0
  156. package/docs/specs/investigations/digests/2026-09-28.md +47 -0
  157. package/docs/specs/investigations/digests/2026-10-08.md +62 -0
  158. package/docs/specs/investigations/digests/2026-10-10.md +61 -0
  159. package/docs/specs/investigations/features/debugger-red-loop-first.md +55 -0
  160. package/docs/specs/investigations/features/harness-cross-host-behaviour.md +93 -0
  161. package/docs/specs/investigations/features/optimize-apply-regression-gate.md +66 -0
  162. package/docs/specs/investigations/features/reviewer-smell-baseline.md +57 -0
  163. package/docs/specs/investigations/features/trace-failure-capture.md +69 -0
  164. package/docs/specs/lifecycle_completeness_featureai.md +754 -0
  165. package/docs/specs/local-first-ci.md +42 -0
  166. package/docs/specs/map_codebase_mermaid_togaf_featureai.md +805 -0
  167. package/docs/specs/market-delta-2026-09-superplan.md +310 -0
  168. package/docs/specs/market-ideas-2026-09.md +131 -0
  169. package/docs/specs/market-ideas-2026-10.md +428 -0
  170. package/docs/specs/market-ideas-ledger.md +177 -0
  171. package/docs/specs/memory-optimization-2026-10.md +285 -0
  172. package/docs/specs/multi-track-planning-roots.md +143 -0
  173. package/docs/specs/multi_model_routing_featureai.md +687 -0
  174. package/docs/specs/multi_model_routing_superplan.md +197 -0
  175. package/docs/specs/opus_47_existing_enhancements_featureai.md +247 -0
  176. package/docs/specs/opus_47_extended_features_featureai.md +363 -0
  177. package/docs/specs/opus_47_extended_features_v2_featureai.md +397 -0
  178. package/docs/specs/pan_focus_commands_featureai.md +2336 -0
  179. package/docs/specs/pan_zcode_mcp_bridge_featureai.md +120 -0
  180. package/docs/specs/product-excellence-superplan.md +315 -0
  181. package/docs/specs/product_excellence_program_2026_q4_featureai.md +365 -0
  182. package/docs/specs/production_deployment_readiness_featureai.md +433 -0
  183. package/docs/specs/production_readiness_ship_v1_featureai.md +698 -0
  184. package/docs/specs/real_test_enforcement_featureai.md +213 -0
  185. package/docs/specs/reality-check-2026-09-superplan.md +362 -0
  186. package/docs/specs/right-sized-ceremony.md +74 -0
  187. package/docs/specs/roadmap_and_competition_2026_q4_featureai.md +390 -0
  188. package/docs/specs/self_improvement_loop_featureai.md +875 -0
  189. package/docs/specs/skill-aligned-decomposition.md +155 -0
  190. package/docs/specs/standards_integration_featureai.md +833 -0
  191. package/docs/specs/testing-system-redesign-2026-09.md +267 -0
  192. package/docs/specs/user_reality_testing_system_featureai.md +788 -0
  193. package/docs/specs/watch-triggers.json +22 -0
  194. package/docs/specs/workflow-integration-redesign.md +742 -0
  195. package/docs/specs/workflow_state_consistency_featureai.md +183 -0
  196. package/hooks/dist/pan-context-monitor.js +9 -2
  197. package/hooks/dist/pan-cost-logger.js +5 -0
  198. package/hooks/dist/pan-guard.js +28 -5
  199. package/hooks/dist/pan-stop-guard.js +9 -9
  200. package/hooks/dist/pan-trace-logger.js +5 -0
  201. package/package.json +10 -2
  202. package/pan-wizard-core/bin/lib/agents-md.cjs +57 -8
  203. package/pan-wizard-core/bin/lib/autodev-report.cjs +180 -0
  204. package/pan-wizard-core/bin/lib/autodev.cjs +642 -0
  205. package/pan-wizard-core/bin/lib/ci.cjs +143 -0
  206. package/pan-wizard-core/bin/lib/commands.cjs +1 -1
  207. package/pan-wizard-core/bin/lib/config.cjs +41 -5
  208. package/pan-wizard-core/bin/lib/constants.cjs +27 -1
  209. package/pan-wizard-core/bin/lib/core.cjs +31 -14
  210. package/pan-wizard-core/bin/lib/cost.cjs +160 -4
  211. package/pan-wizard-core/bin/lib/cross-family.cjs +164 -0
  212. package/pan-wizard-core/bin/lib/debug-cases.cjs +104 -0
  213. package/pan-wizard-core/bin/lib/doc-lint.cjs +5 -3
  214. package/pan-wizard-core/bin/lib/failure-class.cjs +81 -0
  215. package/pan-wizard-core/bin/lib/findings.cjs +148 -9
  216. package/pan-wizard-core/bin/lib/focus.cjs +1 -1
  217. package/pan-wizard-core/bin/lib/git.cjs +1 -1
  218. package/pan-wizard-core/bin/lib/hosts.cjs +88 -0
  219. package/pan-wizard-core/bin/lib/init.cjs +7 -2
  220. package/pan-wizard-core/bin/lib/knowledge.cjs +2 -0
  221. package/pan-wizard-core/bin/lib/merge.cjs +50 -0
  222. package/pan-wizard-core/bin/lib/mutate.cjs +268 -0
  223. package/pan-wizard-core/bin/lib/next-action.cjs +112 -0
  224. package/pan-wizard-core/bin/lib/optimize.cjs +2 -2
  225. package/pan-wizard-core/bin/lib/phase-gate.cjs +15 -9
  226. package/pan-wizard-core/bin/lib/phase-remove.cjs +91 -11
  227. package/pan-wizard-core/bin/lib/phase.cjs +85 -13
  228. package/pan-wizard-core/bin/lib/plan-lint.cjs +310 -0
  229. package/pan-wizard-core/bin/lib/preview.cjs +26 -4
  230. package/pan-wizard-core/bin/lib/requirements-lint.cjs +130 -0
  231. package/pan-wizard-core/bin/lib/research-lint.cjs +268 -0
  232. package/pan-wizard-core/bin/lib/review-deep.cjs +14 -22
  233. package/pan-wizard-core/bin/lib/review-vote.cjs +105 -0
  234. package/pan-wizard-core/bin/lib/roadmap.cjs +1 -0
  235. package/pan-wizard-core/bin/lib/runner.cjs +2 -16
  236. package/pan-wizard-core/bin/lib/sizing.cjs +356 -0
  237. package/pan-wizard-core/bin/lib/squads.cjs +1 -1
  238. package/pan-wizard-core/bin/lib/state-write.cjs +247 -0
  239. package/pan-wizard-core/bin/lib/test-gate.cjs +433 -0
  240. package/pan-wizard-core/bin/lib/uat.cjs +149 -0
  241. package/pan-wizard-core/bin/lib/usage.cjs +104 -0
  242. package/pan-wizard-core/bin/lib/verdict.cjs +26 -3
  243. package/pan-wizard-core/bin/lib/verify-plan-set.cjs +30 -1
  244. package/pan-wizard-core/bin/lib/verify-preflight.cjs +2 -2
  245. package/pan-wizard-core/bin/lib/verify-red.cjs +174 -0
  246. package/pan-wizard-core/bin/lib/verify-retro.cjs +3 -2
  247. package/pan-wizard-core/bin/lib/verify-scope.cjs +143 -2
  248. package/pan-wizard-core/bin/lib/verify.cjs +86 -31
  249. package/pan-wizard-core/bin/lib/worktree.cjs +70 -0
  250. package/pan-wizard-core/bin/pan-tools.cjs +257 -28
  251. package/pan-wizard-core/learnings/universal/autonomous-loop.md +2 -2
  252. package/pan-wizard-core/mcp/orchestrator.cjs +4 -1
  253. package/pan-wizard-core/references/continuation-format.md +3 -3
  254. package/pan-wizard-core/references/design-methodology.md +1 -1
  255. package/pan-wizard-core/references/guardrails.md +1 -1
  256. package/pan-wizard-core/references/handoff-decisions.md +1 -1
  257. package/pan-wizard-core/references/model-profiles.md +2 -3
  258. package/pan-wizard-core/references/questioning.md +24 -0
  259. package/pan-wizard-core/references/ui-brand.md +4 -6
  260. package/pan-wizard-core/references/verdict-contract.md +2 -1
  261. package/pan-wizard-core/templates/config.json +10 -2
  262. package/pan-wizard-core/templates/debug.md +1 -0
  263. package/pan-wizard-core/templates/design.md +5 -4
  264. package/pan-wizard-core/templates/discovery.md +2 -2
  265. package/pan-wizard-core/templates/phase-prompt.md +7 -0
  266. package/pan-wizard-core/templates/requirements.md +9 -0
  267. package/pan-wizard-core/templates/research.md +10 -0
  268. package/pan-wizard-core/templates/summary-complex.md +5 -1
  269. package/pan-wizard-core/templates/summary-minimal.md +4 -0
  270. package/pan-wizard-core/templates/summary-standard.md +5 -1
  271. package/pan-wizard-core/templates/summary.md +8 -1
  272. package/pan-wizard-core/workflows/add-phase.md +4 -6
  273. package/pan-wizard-core/workflows/assumptions.md +19 -1
  274. package/pan-wizard-core/workflows/diagnose-issues.md +2 -0
  275. package/pan-wizard-core/workflows/discuss-phase.md +4 -2
  276. package/pan-wizard-core/workflows/exec-lean.md +69 -0
  277. package/pan-wizard-core/workflows/exec-phase.md +79 -15
  278. package/pan-wizard-core/workflows/execute-plan.md +3 -1
  279. package/{commands/pan → pan-wizard-core/workflows}/focus-doc-audit.md +13 -29
  280. package/{commands/pan → pan-wizard-core/workflows}/focus-drift-walking.md +3 -18
  281. package/{commands/pan → pan-wizard-core/workflows}/focus-exec.md +8 -23
  282. package/{commands/pan → pan-wizard-core/workflows}/focus-plan.md +6 -20
  283. package/{commands/pan → pan-wizard-core/workflows}/focus-scan.md +3 -16
  284. package/{commands/pan → pan-wizard-core/workflows}/focus-sync.md +7 -21
  285. package/pan-wizard-core/workflows/health.md +6 -0
  286. package/pan-wizard-core/workflows/help.md +114 -77
  287. package/pan-wizard-core/workflows/insert-phase.md +4 -6
  288. package/pan-wizard-core/workflows/learn.md +3 -3
  289. package/pan-wizard-core/workflows/milestone-audit.md +2 -2
  290. package/pan-wizard-core/workflows/milestone-new.md +38 -95
  291. package/pan-wizard-core/workflows/new-project.md +96 -258
  292. package/pan-wizard-core/workflows/optimize.md +107 -5
  293. package/pan-wizard-core/workflows/phase-tests.md +10 -0
  294. package/pan-wizard-core/workflows/plan-phase.md +106 -45
  295. package/pan-wizard-core/workflows/profile.md +50 -0
  296. package/pan-wizard-core/workflows/progress.md +10 -0
  297. package/pan-wizard-core/workflows/quick.md +34 -43
  298. package/pan-wizard-core/workflows/remove-phase.md +11 -1
  299. package/pan-wizard-core/workflows/research-phase.md +118 -40
  300. package/pan-wizard-core/workflows/resume-project.md +18 -2
  301. package/pan-wizard-core/workflows/settings.md +33 -19
  302. package/pan-wizard-core/workflows/todo-check.md +6 -2
  303. package/pan-wizard-core/workflows/transition.md +22 -4
  304. package/pan-wizard-core/workflows/uat-walkthrough.md +90 -0
  305. package/pan-wizard-core/workflows/update.md +1 -1
  306. package/pan-wizard-core/workflows/verify-phase.md +39 -46
  307. package/scripts/build-agent-plugin.js +35 -15
  308. package/scripts/build-help.cjs +106 -0
  309. package/scripts/build-plugin.js +10 -1
  310. package/scripts/deprecate-old-versions.js +70 -3
  311. package/scripts/generate-skills-docs.py +1 -1
  312. package/scripts/git-hooks/pre-push +27 -0
  313. package/scripts/install-git-hooks.js +7 -5
  314. package/scripts/install-pre-push.cjs +38 -0
  315. package/scripts/land.cjs +151 -0
  316. package/scripts/local-ci.cjs +501 -0
  317. package/scripts/local-schedule.cjs +277 -0
  318. package/scripts/mutation-probe.cjs +7 -80
  319. package/scripts/prompt-lint.cjs +32 -0
  320. package/scripts/prompt-rules.cjs +156 -0
  321. package/scripts/run-tests.cjs +7 -1
  322. package/scripts/test-quality-lint.cjs +3 -3
  323. package/scripts/test-surface.cjs +175 -6
  324. package/agents/pan-research-synthesizer.md +0 -246
  325. package/commands/pan/audit-deployment.md +0 -386
  326. package/commands/pan/dashboard.md +0 -22
  327. package/commands/pan/discord.md +0 -19
  328. package/commands/pan/mcp-bridge.md +0 -143
  329. package/commands/pan/milestone-cleanup.md +0 -19
  330. package/commands/pan/milestone-gaps.md +0 -35
  331. package/commands/pan/optimize.md +0 -115
  332. package/commands/pan/profile.md +0 -76
  333. package/commands/pan/research-phase.md +0 -189
  334. package/pan-wizard-core/bin/lib/bridge.cjs +0 -269
  335. package/pan-wizard-core/templates/research-project/architecture.md +0 -204
  336. package/pan-wizard-core/templates/research-project/features.md +0 -147
  337. package/pan-wizard-core/templates/research-project/pitfalls.md +0 -200
  338. package/pan-wizard-core/templates/research-project/stack.md +0 -120
  339. package/pan-wizard-core/templates/research-project/summary.md +0 -170
@@ -0,0 +1,4582 @@
1
+ # pan-tools.cjs CLI Reference
2
+
3
+ Complete reference for `pan-tools.cjs`, the central CLI dispatcher behind PAN Wizard workflows. The dispatcher routes top-level commands and nested subcommands to core modules. Shipped commands, workflows and agents call pan-tools for state management, verification, scaffolding, context gathering, prompt-cache measurement, cross-phase memory, model-capability routing, the Spec B v2 feature set (cost dashboard, bus infrastructure, foresight previews, deep-review merge, knowledge retrieval, counterfactual worktree, MCP bridge), the optimization additions (circular optimization loop, `/pan:git` family, `distill` AI code-bloat optimizer), and the self-improvement loop (`experiment`) plus vendored markdown linter (`doc-lint`).
4
+
5
+ ```bash
6
+ node pan-tools.cjs <command> [args] [--raw] [--verbose] [--cwd <path>]
7
+ ```
8
+
9
+ ---
10
+
11
+ ## Table of Contents
12
+
13
+ - [Global Behavior](#global-behavior)
14
+ - [Command Index](#command-index)
15
+ - [1. State Commands](#1-state-commands)
16
+ - [2. State Progression](#2-state-progression)
17
+ - [3. Phase Operations](#3-phase-operations)
18
+ - [4. Phase Listing](#4-phase-listing)
19
+ - [5. Roadmap Operations](#5-roadmap-operations)
20
+ - [6. Requirements Operations](#6-requirements-operations)
21
+ - [7. Milestone Operations](#7-milestone-operations)
22
+ - [8. Validation](#8-validation)
23
+ - [9. Verification Suite](#9-verification-suite)
24
+ - [10. Progress & Context](#10-progress--context)
25
+ - [11. Todos](#11-todos)
26
+ - [12. Scaffolding](#12-scaffolding)
27
+ - [13. Frontmatter CRUD](#13-frontmatter-crud)
28
+ - [14. Template Operations](#14-template-operations)
29
+ - [15. Config Operations](#15-config-operations)
30
+ - [16. Utility Commands](#16-utility-commands)
31
+ - [17. Compound Init Commands](#17-compound-init-commands)
32
+ - [18. Focus Commands](#18-focus-commands)
33
+ - [19. Standards Commands](#19-standards-commands)
34
+ - [20. Operations Commands](#20-operations-commands)
35
+ - [20.1 Capability-Aware Commands](#201-capability-aware-commands)
36
+ - [21. Codebase Commands](#21-codebase-commands)
37
+ - [22. Spec B v2 Commands](#22-spec-b-v2-commands)
38
+ - [23. Self-Improvement Loop Commands](#23-self-improvement-loop-commands)
39
+ - [24. Doc-Lint Commands](#24-doc-lint-commands)
40
+ - [25. Skill-Aligned Decomposition Commands](#25-skill-aligned-decomposition-commands)
41
+ - [26. Hygiene Commands](#26-hygiene-commands)
42
+ - [27. Optimization, Git and Distill Commands](#27-optimization-git-and-distill-commands)
43
+ - [28. Evidence Loop Commands](#28-evidence-loop-commands)
44
+ - [29. Bot Army and Report Commands](#29-bot-army-and-report-commands)
45
+
46
+ ---
47
+
48
+ ## Global Behavior
49
+
50
+ ### Output Format
51
+
52
+ **Output is JSON** unless `--raw` is passed (or a flag such as `hud --stdout` asks for HTML). Under `--raw`, a command that has a plain-text form prints it instead: usually a single value suitable for shell variable capture (e.g., a directory path, a count, or `true`/`false`), for some commands a human-readable summary. A command without one prints its JSON under `--raw` too (every `init *`, `state json`, `findings dispose`).
53
+
54
+ ### Output Contracts (`contract`)
55
+
56
+ Some machine-readable outputs carry a `contract` field: a version string for their shape. The outputs are `state` (`state load`), `state json`, `progress` (`progress json`), every `findings` output, and the MCP resources `pan://state`, `pan://progress` and `pan://findings` that wrap them. The judges' `pan-verdict` block has one too. The rule is additive: fields are added within a major version (`1.x`) and never renamed or removed; a rename or removal is a major bump. Consumers pin the major, accept any `1.x`, and ignore fields they do not know. The key=value `--raw` form of `state load` carries no `contract` line, and no error payload (`{"error": …}`) from any of these outputs carries one.
57
+
58
+ ### Debug Logging
59
+
60
+ Pass `--verbose` to any command to set `PAN_VERBOSE=1`. Few code paths read it: the archived-phase lookup writes a `[pan-tools]` line to stderr when `.planning/milestones/` cannot be read, and `drift-check` adds a `per_file` breakdown to its JSON. Nothing traces dispatch, and most commands print nothing extra.
61
+
62
+ ### Large Output (`@file:` Protocol)
63
+
64
+ When JSON output exceeds ~50 KB, the tool writes it to a temporary file and prints `@file:<tmpdir>/pan-XXXXXX/out.json` instead (a fresh private directory under the OS temp dir, created with `mkdtemp`). Callers must detect the `@file:` prefix and read the file. If the temp file cannot be written, the JSON is truncated to 50 000 characters and printed to stdout instead, so treat a parse failure on a large payload as that fallback. This prevents context window pollution when AI agents consume the output.
65
+
66
+ ### Exit Codes
67
+
68
+ | Code | Meaning |
69
+ |------|---------|
70
+ | `0` | The command answered the question it was asked — including when the answer is empty or negative |
71
+ | `1` | Failure, refusal, or a gate that tripped: the command did not produce the result the caller wanted |
72
+ | `2` | `doc-lint` only — the schema file itself is malformed, so nothing could be linted (distinct from *finding* violations). Emitted on **both** the `--raw` and the JSON path; the JSON body additionally lists `schema_errors` |
73
+
74
+ **The exit code is the authoritative failure signal.** Gate on it. Do not gate on the presence of an `error` key in the body — see "Error Shape" below for why that test gives the wrong answer.
75
+
76
+ ### Error Shape
77
+
78
+ pan-tools has **two** error mechanisms. They differ in where the text lands, not in the exit code:
79
+
80
+ | Mechanism | Text goes to | stdout | Exit code |
81
+ |---|---|---|---|
82
+ | `error(msg)` | **stderr**, as a bare `Error: <msg>` line — no JSON | empty | always `1` |
83
+ | `output(payload, …)` with a truthy top-level **error-family** key | **stdout**, as `{ "error": "<description>" }` (or, under `--raw`, the command's plain-text form when it has one) | the payload | `1` unless the command opts out (below) |
84
+
85
+ The **error family** is `error` and any key ending in `_error` — `worktree_error` (`whatif prepare`), `drain_error` (`bus drain`). A renamed error key is still an error key. Plural collections (`errors`, `schema_errors`) and counters (`error_count`) are *not* in the family: they are the detail of a verdict payload, and an empty array is truthy in JavaScript, so treating them as failure signals would fail every clean run. Commands whose result is a verdict set their exit code explicitly instead.
86
+
87
+ `error()` is used for usage errors and unmet preconditions the caller cannot act on programmatically (a missing required argument). `output()` carries a machine-readable body: a failure the caller may want to branch on (`{"error": "state.md not found"}`, `{"error": "not_a_git_repo"}`) or a deliberate refusal that protected something (`{"error": "File already exists"}`, `{"error": "dirty_working_tree"}` — the file was not overwritten, the reset did not run).
88
+
89
+ Error messages follow the pattern `"<thing> not found"` with actionable hints where appropriate.
90
+
91
+ **A JSON `error` body carries a non-zero exit.** This was not always true: every `output({ error: … })` site once exited `0`, so `pan-tools state json` in a project with no `state.md` printed `{"error":"state.md not found"}` **and reported success** — invisible to any orchestrator, hook, CI step, or autonomous loop gating on the exit code. `output()` now derives the code from the payload in one place (`core.cjs`), so the two mechanisms agree.
92
+
93
+ #### A valid-but-empty answer is not an error
94
+
95
+ An empty result is a *result*. `focus plan` on a burned-down backlog, `verify artifacts` on a plan that declares no artifacts, `template select` falling back to the documented default — each answered correctly and **exits `0`**. Some of these still carry an `error` string, because it is the only field available to explain *why* the answer is empty (`{"error": "No work items found. Run focus scan first or add phases/todos."}` at exit `0`). Such sites opt out explicitly via `core.cjs`'s `EXIT_OK`, each with a comment giving its reason; `grep -rn "EXIT_OK" pan-wizard-core/bin` enumerates every one.
96
+
97
+ That is exactly why the exit code, not the `error` key, is the failure test.
98
+
99
+ #### `<verb>: false` payloads
100
+
101
+ Mutating commands report the outcome as a past-tense flag: `{committed}`, `{updated}`, `{added}`, `{resolved}`, `{pushed}`, `{created}`. **The flag alone does not tell you whether something went wrong**, and the exit code does:
102
+
103
+ | Payload | Exit | Why |
104
+ |---|---|---|
105
+ | `{committed: false, reason: "commit_failed", error: …}` | `1` | git refused; nothing landed |
106
+ | `{committed: false, reason: …, error: "commit_blocked"}` | `1` | a safety check protected you; nothing landed (`reason` names the check: `sensitive_file_detected` or `deleted_files_detected`) |
107
+ | `{updated: false, reason: "state.md not found", error: …}` | `1` | the write had nowhere to go |
108
+ | `{resolved: false, reason: "no matching blocker", error: …}` | `1` | the blocker is still open |
109
+ | `{committed: false, reason: "nothing_to_commit"}` | `0` | no change was needed |
110
+ | `{committed: false, reason: "skipped_commit_docs_false"}` | `0` | your config said not to |
111
+ | `{advanced: false, reason: "last_plan"}` | `0` | normal end of phase |
112
+ | `{updated: false, reason: "No plans found"}` | `0` | nothing to sync |
113
+ | `{available: false, reason: "BRAVE_API_KEY not set"}` | `0` | capability unconfigured, not broken |
114
+ | `{found: false, phase_number: 12}` | `0` | the answer to the question is "no" |
115
+ | `{clean: false, …}` / `{exists: false, …}` | `0` | a description of state |
116
+
117
+ The failures carry an error-family key and the answers do not — which is *why* the same shape can mean both. This split is a per-command judgement recorded in the source next to each payload, not something you can infer from the shape.
118
+
119
+ `{available: false}` is worth one more line, because it appears with and without an error key on purpose: `websearch` with no `BRAVE_API_KEY` is unconfigured (exit `0`, degrade gracefully), while a configured search that gets an API 5xx is broken (exit `1`).
120
+
121
+ #### How a caller tells them apart
122
+
123
+ ```sh
124
+ out=$(pan-tools <command>) # stderr passes through to the terminal
125
+ code=$?
126
+ if [ "$code" -ne 0 ]; then
127
+ # Failed, refused, or a gate tripped. The reason is in whichever stream spoke:
128
+ # $out non-empty -> JSON body on stdout; read its .error
129
+ # $out empty -> a bare "Error: ..." line went to stderr
130
+ :
131
+ else
132
+ # Answered. The answer may legitimately be empty and may carry an `error`
133
+ # string saying why -- branch on the data, never on the `error` key.
134
+ :
135
+ fi
136
+ ```
137
+
138
+ A gate whose verdict has no `error` key sets its code explicitly instead: `links validate` (`0` pass / `1` fail), `verify reconcile` (`0` reconciled / `1` contradiction), `verify stubs --gate`, `campaign due`. Those are documented per command.
139
+
140
+ **Help:** `pan-tools --help` (also `-h` or `help`) prints the command list and exits `0` — an explicit help request is a success. A *missing* command prints the same usage as an error and exits `1`.
141
+
142
+ ### `--cwd <path>`
143
+
144
+ Override the working directory. Accepts `--cwd /path` or `--cwd=/path`. Useful when subagents run outside the project root.
145
+
146
+ ### Planning root: `--track` / `--planning-dir` / `--all-tracks`
147
+
148
+ `--cwd` moves the *project* root. These move the *planning* root inside it, so a repo holding several planning trees (several independently-planned products in one repo, a monorepo, a spike kept beside the mainline) can address any of them. See [ADR-0043](decisions/ADR-0043-addressable-planning-roots.md).
149
+
150
+ | Flag | Effect |
151
+ |---|---|
152
+ | `--track <name>` | Act on `.planning/tracks/<name>/`. Name must be a slug (`[A-Za-z0-9][A-Za-z0-9._-]*`) — it becomes a single path segment. |
153
+ | `--planning-dir <path>` | Act on an arbitrary project-relative planning tree. Absolute, drive-relative, and `..`-containing paths are rejected. |
154
+ | `--all-tracks` | Act on the root tree **and** every discovered track. Implemented for `hygiene scan`/`hygiene clean` and `init milestone-op`; other commands ignore it. |
155
+
156
+ Both value flags accept `--flag value` and `--flag=value`, and are mutually exclusive. Environment equivalents `PAN_TRACK` and `PAN_PLANNING_DIR` apply when no flag is given; flags win, and `PAN_PLANNING_DIR` outranks `PAN_TRACK`.
157
+
158
+ **Tracks are discovered, not declared.** Any directory under `.planning/tracks/` carrying a planning spine (`state.md`, `roadmap.md`, `phases/`, `focus/`, `orchestration/`, …) is a track; an incidental folder is not.
159
+
160
+ **Commands report the root they resolved.** The `hygiene` payloads and every `init *` payload except `init new-milestone` and `init verify-work` carry `planning_root`, `track`, `planning_root_source` (`default` / `env:PAN_TRACK` / `flag:--track` / …), and `planning_root_exists` (`init milestone-op --all-tracks` carries them on each entry of `tracks[]`). A mistyped `--track` therefore shows `planning_root_exists: false` next to its zero findings, instead of reading as a healthy project.
161
+
162
+ ```bash
163
+ pan-tools hygiene scan --track verify --raw
164
+ pan-tools hygiene scan --all-tracks --raw
165
+ PAN_TRACK=verify pan-tools init milestone-op
166
+ ```
167
+
168
+ ### Module Architecture
169
+
170
+ The dispatcher (`pan-tools.cjs`) routes commands to the core modules:
171
+
172
+ | Module | Purpose |
173
+ |--------|---------|
174
+ | `state.cjs` | state.md read/write/parse |
175
+ | `state-compact.cjs` | `state compact` — moves settled history out of state.md into state-history.md so it stops being re-read into every agent call. Archive-first, dry-run by default. |
176
+ | `commands.cjs` | Utility commands (slug, timestamp, commit, batch-commit, estimate-cost, rollback, progress, etc.) |
177
+ | `phase.cjs` | Phase directory and plan operations |
178
+ | `init.cjs` | Compound workflow context gathering |
179
+ | `verify.cjs` | Verification, validation, and retrospective (including `retro --write-memory`); facade over the verify-* submodules |
180
+ | `verify-drift.cjs` | Convention-drift detection (`drift-check`); extracted from verify.cjs, re-exported through it |
181
+ | `verify-retro.cjs` | Milestone retrospective (`retro`); extracted from verify.cjs, re-exported through it |
182
+ | `verify-deploy.cjs` | Deployment validation (`validate deployment`); extracted from verify.cjs, re-exported through it |
183
+ | `verify-preflight.cjs` | Pre-execution gates (`preflight`, `deps validate`); extracted from verify.cjs, re-exported through it |
184
+ | `suggest.cjs` | "Did you mean" corrections for an unknown command. Pure; the group→subcommand index is parsed from the dispatcher's own `Unknown <group> subcommand … Available:`, `Unknown init workflow: … Available:` and `<group> subcommand required. Available:` strings on the error path only, so no second list can drift and a healthy call pays nothing |
185
+ | `commands-learnings.cjs` | Error patterns, session history, learnings lifecycle; extracted from commands.cjs, re-exported through it |
186
+ | `phase-remove.cjs` | Phase removal + renumbering cascade (`phase remove`); extracted from phase.cjs, re-exported through it |
187
+ | `roadmap.cjs` | roadmap.md parsing and updates, and `roadmap slice` (one phase's part of roadmap.md and requirements.md) |
188
+ | `roadmap-compact.cjs` | `roadmap compact [--apply] [--keep N]`: moves shipped phases' sections out of roadmap.md into roadmap-history.md and leaves a stub. Dry-run by default. Also reads a compacted phase's full section back for `roadmap get-phase`, `roadmap slice` and `report phase`. |
189
+ | `frontmatter.cjs` | YAML frontmatter CRUD |
190
+ | `config.cjs` | config.json management |
191
+ | `template.cjs` | Template selection and filling |
192
+ | `milestone.cjs` | Milestone archival and requirements |
193
+ | `context-budget.cjs` | Context window utilization (cache metrics surfaced in health output; cache block classified `ok`/`warn`/`critical`/`absent` with remediation advice) |
194
+ | `focus.cjs` | Focus workflow scan/plan/sync/exec/auto (the dispatcher answers `focus design` itself), `focus classify-stages`, `focus reflection` |
195
+ | `codebase.cjs` | Codebase analysis: detect-languages, analyze-imports, best-practices, `codebase estimate-size` |
196
+ | `memory.cjs` | **(E-4)** Cross-phase agent memory: `memory read`, `memory append`, `memory list`, `memory compact`, `memory select`, `memory record`, `memory prune`, `memory budget` |
197
+ | `memory-optimize.cjs` | Reconcile the always-loaded project memory: `memory optimize [--apply] [--keep N]` (dedupe / placeholder-strip / cap-with-archive state.md); also runs automatically in the focus + normal flows |
198
+ | `memory-rebuild.cjs` | Regenerate derived tools-memory: `memory rebuild [--apply]` (AGENTS.md PAN section, CLAUDE.md bridge, state.md frontmatter) |
199
+ | `agents-md.cjs` | Single source for the AGENTS.md PAN section and PAN's CLAUDE.md block (the `@AGENTS.md` import plus a `# Compact instructions` section Claude Code reads when it compacts), shared by the installer and `memory rebuild` |
200
+ | `core.cjs` (CLI surface) | **(E-1)** Cached context: `cache prime [--summary]` lists the stable planning files agents re-read (wraps `buildCachedContext`). It measures them and primes nothing: the host caches each agent's prompt prefix itself. |
201
+ | `cost.cjs` | **(Y-6)** Cost dashboard: `cost report`, `cost append`, `cost clear`, `cost limits` (the host's usage-limit windows as the status line last saw them), plus `models check` rate-table staleness. Log at `.planning/metrics/tokens.jsonl`. |
202
+ | `cost-rebuild.cjs` | `cost rebuild`: rebuilds the ledger from Claude Code's session and per-agent transcripts (usage deduped per turn, agents typed from the main thread's `Agent` calls, Workflow-tool subagents included, one main-thread row per session); supersedes hook rows of rebuilt sessions, keeps the rest, moves the old ledger aside. |
203
+ | `bus.cjs` | **(Y-7)** Agent message channels: `bus publish`, `bus drain`, `bus list`. Channels at `.planning/bus/<channel>.jsonl`. |
204
+ | `preview.cjs` | **(Y-1)** Foresight: `preview phase <N>`, `preview phases`, `preview milestone`. |
205
+ | `review-deep.cjs` | **(Y-2)** Deep review merge: `review-deep merge`, `review-deep analyze`. |
206
+ | `knowledge.cjs` | **(Y-3)** Grounded Q&A: `knowledge ask`, `knowledge discuss`, `knowledge playbook`. |
207
+ | `whatif.cjs` | **(Y-4)** Counterfactual worktree: `whatif prepare`, `whatif report`, `whatif cleanup`. |
208
+ | `optimize.cjs` | Circular optimization loop: `optimize trace init/end/current/list/show/log/reconcile`, `optimize learn`, `optimize apply`, `optimize revert`, `optimize list`, `optimize stats`. Logs at `.planning/optimization/traces/<session>/trace.jsonl`. `reconcile` rewrites `session.json` counters from `trace.jsonl` so hook-driven auto-sessions report real numbers. |
209
+ | `git.cjs` | Phase-aware git workflow: `git commit/branch/push/status/log/stash/diff/rollback/tag/sync`. Reuses `runCommitSafetyChecks` for commit hardening. |
210
+ | `distill.cjs` | AI code-bloat optimizer (multi-pass pipeline): `distill scan/analyze/report`. Cross-session memory at `.planning/memory/distill-patterns.md`. |
211
+ | `doc-lint.cjs` | Markdown frontmatter + structure linter: `doc-lint <dir>`, `doc-lint schema-check`, `doc-lint counts <dir>`, `doc-lint flags`. |
212
+ | `experiment.cjs` | Self-improvement loop scaffolding: `experiment new/list/manifest/harvest/prune`. |
213
+ | `runner.cjs` | External agent runner: `experiment run/status/stop`. Spawns Claude/Codex/Gemini/OpenCode via `spawnSync`. |
214
+ | `learn-lint.cjs` | Learnings-store integrity linter: `learn lint`. Checks L-001..L-007 (duplicate IDs, dangling cross-refs, empty source_experiments, PAN-internal terms in universal-scope rules, revision marker without `superseded_by`, universal-scope pattern citing an internal pattern id, a `cites` entry whose file or symbol is gone). |
215
+ | `learn-index.cjs` | Learnings index + queries: `learn build-index` (writes `pan-wizard-core/learnings/index.json` with topic→agent-relevance map), `learn topics-for --agent <role> [--cue <text>]` (budget-aware topic selection per agent role; with a cue, topics are ranked by how well they match it, falling back to the role when nothing matches). Replaces "skim universal/" with targeted load. |
216
+ | `squads.cjs` | **(ADR-0032)** Bot-army squad registry: `squad list`, `squad show <name>`. Role-scoped squads (`squad list` enumerates them), each carrying a model tier + an **advisory** access contract — the module's own header says it "modifies no agent and changes no execution path", so those labels are the contract the conductor is instructed to honour and the enforced grant stays each agent's `tools:` frontmatter. Registry only — drives `/pan:army` and `pan-conductor` campaign mode. |
217
+ | `worktree.cjs` | **(ADR-0033)** Branch-per-agent isolation: `worktree list`, `worktree create <task>` (`--base`), `worktree remove <path>` (`--branch`, `--force`), `worktree cleanup` (`--force`; campaign teardown sweep, v3.25+). `army/<task>` branches + isolated git worktrees so parallel builders never collide. |
218
+ | `campaign.cjs` | **(ADR-0034)** Scheduled self-resuming campaigns: `campaign schedule` (arm: `--cadence`/`--daily-budget`/`--goal`/`--source`/`--pause`/`--resume`/`--disable`/`--enforce-budget`/`--advisory-budget`), `campaign status`, `campaign due` (host-scheduler gate), `campaign record-run`. Descriptor at `.planning/orchestration/schedule.json`; PAN owns the due-check, the host fires `/pan:army --continue`. Merge gate unaffected. |
219
+ | `hud.cjs` | **(ADR-0035)** Single-page HTML dashboard: `hud` (`--out`/`--open`/`--stdout`). Aggregates project + army state (mission, command stack, campaign, safety harness, worktrees, roadmap, telemetry, requirements/quality, activity) into one self-contained file (default `.planning/hud.html`). Read-only view — no new state; army panels self-hide on plain projects. |
220
+ | `skill-align.cjs` | **(ADR-0038)** Skill-Aligned Decomposition pass: `skills index` (on-the-fly index of commands/templates/references/learnings), `skills align --draft-file <p>` (score draft planner tasks against the skill surface, return budget-bounded vocabulary hints). Advisory, fail-open; used by `pan-planner` before grouping tasks into plans. |
221
+ | `hygiene.cjs` | Project cleanup + version alignment: `hygiene scan` (version drift per runtime manifest, legacy uppercase filenames, .tmp orphans, memory bloat, stale or expired agent-memory entries, memory files PAN does not read as memory, the size of Claude Code's own memory index (report-only), poisoned ledgers, stale traces and optimization reports, oversized cached context, fragment planning dirs, foreign and shared planning trees), `hygiene clean [--apply]` (dry-run by default; safe fixes only — renames, compaction of state.md, roadmap.md and agent memory, memory pruning, quarantine-by-rename, trace pruning; installer re-runs and fragment removal stay manual). |
222
+ | `phase-report.cjs` | Per-phase HTML report + project timeline index: `report phase <N>` and `report index` (`--out`/`--open`/`--stdout`; `index` also `--bundle` for one self-contained inlined file), `report all` (`--open` opens the index; writes every phase report plus the index to their default paths). Reuses `hud.cjs` rendering to produce self-contained files (per-phase `.planning/phases/<NN-slug>/<NN>-report.html`; index `.planning/report-index.html`; bundle `.planning/report-bundle.html`). Read-only view — writes only its rendered file(s), no new state; deterministic (unchanged phase data rewrites nothing); a phase-less project has nothing to report. Opt-in auto-generation at the verify→complete gate, focus-auto checkpoints, and army INTEGRATE via `workflow.phase_reports`. |
223
+ | `links.cjs` | Doc-Code Link Graph engine behind `links validate` (ADR-0027): parses frontmatter link declarations, resolves doc↔code references, and reports dangling/stale links. |
224
+ | `constants.cjs` | Shared constants used across the dispatcher — e.g. `COMMAND_RENAME_MAP` (legacy→current command names) and `FOCUS_CATEGORIES`. No CLI surface; imported by other modules. |
225
+ | `lock.cjs` | Advisory file-locking helper serializing concurrent writes to shared `.planning/` state. No CLI surface; imported where write races are possible. |
226
+ | `utils.cjs` | Planning-root path builders (`planningPath()`, `planningRel()`, `phasesPath()`, `milestonesPath()`), `fileAccessible()`/`readJsonFile()`, plan/summary file filters and `classifyPhaseStatus()`, shared by the other modules. No CLI surface. (`toPosix()` and `safeReadFile()` live in `core.cjs`.) |
227
+ | `planning-root.cjs` | Resolves WHICH planning tree a command acts on (`--track` / `--planning-dir` / `PAN_TRACK` / `PAN_PLANNING_DIR`, default `.planning`), discovers tracks under `.planning/tracks/`, and reports each resolution's provenance. Leaf module — requires only `fs`/`path`. `utils.planningPath()` / `utils.planningRel()` are the only path constructors built on it. No CLI surface. |
228
+ | `foreign-planning.cjs` | **(2026-09)** Detects a `.planning/` that another tool writes to, from markers PAN never writes. **gsd-core owns its tree:** its markers are `HANDOFF.json`, `.gsd-allow-shrink`, two or more gsd-only directories, or its flat dotted config keys. `hygiene scan`/`clean` raises one `foreign-planning-tree` warning and refuses the rename fix, `validate health` reports `E006` and stops before `E002`–`E005`, and `init new-project` refuses with an error payload; the remedy is `--planning-dir <dir>`. **planning-with-files shares the tree:** its markers are `.active_plan`, `.attestation`, `ledger-*.jsonl`, a dated task directory holding `task_plan.md`, or two of its directories (`sessions/`, dated task directories without the plan file). PAN keeps working: `hygiene` raises a `shared-planning-tree` info finding, `validate health` reports `I004`, and `init new-project` returns `shared_planning_tree`. No CLI surface of its own. |
229
+ | `verdict.cjs` | **(evidence loop)** The `pan-verdict` contract: parses the last fenced `pan-verdict` block of a judge's report, validates it (outcome, finding classes and severities, limits), reads a verification.md frontmatter through an adapter, and derives finding ids and artifact signatures. Pure; no CLI surface of its own. |
230
+ | `findings.cjs` | **(evidence loop)** The findings ledger `.planning/findings.jsonl` behind `findings record`, `findings list`, `findings dispose`, `findings debt`, `findings suppressions` and `findings prove`, and the MCP resource `pan://findings`. It is append-only and folded on read. A finding closes only on evidence (a judge's `resolved` answer, the verifier's `gaps_closed`, or a passing code fact); one a report neither repeats nor answers stays open as `unaddressed`. A regressed fix reopens, and deliberate dispositions stick, except that a dismissal only suppresses its finding until the file changes, and never a security or logic finding. Each recorded verdict is logged to the trace. |
231
+ | `phase-gate.cjs` | **(program V1)** `phase gate <N>`: whether a phase may be marked complete, in code. Refuses (one reason code each) on a plan without a summary, no verification, a failed one (with code facts applied), `human_needed` without a recorded approval, a stale verification, a reconcile contradiction, a failed test gate, open critical/high findings, committed stubs, or a test-integrity violation; what it could not check is listed in `not_checked`. `phase complete`, the native `pan-exec-waves` script and the MCP merge request all use it. |
232
+ | `verify-integrity.cjs` | **(program V3)** `verify test-integrity <phase>`: walks the phase's plan commits and reports a deleted test, a skip or only marker added (JS, Python, Go, Rust, Java), assertions dropped, an assertion edited in a test no plan declares, a rewritten snapshot, or an assertion that cannot fail; a renamed test is a warning. Uses verify-scope's `committedFiles()`. |
233
+ | `verify-plan-set.cjs` | **(program V7)** `verify plan-set <phase>`: the plan checks code can make, before the plan checker runs (requirement coverage, same-wave file overlap, missing, self and future references, cycles, wave gaps). `planCheckStatus()` binds a passing plan check to the plans it checked through the `plans_sha` the recorder stores with every plan-checker verdict; `planDrift()` lists planned files changed since a plan's `planned_commit`. `init execute-phase` carries both as `plan_check` and `plan_drift`. |
234
+ | `verify-scope.cjs` | **(evidence loop)** `verify scope <phase>`: the files a phase changed that its plans did not declare in `files_modified`, taken from its plan commits and summary key-files, for the verifier's unrequested-work check. `verify stale <phase>`: whether the files a verification covered changed since its `verified_commit`. `review scope <phase>` (program R1): the files a reviewer reads (the phase's commits plus uncommitted and untracked work) and the plans' intent. Re-exported through verify.cjs. |
235
+ | `plan-lint.cjs` | **(program P4, P2)** `verify review-focus <phase>`: every plan has a `## Review Focus` of at most five items, each naming its owning task and a test, or one `None — <reason>` line. `verify decisions <phase>`: plans cite only `DD-N` decision ids the phase design records, and every recorded decision is cited. `/pan:plan-phase` runs both with `verify plan-set` before the plan checker. `verify gates <phase>`: a task's `<verify>` command that already passes before any work is flagged. |
236
+ | `research-lint.cjs` | **(program P3)** `research lint <phase\|path>`: the research file's `## Claims` table (claim · type · source · checked), or the answered rows of its answers table. A claim with no source or no date is an error; one older than its type's freshness window is `stale`. `research check <questions> <research> [--market]`: at most five questions, each answered with a source or `unknown` with an assumption, at most five comparable products. |
237
+ | `usage.cjs` | **(program L9)** `usage`: a local census of the installed commands and agents against the project's cost ledger and trace sessions: used (calls, last use), never used, and commands started but never finished. Local files only. |
238
+ | `test-gate.cjs` | **(program V2)** `test run` and `test detect`: finds the project's test command in any language (or `workflow.test_command`), runs it, parses the runner's output into stable failing-test ids, and compares a phase's run with its baseline (`{NN}-test-baseline.json`, `{NN}-test-run.json`). The phase gate and the verdict recorder read the run record. |
239
+ | `verify-red.cjs` | **(program V4)** `verify red <phase>`: runs each test file the phase changed at the phase's base (in a throwaway worktree) and at HEAD; a test that passes before the phase's code exists is `not_red`. Also exports the phase-base and worktree helpers `verify mutate` uses. |
240
+ | `mutate.cjs` | **(program V5)** `verify mutate --phase N`: a seeded sample of single-operator mutations on the lines the phase changed, each run against the whole suite in a worktree at HEAD; writes `{NN}-mutation.json`. Report-only unless `workflow.mutation_gate` is `gate`. |
241
+ | `autodev.cjs` | **(program A1)** `autodev run/resume/status`: the deterministic outer loop — reads `.planning/`, asks `next-action` for the next unit, decides in code whether to stop, runs each unit in a fresh headless host session (a lean phase is planned by the engine through `size phase-plan` and executed through `workflows/exec-lean.md` in one session); ledger and state under `.planning/autodev/`. |
242
+ | `autodev-report.cjs` | **(program A5)** `autodev replay` (re-runs the stop logic over `blackbox.jsonl`) and `autodev report` (`dossier.md`/`dossier.html` beside the ledger). |
243
+ | `failure-class.cjs` | **(program A4)** Classifies a failed autodev unit and routes it: retry with the error tail, fresh restart, tier escalation, quarantine, ask a human, or stop on budget. No CLI surface. |
244
+ | `ci.cjs` | **(program A2)** CI as autodev's oracle: `gh pr checks` (`--ci`), or the project's own `npm run ci:local`/`npm test` (`--ci local`); fix rounds and a breaker. No CLI surface of its own. |
245
+ | `hosts.cjs` | **(program A0)** One table of how PAN runs each host CLI: the autonomous runner argv, the model family, the read-only review argv, the headless harness argv. No CLI surface. |
246
+ | `next-action.cjs` | **(program L7)** `next-action`: reads roadmap.md and the phase directories into the MCP orchestrator's snapshot and maps its decision to a command. |
247
+ | `sizing.cjs` | Right-sized ceremony: `size phase/task` decide lean or full per piece of work, `size phase-plan` writes a lean phase's plans from its requirements (in waves, at most `workflow.lean_max_requirements` tasks each) and records their check, `size quick-plan` writes a lean quick task's plan, `size plan-check` is the lean path's plan check in code. |
248
+ | `cross-family.cjs` | **(program R4)** `review cross-family <phase>`: one read-only review on another host's CLI, off unless `workflow.cross_family_review` names a host. |
249
+ | `review-vote.cjs` | **(program R2)** `review vote`: merges k reviewer passes; a finding survives only with a quorum. |
250
+ | `merge.cjs` | **(program L6a)** `merge request` / `merge confirm`: the human merge gate on the CLI; confirm only in an interactive terminal. |
251
+ | `uat.cjs` | **(program L8)** `uat record` / `uat status`: UAT results written through the engine into `{phase}-uat.md`. |
252
+ | `debug-cases.cjs` | **(program P5)** `debug repro <slug>` / `debug similar "<symptom>"`: a debug session's reproduction as its closing check, and the resolved case base. |
253
+ | `state-write.cjs` | **(program A7b)** `state init`, `state section`, `state quick-task`, `state apply`: the writers of state.md as a whole or by section. |
254
+ | `requirements-lint.cjs` | **(program V8)** `verify requirements` (EARS criteria) and `verify traceability` (runs each requirement's mapped tests); `phase complete` ticks through it. |
255
+
256
+ ---
257
+
258
+ ## Command Index
259
+
260
+ Quick reference of all CLI commands grouped by category.
261
+
262
+ | # | Command | Category | Module |
263
+ |---|---------|----------|--------|
264
+ | 1 | `state load` | State | state.cjs |
265
+ | 2 | `state json` | State | state.cjs |
266
+ | 3 | `state update` | State | state.cjs |
267
+ | 4 | `state get` | State | state.cjs |
268
+ | 5 | `state patch` | State | state.cjs |
269
+ | 6 | `state-snapshot` | State | state.cjs |
270
+ | 7 | `state advance-plan` | State Progression | state.cjs |
271
+ | 8 | `state record-metric` | State Progression | state.cjs |
272
+ | 9 | `state update-progress` | State Progression | state.cjs |
273
+ | 10 | `state add-decision` | State Progression | state.cjs |
274
+ | 11 | `state add-blocker` | State Progression | state.cjs |
275
+ | 12 | `state resolve-blocker` | State Progression | state.cjs |
276
+ | 13 | `state record-session` | State Progression | state.cjs |
277
+ | 14 | `phase next-decimal` | Phase Ops | phase.cjs |
278
+ | 15 | `phase add` | Phase Ops | phase.cjs |
279
+ | 16 | `phase insert` | Phase Ops | phase.cjs |
280
+ | 17 | `phase remove` | Phase Ops | phase-remove.cjs |
281
+ | 18 | `phase complete` | Phase Ops | phase.cjs |
282
+ | 19 | `phases list` | Phase Listing | phase.cjs |
283
+ | 20 | `phase-plan-index` | Phase Listing | phase.cjs |
284
+ | 21 | `find-phase` | Phase Listing | phase.cjs |
285
+ | 22 | `roadmap get-phase` | Roadmap | roadmap.cjs |
286
+ | 23 | `roadmap analyze` | Roadmap | roadmap.cjs |
287
+ | 24 | `roadmap update-plan-progress` | Roadmap | roadmap.cjs |
288
+ | 25 | `requirements mark-complete` | Requirements | milestone.cjs |
289
+ | 26 | `milestone complete` | Milestone | milestone.cjs |
290
+ | 27 | `validate consistency` | Validation | verify.cjs |
291
+ | 28 | `validate health` | Validation | verify.cjs |
292
+ | 29 | `validate deployment` | Validation | verify-deploy.cjs |
293
+ | 30 | `verify-summary` | Verification | verify.cjs |
294
+ | 31 | `verify plan-structure` | Verification | verify.cjs |
295
+ | 32 | `verify phase-completeness` | Verification | verify.cjs |
296
+ | 33 | `verify references` | Verification | verify.cjs |
297
+ | 34 | `verify commits` | Verification | verify.cjs |
298
+ | 35 | `verify artifacts` | Verification | verify.cjs |
299
+ | 36 | `verify key-links` | Verification | verify.cjs |
300
+ | 37 | `progress` | Progress | commands.cjs |
301
+ | 38 | `next-action` | Progress | next-action.cjs |
302
+ | 39 | `context-budget` | Progress | context-budget.cjs |
303
+ | 40 | `todo complete` | Todos | commands.cjs |
304
+ | 41 | `list-todos` | Todos | commands.cjs |
305
+ | 42 | `scaffold context` | Scaffolding | commands.cjs |
306
+ | 43 | `scaffold uat` | Scaffolding | commands.cjs |
307
+ | 44 | `scaffold verification` | Scaffolding | commands.cjs |
308
+ | 45 | `scaffold phase-dir` | Scaffolding | commands.cjs |
309
+ | 46 | `frontmatter get` | Frontmatter | frontmatter.cjs |
310
+ | 47 | `frontmatter set` | Frontmatter | frontmatter.cjs |
311
+ | 48 | `frontmatter merge` | Frontmatter | frontmatter.cjs |
312
+ | 49 | `frontmatter validate` | Frontmatter | frontmatter.cjs |
313
+ | 50 | `template select` | Template | template.cjs |
314
+ | 51 | `template fill summary` | Template | template.cjs |
315
+ | 52 | `template fill plan` | Template | template.cjs |
316
+ | 53 | `template fill verification` | Template | template.cjs |
317
+ | 54 | `config-ensure-section` | Config | config.cjs |
318
+ | 55 | `config-get` | Config | config.cjs |
319
+ | 56 | `config-set` | Config | config.cjs |
320
+ | 57 | `resolve-model` | Utility | commands.cjs |
321
+ | 58 | `commit` | Utility | commands.cjs |
322
+ | 59 | `generate-slug` | Utility | commands.cjs |
323
+ | 60 | `current-timestamp` | Utility | commands.cjs |
324
+ | 61 | `verify-path-exists` | Utility | commands.cjs |
325
+ | 62 | `history-digest` | Utility | commands.cjs |
326
+ | 63 | `summary-extract` | Utility | commands.cjs |
327
+ | 64 | `websearch` | Utility | commands.cjs |
328
+ | 65 | `rollback-snapshot` | Utility | commands.cjs |
329
+ | 66 | `batch-commit` | Utility | commands.cjs |
330
+ | 67 | `estimate-cost` | Utility | commands.cjs |
331
+ | 68 | `init execute-phase` | Init | init.cjs |
332
+ | 69 | `init plan-phase` | Init | init.cjs |
333
+ | 70 | `init new-project` | Init | init.cjs |
334
+ | 71 | `init new-milestone` | Init | init.cjs |
335
+ | 72 | `init quick` | Init | init.cjs |
336
+ | 73 | `init resume` | Init | init.cjs |
337
+ | 74 | `init verify-work` | Init | init.cjs |
338
+ | 75 | `init phase-op` | Init | init.cjs |
339
+ | 76 | `init todos` | Init | init.cjs |
340
+ | 77 | `init milestone-op` | Init | init.cjs |
341
+ | 78 | `init map-codebase` | Init | init.cjs |
342
+ | 79 | `init progress` | Init | init.cjs |
343
+ | 80 | `focus scan` | Focus | focus.cjs |
344
+ | 81 | `focus plan` | Focus | focus.cjs |
345
+ | 82 | `focus sync` | Focus | focus.cjs |
346
+ | 83 | `focus exec` | Focus | focus.cjs |
347
+ | 84 | `focus auto` | Focus | focus.cjs |
348
+ | 85 | `focus design` | Focus | pan-tools.cjs (answered inline) |
349
+ | 86 | `standards list` | Standards | config.cjs |
350
+ | 87 | `standards select` | Standards | config.cjs |
351
+ | 88 | `standards remove` | Standards | config.cjs |
352
+ | 89 | `standards status` | Standards | config.cjs |
353
+ | 90 | `standards recommend` | Standards | config.cjs |
354
+ | 91 | `standards phase-track` | Standards | config.cjs |
355
+ | 92 | `standards tools` | Standards | config.cjs |
356
+ | 93 | `preflight` | Operations | verify-preflight.cjs |
357
+ | 94 | `dashboard` | Operations | state.cjs |
358
+ | 95 | `learnings extract` | Operations | commands-learnings.cjs |
359
+ | 96 | `learnings list` | Operations | commands-learnings.cjs |
360
+ | 97 | `learnings prune` | Operations | commands-learnings.cjs |
361
+ | 98 | `deps validate` | Operations | verify-preflight.cjs |
362
+ | 99 | `drift-check` | Operations | verify-drift.cjs |
363
+ | 100 | `retro` | Operations | verify-retro.cjs |
364
+ | 101 | `codebase detect-languages` | Codebase | codebase.cjs |
365
+ | 102 | `codebase analyze-imports` | Codebase | codebase.cjs |
366
+ | 103 | `codebase best-practices` | Codebase | codebase.cjs |
367
+ | 104 | `codebase estimate-size` | Spec A E-2 | codebase.cjs |
368
+ | 105 | `memory read` | Spec A E-4 | memory.cjs |
369
+ | 106 | `memory append` | Spec A E-4 | memory.cjs |
370
+ | 107 | `memory list` | Spec A E-4 | memory.cjs |
371
+ | 108 | `memory compact` | Spec A E-4 | memory.cjs |
372
+ | 109 | `cache prime` | Spec A E-1 | core.cjs |
373
+ | 110 | `focus classify-stages` | Spec A E-6 | focus.cjs |
374
+ | 111 | `focus reflection` | Spec A E-10 | focus.cjs |
375
+ | 112 | `cost report` | Spec B v2 Y-6 | cost.cjs |
376
+ | 113 | `cost append` | Spec B v2 Y-6 | cost.cjs |
377
+ | 114 | `cost clear` | Spec B v2 Y-6 | cost.cjs |
378
+ | 115 | `bus publish` | Spec B v2 Y-7 | bus.cjs |
379
+ | 116 | `bus drain` | Spec B v2 Y-7 | bus.cjs |
380
+ | 117 | `bus list` | Spec B v2 Y-7 | bus.cjs |
381
+ | 118 | `preview phase` | Spec B v2 Y-1 | preview.cjs |
382
+ | 119 | `preview phases` | Spec B v2 Y-1 | preview.cjs |
383
+ | 120 | `preview milestone` | Spec B v2 Y-1 | preview.cjs |
384
+ | 121 | `review-deep merge` | Spec B v2 Y-2 | review-deep.cjs |
385
+ | 122 | `review-deep analyze` | Spec B v2 Y-2 | review-deep.cjs |
386
+ | 123 | `knowledge ask` | Spec B v2 Y-3 | knowledge.cjs |
387
+ | 124 | `knowledge discuss` | Spec B v2 Y-3 | knowledge.cjs |
388
+ | 125 | `knowledge playbook` | Spec B v2 Y-3 | knowledge.cjs |
389
+ | 126 | `whatif prepare` | Spec B v2 Y-4 | whatif.cjs |
390
+ | 127 | `whatif report` | Spec B v2 Y-4 | whatif.cjs |
391
+ | 128 | `whatif cleanup` | Spec B v2 Y-4 | whatif.cjs |
392
+ | 129 | `optimize trace init` | Optimization | optimize.cjs |
393
+ | 130 | `optimize trace end` | Optimization | optimize.cjs |
394
+ | 131 | `optimize trace current` | Optimization | optimize.cjs |
395
+ | 132 | `optimize trace list` | Optimization | optimize.cjs |
396
+ | 133 | `optimize trace log` | Optimization | optimize.cjs |
397
+ | 134 | `optimize learn` | Optimization | optimize.cjs |
398
+ | 135 | `optimize apply` | Optimization | optimize.cjs |
399
+ | 136 | `optimize list` | Optimization | optimize.cjs |
400
+ | 137 | `optimize stats` | Optimization | optimize.cjs |
401
+ | 138 | `learn` (alias) | Optimization | optimize.cjs |
402
+ | 139 | `git commit` | Git | git.cjs |
403
+ | 140 | `git branch` | Git | git.cjs |
404
+ | 141 | `git push` | Git | git.cjs |
405
+ | 142 | `git status` | Git | git.cjs |
406
+ | 143 | `git log` | Git | git.cjs |
407
+ | 144 | `git stash` | Git | git.cjs |
408
+ | 145 | `git diff` | Git | git.cjs |
409
+ | 146 | `git rollback` | Git | git.cjs |
410
+ | 147 | `git tag` | Git | git.cjs |
411
+ | 148 | `git sync` | Git | git.cjs |
412
+ | 149 | `distill scan` | Distill | distill.cjs |
413
+ | 150 | `distill analyze` | Distill | distill.cjs |
414
+ | 151 | `distill report` | Distill | distill.cjs |
415
+ | 152 | `experiment new` | Self-Improvement Loop | experiment.cjs |
416
+ | 153 | `experiment list` | Self-Improvement Loop | experiment.cjs |
417
+ | 154 | `experiment manifest` | Self-Improvement Loop | experiment.cjs |
418
+ | 155 | `experiment run` | Self-Improvement Loop | runner.cjs |
419
+ | 156 | `experiment status` | Self-Improvement Loop | runner.cjs |
420
+ | 157 | `experiment stop` | Self-Improvement Loop | runner.cjs |
421
+ | 158 | `experiment harvest` | Self-Improvement Loop | experiment.cjs |
422
+ | 159 | `experiment prune` | Self-Improvement Loop | experiment.cjs |
423
+ | 160 | `doc-lint <dir>` | Doc-Lint | doc-lint.cjs |
424
+ | 161 | `doc-lint schema-check` | Doc-Lint | doc-lint.cjs |
425
+ | 162 | `learn promote` | Learnings | optimize.cjs |
426
+ | 163 | `learn unpromote` | Learnings | optimize.cjs |
427
+ | 164 | `learn list-promoted` | Learnings | optimize.cjs |
428
+ | 165 | `learn lint` | Learnings | learn-lint.cjs |
429
+ | 166 | `learn build-index` | Learnings | learn-index.cjs |
430
+ | 167 | `learn topics-for` | Learnings | learn-index.cjs |
431
+ | 168 | `links validate` | Doc-Code Link Graph | links.cjs |
432
+ | 169 | `squad list` | Bot Army | squads.cjs |
433
+ | 170 | `squad show` | Bot Army | squads.cjs |
434
+ | 171 | `worktree list` | Bot Army | worktree.cjs |
435
+ | 172 | `worktree create` | Bot Army | worktree.cjs |
436
+ | 173 | `worktree remove` | Bot Army | worktree.cjs |
437
+ | 174 | `worktree cleanup` | Bot Army | worktree.cjs |
438
+ | 175 | `campaign schedule` | Bot Army | campaign.cjs |
439
+ | 176 | `campaign status` | Bot Army | campaign.cjs |
440
+ | 177 | `campaign due` | Bot Army | campaign.cjs |
441
+ | 178 | `campaign record-run` | Bot Army | campaign.cjs |
442
+ | 179 | `hud` | Observability | hud.cjs |
443
+ | 180 | `models check` | Cost | cost.cjs |
444
+ | 181 | `skills index` | Planning (SAD) | skill-align.cjs |
445
+ | 182 | `skills align` | Planning (SAD) | skill-align.cjs |
446
+ | 183 | `hygiene scan` | System | hygiene.cjs |
447
+ | 184 | `hygiene clean` | System | hygiene.cjs |
448
+ | 185 | `report phase <N>` | Observability | phase-report.cjs |
449
+ | 186 | `report index` | Observability | phase-report.cjs |
450
+ | 187 | `report all` | Observability | phase-report.cjs |
451
+ | 188 | `memory optimize` | Memory | memory-optimize.cjs |
452
+ | 189 | `memory rebuild` | Memory | memory-rebuild.cjs |
453
+ | 190 | `optimize trace reconcile` | Optimization | optimize.cjs |
454
+ | 191 | `verify reconcile` | Verification | verify.cjs |
455
+ | 192 | `verify stubs` | Verification | verify.cjs |
456
+ | 193 | `memory select` | Memory | memory.cjs |
457
+ | 194 | `memory budget` | Memory | memory.cjs |
458
+ | 195 | `doc-lint counts` | Linting | doc-lint.cjs |
459
+ | 196 | `doc-lint flags` | Linting | doc-lint.cjs |
460
+ | 197 | `state compact` | State Progression | state-compact.cjs |
461
+ | 198 | `optimize trace show` | Optimization | optimize.cjs |
462
+ | 199 | `cost rebuild` | Cost | cost-rebuild.cjs |
463
+ | 200 | `findings record` | Evidence loop | findings.cjs |
464
+ | 201 | `findings list` | Evidence loop | findings.cjs |
465
+ | 202 | `findings dispose` | Evidence loop | findings.cjs |
466
+ | 203 | `findings debt` | Evidence loop | findings.cjs |
467
+ | 204 | `verify scope` | Verification | verify-scope.cjs |
468
+ | 205 | `optimize revert` | Optimization | optimize.cjs |
469
+ | 206 | `verify stale` | Verification | verify-scope.cjs |
470
+ | 207 | `version` | Utility | commands.cjs |
471
+ | 208 | `roadmap slice` | Roadmap | roadmap.cjs |
472
+ | 209 | `roadmap compact` | Roadmap | roadmap-compact.cjs |
473
+ | 210 | `memory prune` | Memory | memory.cjs |
474
+ | 211 | `memory record` | Memory | memory.cjs |
475
+ | 212 | `cost limits` | Cost | cost.cjs |
476
+ | 213 | `verify plan-set` | Verification | verify-plan-set.cjs |
477
+ | 214 | `verify test-integrity` | Verification | verify-integrity.cjs |
478
+ | 215 | `phase gate` | Phase Ops | phase-gate.cjs |
479
+ | 216 | `test run` | Verification | test-gate.cjs |
480
+ | 217 | `test detect` | Verification | test-gate.cjs |
481
+ | 218 | `verify red` | Verification | verify-red.cjs |
482
+ | 219 | `verify mutate` | Verification | mutate.cjs |
483
+ | 220 | `review scope` | Verification | verify-scope.cjs |
484
+ | 221 | `findings suppressions` | Evidence loop | findings.cjs |
485
+ | 222 | `verify review-focus` | Verification | plan-lint.cjs |
486
+ | 223 | `verify decisions` | Verification | plan-lint.cjs |
487
+ | 224 | `research lint` | Verification | research-lint.cjs |
488
+ | 225 | `state snapshot` (alias of `state-snapshot`) | State | state.cjs |
489
+ | 226 | `cost budget` | Cost | cost.cjs |
490
+ | 227 | `usage` | Observability | usage.cjs |
491
+ | 228 | `autodev run` | Autonomy | autodev.cjs |
492
+ | 229 | `autodev resume` | Autonomy | autodev.cjs |
493
+ | 230 | `autodev status` | Autonomy | autodev.cjs |
494
+ | 231 | `autodev report` | Autonomy | autodev-report.cjs |
495
+ | 232 | `autodev replay` | Autonomy | autodev-report.cjs |
496
+ | 233 | `worktree preflight` | Autonomy | worktree.cjs |
497
+ | 234 | `review cross-family` | Verification | cross-family.cjs |
498
+ | 235 | `review vote` | Verification | review-vote.cjs |
499
+ | 236 | `findings prove` | Evidence loop | findings.cjs |
500
+ | 237 | `verify gates` | Verification | plan-lint.cjs |
501
+ | 238 | `verify requirements` | Verification | requirements-lint.cjs |
502
+ | 239 | `verify traceability` | Verification | requirements-lint.cjs |
503
+ | 240 | `state init` | State | state-write.cjs |
504
+ | 241 | `state section` | State | state-write.cjs |
505
+ | 242 | `state quick-task` | State | state-write.cjs |
506
+ | 243 | `state apply` | State | state-write.cjs |
507
+ | 244 | `merge request` | Bot Army | merge.cjs |
508
+ | 245 | `merge confirm` | Bot Army | merge.cjs |
509
+ | 246 | `uat record` | Verification | uat.cjs |
510
+ | 247 | `uat status` | Verification | uat.cjs |
511
+ | 248 | `debug repro` | Verification | debug-cases.cjs |
512
+ | 249 | `debug similar` | Verification | debug-cases.cjs |
513
+ | 250 | `research check` | Verification | research-lint.cjs |
514
+
515
+ ---
516
+
517
+ ## 1. State Commands
518
+
519
+ Commands for reading and writing state.md — the central project state file.
520
+
521
+ ### `state load`
522
+
523
+ Load project config and planning state. Workflows that need no compound context call it (`/pan:settings`, `/pan:settings --profile`, `/pan:debug`); most workflows start with an `init <workflow>` call ([§17](#17-compound-init-commands)) instead.
524
+
525
+ ```bash
526
+ pan-tools state load [--raw]
527
+ pan-tools state # bare `state` is an alias for `state load`
528
+ ```
529
+
530
+ **JSON output:**
531
+ ```json
532
+ {
533
+ "contract": "1.0",
534
+ "config": { "model_profile": "balanced", "commit_docs": true, ... },
535
+ "state_raw": "<full state.md content>",
536
+ "state_exists": true,
537
+ "roadmap_exists": true,
538
+ "config_exists": true
539
+ }
540
+ ```
541
+
542
+ **`--raw` output:** Key=value lines: `model_profile=balanced`, `commit_docs=true`, etc.
543
+
544
+ **Missing state.md is not an error here:** `state load` returns `state_exists: false` with an empty `state_raw` and exits `0`. `state json`, `state get` and the state-progression commands return `{ "error": "state.md not found" }` (exit 1); `state compact` reports it as a bare `Error: … not found — nothing to compact` line on stderr (exit 1).
545
+
546
+ **Implementation:** `state.cjs → cmdStateLoad()` — reads state.md with a tolerant `fs.readFileSync` (absence is not an error).
547
+
548
+ ---
549
+
550
+ ### `state json`
551
+
552
+ Output state.md frontmatter as JSON. Returns the YAML frontmatter block parsed into a JSON object. If no frontmatter exists, it is built from the markdown body.
553
+
554
+ ```bash
555
+ pan-tools state json [--raw]
556
+ ```
557
+
558
+ **JSON output:**
559
+ ```json
560
+ {
561
+ "pan_state_version": "1.0",
562
+ "milestone": "v1.0",
563
+ "milestone_name": "Core Platform",
564
+ "current_phase": "05",
565
+ "current_phase_name": "Setup",
566
+ "current_plan": "2",
567
+ "status": "executing",
568
+ "last_updated": "2026-02-27T12:00:00.000Z",
569
+ "last_activity": "2026-02-27",
570
+ "progress": {
571
+ "total_phases": 12,
572
+ "completed_phases": 4,
573
+ "total_plans": 30,
574
+ "completed_plans": 18,
575
+ "percent": 60
576
+ },
577
+ "contract": "1.0"
578
+ }
579
+ ```
580
+
581
+ `contract` is the output's shape version (see [Output Contracts](#output-contracts-contract)), never a state.md field.
582
+
583
+ **Implementation:** `state.cjs → cmdStateJson()` — Parses YAML frontmatter first, falls back to markdown body parsing.
584
+
585
+ ---
586
+
587
+ ### `state update <field> <value>`
588
+
589
+ Update a single `**Field:** value` line in state.md.
590
+
591
+ ```bash
592
+ pan-tools state update "Current Phase" "06"
593
+ ```
594
+
595
+ **JSON output:**
596
+ ```json
597
+ { "updated": true }
598
+ ```
599
+
600
+ If the field is not found (exit 1):
601
+ ```json
602
+ { "updated": false, "reason": "Field \"Current Phase\" not found in state.md", "error": "field_not_found" }
603
+ ```
604
+
605
+ A missing state.md gives `error: "state_not_found"` (exit 1). Output is always JSON: `--raw` is not read.
606
+
607
+ **Note:** Field names match the bold markdown format (e.g., `"Current Phase"`, `"Status"`), case-insensitively; only the first matching line is updated.
608
+
609
+ ---
610
+
611
+ ### `state get [section]`
612
+
613
+ Get full state.md content, or a specific field/section.
614
+
615
+ ```bash
616
+ pan-tools state get # Full content
617
+ pan-tools state get "Status" # Extract **Status:** value
618
+ pan-tools state get "Session Continuity" # Extract the ## Session Continuity section (the whole heading text)
619
+ ```
620
+
621
+ **JSON output (full):**
622
+ ```json
623
+ { "content": "<full state.md content>" }
624
+ ```
625
+
626
+ **JSON output (field):**
627
+ ```json
628
+ { "Status": "Executing plan 3 of 5" }
629
+ ```
630
+
631
+ **`--raw` output:** The matched value or section text.
632
+
633
+ ---
634
+
635
+ ### `state patch --field val ...`
636
+
637
+ > If state.md is missing, `state patch` prints `Error: state.md not found` on stderr (exit 1) rather than a JSON body.
638
+
639
+ Batch update multiple `**Field:** value` lines in a single operation.
640
+
641
+ ```bash
642
+ pan-tools state patch --Status "Ready to execute" --"Current Plan" "3"
643
+ ```
644
+
645
+ **JSON output:**
646
+ ```json
647
+ { "updated": ["Status", "Current Plan"], "failed": [] }
648
+ ```
649
+
650
+ **Note:** Flag names are converted to field names by stripping the leading `--`. Use quotes for multi-word field names.
651
+
652
+ ---
653
+
654
+ ### `state-snapshot`
655
+
656
+ Structured parse of state.md into a comprehensive JSON object. Unlike `state json` (which returns frontmatter), this parses the full markdown body including decisions, blockers, and session info.
657
+
658
+ ```bash
659
+ pan-tools state-snapshot
660
+ pan-tools state snapshot # equivalent alias — the spaced form dispatches to the same handler
661
+ ```
662
+
663
+ **JSON output:**
664
+ ```json
665
+ {
666
+ "current_phase": "05",
667
+ "current_phase_name": "Setup",
668
+ "total_phases": 12,
669
+ "current_plan": "2",
670
+ "total_plans_in_phase": 4,
671
+ "status": "Executing",
672
+ "progress_percent": 60,
673
+ "last_activity": "2026-02-27",
674
+ "last_activity_desc": "Completed plan 1 of phase 5",
675
+ "decisions": [{ "phase": "04", "summary": "...", "rationale": "..." }],
676
+ "blockers": ["Waiting on API key"],
677
+ "paused_at": null,
678
+ "session": {
679
+ "last_date": "2026-02-27T12:00:00.000Z",
680
+ "stopped_at": "Finished task 3",
681
+ "resume_file": "None"
682
+ }
683
+ }
684
+ ```
685
+
686
+ **Difference from `state json`:** `state json` returns YAML frontmatter fields. `state-snapshot` parses the full markdown body into a structured object with decisions, blockers, and session data extracted from markdown sections.
687
+
688
+ ---
689
+
690
+ ## 2. State Progression
691
+
692
+ Commands for advancing workflow state — plan counters, metrics, decisions, blockers, and session continuity.
693
+
694
+ ### `state compact [--apply] [--keep-days N]`
695
+
696
+ Move settled history out of `state.md` into `state-history.md`. See [ADR-0044](decisions/ADR-0044-bound-the-cached-context.md).
697
+
698
+ `state.md` is in the cached context block, so **every byte is re-read into every agent call** — and its section writers only append, so the file can only grow. In the field this reached 54 KB, of which 30 KB was a ten-week-old session log, a resolved milestone audit, and three phase closures: ~7k tokens of finished work re-read on every call.
699
+
700
+ ```bash
701
+ pan-tools state compact # dry-run: what would move, and what it saves
702
+ pan-tools state compact --apply
703
+ pan-tools state compact --keep-days 90
704
+ ```
705
+
706
+ **What moves:** a section whose heading is dated past the retention window (default 30 days), or whose heading says "closure"/"closed".
707
+
708
+ **What never moves:**
709
+ - headings PAN reads or writes (matched as heading prefixes: Decisions, Accumulated…, Blockers, Concerns, Session, Next Action, Phase Progress, Project Reference, Source Authority, Toolchain, Metrics, Current…, Status) — `state get <section>` can read any heading by name
710
+ - any section carrying a field the frontmatter is rebuilt from (`**Status:**`, `**Current Phase:**`, `**Progress:**`, …), because `syncStateFrontmatter` regenerates frontmatter from the **first** such match in the body
711
+ - anything unrecognised — the classifier declines rather than guesses
712
+ - everything, when archiving would not actually shrink the file (on a small `state.md` the pointer costs more than the section)
713
+
714
+ **Safety:** nothing is deleted. History is appended to `state-history.md` **first**, then `state.md` is rewritten with a pointer where each archived section stood — an interruption can only duplicate, never lose. The dry-run's byte count comes from the same rebuild `--apply` uses.
715
+
716
+ **Key output fields:** `sections[]` (each with `title`, `bytes`, `archive`, `reason`), `archivable[]`, `bytes_before` / `bytes_after`, `tokens_before` / `tokens_after`, `tokens_saved_per_call`.
717
+
718
+ ### `state advance-plan`
719
+
720
+ Increment the current plan counter. If the last plan is reached, set status to "ready for verification".
721
+
722
+ ```bash
723
+ pan-tools state advance-plan [--raw]
724
+ ```
725
+
726
+ **JSON output (advanced):**
727
+ ```json
728
+ { "advanced": true, "previous_plan": 2, "current_plan": 3, "total_plans": 5 }
729
+ ```
730
+
731
+ **JSON output (last plan):**
732
+ ```json
733
+ { "advanced": false, "reason": "last_plan", "current_plan": 5, "total_plans": 5, "status": "ready_for_verification" }
734
+ ```
735
+
736
+ **`--raw` output:** `true` or `false`.
737
+
738
+ ---
739
+
740
+ ### `state record-metric --phase N --plan M --duration Xmin [--tasks N] [--files N]`
741
+
742
+ Record execution metrics in the Performance Metrics table in state.md.
743
+
744
+ ```bash
745
+ pan-tools state record-metric --phase 5 --plan 1 --duration 12min --tasks 4 --files 6
746
+ ```
747
+
748
+ **Flags (all required unless noted):**
749
+ - `--phase N` — Phase number
750
+ - `--plan M` — Plan number
751
+ - `--duration Xmin` — Duration string (e.g., `12min`, `2h`)
752
+ - `--tasks N` — Task count (optional)
753
+ - `--files N` — Files modified count (optional)
754
+
755
+ **JSON output:**
756
+ ```json
757
+ { "recorded": true, "phase": "5", "plan": "1", "duration": "12min" }
758
+ ```
759
+
760
+ ---
761
+
762
+ ### `state update-progress`
763
+
764
+ Recalculate and update the progress bar in state.md based on SUMMARY/PLAN counts across all phases.
765
+
766
+ ```bash
767
+ pan-tools state update-progress [--raw]
768
+ ```
769
+
770
+ **JSON output:**
771
+ ```json
772
+ {
773
+ "updated": true,
774
+ "percent": 60,
775
+ "completed": 18,
776
+ "total": 30,
777
+ "bar": "[██████░░░░] 60%"
778
+ }
779
+ ```
780
+
781
+ **`--raw` output:** The progress bar string.
782
+
783
+ ---
784
+
785
+ ### `state add-decision --summary "..." [--phase N] [--rationale "..."] [--summary-file path] [--rationale-file path]`
786
+
787
+ Add a decision entry to the Decisions section in state.md.
788
+
789
+ ```bash
790
+ pan-tools state add-decision --summary "Use PostgreSQL" --phase 5 --rationale "Better JSON support"
791
+ pan-tools state add-decision --summary-file /tmp/decision.txt --phase 5
792
+ ```
793
+
794
+ **Flags:**
795
+ - `--summary "..."` — Decision summary text (or use `--summary-file`)
796
+ - `--phase N` — Phase number (optional)
797
+ - `--rationale "..."` — Rationale text (or use `--rationale-file`)
798
+ - `--summary-file path` — Read summary from file (avoids shell escaping issues with `$` signs)
799
+ - `--rationale-file path` — Read rationale from file
800
+
801
+ **JSON output:**
802
+ ```json
803
+ { "added": true, "decision": "- [Phase 5]: Use PostgreSQL — Better JSON support" }
804
+ ```
805
+
806
+ **Note:** Use `--summary-file` and `--rationale-file` for text containing shell-sensitive characters like `$`.
807
+
808
+ ---
809
+
810
+ ### `state add-blocker --text "..." | --text-file path`
811
+
812
+ Add a blocker to the Blockers section in state.md (`## Blockers`, `Blockers/Concerns` or `Concerns`), removing a `None yet` placeholder. Either flag gives the text; `--text-file` wins when both are passed. A missing section exits 1 with `error: "blockers_section_missing"`; `--raw` prints `true` or `false`.
813
+
814
+ ```bash
815
+ pan-tools state add-blocker --text "Waiting on API credentials"
816
+ pan-tools state add-blocker --text-file /tmp/blocker.txt
817
+ ```
818
+
819
+ **JSON output:**
820
+ ```json
821
+ { "added": true, "blocker": "Waiting on API credentials" }
822
+ ```
823
+
824
+ ---
825
+
826
+ ### `state resolve-blocker --text "..."`
827
+
828
+ Remove a blocker from the Blockers section by matching text (case-insensitive substring match).
829
+
830
+ ```bash
831
+ pan-tools state resolve-blocker --text "API credentials"
832
+ ```
833
+
834
+ **JSON output:**
835
+ ```json
836
+ { "resolved": true, "blocker": "API credentials" }
837
+ ```
838
+
839
+ If no match: `{ "resolved": false, "reason": "no matching blocker", "blocker": "API credentials", "error": "blocker_not_matched" }` — exit 1.
840
+
841
+ ---
842
+
843
+ ### `state record-session --stopped-at "..." [--resume-file path]`
844
+
845
+ Update session continuity fields in state.md (Last session, Stopped At, Resume File). **Side effect:** after a successful write this is the normal-flow memory checkpoint — `memory optimize` runs automatically unless `memory.auto_optimize` is `false`.
846
+
847
+ ```bash
848
+ pan-tools state record-session --stopped-at "Finished plan 3, starting plan 4"
849
+ pan-tools state record-session --stopped-at "Mid-task" --resume-file .planning/phases/05-setup/05-02-plan.md
850
+ ```
851
+
852
+ **Flags:**
853
+ - `--stopped-at "..."` — Description of where work stopped
854
+ - `--resume-file path` — File to resume from (default: `None`)
855
+
856
+ **JSON output:**
857
+ ```json
858
+ { "recorded": true, "updated": ["Last session", "Stopped At", "Resume File"] }
859
+ ```
860
+
861
+ The fields are `**Field:** value` lines (`Last session` or `Last Date`, `Stopped At`, `Resume File`); only the ones present are updated. When none is present it prints `{ "recorded": false, "reason": "No session fields found in state.md", "error": "session_fields_missing" }` and exits 1. `--raw` prints `true` or `false`.
862
+
863
+ ### `state init` · `state section <name>` · `state quick-task` · `state apply` (program A7b)
864
+
865
+ The writers of state.md as a whole or by section; every shipped writer goes through them, so the pre-tool guard's `enforce` can deny a direct write.
866
+
867
+ ```bash
868
+ pan-tools state init [--core-value V] [--focus F] [--phase N] [--phase-name S] [--total-phases N] [--status S] [--activity A] [--force] [--keep-context]
869
+ pan-tools state section "<name>" (--append "<line>" | --set "<text>") [--under "<section>"]
870
+ pan-tools state quick-task --description D --commit SHA --directory DIR [--status S] [--date YYYY-MM-DD]
871
+ pan-tools state apply --json '{"fields": {...}, "sections": {"Name": {"append": [...]} | {"set": "..."}}}'
872
+ ```
873
+
874
+ `init` writes a fresh state.md from the template's sections and refuses over an existing one (`state_exists`) unless `--force` (a backup is kept; `--keep-context` carries the old context over): `{written, path, backup, kept_context}`. `section` appends to or sets one section, creating it when missing. `quick-task` adds one row to the Quick Tasks Completed table (`--raw`: the row number). `apply` writes bold fields and sections in one write (`{written, updated[], failed[]}`). All but `init` need state.md (`state_missing`).
875
+
876
+ **Module:** `state-write.cjs`
877
+
878
+ ---
879
+
880
+ ## 3. Phase Operations
881
+
882
+ Commands for managing phase lifecycle — adding, inserting, removing, and completing phases.
883
+
884
+ ### `phase next-decimal <phase>`
885
+
886
+ Calculate the next available decimal phase number for inserting a sub-phase.
887
+
888
+ ```bash
889
+ pan-tools phase next-decimal 5 [--raw]
890
+ ```
891
+
892
+ **JSON output:**
893
+ ```json
894
+ {
895
+ "found": true,
896
+ "base_phase": "05",
897
+ "next": "05.1",
898
+ "existing": []
899
+ }
900
+ ```
901
+
902
+ If decimals exist: `{ ..., "next": "05.3", "existing": ["05.1", "05.2"] }`.
903
+
904
+ **`--raw` output:** The next decimal (e.g., `05.1`).
905
+
906
+ ---
907
+
908
+ ### `phase add <description>`
909
+
910
+ Append a new phase to roadmap.md and create the corresponding directory on disk.
911
+
912
+ ```bash
913
+ pan-tools phase add "API Integration Layer" [--raw]
914
+ ```
915
+
916
+ **JSON output:**
917
+ ```json
918
+ {
919
+ "phase_number": 13,
920
+ "padded": "13",
921
+ "name": "API Integration Layer",
922
+ "slug": "api-integration-layer",
923
+ "directory": ".planning/phases/13-api-integration-layer"
924
+ }
925
+ ```
926
+
927
+ **`--raw` output:** The padded phase number.
928
+
929
+ **Side effects:** Creates `.planning/phases/NN-slug/` (with a `.gitkeep`), appends a new `### Phase N:` detail section to roadmap.md, and adds a `- [ ] **Phase N: <name>**` checklist entry when the roadmap has a checklist.
930
+
931
+ ---
932
+
933
+ ### `phase insert <after> <description>`
934
+
935
+ Insert a decimal phase after an existing phase. Creates the directory and updates roadmap.md.
936
+
937
+ ```bash
938
+ pan-tools phase insert 5 "Emergency Hotfix" [--raw]
939
+ ```
940
+
941
+ **JSON output:**
942
+ ```json
943
+ {
944
+ "phase_number": "05.1",
945
+ "after_phase": "5",
946
+ "name": "Emergency Hotfix",
947
+ "slug": "emergency-hotfix",
948
+ "directory": ".planning/phases/05.1-emergency-hotfix"
949
+ }
950
+ ```
951
+
952
+ **`--raw` output:** The decimal phase number (e.g., `05.1`).
953
+
954
+ ---
955
+
956
+ ### `phase remove <phase> [--force] [--with-decimals]`
957
+
958
+ Remove a phase, delete its directory, and renumber all subsequent phases. Also updates roadmap.md and state.md. Removing a decimal phase renumbers its higher siblings (5.3 becomes 5.2).
959
+
960
+ An integer phase that still has decimal phases inserted after it is refused, with their numbers in the error, and nothing changes: removed alone, they would be left behind and read as decimals of the phase renumbered into its place.
961
+
962
+ ```bash
963
+ pan-tools phase remove 7
964
+ pan-tools phase remove 5.2 --force # Force removal even if summaries exist
965
+ pan-tools phase remove 5 --with-decimals # Remove phase 5 and its 5.1, 5.2 …
966
+ ```
967
+
968
+ **Arguments:**
969
+ - `<phase>` — Phase number to remove
970
+ - `--force` — Allow removal of phases with executed plans (summary.md files), the decimals included
971
+ - `--with-decimals` — Also remove the integer phase's decimal phases; the output lists them as `removed_decimals`
972
+
973
+ **JSON output:**
974
+ ```json
975
+ {
976
+ "removed": "7",
977
+ "directory_deleted": "07-feature-name",
978
+ "renamed_directories": [
979
+ { "from": "08-next-feature", "to": "07-next-feature" }
980
+ ],
981
+ "renamed_files": [
982
+ { "from": "08-01-plan.md", "to": "07-01-plan.md" }
983
+ ],
984
+ "roadmap_updated": true,
985
+ "state_updated": true
986
+ }
987
+ ```
988
+
989
+ **Warning:** Without `--force`, removal is blocked if the phase has any summary.md files (indicating completed work).
990
+
991
+ ---
992
+
993
+ ### `phase gate <phase> [--mode warn|enforce]`
994
+
995
+ Whether a phase may be marked complete (program V1). It prints `{phase, mode, ok, reasons[], facts, not_checked[]}` and exits 1 when it refuses (`--raw`: `pass` or `refuse`). Reason codes: `no_plans`, `missing_summary`, `no_verification`, `verification_unreadable`, `verification_failed` (a failed test gate under `passed` counts), `human_needed_unapproved` (approve a human item with `findings dispose <id> --as decision --reason "…"`), `verification_stale`, `reconcile_contradiction`, `test_failed`, `open_findings` (critical or high, any judge), `stubs_found` (in the files the phase committed), `integrity_violation` (`verify test-integrity`). `not_checked` lists what it could not check, never a refusal: a skipped or unrecorded test gate, an unknown freshness, no git, no plan commits, the verifier switched off. `--mode` overrides `workflow.phase_gate` for this call. The mode changes what `phase complete` does with a refusal, not the gate's own verdict: `phase gate` exits 1 and prints `refuse` on a refusal under `warn` too.
996
+
997
+ ### `phase complete <phase> [--no-commit] [--force --reason "<why>"]`
998
+
999
+ Mark a phase as done. Updates roadmap.md checkboxes and progress table, advances state.md to the next phase, and updates requirements.md traceability.
1000
+
1001
+ **The phase gate runs first.** Under `workflow.phase_gate: enforce` (the default; a config without the key, or with any other value, reads as `enforce`) a refusal exits 1 with `{error: "phase_gate_refused", reason, gate}` and changes nothing. Under `warn` the phase is marked complete and the output carries `gate: {ok, mode, reasons, not_checked}`. `--force --reason "<why>"` completes past a refusal and records a `decision` disposition for every open finding the gate counted (output: `gate_override`); `--force` without `--reason` is refused in either mode.
1002
+
1003
+ ```bash
1004
+ pan-tools phase complete 5 [--no-commit]
1005
+ # Auto-commits .planning/ ("docs(NN): complete phase — <name>") and reports commit_hash,
1006
+ # unless --no-commit is passed, commit_docs is false, the planning tree is gitignored, or cwd is not a git repo.
1007
+ ```
1008
+
1009
+ **JSON output:**
1010
+ ```json
1011
+ {
1012
+ "completed_phase": "5",
1013
+ "phase_name": "setup",
1014
+ "plans_executed": "3/3",
1015
+ "next_phase": "06",
1016
+ "next_phase_name": "api-layer",
1017
+ "is_last_phase": false,
1018
+ "date": "2026-02-27",
1019
+ "roadmap_updated": true,
1020
+ "state_updated": true,
1021
+ "gate": { "ok": true, "mode": "warn", "reasons": [], "not_checked": [] }
1022
+ }
1023
+ ```
1024
+
1025
+ `commit_hash`, `gate_override` (`{reason, refused_by[], disposed[]}`: the `--reason` text, the refusal codes it overrode, and the findings it recorded a `decision` for), `roadmap_warning` and `requirements_warning` are added when they apply. `gate` is `{error}` when the gate itself could not run.
1026
+
1027
+ **Side effects:** Checks off the phase checkbox in roadmap.md, updates the progress table row, advances `Current Phase` in state.md, and marks the requirements on the phase's own `**Requirements:**` line complete in requirements.md (checkbox and traceability row), reading the phase's section or, in an older roadmap, the line under its checklist entry. Both padded and unpadded phase spellings tick the same lines (`phase complete 01` and `phase complete 1` are equivalent).
1028
+
1029
+ **When the tick cannot land** (roadmap unreadable, no checklist entry names the phase, or the write fails), the result carries a `roadmap_warning` string describing why and `roadmap_updated` is `false` — the completion is never silently unrecorded. A `requirements_warning` string appears when requirements.md exists but could not be written; `roadmap_updated` is unaffected by it.
1030
+
1031
+ ---
1032
+
1033
+ ## 4. Phase Listing
1034
+
1035
+ Commands for discovering phase directories and their contents.
1036
+
1037
+ ### `phases list [--type plans|summaries] [--phase N] [--include-archived]`
1038
+
1039
+ List phase directories or files within phases.
1040
+
1041
+ ```bash
1042
+ pan-tools phases list [--raw]
1043
+ pan-tools phases list --type plans --phase 5 [--raw]
1044
+ pan-tools phases list --include-archived [--raw]
1045
+ ```
1046
+
1047
+ **Flags:**
1048
+ - `--type plans|summaries` — List files of a specific type instead of directories
1049
+ - `--phase N` — Filter to a specific phase
1050
+ - `--include-archived` — Include phases from archived milestones
1051
+
1052
+ **JSON output (directories):**
1053
+ ```json
1054
+ { "directories": ["05-setup", "06-api-layer", "07-auth"], "count": 3 }
1055
+ ```
1056
+
1057
+ **JSON output (files):**
1058
+ ```json
1059
+ { "files": ["05-01-plan.md", "05-02-plan.md"], "count": 2, "phase_dir": "setup" }
1060
+ ```
1061
+
1062
+ `phase_dir` is the matched directory's name without its number, and `null` without `--phase`. A `--phase` that matches nothing exits 1 with `error: "Phase not found"`. With `--include-archived`, an archived entry reads `"<dir> [<milestone>]"`.
1063
+
1064
+ **`--raw` output:** Newline-separated list.
1065
+
1066
+ ---
1067
+
1068
+ ### `phase-plan-index <phase>`
1069
+
1070
+ Index all plans within a phase, grouped by wave, with completion status. Used by the execute-phase workflow to determine which plans to run and in what order.
1071
+
1072
+ ```bash
1073
+ pan-tools phase-plan-index 5
1074
+ pan-tools phase-plan-index 5 --failed 05-01,05-02
1075
+ ```
1076
+
1077
+ **JSON output:**
1078
+ ```json
1079
+ {
1080
+ "phase": "05",
1081
+ "plans": [
1082
+ {
1083
+ "id": "05-01",
1084
+ "wave": 1,
1085
+ "autonomous": true,
1086
+ "objective": "Set up database schema",
1087
+ "files_modified": ["src/db/schema.ts"],
1088
+ "depends_on": [],
1089
+ "task_count": 3,
1090
+ "has_summary": true
1091
+ }
1092
+ ],
1093
+ "waves": { "1": ["05-01", "05-02"], "2": ["05-03"] },
1094
+ "incomplete": ["05-03"],
1095
+ "has_checkpoints": false
1096
+ }
1097
+ ```
1098
+
1099
+ **`--failed <ids>`** (comma-separated plan ids) adds `failed` and `blocked`. `blocked` is `[{id, blocked_by[]}]`: every unfinished plan whose `depends_on` reaches a failed plan, directly or through another blocked plan, with the failed plans behind it. A `depends_on` entry may be the full id (`05-01`) or the plan number (`01`). `/pan:exec-phase` uses it after a failed plan, so continuing never runs a plan whose dependency failed.
1100
+
1101
+ **Note:** Plans within the same wave can execute in parallel (via parallel agents). Plans in wave 2 depend on wave 1 completion.
1102
+
1103
+ ---
1104
+
1105
+ ### `find-phase <phase>`
1106
+
1107
+ Find a phase directory by number. Searches `.planning/phases/` only; archived milestone phases are not consulted (use `phases list --include-archived` to see them).
1108
+
1109
+ ```bash
1110
+ pan-tools find-phase 5 [--raw]
1111
+ pan-tools find-phase 12A.1 [--raw]
1112
+ ```
1113
+
1114
+ **JSON output:**
1115
+ ```json
1116
+ {
1117
+ "found": true,
1118
+ "directory": ".planning/phases/05-setup",
1119
+ "phase_number": "05",
1120
+ "phase_name": "setup",
1121
+ "plans": ["05-01-plan.md", "05-02-plan.md"],
1122
+ "summaries": ["05-01-summary.md"]
1123
+ }
1124
+ ```
1125
+
1126
+ **`--raw` output:** The directory path (e.g., `.planning/phases/05-setup`) or empty string if not found.
1127
+
1128
+ ---
1129
+
1130
+ ## 5. Roadmap Operations
1131
+
1132
+ Commands for reading and updating roadmap.md — the project phase plan.
1133
+
1134
+ ### `roadmap get-phase <phase>`
1135
+
1136
+ Extract a phase section from roadmap.md. The number matches padded or not: `5` and `05` (the padded `phase_number` `init` returns) both find `### Phase 5:`.
1137
+
1138
+ ```bash
1139
+ pan-tools roadmap get-phase 5 [--raw]
1140
+ ```
1141
+
1142
+ **JSON output:**
1143
+ ```json
1144
+ {
1145
+ "found": true,
1146
+ "phase_number": "5",
1147
+ "phase_name": "Database Setup",
1148
+ "goal": "Establish schema and migrations",
1149
+ "success_criteria": ["All tables created", "Seed data loaded"],
1150
+ "section": "### Phase 5: Database Setup\n\n**Goal:** ..."
1151
+ }
1152
+ ```
1153
+
1154
+ **`--raw` output:** The full markdown section text.
1155
+
1156
+ `phase_number` echoes the argument as given. A phase not in the roadmap returns `{found: false, phase_number}` (exit 0); a phase listed in the checklist with no `### Phase N:` section exits 1 with `error: "malformed_roadmap"`; a missing roadmap.md exits 1.
1157
+
1158
+ **A compacted phase** (one `roadmap compact` moved out) leaves a stub in roadmap.md. For it, `get-phase` returns the full section and its success criteria from `roadmap-history.md`, and adds `"compacted": true`, `"history_path"` and `"history_found"`. If the history file is gone, you get the stub's goal and `"history_found": false`. `roadmap slice` and `report phase` read a compacted phase the same way.
1159
+
1160
+ ---
1161
+
1162
+ ### `roadmap analyze`
1163
+
1164
+ Full roadmap parse with on-disk status for every phase. This is the most comprehensive view of project progress.
1165
+
1166
+ ```bash
1167
+ pan-tools roadmap analyze
1168
+ ```
1169
+
1170
+ **JSON output:**
1171
+ ```json
1172
+ {
1173
+ "milestones": [{ "heading": "v1.0 Core Platform", "version": "v1.0" }],
1174
+ "phases": [
1175
+ {
1176
+ "number": "5",
1177
+ "name": "Database Setup",
1178
+ "goal": "Establish schema",
1179
+ "depends_on": "Phase 4",
1180
+ "plan_count": 3,
1181
+ "summary_count": 3,
1182
+ "has_context": true,
1183
+ "has_research": false,
1184
+ "disk_status": "complete",
1185
+ "roadmap_complete": true
1186
+ }
1187
+ ],
1188
+ "phase_count": 12,
1189
+ "completed_phases": 4,
1190
+ "total_plans": 30,
1191
+ "total_summaries": 18,
1192
+ "progress_percent": 60,
1193
+ "current_phase": "5",
1194
+ "next_phase": "6",
1195
+ "missing_phase_details": null
1196
+ }
1197
+ ```
1198
+
1199
+ **Disk status values:** `no_directory`, `empty`, `discussed`, `researched`, `planned`, `partial`, `complete`.
1200
+
1201
+ ---
1202
+
1203
+ ### `roadmap update-plan-progress <phase>`
1204
+
1205
+ Update the progress table row and the `**Plans:**` count in roadmap.md for a specific phase from the on-disk PLAN/SUMMARY counts (the number matches padded or not); when the phase is complete it also ticks the phase checkbox (with the completion date), and it ticks each plan's own checkbox whose summary exists.
1206
+
1207
+ The table cells are found **by header name**: the row whose first cell names the phase, in a table whose header has a Plans column and a Status column, gets those two cells (and a Completed column's date) rewritten; every other cell is left as it was. A table without Plans and Status columns is not touched — the output says `table_updated: false` with the reason in `table_reason`. The `**Plans:**` line is rewritten only inside the phase's own section, never the next phase's.
1208
+
1209
+ ```bash
1210
+ pan-tools roadmap update-plan-progress 5 [--raw]
1211
+ ```
1212
+
1213
+ **JSON output:**
1214
+ ```json
1215
+ {
1216
+ "updated": true,
1217
+ "phase": "5",
1218
+ "plan_count": 3,
1219
+ "summary_count": 3,
1220
+ "status": "Complete",
1221
+ "complete": true,
1222
+ "table_updated": true
1223
+ }
1224
+ ```
1225
+
1226
+ **`--raw` output:** `3/3 Complete` (plus ` (progress table not updated)` when no table row was rewritten).
1227
+
1228
+ ---
1229
+
1230
+ ### `roadmap slice <phase> [--write]`
1231
+
1232
+ The slice is what work on one phase needs from roadmap.md and requirements.md:
1233
+ - **Every phase:** one line each, taken from the roadmap's checklist (any box mark), with this phase marked.
1234
+ - **This phase:** its section.
1235
+ - **Dependencies:** the goals of the phases it depends on.
1236
+ - **Its requirements:** the requirement lines it names, with their detail lines and their traceability rows under the table header.
1237
+
1238
+ plan-phase (its `--research-only` form too), discuss-phase, exec-phase, verify-phase and the phase transition write it. The researcher, planner, plan checker, executors and verifier read it in place of the whole files, which a long project otherwise re-reads on every turn of every spawn. On a 54-phase field project that was ~146k tokens down to ~3k. Plans name the slice in their `<context>`; an executor given an older plan that names `@.planning/roadmap.md` reads the slice instead.
1239
+
1240
+ Requirement ids are taken by shape — a `CAT-01`-style token anywhere on the `**Requirements**:` line, or a comma-separated item that is a single token carrying a digit. So a line written as prose still yields its ids.
1241
+
1242
+ ```bash
1243
+ pan-tools roadmap slice 5 # the slice and its sizes
1244
+ pan-tools roadmap slice 5 --write # write <phase dir>/05-roadmap-slice.md
1245
+ ```
1246
+
1247
+ **JSON output:**
1248
+ ```json
1249
+ {
1250
+ "found": true,
1251
+ "phase_number": "5",
1252
+ "phase_name": "Payments",
1253
+ "requirement_ids": ["PAY-01", "PAY-02"],
1254
+ "missing_requirement_ids": [],
1255
+ "depends_on": ["4"],
1256
+ "tokens": 812,
1257
+ "whole_tokens": 9650,
1258
+ "smaller_pct": 92,
1259
+ "content": "# Roadmap slice — Phase 5: Payments\n…"
1260
+ }
1261
+ ```
1262
+
1263
+ **With `--write`:**
1264
+ - Drops `content` and adds `written`, plus `changed` and `path` when it wrote; with no phase directory yet it is `written: false` with a `reason`.
1265
+ - `--raw` prints the path. It prints nothing when nothing was written (no phase directory yet), and the workflows then fall back to the whole files.
1266
+ - A run that would produce the same slice does not rewrite the file, and a CRLF checkout of it keeps CRLF.
1267
+
1268
+ **Not found:** `{found: false, phase_number}` (exit 0) when roadmap.md has no section for the phase; a missing roadmap.md exits 1. Without `--write`, `--raw` prints the slice markdown.
1269
+
1270
+ ---
1271
+
1272
+ ### `roadmap compact [--apply] [--keep N]`
1273
+
1274
+ Moves shipped phases' detail out of roadmap.md into `roadmap-history.md`, the way `state compact` bounds state.md. A roadmap only ever grows; one 54-phase field project's reached 476 KB.
1275
+
1276
+ **What moves:** a phase whose checklist line is ticked, unless it is the current phase in state.md or one of the `--keep` most recently shipped phases (default 2). The current phase never takes one of those slots.
1277
+
1278
+ **What stays:** a stub with the heading, the `**Goal**`, `**Depends on**` and `**Requirements**` lines and any `<!-- model_tier: … -->` override, plus a pointer to the history. `roadmap analyze` and the dependency goals in a `roadmap slice` read the stub as before. Readers that need the rest (`roadmap get-phase`, `report phase`, a slice of that phase) find the stub and read the full section from the history.
1279
+
1280
+ **Safety:**
1281
+ - **Nothing is lost.** The history is written before roadmap.md, so an interrupted run can duplicate a section but never lose one.
1282
+ - **No double moves.** A section already compacted is never compacted again.
1283
+ - **No growth.** A compaction that would not shrink the file is declined.
1284
+ - **Boundaries hold.** A section ends at the next phase heading, the next heading at its level or above, or a `<details>` boundary, so a trailing progress table or a milestone's `</details>` stays where it is.
1285
+ - **Line endings are kept.** The file is parsed on LF and written back in its own ending.
1286
+
1287
+ Dry run by default. `hygiene scan` offers it as the `compact-roadmap` fix when roadmap.md is over the per-file cached-context size (`CACHE_FILE_WARN_TOKENS`, about 6k tokens) and there is something to move, and `hygiene clean --apply` runs it.
1288
+
1289
+ ```bash
1290
+ pan-tools roadmap compact # what would move
1291
+ pan-tools roadmap compact --apply # move it
1292
+ pan-tools roadmap compact --apply --keep 0
1293
+ ```
1294
+
1295
+ **JSON output (abridged):**
1296
+ ```json
1297
+ {
1298
+ "found": true,
1299
+ "history_path": ".planning/roadmap-history.md",
1300
+ "keep": 2,
1301
+ "current_phase": "5",
1302
+ "archivable": [{ "phase": "1", "title": "Base", "bytes": 1412, "archive": true, "reason": "shipped" }],
1303
+ "tokens_before": 120951,
1304
+ "tokens_after": 38683,
1305
+ "applied": false,
1306
+ "dry_run": true
1307
+ }
1308
+ ```
1309
+
1310
+ ---
1311
+
1312
+ ## 6. Requirements Operations
1313
+
1314
+ ### `requirements mark-complete <ids>`
1315
+
1316
+ Mark requirement IDs as complete in requirements.md (updates checkboxes and traceability table).
1317
+
1318
+ ```bash
1319
+ pan-tools requirements mark-complete REQ-01,REQ-02
1320
+ pan-tools requirements mark-complete REQ-01 REQ-02
1321
+ pan-tools requirements mark-complete "[REQ-01, REQ-02]"
1322
+ ```
1323
+
1324
+ **Accepts:** Comma-separated, space-separated, or bracket-wrapped formats.
1325
+
1326
+ **JSON output:**
1327
+ ```json
1328
+ {
1329
+ "updated": true,
1330
+ "marked_complete": ["REQ-01", "REQ-02"],
1331
+ "not_found": [],
1332
+ "total": 2
1333
+ }
1334
+ ```
1335
+
1336
+ **`--raw` output:** `2/2 requirements marked complete`. An id that is already complete has nothing left to tick and is listed under `not_found` too (`updated: false` when no id changed).
1337
+
1338
+ ---
1339
+
1340
+ ## 7. Milestone Operations
1341
+
1342
+ ### `milestone complete <version> [--name <name>] [--archive-phases] [--no-commit]`
1343
+
1344
+ Archive a milestone: gathers stats, copies roadmap.md and requirements.md into `milestones/` (the originals stay), creates/appends milestones.md, and marks the milestone complete in state.md. Auto-commits `.planning/` and creates the git tag `milestone-<version>` (payload `commit_hash`, `tag`) unless `--no-commit` is passed, `commit_docs` is false, the planning tree is gitignored, or cwd is not a git repo.
1345
+
1346
+ ```bash
1347
+ pan-tools milestone complete v1.0 --name "Core Platform" --archive-phases
1348
+ ```
1349
+
1350
+ **Arguments:**
1351
+ - `<version>` — Milestone version (e.g., `v1.0`)
1352
+ - `--name <name>` — Human-readable milestone name (multi-word supported)
1353
+ - `--archive-phases` — Move phase directories to `milestones/vX.Y-phases/`
1354
+ - `--no-commit` — Skip the auto-commit and the tag
1355
+
1356
+ `name` defaults to the version when `--name` is not given.
1357
+
1358
+ **JSON output:**
1359
+ ```json
1360
+ {
1361
+ "version": "v1.0",
1362
+ "name": "Core Platform",
1363
+ "date": "2026-02-27",
1364
+ "phases": 12,
1365
+ "plans": 30,
1366
+ "tasks": 85,
1367
+ "accomplishments": ["Built auth module", "Created API layer"],
1368
+ "archived": {
1369
+ "roadmap": true,
1370
+ "requirements": true,
1371
+ "audit": false,
1372
+ "phases": true
1373
+ },
1374
+ "milestones_updated": true,
1375
+ "state_updated": true
1376
+ }
1377
+ ```
1378
+
1379
+ **Side effects:** Creates `milestones/` directory, copies roadmap.md and requirements.md there with a version prefix (the requirements copy gets an archive header), optionally moves phase dirs, moves `<version>-milestone-audit.md` into the archive when present (`archived.audit`), sets state.md's `Status` to `<version> milestone complete` and updates `Last Activity` and `Last Activity Description` (nothing else in the body changes; the YAML front matter is re-derived from the body, and written if missing, as on every state.md write); archive failures are reported in `archive_warnings[]`.
1380
+
1381
+ ---
1382
+
1383
+ ## 8. Validation
1384
+
1385
+ Commands for checking project health and consistency.
1386
+
1387
+ ### `validate consistency`
1388
+
1389
+ **Exit code:** always `0` — `passed: false` is a verdict, not an error-family key; gate on `passed`.
1390
+
1391
+ Check phase numbering, disk/roadmap synchronization, plan numbering gaps, and orphaned summaries.
1392
+
1393
+ ```bash
1394
+ pan-tools validate consistency [--raw]
1395
+ ```
1396
+
1397
+ **JSON output:**
1398
+ ```json
1399
+ {
1400
+ "passed": true,
1401
+ "errors": [],
1402
+ "warnings": [
1403
+ "Gap in phase numbering: 5 → 7",
1404
+ "Phase 3 in roadmap.md but no directory on disk"
1405
+ ],
1406
+ "warning_count": 2
1407
+ }
1408
+ ```
1409
+
1410
+ **`--raw` output:** `passed` or `failed`.
1411
+
1412
+ ---
1413
+
1414
+ ### `validate health [--repair [--only <class>,...]] [--standards] [--full] [--drift] [--links]`
1415
+
1416
+ Comprehensive `.planning/` integrity check. Validates project.md, roadmap.md, state.md, config.json, phase directory naming, and orphaned plans. With `--standards`, also checks standards compliance. With `--drift`, runs convention drift analysis. With `--links`, attaches a doc-code link-graph summary (ADR-0027).
1417
+
1418
+ ```bash
1419
+ pan-tools validate health
1420
+ pan-tools validate health --repair
1421
+ pan-tools validate health --standards
1422
+ pan-tools validate health --full
1423
+ pan-tools validate health --drift
1424
+ pan-tools validate health --links
1425
+ ```
1426
+
1427
+ **Flags:**
1428
+ - `--repair` — Attempt automatic repairs: create/reset config.json, regenerate state.md (a timestamped `state.md.bak-<ts>` backup is written first and reported as `backupState`), and tick requirement/plan checkboxes for `STATE_REQ_DRIFT`/`STATE_ROADMAP_DRIFT` (`syncRequirements`/`syncRoadmap`). The `status`, the issue lists and the exit code describe the tree as it was before the repairs ran; run `validate health` again to see the result.
1429
+ - `--standards` — Include standards compliance check (reads standards.md, reports per-standard coverage)
1430
+ - `--full` — Run tests and build checks (slower, includes test_status and build_status)
1431
+ - `--drift` — Run convention drift analysis (includes drift_status with score and violations)
1432
+ - `--links` — Attach `link_graph` summary (ADR-0027). Errors degrade health to a `LINKS_ERR` warning (advisory, non-blocking). Run `pan-tools links validate` standalone for the full finding list.
1433
+ - `--only <class>,...` — With `--repair`, run only the named repair classes (`regenerateState`, `createConfig`, `resetConfig`, `syncRequirements`, `syncRoadmap`); the result lists the available classes as `repairs_available`.
1434
+
1435
+ **Exit code:** `1` when `status` is `broken` (a missing `.planning/` included), `0` for `healthy` and `degraded` — warnings are not failures. This is a verdict command: it sets the code explicitly instead of deriving it from an `error` key (see "Error Shape" above), so gate on the exit code, not on `errors.length`.
1436
+
1437
+ **JSON output:**
1438
+ ```json
1439
+ {
1440
+ "status": "broken",
1441
+ "errors": [
1442
+ { "code": "E004", "message": "state.md not found", "fix": "Run /pan:health --repair to regenerate", "repairable": true }
1443
+ ],
1444
+ "warnings": [
1445
+ { "code": "W003", "message": "config.json not found", "fix": "Run /pan:health --repair to create with defaults", "repairable": true }
1446
+ ],
1447
+ "info": [
1448
+ { "code": "I001", "message": "05-setup/05-03-plan.md has no summary.md", "fix": "May be in progress", "repairable": false }
1449
+ ],
1450
+ "repairable_count": 2,
1451
+ "repairs_performed": [
1452
+ { "action": "regenerateState", "success": true, "path": "state.md" },
1453
+ { "action": "createConfig", "success": true, "path": "config.json" }
1454
+ ]
1455
+ }
1456
+ ```
1457
+
1458
+ **Status values:** `healthy`, `degraded`, `broken`. `--raw` prints the same JSON.
1459
+
1460
+ **Error/warning codes:**
1461
+
1462
+ | Code | Severity | Description | Repairable |
1463
+ |------|----------|-------------|------------|
1464
+ | E001 | error | `.planning/` directory not found | No |
1465
+ | E002 | error | `project.md` not found (phase-model projects only — a focus-model tree reports I003 instead) | No |
1466
+ | E003 | error | `roadmap.md` not found (phase-model projects only — a focus-model tree reports I003 instead) | No |
1467
+ | E004 | error | `state.md` not found (phase-model projects only — a focus-model tree reports I003 instead) | Yes |
1468
+ | E005 | error | `config.json` JSON parse error | Yes |
1469
+ | E006 | error | `.planning/` belongs to another tool (gsd-core markers found); PAN stops before E002–E005 and `--repair` writes nothing. A tool that shares the tree instead reports I004 | No |
1470
+ | W001 | warning | `project.md` missing required section | No |
1471
+ | W002 | warning | `state.md` references non-existent phase | Yes |
1472
+ | W003 | warning | `config.json` not found | Yes |
1473
+ | W004 | warning | `config.json` invalid `model_profile` value | No |
1474
+ | W005 | warning | Phase directory naming doesn't match `NN-name` format | No |
1475
+ | W006 | warning | Phase in ROADMAP at or behind the current phase, but no directory on disk | No |
1476
+ | W007 | warning | Phase on disk but not in ROADMAP | No |
1477
+ | I001 | info | Plan without SUMMARY (may be in progress) | No |
1478
+ | I002 | info | Phase in ROADMAP ahead of the current phase, not planned yet | No |
1479
+ | I003 | info | The tree runs the focus model or an orchestration campaign, so the phase-model checks (E002–E004, W001, W002, W005–W007, I001, I002, the state-consistency and verification gates) do not apply | No |
1480
+ | I004 | info | Another tool shares the planning tree (planning-with-files markers: `.active_plan`, `.attestation`, `ledger-*.jsonl`, a dated task directory holding `task_plan.md`, or two of its directories such as `sessions/`). PAN leaves its files alone and keeps checking its own | No |
1481
+ | I005 | info | The planning tree is ignored by git while `commit_docs` is true, so planning commits commit nothing. planning-with-files adds `.planning/` to `.gitignore` by default. Fix: remove the entry, or set `commit_docs` to false | No |
1482
+ | I006 | info | `config.json` holds keys PAN does not read, usually a typo. The fix names the nearest known key. A key starting with lowercase `x-` is your own metadata and is never reported. Fix: correct the spelling or remove the key | No |
1483
+ | STATE_REQ_DRIFT | warning | `state.md` shows all plans complete but `requirements.md` has unchecked boxes | Yes |
1484
+ | STATE_ROADMAP_DRIFT | warning | `state.md` shows all plans complete but `roadmap.md` has unchecked plan boxes | Yes |
1485
+ | VERIFICATION_GATE_MISSING | warning | Phase has completed plans but no verification record (verifier enabled) | No |
1486
+
1487
+ Codes emitted only under the corresponding flag:
1488
+
1489
+ | Code | Severity | Flag | Description | Repairable |
1490
+ |------|----------|------|-------------|------------|
1491
+ | TESTS_FAIL | error | `--full` | Test run exited non-zero | No |
1492
+ | BUILD_FAIL | error | `--full` | Build exited non-zero | No |
1493
+ | DRIFT_HIGH | warning | `--drift` | Convention drift verdict is `high` | No |
1494
+ | DRIFT_MEDIUM | info | `--drift` | Convention drift verdict is `medium` | No |
1495
+ | LINKS_ERR | warning | `--links` | Link graph has broken refs or uncovered backlink contracts | No |
1496
+ | STD-000 | info | `--standards` | No `standards.md` found — no standards selected | No |
1497
+ | STD-001 | info | `--standards` | `standards.md` exists but contains no recognized standards | No |
1498
+ | STD-*id* | warning / info | `--standards` | Per-standard coverage (warning at 0% verified, otherwise info) | No |
1499
+ | STD-SUMMARY | info | `--standards` | Overall coverage across selected standards | No |
1500
+
1501
+ Both tables are exhaustive: every code `validate health` can emit is listed above, and
1502
+ `tests/verify-health-codes.test.cjs` fails if a new code is added to the implementation
1503
+ without a row here.
1504
+
1505
+ ### `validate deployment`
1506
+
1507
+ Validates PAN installations in the current directory. Detects all installed runtimes (by checking for `pan-file-manifest.json`), then for each runtime validates: manifest file hashes match on-disk files, settings integrity, hook path resolution, and the **MCP server registration** the installer wrote.
1508
+
1509
+ ```bash
1510
+ pan-tools validate deployment [--raw]
1511
+ ```
1512
+
1513
+ **JSON output** — transcribed from a real run, not hand-written:
1514
+ ```json
1515
+ {
1516
+ "status": "clean",
1517
+ "runtimes_found": 1,
1518
+ "runtimes": {
1519
+ "claude": {
1520
+ "status": "clean",
1521
+ "version": "3.26.0-rc.2",
1522
+ "total_files": 322,
1523
+ "missing": [],
1524
+ "modified": [],
1525
+ "orphaned": [],
1526
+ "settings_ok": true,
1527
+ "settings_issues": [],
1528
+ "mcp": {
1529
+ "ok": true,
1530
+ "registered": true,
1531
+ "path": ".mcp.json",
1532
+ "server": "…/pan-wizard-core/mcp/server.cjs",
1533
+ "issues": []
1534
+ }
1535
+ }
1536
+ }
1537
+ }
1538
+ ```
1539
+
1540
+ > **`runtimes` is an OBJECT keyed by runtime name, not an array.** This block previously
1541
+ > showed an array with a `runtime` field inside each entry — a shape the code has never
1542
+ > emitted. Read it with `result.runtimes.claude`, not `result.runtimes[0]`.
1543
+
1544
+ **Status values:** `clean` (all files match), `modified` (hash mismatch, or an MCP registration problem), `broken` (files missing). **Exit code:** `0` whatever the `status` (gate on `status`); `1` only when no PAN install is found here (`{"error": "No PAN installations found in this directory"}`).
1545
+
1546
+ **The `mcp` block** reports the registration for runtimes PAN registers (`claude`, `copilot`, `gemini`, `opencode`). It distinguishes five failures that used to be indistinguishable — all previously reported `clean`:
1547
+
1548
+ | `mcp.issues[0]` names | Meaning |
1549
+ |---|---|
1550
+ | *missing* | the config file was never written — registration did not happen |
1551
+ | *no "pan" entry* | the config parses but carries no `pan` registration under the runtime's container key |
1552
+ | *names no server path* | a `pan` entry exists but has no `args[0]` / `command[1]` to check |
1553
+ | *unreadable* | the file exists but is not valid JSON, so PAN deliberately left it untouched |
1554
+ | *does not exist* | a `pan` entry is registered but its server path resolves to nothing |
1555
+
1556
+ `mcp.skipped` appears instead of an error in two legitimate cases: `no-registration-by-design` (Codex — its MCP config is TOML, which PAN prints rather than merges) and `bridge-not-in-this-install` (a deployment made before the bridge shipped, which correctly has nothing to verify).
1557
+
1558
+ ---
1559
+
1560
+ ## 9. Verification Suite
1561
+
1562
+ Commands for verifying plan structure, phase completeness, file references, git commits, and build artifacts. Used by the pan-verifier and pan-plan-checker agents.
1563
+
1564
+ **Exit code:** these verdict commands exit `0` whether the verdict passes or fails (exit `1` comes only from an unreadable input — `File not found` for `plan-structure`/`references`/`artifacts`/`key-links`, `Phase not found` or an unreadable phase directory for `phase-completeness` — and from usage errors; `verify-summary` reports a missing summary as `passed: false` at exit `0`) — gate on each command's verdict field, not the exit code: `passed` (`verify-summary`), `valid` (`plan-structure`, `references`), `complete` (`phase-completeness`), `all_valid` (`commits`, whose `valid` is the list of hashes found), `all_passed` (`artifacts`), `all_verified` (`key-links`). `verify reconcile` and `verify stubs --gate` are the exceptions and set the code explicitly; `verify scope` and `verify stale` exit `1` for a missing or unknown phase.
1565
+
1566
+ ### `verify-summary <path> [--check-count N]`
1567
+
1568
+ Verify a summary.md file: checks existence, spot-checks the first `--check-count` file paths containing a `/` that it mentions (backticked paths first, then the path after `Created:`, `Modified:`, `Added:`, `Updated:` or `Edited:`, in any case — so the summary template's inline `key-files` list, `created: [src/a.ts]`, is read as `[src/a.ts` and reported missing), checks that a commit hash it mentions exists (the first three are tried), and looks for self-check section status. `passed` fails on a missing summary, a missing referenced file or a failed self-check; a commit not in git history is listed in `errors` but does not fail `passed`.
1569
+
1570
+ ```bash
1571
+ pan-tools verify-summary .planning/phases/05-setup/05-01-summary.md [--raw]
1572
+ pan-tools verify-summary path/to/summary.md --check-count 5
1573
+ ```
1574
+
1575
+ **Arguments:**
1576
+ - `<path>` — Relative path to the summary.md file
1577
+ - `--check-count N` — Number of referenced files to spot-check (default: 2; `0` and non-numeric values fall back to 2, others are clamped to 1–20)
1578
+
1579
+ **JSON output:**
1580
+ ```json
1581
+ {
1582
+ "passed": true,
1583
+ "checks": {
1584
+ "summary_exists": true,
1585
+ "files_created": { "checked": 2, "found": 2, "missing": [] },
1586
+ "commits_exist": true,
1587
+ "self_check": "passed"
1588
+ },
1589
+ "errors": []
1590
+ }
1591
+ ```
1592
+
1593
+ **`--raw` output:** `passed` or `failed`.
1594
+
1595
+ ---
1596
+
1597
+ ### `verify plan-structure <file>`
1598
+
1599
+ Check a plan.md file for required frontmatter fields and valid `<task>` element structure.
1600
+
1601
+ ```bash
1602
+ pan-tools verify plan-structure .planning/phases/05-setup/05-01-plan.md [--raw]
1603
+ ```
1604
+
1605
+ **JSON output:**
1606
+ ```json
1607
+ {
1608
+ "valid": true,
1609
+ "errors": [],
1610
+ "warnings": ["Task 'setup' missing <verify>"],
1611
+ "task_count": 3,
1612
+ "tasks": [
1613
+ { "name": "setup", "type": "auto", "hasFiles": true, "hasAction": true, "hasVerify": false, "hasDone": true },
1614
+ { "name": "Select session storage", "type": "checkpoint:decision", "auto_select": "cookie" }
1615
+ ],
1616
+ "frontmatter_fields": ["phase", "plan", "type", "wave", "depends_on", "files_modified", "autonomous", "must_haves"]
1617
+ }
1618
+ ```
1619
+
1620
+ **Required frontmatter:** `phase`, `plan`, `type`, `wave`, `depends_on`, `files_modified`, `autonomous`, `must_haves`.
1621
+
1622
+ **Tasks.** An auto task needs `<name>` and `<action>` (errors) and should have `<verify>`, `<done>` and `<files>` (warnings). A checkpoint is checked against its own shape instead: `checkpoint:human-verify` needs `<what-built>` and `<how-to-verify>`, `checkpoint:decision` needs `<decision>` and `<options>` with at least one `<option id="…">`, and `checkpoint:human-action` needs `<action>`. A decision's optional `auto_select` must name one of its option ids (error), and a decision without it is a warning, because auto mode stops there for a human (market item M22). A fenced code block of more than 20 lines in an auto task's `<action>` is a warning: a plan records decisions, not implementation (M32). A plan with checkpoint tasks must set `autonomous: false` (error). A plan in wave 2 or later with an empty `depends_on` gets a warning.
1623
+
1624
+ **`--raw` output:** `valid` or `invalid`.
1625
+
1626
+ ---
1627
+
1628
+ ### `verify phase-completeness <phase>`
1629
+
1630
+ Check that all plans in a phase have corresponding summaries.
1631
+
1632
+ ```bash
1633
+ pan-tools verify phase-completeness 5 [--raw]
1634
+ ```
1635
+
1636
+ **JSON output:**
1637
+ ```json
1638
+ {
1639
+ "complete": true,
1640
+ "phase": "05",
1641
+ "plan_count": 3,
1642
+ "summary_count": 3,
1643
+ "incomplete_plans": [],
1644
+ "orphan_summaries": [],
1645
+ "errors": [],
1646
+ "warnings": []
1647
+ }
1648
+ ```
1649
+
1650
+ **`--raw` output:** `complete` or `incomplete`.
1651
+
1652
+ ---
1653
+
1654
+ ### `verify references <file>`
1655
+
1656
+ Check that `@`-references and backtick file paths in a document resolve to existing files.
1657
+
1658
+ ```bash
1659
+ pan-tools verify references .planning/phases/05-setup/05-01-plan.md [--raw]
1660
+ ```
1661
+
1662
+ **JSON output:**
1663
+ ```json
1664
+ {
1665
+ "valid": true,
1666
+ "found": 5,
1667
+ "missing": [],
1668
+ "total": 5
1669
+ }
1670
+ ```
1671
+
1672
+ **`--raw` output:** `valid` or `invalid`.
1673
+
1674
+ ---
1675
+
1676
+ ### `verify commits <hash1> [hash2] ...`
1677
+
1678
+ Batch verify that commit hashes exist in the git history.
1679
+
1680
+ ```bash
1681
+ pan-tools verify commits abc1234 def5678 ghi9012 [--raw]
1682
+ ```
1683
+
1684
+ **JSON output:**
1685
+ ```json
1686
+ {
1687
+ "all_valid": true,
1688
+ "valid": ["abc1234", "def5678", "ghi9012"],
1689
+ "invalid": [],
1690
+ "total": 3
1691
+ }
1692
+ ```
1693
+
1694
+ **`--raw` output:** `valid` or `invalid`.
1695
+
1696
+ ---
1697
+
1698
+ ### `verify artifacts <plan-file>`
1699
+
1700
+ Check that `must_haves.artifacts` from a plan.md frontmatter exist on disk and meet specified criteria (min_lines, contains, exports).
1701
+
1702
+ ```bash
1703
+ pan-tools verify artifacts .planning/phases/05-setup/05-01-plan.md [--raw]
1704
+ ```
1705
+
1706
+ **JSON output:**
1707
+ ```json
1708
+ {
1709
+ "all_passed": true,
1710
+ "passed": 2,
1711
+ "total": 2,
1712
+ "artifacts": [
1713
+ { "path": "src/db/schema.ts", "exists": true, "issues": [], "passed": true }
1714
+ ]
1715
+ }
1716
+ ```
1717
+
1718
+ **`--raw` output:** `valid` or `invalid`. Both exit 0; callers read `all_passed`. A plan with no `must_haves.artifacts` returns an `error` but exits 0; a missing plan file exits 1.
1719
+
1720
+ ---
1721
+
1722
+ ### `verify key-links <plan-file>`
1723
+
1724
+ Check that `must_haves.key_links` from a plan.md frontmatter are satisfied (source references target, or pattern matches).
1725
+
1726
+ ```bash
1727
+ pan-tools verify key-links .planning/phases/05-setup/05-01-plan.md [--raw]
1728
+ ```
1729
+
1730
+ **JSON output:**
1731
+ ```json
1732
+ {
1733
+ "all_verified": true,
1734
+ "verified": 2,
1735
+ "total": 2,
1736
+ "links": [
1737
+ { "from": "src/index.ts", "to": "src/db/schema.ts", "via": "import", "verified": true, "detail": "Pattern found in source" }
1738
+ ]
1739
+ }
1740
+ ```
1741
+
1742
+ **`--raw` output:** `valid` or `invalid`.
1743
+
1744
+ ---
1745
+
1746
+ ### `verify reconcile <phase>`
1747
+
1748
+ Cross-check a phase's recorded verification against the mechanical checks of every plan's `must_haves`, so a verification.md whose `status` claims a pass while an artifact substance check fails or a key link is unwired is caught (each artifact and link in the result names its `plan`). Exits `1` on a contradiction (and the phase gate refuses with `reconcile_contradiction`) and `0` otherwise, including a phase with no directory, no verification.md or no `must_haves`, which report `reconciled: true` with a `note`.
1749
+
1750
+ ```bash
1751
+ pan-tools verify reconcile 5 [--raw]
1752
+ ```
1753
+
1754
+ **`--raw` output:** `valid` or `invalid`.
1755
+
1756
+ ---
1757
+
1758
+ ### `verify scope <phase>`
1759
+
1760
+ The files a phase changed that none of its plans declared: the mechanical half of the verifier's unrequested-work check (market item M11).
1761
+ - **Changed:** the files the phase's plan commits touched (subjects of the form `{type}({phase}-{plan}):`, with or without zero padding, no merges, read from a bounded window of recent history — `COMMIT_SCAN_LIMIT` in `verify-scope.cjs`), plus the summaries' `key-files`.
1762
+ - **Declared:** the union of the plans' `files_modified`; a declared directory ends in `/`.
1763
+ - **Excluded:** the planning tree, lockfiles, and PAN's own runtime directories, each listed in `excluded[]` with a reason.
1764
+
1765
+ A test for a declared file carries `hint: test_for_declared`. The verifier judges the candidates and records the real ones as `unrequested:` in verification.md.
1766
+
1767
+ ```bash
1768
+ pan-tools verify scope 3 # {phase, directory, declared[], changed[], candidates[{path, hint, sources}], excluded[], git, commits}
1769
+ pan-tools verify scope 3 --raw # the candidate count
1770
+ ```
1771
+
1772
+ Works without git (summaries only; `git: false`). Exits 1 without a phase or for an unknown one.
1773
+
1774
+ ### `verify stale <phase>`
1775
+
1776
+ Whether a phase's verification still describes the code (market item M27). The verifier records the commit it checked as `verified_commit` in verification.md. Any file the phase covers that changed since that commit makes the verification stale, whether the change is committed or not. The covered files are the plans' `files_modified` and the summaries' `key-files`, minus the planning tree, lockfiles and PAN's runtime directories.
1777
+
1778
+ ```bash
1779
+ pan-tools verify stale 3 # {phase, state, verified_commit, verification, changed_since[], reason?}
1780
+ pan-tools verify stale 3 --raw # fresh | stale | unverified | unknown
1781
+ ```
1782
+
1783
+ `state` is `fresh`, `stale`, `unverified` (no verification file) or `unknown`. `unknown` comes with a `reason`: the verification records no `verified_commit`, the commit is no longer in the repository (rebased or squashed), the phase declares no files, or git is unavailable. `progress json` adds `verification` to each verified phase (and `changed_since_verification` when it is stale), and `progress table` marks a stale phase `Complete (verification stale)`. Exits 1 without a phase or for an unknown one.
1784
+
1785
+ ### `verify plan-set <phase>`
1786
+
1787
+ The checks a plan set has to pass that code can make (program V7). `/pan:plan-phase` runs it before spawning the plan checker, and also when the checker is skipped (`--skip-verify`, `workflow.plan_check` off), and sends its errors straight back to the planner.
1788
+
1789
+ ```bash
1790
+ pan-tools verify plan-set 3 # {phase, ok, errors[], warnings[], plans, plans_sha, plan_check, drift[]}
1791
+ pan-tools verify plan-set 3 --raw # valid | invalid
1792
+ ```
1793
+
1794
+ **Errors** (exit 1): `uncovered_requirement` (a roadmap requirement for the phase that no plan lists), `same_wave_overlap` (two plans in one wave modifying the same file with no dependency between them), `missing_reference` (a `depends_on` that names no plan of the phase), `self_reference`, `future_reference` (a dependency in the same or a later wave), `cycle`. **Warnings**: `unknown_requirement` (a plan lists a requirement the roadmap does not give the phase) and `wave_gap` (a plan later than its dependencies need). A dependency on another phase's plan (`02-04` from phase 3) is not checked.
1795
+
1796
+ `plan_check` says whether the plans are the ones that passed the plan check. The recorder stores a hash of the phase's plan files (`plans_sha`, line endings normalised) with every `pan-plan-checker` verdict. `status` is `ok`, `stale` (edited since the check), `failed`, `missing`, `legacy` (recorded before hashing; a warning) or `disabled` (`workflow.plan_check` is off). `drift` lists, for each plan without a summary that records `planned_commit`, the `files_modified` that changed between that commit and `HEAD`. `init execute-phase` carries the same two as `plan_check` and `plan_drift`, and exec-phase stops on `stale`, `failed` or `missing` unless the user gives a reason. Exits 1 for an unknown phase or a phase with no plans.
1797
+
1798
+ ### `verify review-focus <phase>`
1799
+
1800
+ Every plan names what its reviewer should check first (program P4): under `## Review Focus`, at most five inputs or failure modes its requirements imply that no test exercises yet, each pinned to the task that owns the code and the test that task adds, or one `None — <reason>` line. `/pan:plan-phase` runs it with `verify plan-set` and sends errors back to the planner; `review scope` hands the section to the reviewer.
1801
+
1802
+ ```bash
1803
+ pan-tools verify review-focus 3 # {phase, ok, plans: [{plan, file, status, items}], errors[]}
1804
+ pan-tools verify review-focus 3 --raw # valid | invalid
1805
+ ```
1806
+
1807
+ **Errors** (exit 1), each `{code, plan, detail}`: `review_focus_missing` (no section), `review_focus_empty` (no items and no `None — <reason>` with a reason of three or more words), `review_focus_too_many` (more than five items), `review_focus_no_task` (an item that names no `Task N`), `review_focus_unknown_task` (a `Task N` past the plan's task count), `review_focus_no_test` (an item that names no test). A plan's `status` is `ok`, `none`, `missing` or `invalid`.
1808
+
1809
+ ### `verify decisions <phase>`
1810
+
1811
+ The decision spine (program P2). The phase design (`{phase}-design.md`) records each decision that passes the three-question test with an id (`DD-1`, `DD-2` …) in the first column of its design-decisions table, or at the head of a list item or heading. Plans cite the ids they implement.
1812
+
1813
+ ```bash
1814
+ pan-tools verify decisions 3 # {phase, ok, status, design, decisions[], cited: {id: [plans]}, errors[], warnings[]}
1815
+ pan-tools verify decisions 3 --raw # valid | invalid
1816
+ ```
1817
+
1818
+ **Errors** (exit 1): `decision_unrecorded` (a plan cites an id the design does not record), `decision_uncited` (a recorded decision no plan cites), `decision_duplicate` (an id recorded twice). `status` is `checked`, `no_design` (nothing to check) or `legacy` (a design whose decisions carry no ids: a `decisions_without_ids` warning, never a refusal).
1819
+
1820
+ ### `verify gates <phase>`
1821
+
1822
+ Runs each backticked command in every task's `<verify>` before any work. A gate that already passes (`gate_vacuous`, unless the verify text marks it a regression guard) or whose pattern does not compile (`gate_invalid`) is an error (exit 1).
1823
+
1824
+ ```bash
1825
+ pan-tools verify gates 3 # {phase, ok, gates: [{plan, task, command, status, regression_guard?}], errors[]}
1826
+ pan-tools verify gates 3 --raw # valid | invalid
1827
+ ```
1828
+
1829
+ **Module:** `plan-lint.cjs`
1830
+
1831
+ ### `verify requirements` · `verify traceability [<REQ-ID> ...]` (program V8)
1832
+
1833
+ `verify requirements` fails a v1 requirement in `requirements.md` with no EARS acceptance criterion (`req_no_scenario`, exit 1). `verify traceability` runs the tests each requirement's `Test:` sub-bullets name: `passed`, `failing` (exit 1; a missing test file counts), `not_checked` (a criterion with no mapped test) or `legacy` (no criteria). Named ids (`AUTH-01`) trace only those requirements; an argument that is not a requirement id (`auth-01`) is an error (exit 1, `not a requirement id`), not a fallback to every requirement. A missing `requirements.md` is `requirements_not_found` (exit 1) for both. `phase complete` ticks a requirement with criteria only when its trace passes.
1834
+
1835
+ ```bash
1836
+ pan-tools verify requirements # {ok, requirements: [{id, criteria, tests}], errors[]}; --raw valid | invalid
1837
+ pan-tools verify traceability # {requirements: [{id, status, tests}], passed, failing, not_checked, legacy}; --raw passed | failing | not_checked
1838
+ ```
1839
+
1840
+ **Module:** `requirements-lint.cjs`
1841
+
1842
+ ### `uat record <phase> --test N --result pass|issue|skipped [--reported "..."] [--severity S] [--reason "..."]` · `uat status <phase>` (program L8)
1843
+
1844
+ `record` sets one test's result in the phase's `{phase}-uat.md` and rebuilds `## Summary` and `## Gaps` (diagnosis fields a gap already carries survive); the session becomes `complete` when no test is pending. `--severity` is `blocker`, `major` (the default for an issue), `minor` or `cosmetic`. `status` prints the counts and the gaps (`--raw`: `N passed, M issues, K pending`). Exit 1 with `uat_not_found`, `invalid_result`, `invalid_severity` or `test_not_found`.
1845
+
1846
+ **Module:** `uat.cjs`
1847
+
1848
+ ### `debug repro <slug> [--timeout <ms>]` · `debug similar "<symptom>" [--limit N]` (program P5)
1849
+
1850
+ `repro` runs the `command:` in a debug session's `## Reproduction` (`.planning/debug/<slug>.md`, or `resolved/`): `{slug, result, closable, requirement, command, exit}`; `result` is `green` (exit 0), `red` or `none` (no command recorded; exit 1 unless green). `similar` ranks the resolved sessions by token overlap with the symptom: `{symptom, cases: [{slug, score, root_cause, fix, reproduction}]}` (top 5 by default; `--raw`: the slugs).
1851
+
1852
+ **Module:** `debug-cases.cjs`
1853
+
1854
+ ### `usage`
1855
+
1856
+ Which of the installed commands and agents this project has used (program L9), from its own files only: the cost ledger (`command`, `agent` per row) and the trace sessions (`command`, `ended_at`). The inventory is the install `pan-tools` ships in: its `commands/pan/*.md` or `skills/pan-*`, and its `agents/pan-*`.
1857
+
1858
+ ```bash
1859
+ pan-tools usage # {local_only, commands: {used[], never_used[], unfinished[], installed}, agents: {used[], never_used[], installed}, sources}
1860
+ pan-tools usage --raw # N of M commands used, K unfinished; A of B agents used
1861
+ ```
1862
+
1863
+ A used command is `{name, calls, last_used, sessions, finished}`, a used agent `{name, calls, last_used}`; both lists are sorted by calls. A command name is matched with or without its `/pan:` or `pan-` prefix. **`unfinished`** lists used commands whose every trace session stopped without `ended_at`, the newest started more than six hours ago (a newer open session may still be running). Nothing leaves the machine. Exits 0.
1864
+
1865
+ ### `research lint <phase|path> [--now YYYY-MM-DD]`
1866
+
1867
+ Research claims with provenance and freshness (program P3). The phase's research file (or the file at the path) lists the claims plans will rest on in a `## Claims` table with the columns Claim, Type, Source and Checked. A research file with no `## Claims` table but an answers table (`research check`, below) is read from its answered rows: each answer is a `general` claim, its source is the Source cell and its checked date is the `YYYY-MM-DD` date in that cell; `unknown` rows are not claims.
1868
+
1869
+ ```bash
1870
+ pan-tools research lint 3 # {file, ok, claims, errors[], warnings[], stale[], windows}
1871
+ pan-tools research lint .planning/research/stack.md --raw # valid | invalid
1872
+ ```
1873
+
1874
+ **Errors** (exit 1), each `{code, row, claim, detail}` (`claims_missing` carries only `code` and `detail`): `claims_missing` (no `## Claims` section and no answers table), `claim_no_source` (empty, or a placeholder such as `[Official docs URL]`), `claim_no_date` (not `YYYY-MM-DD`), `claim_future_date`. **Warnings:** `claim_unknown_type` (read as `general`). **`stale`** lists `{row, claim, type, checked, age_days, window_days}` for each claim older than its type's window: `version`, `pricing` and `security` 30 days, `api` 90, `general` 180, `practice` 365. Stale is not an error: `/pan:plan-phase` passes the lint to the plan checker, which flags the stale claims a plan depends on. `--now` fixes today's date. Exits 1 with `{error, reason}` for an unknown phase (`phase_not_found`), a phase with no research file (`no_research`), a missing path (`file_not_found`) or a `--now` that is not `YYYY-MM-DD` (`invalid_now`).
1875
+
1876
+ ### `research check <questions file> <research file> [--market]`
1877
+
1878
+ Research answers questions, bounded. The workflows that research (`/pan:new-project --research`, `/pan:plan-phase --research`, `/pan:milestone-new`) write at most five open questions that block building as a numbered list (`1. …`), one researcher answers them in a table `| # | Question | Answer | Source | Confidence | Decision it settles |`, and the workflow runs this check after the researcher, sends its errors back once, then proceeds with the unknowns recorded.
1879
+
1880
+ ```bash
1881
+ pan-tools research check .planning/research/questions.md .planning/research/research-summary.md # {questions_file, research_file, ok, questions, answered, unknown, products, limits, errors[], warnings[]}
1882
+ pan-tools research check q.md r.md --market --raw # valid | invalid
1883
+ ```
1884
+
1885
+ **Errors** (exit 1), each `{code, detail}` (with `question` when it names one): `no_questions` (the questions file lists none: no research should run), `too_many_questions` (more than five), `answers_missing` (no table with Question, Answer and Source columns), `question_unanswered` (a question with no row, or an empty answer), `answer_no_source` (an answer whose source is empty or a placeholder), `unknown_without_assumption` (an answer starting `unknown` that names no assumption, as in `unknown — assume: …`), `too_many_products` (a table whose first column is `Product` with more than five rows), `products_missing` (`--market` and no such table). **Warnings:** `answer_no_confidence` (not HIGH, MEDIUM or LOW), `extra_row` (a row that answers no listed question). Exits 1 with `{error, reason}` when either file is missing (`file_not_found`).
1886
+
1887
+ ### `verify test-integrity <phase>`
1888
+
1889
+ Did the phase weaken the tests that judge it (program V3)? It walks the phase's own plan commits (subjects like `feat(03-01): …`) one at a time and reads each test file's diff.
1890
+
1891
+ ```bash
1892
+ pan-tools verify test-integrity 3 # {phase, checked, ok, commits, violations[], warnings[], reason?}
1893
+ pan-tools verify test-integrity 3 --raw # clean | violated | not_checked
1894
+ ```
1895
+
1896
+ **Violations** (exit 1), each `{code, file, commit, detail}`: `test_deleted`; `skip_marker_added` (`.skip`/`.only`/`.todo`, `xit`, a node:test `skip` option, `@pytest.mark.skip`/`skipif`/`xfail`, `pytest.skip()`, `@unittest.skip`, `t.Skip`, `#[ignore]`, `@Disabled`/`@Ignore`); `assertions_dropped` (more assertion lines removed than added in one file); `undeclared_test_edit` (an existing assertion changed in a test no plan lists in `files_modified`); `snapshot_rewritten` (an undeclared snapshot changed or deleted); `always_true_assert` (`assert(true)`, `expect(true).toBe(true)`, `assert True`, `assert!(true)` …). **Warnings:** `test_renamed`. A test file is a path under `test/`, `tests/`, `__tests__/` or `spec/`, a `*.test.*`/`*.spec.*` file, `test_*.py`, `*_test.py`/`*_test.go`, `*Test(s).java`/`.kt`/`.cs`, or a Rust file holding `#[test]` or `#[cfg(test)]`. With no git or no plan commits for the phase it reports `checked: false` with a `reason` and exits 0: not checked is never a violation.
1897
+
1898
+ ### `review scope <phase>`
1899
+
1900
+ What a reviewer of the phase reads (program R1): the diff, not the executor's own list of what it touched, with the intent to judge it against. exec-phase passes it to pan-reviewer.
1901
+
1902
+ ```bash
1903
+ pan-tools review scope 3 # {phase, directory, base, files: [{path, sources, listed}], unlisted[], excluded[], intent: {plans[], requirements[], missing_requirements[]}, git, commits}
1904
+ pan-tools review scope 3 --raw # the file paths, one per line
1905
+ ```
1906
+
1907
+ **Files** are those the phase's plan commits changed (subjects like `feat(03-01): …`), the summaries' key-files, and the working tree's uncommitted and untracked files; `sources` says which of `commit`, `summary`, `uncommitted` and `untracked` each came from. The planning tree, lockfiles and PAN's runtime directories are `excluded`, and a file the phase deleted is left out. `listed` is whether a summary's key-files names the file; **`unlisted`** collects the ones it does not, the files a review driven by the summaries never saw. **`intent.plans`** holds each plan's `objective`, `must_haves` (truths, artifacts, key_links), its `## Review Focus` section (`review_focus`, null without one) and the requirement ids it claims; **`intent.requirements`** the text of each claimed id from requirements.md, with ids it does not find in `missing_requirements`. `base` is the parent of the oldest plan commit. Exit 1 for a missing or unknown phase.
1908
+
1909
+ ### `review cross-family <phase> [--host <name>] [--reps N] [--dry-run]`
1910
+
1911
+ The cross-family reviewer lane (program R4). Runs the phase review once more on another host's CLI, so a model from a different family checks the change. The run is read-only: the argv comes from `hosts.cjs` (`review`) and carries no write or auto-approve flag. The prompt holds the verdict contract, the plans' intent, the files in scope and the phase's diff.
1912
+
1913
+ Off unless `workflow.cross_family_review` names a host (`claude`, `codex`, `copilot`, `gemini`, `opencode`); while it is off it returns `{enabled: false, reason: "lane_off"}` and runs nothing. `--host` overrides the configured host for one run. `--dry-run` prints `lane`, `host`, `family`, `bin`, `argv`, the prompt and `prompt_file` (when it would use one) and spawns nothing. `--reps N` (at most 10) runs it N times and reports each run's verdict, and `verdict` is the most common one.
1914
+
1915
+ On Windows a host binary that resolves to an `.exe` or `.com` is launched directly by its path; anything else (an npm `.cmd` shim) goes through cmd.exe, with each argument holding whitespace, a quote or a cmd operator quoted. A prompt passed as an argument goes through `cross-family-<host>-prompt.md` (the argument tells the host to read it) when it runs through cmd.exe or is too long for a command line.
1916
+
1917
+ Writes `.planning/reviews/<NN>/cross-family-<host>.md` with the host's answer. `review-deep merge … --cross-file <that file>` merges its findings as a fourth source (`coverage.by_source.cross_family`).
1918
+
1919
+ **Module:** `cross-family.cjs`
1920
+
1921
+ ### `review vote [--phase N] [--quorum K] [--out <file>] <pass-file>...` (program R2)
1922
+
1923
+ Merges k reviewer passes (`workflow.review_mode: lenses`): a finding survives only when a quorum of passes reported it (default: half the passes, rounded up), at the highest severity any pass gave it, and the verdict is recomputed from what survived. `--out` writes the merged report, which carries a normal `pan-verdict` block for `findings record`. Prints `{ok, passes, quorum, kept, dropped, verdict, out}` (`--raw`: the verdict); exit 1 on a pass with no readable block (`unreadable_pass`) or no pass at all (`no_passes`).
1924
+
1925
+ **Module:** `review-vote.cjs`
1926
+
1927
+ ### `verify red <phase> [--timeout <ms>]`
1928
+
1929
+ Does each test the phase wrote test what the phase built (program V4)? A test that passes before the phase's code exists cannot have caught anything.
1930
+
1931
+ ```bash
1932
+ pan-tools verify red 3 # {phase, checked, ok, base, caught, files: [{file, verdict, command, base_exit, head_exit, detail?}], reason?}
1933
+ pan-tools verify red 3 --raw # caught | not_caught | not_checked
1934
+ ```
1935
+
1936
+ The **base** is the parent of the phase's oldest plan commit (subjects like `feat(03-01): …`). For each test file those commits added or changed, it runs that one file twice: in a throwaway git worktree at the base, with the file copied in from HEAD, and at HEAD. The worktree links the project's `node_modules` (untracked, so absent from a checkout) and removes the link before the worktree.
1937
+
1938
+ | `verdict` | When |
1939
+ |---|---|
1940
+ | `caught` | fails at the base (a crash on a missing module counts) and passes at HEAD |
1941
+ | `not_red` | passes at the base: tautological, or testing something the phase did not change |
1942
+ | `not_green` | fails at HEAD |
1943
+ | `not_checked` | no command can run that one file, with the reason |
1944
+
1945
+ The one-file command is `workflow.test_file_command` from config.json (a `{file}` placeholder), else derived from the suite's runner: `node --test <file>`, jest, vitest or mocha through `npx --no-install`, `python -m pytest <file>`, or `go test ./<dir>`. Cargo, Maven, Gradle and make have none. Exit 1 when a file is `not_red` or `not_green`; a phase with no plan commits, no changed test or no runnable one is `checked: false` and exits 0.
1946
+
1947
+ ### `verify mutate --phase N [--max <n>] [--seed <n>] [--timeout <ms>]`
1948
+
1949
+ A mutation report on the phase's diff (program V5): would a test fail if the code the phase wrote were wrong?
1950
+
1951
+ ```bash
1952
+ pan-tools verify mutate --phase 3 # writes 03-mutation.json; exit 0
1953
+ pan-tools verify mutate --phase 3 --raw # "3/5 killed" | aborted:flaky | aborted:suite_fails | not_checked
1954
+ ```
1955
+
1956
+ It takes the lines the phase's plan commits changed (`git diff` from the phase's base, as `verify red` finds it) in source files that are not tests, and swaps one thing at a time on a changed line, outside comments and strings: a comparison flipped (`>=`/`>`, `<=`/`<`), equality inverted (`===`/`!==` in JS and TS, `==`/`!=` in C-family languages and Python), `&&`/`||` (`and`/`or` in Python), a boolean return inverted, `+ 1` for `- 1`. A seeded sample (`--max`, default 8; `--seed`, default 1) runs in a throwaway worktree at HEAD (uncommitted changes are not measured), the project's whole test suite once per mutant: **killed** when it fails or times out, **survived** otherwise.
1957
+
1958
+ Before any mutant, the unmutated suite runs twice: if it fails, the report aborts (`suite_fails`), and if one run passes and the other fails, it aborts as `flaky`, since no mutant could be read against it. The record holds `phase`, `mode`, `seed`, `checked`, `base`, `head`, `command`, `candidates`, `sampled`, `killed`, `survived`, `score` and `survivors[]` (each `{file, line, op, before, after}`); an aborted or unchecked run carries `aborted` and/or `reason` in place of `sampled`, `killed`, `survived` and `score`. The printed result adds `record`, the path it wrote. The phase may also be given positionally (`verify mutate 3`). An unknown phase exits 1; every report exits 0.
1959
+
1960
+ `workflow.mutation_gate` in config.json: `report` (the default; a report, never a refusal), `off` (runs nothing), `gate` (V5G, opt-in: the result carries `gate: {floor, failed}`, the command exits 1 when the score is below `workflow.mutation_floor` (0.6), and `verify reconcile` reports a passed verification over a failed gate as a contradiction, so the phase gate refuses). verify-phase runs it with `--max 5` after a passing test gate and lists survivors under **Test strength**.
1961
+
1962
+ ### `test run [--phase N] [--baseline] [--rebaseline] [--command "<cmd>"] [--timeout <ms>]`
1963
+
1964
+ Run the project's test suite and record the result (program V2): the engine's test gate, in any language.
1965
+
1966
+ ```bash
1967
+ pan-tools test run # run and print the record; writes nothing
1968
+ pan-tools test run --phase 3 --baseline # before execution: 03-test-baseline.json in the phase directory
1969
+ pan-tools test run --phase 3 # after: 03-test-run.json, compared with the baseline
1970
+ pan-tools test run --phase 3 --raw # passed | failed | skipped
1971
+ ```
1972
+
1973
+ **The command:** `workflow.test_command` from config.json, else the first of: package.json's `test` script (`npm test`; npm's placeholder script does not count), pytest (`pytest.ini`, `conftest.py`, `[tool.pytest…]` in pyproject.toml, `setup.cfg` or `tox.ini`: `python -m pytest`), `go.mod` (`go test ./...`), `Cargo.toml` (`cargo test`), `pom.xml` (`mvn -q test`), Gradle (`./gradlew test` or `gradle test`), a Makefile `test:` target (`make test`). `--command` overrides it for one run.
1974
+
1975
+ **The parse:** the runner's own output, by its shape: Node's runner (spec and TAP), jest, pytest, go test (plain or `-v`), cargo, and JUnit XML (also read from `target/surefire-reports/`, `target/failsafe-reports/` and `build/test-results/test/` after Maven or Gradle). Each failing test gets an id (`file > name`, `test_file.py::name`, `pkg > TestName`, `module::name`) that is the same from one run to the next.
1976
+
1977
+ **The status:**
1978
+
1979
+ | `status` | When |
1980
+ |---|---|
1981
+ | `passed` | the suite exited 0; or, against a baseline, every failure was already failing at the baseline (`preexisting_failures`) |
1982
+ | `failed` | a failing test the baseline did not have (`regressions`); a failure with no baseline; or the suite did not run: a non-zero exit with no failing test reported, a crash, a build error, a timeout (`reason: "suite did not run: …"`) |
1983
+ | `skipped` | nothing to run: no test command, or one that ran no tests |
1984
+
1985
+ A `--baseline` run keeps an existing baseline (`kept_existing: true`, nothing run) unless `--rebaseline`: a resumed run or a gaps-only round compares against the phase's start. The record holds `version`, `kind` (`run` or `baseline`), `phase`, `command`, `source` (`flag` under `--command`), `head`, `started_at`, `exit`, `timed_out` (only when it did), `duration_ms`, `runner`, `parsed`, `total`, `passed`, `failed`, `skipped`, `failing[]`, `status`, `reason`, `regressions[]`, `preexisting_failures[]`, `baseline` (`{head, status, failing}`, `failing` being its failure count, when compared with one) and `output_tail` (with no test command, `timed_out`, `baseline` and `output_tail` are absent); with `--phase` the printed result adds `record`, the path it wrote. The command exits 0 whatever the `status`: callers read `status` (an unknown `--phase` exits 1). The verifier copies `test_gate` from it, `verify reconcile` reports a verification whose `test_gate` disagrees with it, the verdict recorder lowers a verifier's pass over a failed run, and `phase gate` refuses on it. `validate health --full` runs the same gate.
1986
+
1987
+ ### `test detect`
1988
+
1989
+ The test command the gate would run, and where it came from: `{command, source}` (`source` is `config`, `package.json`, `pytest`, `go.mod`, `Cargo.toml`, `pom.xml`, `gradle` or `Makefile`), or `{command: null, source: null, reason}`.
1990
+
1991
+ ```bash
1992
+ pan-tools test detect # {command, source} | {command: null, source: null, reason}
1993
+ pan-tools test detect --raw # the command, or nothing
1994
+ ```
1995
+
1996
+ ### `verify stubs [--gate]`
1997
+
1998
+ Scan the uncommitted/changed file set (git diff vs HEAD, staged/index changes, and untracked files, so it gates a handoff) for stub / fake-return markers (`not implemented`, `NotImplemented`, `throw new Error("stub"/"todo")`, HTTP `501`, `coming soon`/`placeholder`, etc.) that indicate unfinished work. With `--gate`, exits non-zero when blocking (high-severity) findings exist; without it, always reports and exits zero.
1999
+
2000
+ ```bash
2001
+ pan-tools verify stubs [--gate] [--raw]
2002
+ ```
2003
+
2004
+ **`--raw` output:** `valid` or `invalid`.
2005
+
2006
+ ---
2007
+
2008
+ ## 10. Progress & Context
2009
+
2010
+ Commands for viewing project progress and estimating context window utilization.
2011
+
2012
+ ### `progress [json|table|bar|health]`
2013
+
2014
+ Render milestone progress in various formats.
2015
+
2016
+ ```bash
2017
+ pan-tools progress # JSON (default)
2018
+ pan-tools progress json
2019
+ pan-tools progress table [--raw]
2020
+ pan-tools progress bar [--raw]
2021
+ pan-tools progress health [--raw]
2022
+ ```
2023
+
2024
+ **Subcommands:**
2025
+
2026
+ | Format | Description | Typical Use |
2027
+ |--------|-------------|-------------|
2028
+ | `json` | Structured data with per-phase breakdown | Machine consumption |
2029
+ | `table` | Markdown table with progress bar header | Human review, status reports |
2030
+ | `bar` | Single-line progress bar | Quick status check |
2031
+ | `health` | Composite A-D health grade from 3 signals | Project health assessment |
2032
+
2033
+ **JSON output (json format):**
2034
+ ```json
2035
+ {
2036
+ "contract": "1.0",
2037
+ "milestone_version": "v1.0",
2038
+ "milestone_name": "Core Platform",
2039
+ "phases": [
2040
+ { "number": "05", "name": "setup", "plans": 3, "summaries": 3, "status": "Complete" }
2041
+ ],
2042
+ "total_plans": 30,
2043
+ "total_summaries": 18,
2044
+ "percent": 60
2045
+ }
2046
+ ```
2047
+
2048
+ **Table format** produces a Markdown table with progress bar header.
2049
+
2050
+ **Bar format** produces a single-line progress bar: `[████████████░░░░░░░░] 18/30 plans (60%)`.
2051
+
2052
+ **Health format** produces a composite project health score:
2053
+
2054
+ ```json
2055
+ {
2056
+ "grade": "B",
2057
+ "composite": 74,
2058
+ "progress": { "score": 60, "completed": 18, "total": 30 },
2059
+ "context": { "score": 100, "utilization": 0.053, "tokens": 10500 },
2060
+ "staleness": { "score": 63, "stalePlans": 3, "totalPhases": 8 },
2061
+ "patterns_count": 5,
2062
+ "session_count": 12
2063
+ }
2064
+ ```
2065
+
2066
+ **Grade mapping:** A (>=80), B (>=60), C (>=40), D (<40).
2067
+
2068
+ **The `context` denominator:** `utilization` is an estimate of `state.md` + `roadmap.md` + `project.md` + every phase's plan files, divided by PAN's own fixed budget constant — 200,000 tokens (`CONTEXT_WINDOW` in `constants.cjs`) — **not a reading of your model's real window.** Nothing detects the model here either; see [`context-budget`](#context-budget) for the same assumption stated in full.
2069
+
2070
+ **Enhanced fields:** `patterns_count` (error patterns from `patterns.md`), `session_count` (session entries from `session-history.md`).
2071
+
2072
+ **`--raw` output (table/bar/health):** The rendered string.
2073
+
2074
+ ---
2075
+
2076
+ ### `autodev run|resume|status|report|replay [--host H] [--max-usd N] [--max-usd-day N] [--max-turns N] [--run ID] [--quality] [--worktrees] [--ci [local]] [--ci-rounds N] [--ci-push] [--ci-base REF]`
2077
+
2078
+ The deterministic outer loop (programs A1a, A1c). A foreground process: each turn reads `.planning/`, asks `next-action` for the next unit, decides in code whether to stop, and runs the unit. Plan, execute and verify units each get a fresh headless session on `--host` (argv from `hosts.cjs`; Copilot CLI has no unattended adapter and is refused with `unsupported_host`); `complete_phase` runs `phase complete` in the engine. A phase the engine sizes lean (`size phase`) is planned with no session (`size phase-plan`, at $0) and its execute unit runs `workflows/exec-lean.md` in one session: the executor's work, one `pan-reviewer` pass (a `NEEDS_FIXES` gets one fix round, what stays open is recorded as deferred) and the verifier; `--quality` keeps `/pan:exec-phase` for every phase.
2079
+
2080
+ Stop reasons: `done` (the milestone audit is next), `human_needed` (a verification with `status: human_needed`, or no roadmap), `gate_failed` (`phase complete` refused), `budget_usd` (spend reached `--max-usd`), `turns` (`--max-turns` units, 20 by default), `no_progress` (the same unit again with the disk unchanged since it ran), plus `forecast_exceeded` and `ci_failed` (below). `run` defaults to `--host claude`.
2081
+
2082
+ Writes `.planning/autodev/<run>/run.json` and `ledger.jsonl`: a `started` row before each unit and a `finished` row after it (`argv`, `exit`, `cost_usd` from Claude's JSON envelope, `start_sha`, `end_sha`, `snapshot_after`), and a `stopped` row with the reason. `resume` continues the latest run (or `--run`), relaunching a unit that was in flight when it died. `.planning/autodev/state.json` keeps each phase's `verified` and `merged` facts. `status` reports the latest run.
2083
+
2084
+ **Failures (A4, `failure-class.cjs`).** A failed session is classified: `compile` and `assertion` are retried with the error tail in the prompt, then at the next tier (Claude gets `--model`); `timeout` and `unknown` are restarted once; `flake` is quarantined; `missing_env`, `merge_conflict`, `integrity_violation` and `scope_violation` stop with `human_needed`; `budget` stops with `budget_usd`. The ledger row carries `failure_class` and `policy`.
2085
+
2086
+ **Spend (A6).** A run needs a cap: `--max-usd` or `budget.max_usd_run`, else `budget_required`. `--max-usd-day` or `budget.max_usd_day` caps today's spend across runs (UTC). Claude sessions get the remainder as `--max-budget-usd`. An execute unit that costs more than the P6 p90 forecast (per-plan p90 × the phase's plans, when the forecast has enough samples) stops the run with `forecast_exceeded`.
2087
+
2088
+ **Record (A5).** `blackbox.jsonl` holds every stop decision with its inputs. `autodev replay [--run ID]` re-runs the stop logic over them and returns `identical` and any `mismatches`. `autodev report [--run ID]` writes `dossier.md` and `dossier.html` beside the ledger.
2089
+
2090
+ **Worktrees (A3).** `--worktrees`: an execute unit whose phase has two or more pending plans runs each plan in its own git worktree and session (`exec-phase N --plan <id>`), sub-wave by sub-wave (`phase-plan-index` reports `sub_waves`: plans of a wave that share a file are split). Each sub-wave's branches pass `worktree preflight` before they are merged into the checkout; a conflict or a red merge stops with `human_needed`.
2091
+
2092
+ **CI (A2, `ci.cjs`).** `--ci`: after each completed phase, `gh pr checks` is the oracle. Checks whose latest run on the base branch (`--ci-base`, else `origin/HEAD`) failed are ignored. A failure starts a fix round (a `ci_fix` session given the failing names and `gh run view --log-failed` tail); `--ci-rounds N` (3) rounds later the run stops with `ci_failed`. Checks for a commit other than HEAD are `stale` and stop the run; `--ci-push` pushes after each phase. Each check is a `ci` row in the ledger. `--ci local` reads no pull request: after each completed phase it runs the project's own gate in the working directory (`npm run ci:local` when `package.json` has that script, else `npm test`; neither is `unavailable` and stops the run). The exit code decides; the failing names are the gate ids from `.ci-local/report.json` (else the script name), and the output's last lines are the fix round's feedback.
2093
+
2094
+ **Test safety.** With `PAN_STANDIN_DIR` set (the test suite sets it), autodev, the CI oracle and the cross-family lane run a host CLI only when a stand-in for it is in that directory.
2095
+
2096
+ **Module:** `autodev.cjs`, `failure-class.cjs`, `autodev-report.cjs`, `ci.cjs`
2097
+
2098
+ ### `worktree preflight --branches a,b [--base REF] [--test CMD]`
2099
+
2100
+ The shadow-merge preflight (program A3). In a throwaway worktree at `--base` (HEAD), merges each branch in order and runs the test command (`--test`, else the project's). `verdict`: `clean`, `conflict` (with `conflict.branch` and `conflict.files`), `break_together` (each branch passes alone, `alone` says so, but merged they fail), `branch_fails` (`failing` lists the branches red on their own), `base_failing`, or `no_test`. The checkout is never touched.
2101
+
2102
+ **Module:** `worktree.cjs`
2103
+
2104
+ ### `size phase <N>` · `size task "<description>"` · `size phase-plan <N>` · `size quick-plan --out <file> [--num N] "<description>"` · `size plan-check <N>` (right-sized ceremony)
2105
+
2106
+ Decides in code how much process a piece of work gets (`docs/specs/right-sized-ceremony.md`). `phase` and `task` print `{path, mode, reasons}` (`phase` adds `phase` and `metrics`: `{requirements, unspecified, lean_max_requirements}`; `task --file <path>` sizes a document, such as new-project's brief, instead of a description) (`--raw`: `lean` or `full`): a phase whose requirements (at least one, at most three times `workflow.lean_max_requirements`) each have an acceptance criterion or name what to build is `lean`, and so is a quick task whose description names an artifact and runs to 15 words or more. `mode` is `full` under the `quality` profile, `workflow.ceremony: full` or `--quality`, and then the path is always `full`; `--full` makes one call `full`. `phase-plan` plans a lean phase with no planner session: one task per requirement, in plans of at most `workflow.lean_max_requirements` (5) tasks, each in a wave after the one before (the task's action is the requirement's own text; a requirement that migrates or changes behaviour already built also declares the existing tests that cover the changed files as `supersedes_tests`), checked like `plan-check`, recorded as the phase's plan check and committed when `commit_docs` is on; it prints `{ok, written, path, paths, plan_dir, requirements, plan_check: "recorded", commit}` (`paths` lists every plan written, `path` the first; `supersedes_tests` when it declared any; `commit: null` with `commit_error` when the plan stands but its commit failed), and exits 1 with `not_lean`, `has_plans` or `check_failed` (with the check's `errors`), writing nothing. `autodev` runs it for a lean phase's `plan` unit, at $0. `quick-plan` writes the lean quick task's one-task plan from its description to `--out` (`--num N` numbers it) and prints `{written: true, path}`. `plan-check` runs the plan set, every plan's structure and the plans' grep gates; when all pass it records the phase's plan check from the engine (exit 0, `recorded: true`), otherwise it records nothing and exits 1 with `errors`.
2107
+
2108
+ ```bash
2109
+ pan-tools size phase 2 --raw # lean
2110
+ pan-tools size task "add src/shout.js exporting shout(name) ..." --raw
2111
+ pan-tools size plan-check 2 # {ok, errors, plans, recorded}
2112
+ ```
2113
+
2114
+ **Module:** `sizing.cjs`
2115
+
2116
+ ### `next-action`
2117
+
2118
+ The one router `/pan:progress`, `/pan:resume` and the phase transition ask for the next step. It reads `roadmap.md` and the phase directories into the snapshot the MCP orchestrator's state machine keys on, and prints `{action, phase, reason, command, done}` (`--raw`: the command, or the action when there is none).
2119
+
2120
+ ```bash
2121
+ pan-tools next-action
2122
+ ```
2123
+
2124
+ `action` is one of `plan`, `execute`, `verify`, `complete_phase`, `plan_gaps`, `audit_milestone`, `new_milestone`, `new_project` (no roadmap) or `stop`; `command` is the command that carries it out (for example `/pan:plan-phase 3`).
2125
+
2126
+ **Module:** `next-action.cjs`
2127
+
2128
+ ---
2129
+
2130
+ ### `context-budget`
2131
+
2132
+ Estimate context window utilization for the current phase. Measures how much of the assumed context window would be consumed by loading project files, roadmap, state, and plans for the active phase. The denominator is PAN's own fixed budget constant — 200,000 tokens (`CONTEXT_WINDOW` in `constants.cjs`) — **not a reading of your model's real window.** Nothing detects the model here; the number is a deliberately conservative planning assumption, so on a larger-context model the real headroom is greater than the report implies. That constant is the window size PAN commits to, and `progress health`'s `context` block divides by the same one.
2133
+
2134
+ ```bash
2135
+ pan-tools context-budget [--raw]
2136
+ ```
2137
+
2138
+ The `cache` block classifies the cached context (`status` of `ok` / `warn` / `critical` / `absent`, with advice naming the largest file) and, under `cache.ttl`, reads the cost ledger for the prompt-cache **lifetime** signal: how many cache writes followed an idle gap of five to sixty minutes — the misses a one-hour subagent cache lifetime would have avoided. `cache.ttl.recommend` turns true only when that recurs, and `cache.ttl.advice` then names the Claude Code setting (`subagentPromptCacheTtl`) with its cost trade-off. Suspect ledger rows are excluded from the count. Rows that recorded the cache-write lifetime split (`cache_write_1h_tokens`/`cache_write_5m_tokens`, written when the transcript carries it) count only their five-minute writes — a one-hour write outlives the gap — and `cache.ttl.basis` says which way the count was made: `measured`, `mixed`, or `inferred` from gaps alone; `measured_rows`, `cache_write_1h_tokens` and `cache_write_5m_tokens` carry the measured totals.
2139
+
2140
+ **JSON output:**
2141
+ ```json
2142
+ {
2143
+ "status": "healthy",
2144
+ "currentPhase": "01",
2145
+ "phaseDirectory": ".planning/phases/01-setup",
2146
+ "contextWindow": 200000,
2147
+ "budgetUtilization": 0.005,
2148
+ "plans": 2,
2149
+ "incompletePlans": 1,
2150
+ "tokens": {
2151
+ "project": 250,
2152
+ "roadmap": 150,
2153
+ "state": 100,
2154
+ "plans": 500,
2155
+ "total": 1000
2156
+ },
2157
+ "recommendation": "Within budget. ~796 more plans could fit before degradation.",
2158
+ "modelProfile": "balanced",
2159
+ "cache": { "status": "ok", "total_tokens": 500, "ttl": { "recommend": false, "setting": "subagentPromptCacheTtl" } },
2160
+ "relevanceSignal": null
2161
+ }
2162
+ ```
2163
+
2164
+ *The `cache` object is abbreviated here — its full field list (`block_count`, `block_paths`, `block_tokens`, `total_bytes`, `total_tokens`, `eligible_pct`, `status`, `warn_tokens`, `crit_tokens`, `file_warn_tokens`, `advice`, `ttl.*`, `sha`) is listed here; the paragraph above describes `status`, `advice` and `ttl`.*
2165
+
2166
+ **Status thresholds:**
2167
+
2168
+ | Status | Utilization | Meaning |
2169
+ |--------|------------|---------|
2170
+ | `healthy` | < 60% | Proceed normally |
2171
+ | `warning` | 60% to <80% | Consider splitting the phase |
2172
+ | `critical` | ≥ 80% | Phase too large, split recommended |
2173
+ | `idle` | N/A | No current phase set |
2174
+
2175
+ **`--raw` output:** Human-readable text with budget breakdown.
2176
+
2177
+ ---
2178
+
2179
+ ## 11. Todos
2180
+
2181
+ ### `todo complete <filename>`
2182
+
2183
+ Move a todo file from `.planning/todos/pending/` to `.planning/todos/completed/`, adding a `completed: YYYY-MM-DD` line as the first line of its front matter (or of the file, when it has no front matter).
2184
+
2185
+ ```bash
2186
+ pan-tools todo complete improve-error-handling.md [--raw]
2187
+ ```
2188
+
2189
+ **JSON output:**
2190
+ ```json
2191
+ { "completed": true, "file": "improve-error-handling.md", "date": "2026-02-27" }
2192
+ ```
2193
+
2194
+ **`--raw` output:** `completed`.
2195
+
2196
+ A file not in `pending/` prints `Error: Todo not found: <filename>` on stderr and exits 1.
2197
+
2198
+ ---
2199
+
2200
+ ### `list-todos [area]`
2201
+
2202
+ Count and enumerate pending todo files from `.planning/todos/pending/`.
2203
+
2204
+ ```bash
2205
+ pan-tools list-todos [--raw]
2206
+ pan-tools list-todos refactoring [--raw]
2207
+ ```
2208
+
2209
+ **Arguments:**
2210
+ - `[area]` — Optional area filter (matches the `area:` frontmatter field in todo files)
2211
+
2212
+ **JSON output:**
2213
+ ```json
2214
+ {
2215
+ "count": 3,
2216
+ "todos": [
2217
+ {
2218
+ "file": "improve-error-handling.md",
2219
+ "created": "2026-02-20",
2220
+ "title": "Improve error handling in parser",
2221
+ "area": "refactoring",
2222
+ "path": ".planning/todos/pending/improve-error-handling.md"
2223
+ }
2224
+ ]
2225
+ }
2226
+ ```
2227
+
2228
+ **`--raw` output:** The count as a string.
2229
+
2230
+ ---
2231
+
2232
+ ## 12. Scaffolding
2233
+
2234
+ Commands for creating template files in phase directories. All scaffold commands use the same module entry point with type dispatch.
2235
+
2236
+ ### `scaffold context --phase <N>`
2237
+
2238
+ Create a context.md template file in a phase directory.
2239
+
2240
+ ```bash
2241
+ pan-tools scaffold context --phase 5 [--raw]
2242
+ ```
2243
+
2244
+ **JSON output:**
2245
+ ```json
2246
+ { "created": true, "path": ".planning/phases/05-setup/05-context.md" }
2247
+ ```
2248
+
2249
+ If the file already exists: `{ "created": false, "reason": "already_exists", "path": … }` (exit `0`, nothing overwritten). `--name "..."` overrides the phase name written into the template.
2250
+
2251
+ ---
2252
+
2253
+ ### `scaffold uat --phase <N>`
2254
+
2255
+ Create a uat.md template file in a phase directory.
2256
+
2257
+ ```bash
2258
+ pan-tools scaffold uat --phase 5 [--raw]
2259
+ ```
2260
+
2261
+ **JSON output:**
2262
+ ```json
2263
+ { "created": true, "path": ".planning/phases/05-setup/05-uat.md" }
2264
+ ```
2265
+
2266
+ ---
2267
+
2268
+ ### `scaffold verification --phase <N>`
2269
+
2270
+ Create a verification.md template file in a phase directory.
2271
+
2272
+ ```bash
2273
+ pan-tools scaffold verification --phase 5 [--raw]
2274
+ ```
2275
+
2276
+ **JSON output:**
2277
+ ```json
2278
+ { "created": true, "path": ".planning/phases/05-setup/05-verification.md" }
2279
+ ```
2280
+
2281
+ ---
2282
+
2283
+ ### `scaffold phase-dir --phase <N> --name <name>`
2284
+
2285
+ Create a new phase directory under `.planning/phases/`.
2286
+
2287
+ ```bash
2288
+ pan-tools scaffold phase-dir --phase 5 --name "Database Setup" [--raw]
2289
+ ```
2290
+
2291
+ **Flags:**
2292
+ - `--phase <N>` — Phase number
2293
+ - `--name <name>` — Phase name (used to generate slug for directory). Put it last: it takes every following word that is not a flag, so `--name "Database Setup" --phase 8` creates `08-database-setup-8`.
2294
+
2295
+ **JSON output:**
2296
+ ```json
2297
+ {
2298
+ "created": true,
2299
+ "directory": ".planning/phases/05-database-setup"
2300
+ }
2301
+ ```
2302
+
2303
+ **`--raw` output:** The project-relative directory (e.g. `.planning/phases/05-database-setup`).
2304
+
2305
+ ---
2306
+
2307
+ ## 13. Frontmatter CRUD
2308
+
2309
+ Commands for reading and writing YAML frontmatter in markdown files. Frontmatter is the YAML block between `---` delimiters at the top of PAN's plan.md, summary.md, and verification.md files.
2310
+
2311
+ ### `frontmatter get <file> [--field <key>]`
2312
+
2313
+ Extract YAML frontmatter from a file as JSON. A plan's `must_haves.artifacts` and `must_haves.key_links` come back as objects (`{ "path": "src/db/schema.ts", "provides": "schema", "min_lines": 1 }`), read by relative indent; `frontmatter set` and `merge` write them back as lists of maps.
2314
+
2315
+ ```bash
2316
+ pan-tools frontmatter get .planning/phases/05-setup/05-01-plan.md
2317
+ pan-tools frontmatter get path/to/plan.md --field wave [--raw]
2318
+ ```
2319
+
2320
+ **JSON output (all fields):**
2321
+ ```json
2322
+ {
2323
+ "phase": "05-setup",
2324
+ "plan": "01",
2325
+ "type": "execute",
2326
+ "wave": "1",
2327
+ "depends_on": [],
2328
+ "files_modified": ["src/db/schema.ts"],
2329
+ "autonomous": "true"
2330
+ }
2331
+ ```
2332
+
2333
+ **JSON output (single field):**
2334
+ ```json
2335
+ { "wave": "1" }
2336
+ ```
2337
+
2338
+ With `--field`, `--raw` prints the value JSON-encoded (a string keeps its quotes: `"1"`). A missing file (`error: "File not found"`) or an absent field (`error: "Field not found"`) exits 1.
2339
+
2340
+ ---
2341
+
2342
+ ### `frontmatter set <file> --field <key> --value <jsonVal>`
2343
+
2344
+ Update a single frontmatter field. The value is JSON-parsed (so `"true"` becomes boolean `true`, `"[1,2]"` becomes an array).
2345
+
2346
+ ```bash
2347
+ pan-tools frontmatter set path/to/plan.md --field wave --value 2 [--raw]
2348
+ pan-tools frontmatter set path/to/plan.md --field autonomous --value false [--raw]
2349
+ ```
2350
+
2351
+ **JSON output:**
2352
+ ```json
2353
+ { "updated": true, "field": "wave", "value": 2 }
2354
+ ```
2355
+
2356
+ **`--raw` output:** `true`.
2357
+
2358
+ ---
2359
+
2360
+ ### `frontmatter merge <file> --data '{json}'`
2361
+
2362
+ Merge a JSON object into the file's frontmatter (shallow merge — existing keys are overwritten, new keys are added).
2363
+
2364
+ ```bash
2365
+ pan-tools frontmatter merge path/to/plan.md --data '{"wave": 2, "autonomous": false}' [--raw]
2366
+ ```
2367
+
2368
+ **JSON output:**
2369
+ ```json
2370
+ { "merged": true, "fields": ["wave", "autonomous"] }
2371
+ ```
2372
+
2373
+ **`--raw` output:** `true`.
2374
+
2375
+ ---
2376
+
2377
+ ### `frontmatter validate <file> --schema plan|summary|verification`
2378
+
2379
+ Validate that a file's frontmatter contains all required fields for the given schema.
2380
+
2381
+ ```bash
2382
+ pan-tools frontmatter validate path/to/plan.md --schema plan [--raw]
2383
+ ```
2384
+
2385
+ **Schemas and required fields:**
2386
+
2387
+ | Schema | Required Fields |
2388
+ |--------|----------------|
2389
+ | `plan` | `phase`, `plan`, `type`, `wave`, `depends_on`, `files_modified`, `autonomous`, `must_haves` |
2390
+ | `summary` | `phase`, `plan`, `subsystem`, `tags`, `duration`, `completed` |
2391
+ | `verification` | `phase`, `verified`, `status`, `score` |
2392
+
2393
+ **JSON output:**
2394
+ ```json
2395
+ {
2396
+ "valid": true,
2397
+ "missing": [],
2398
+ "present": ["phase", "plan", "type", "wave", "depends_on", "files_modified", "autonomous", "must_haves"],
2399
+ "schema": "plan"
2400
+ }
2401
+ ```
2402
+
2403
+ **`--raw` output:** `valid` or `invalid`. An invalid result still exits 0; any other `--schema` prints `Error: Unknown schema: <name>. Available: plan, summary, verification` on stderr and exits 1.
2404
+
2405
+ ---
2406
+
2407
+ ## 14. Template Operations
2408
+
2409
+ Commands for selecting and filling plan/summary/verification templates.
2410
+
2411
+ ### `template select <plan-path>`
2412
+
2413
+ Select the optimal SUMMARY template based on a plan's complexity (task count, file count, decisions presence). It reports which of the `minimal`, `standard` or `complex` summary templates fits; no shipped workflow, command or agent calls it (executors write from `templates/summary.md`). An unreadable plan falls back to `standard` with an `error` string (exit `0`).
2414
+
2415
+ ```bash
2416
+ pan-tools template select .planning/phases/05-setup/05-01-plan.md [--raw]
2417
+ ```
2418
+
2419
+ **JSON output:**
2420
+ ```json
2421
+ {
2422
+ "template": "templates/summary-standard.md",
2423
+ "type": "standard",
2424
+ "taskCount": 3,
2425
+ "fileCount": 5,
2426
+ "hasDecisions": false
2427
+ }
2428
+ ```
2429
+
2430
+ **`--raw` output:** The template file path.
2431
+
2432
+ ---
2433
+
2434
+ ### `template fill summary --phase N [--plan M] [--name "..."] [--fields '{json}']`
2435
+
2436
+ Create a pre-filled summary.md file in the phase directory.
2437
+
2438
+ ```bash
2439
+ pan-tools template fill summary --phase 5 --plan 1 --name "Database Setup"
2440
+ pan-tools template fill summary --phase 5 --fields '{"subsystem": "database"}'
2441
+ ```
2442
+
2443
+ **Flags:**
2444
+ - `--phase N` — Phase number (required)
2445
+ - `--plan M` — Plan number (default: `01`)
2446
+ - `--name "..."` — Phase name override
2447
+ - `--fields '{json}'` — Additional frontmatter fields to merge
2448
+
2449
+ **JSON output:**
2450
+ ```json
2451
+ { "created": true, "path": ".planning/phases/05-setup/05-01-summary.md", "template": "summary" }
2452
+ ```
2453
+
2454
+ ---
2455
+
2456
+ ### `template fill plan --phase N [--plan M] [--type execute|tdd] [--wave N] [--name "..."] [--fields '{json}']`
2457
+
2458
+ Create a pre-filled plan.md file in the phase directory. Its `## Context` names the phase's roadmap slice (`<phase dir>/<NN>-roadmap-slice.md`, written by `roadmap slice <N> --write`) instead of `@.planning/roadmap.md`.
2459
+
2460
+ ```bash
2461
+ pan-tools template fill plan --phase 5 --plan 2 --type tdd --wave 1
2462
+ ```
2463
+
2464
+ **Flags:**
2465
+ - `--phase N` — Phase number (required)
2466
+ - `--plan M` — Plan number (default: `01`)
2467
+ - `--type execute|tdd` — Plan type (default: `execute`)
2468
+ - `--wave N` — Wave number (default: `1`)
2469
+ - `--name "..."` — Used only when the phase directory has no name part (e.g. `07/`): the frontmatter `phase` then takes the name's slug (`07-foo-bar`). With a named directory, `phase` comes from the directory (`05-setup`). The heading never uses the name (`# Phase 5 Plan 01: [Title]`).
2470
+ - `--fields '{json}'` — Additional frontmatter fields to merge
2471
+
2472
+ **JSON output:**
2473
+ ```json
2474
+ { "created": true, "path": ".planning/phases/05-setup/05-02-plan.md", "template": "plan" }
2475
+ ```
2476
+
2477
+ ---
2478
+
2479
+ ### `template fill verification --phase N [--name "..."] [--fields '{json}']`
2480
+
2481
+ Create a pre-filled verification.md file in the phase directory.
2482
+
2483
+ ```bash
2484
+ pan-tools template fill verification --phase 5
2485
+ ```
2486
+
2487
+ **JSON output:**
2488
+ ```json
2489
+ { "created": true, "path": ".planning/phases/05-setup/05-verification.md", "template": "verification" }
2490
+ ```
2491
+
2492
+ ---
2493
+
2494
+ ## 15. Config Operations
2495
+
2496
+ Commands for managing `.planning/config.json` — the per-project configuration file.
2497
+
2498
+ ### `config-ensure-section`
2499
+
2500
+ Creates `.planning/` if missing, then initializes `config.json` with defaults; the config file itself is left untouched if it already exists. Merges user-level defaults from `~/.pan-wizard/defaults.json` if available. Auto-detects Brave Search API key availability (`BRAVE_API_KEY` in the environment, or a `~/.pan-wizard/brave_api_key` file; `websearch` itself reads only the environment variable).
2501
+
2502
+ ```bash
2503
+ pan-tools config-ensure-section [--raw]
2504
+ ```
2505
+
2506
+ **JSON output (created):**
2507
+ ```json
2508
+ { "created": true, "path": ".planning/config.json" }
2509
+ ```
2510
+
2511
+ **JSON output (exists):**
2512
+ ```json
2513
+ { "created": false, "reason": "already_exists" }
2514
+ ```
2515
+
2516
+ **`--raw` output:** `created` or `exists`.
2517
+
2518
+ **Selected default config values** (see `buildConfigDefaults()` in `config.cjs` for the full set — it also writes `search_gitignored`, the two branch templates, `workflow.verifier`/`nyquist_validation`/`phase_record_compact`/`phase_gate` (`enforce`), `hooks.guard` (`report`), `budget.default_points`/micro thresholds/`verify_reserve`, and the `commit`, `execution` and `routing` blocks):
2519
+
2520
+ | Key | Default | Description |
2521
+ |-----|---------|-------------|
2522
+ | `model_profile` | `"balanced"` | Agent model selection: `quality`, `balanced`, `budget` |
2523
+ | `commit_docs` | `true` | Auto-commit planning docs after state changes |
2524
+ | `parallelization` | `true` | Enable parallel wave execution |
2525
+ | `branching_strategy` | `"none"` | Git branching: `none`, `phase`, `milestone` |
2526
+ | `workflow.research` | `false` | Research before planning each phase (opt-in; a config that says `true` keeps it) |
2527
+ | `workflow.plan_check` | `true` | Enable plan-checker agent verification loop |
2528
+ | `workflow.ceremony` | `"auto"` | Right-sized ceremony (`size`): `auto` gives a precisely specified phase or quick task the lean path; `full` keeps the full ceremony without changing models (the `quality` profile also means full) |
2529
+ | `workflow.lean_max_requirements` | `5` | The most tasks in one engine-written lean plan; a lean phase with more requirements is split into plans run in order, and above three times this (15) the phase goes to the planner |
2530
+ | `workflow.phase_reports` | `{ enabled: false, open: false, theme: "auto", index: true }` | Opt-in HTML phase reports as a build deliverable. When `enabled`, the verify→complete gate, focus-auto checkpoints, and army INTEGRATE regenerate per-phase reports; the verify→complete gate and army's post-merge Phase 6 (LEARN) also rebuild the timeline index when `index` is set, while the focus-auto checkpoint rebuilds it whatever `index` says; default off. |
2531
+ | `memory.auto_optimize` | (not written; absent = `true`) | Reconcile the always-loaded project memory automatically at the focus-auto checkpoint and the normal-flow session record. No-op when state.md is already lean. Set `false` to opt out and reconcile only via `memory optimize`. |
2532
+ | `budget.enforce` | `false` | Make the spawn/point budget a hard stop. Advisory by default (tracked + surfaced, never stops a run). |
2533
+ | `budget.verify_reserve` | `0.15` | Fraction of the spawn budget (0–0.5) held back for re-verification so it can't be starved. Surfaced as `new_work_budget_remaining` / `into_verify_reserve` always; a hard early stop (`budget_reserve_reached`) only under `budget.enforce` / `--enforce-budget`. Override per-run with `--verify-reserve`. |
2534
+ | `cost.rates` | (built-in rate table) | Per-model `$/1M` overrides for cost estimates, e.g. `{ "<model-id>": { "input": 5, "output": 25, "cache_read": 0.5, "cache_write": 6.25, "cache_write_1h": 10 } }`. `cache_write` is the five-minute cache write; the optional `cache_write_1h` prices the one-hour share of a row that recorded the split, which otherwise bills at `cache_write`. The optional `long` block (`{ "above": 100000, "input": …, "output": …, "cache_read": …, "cache_write": …, "cache_write_1h": … }`) prices the part of a row recorded as `long_prompt` (requests whose prompt was over 100,000 tokens); `above` must be 100000, the figure the logger records at. An override row replaces the built-in row whole, so an override for a model with a long-prompt tier needs its own `long` block, or those requests bill at the base rates. Applied to both `cost append` and aggregate reporting, exactly as written. Precedence: this key, then a Claude Code managed `modelPricing` block (its `overrides` rows, times its `multiplier`), then the built-in table (times the managed `multiplier` when one is set). |
2535
+ | `routing.max_escalations` | `1` | How many tiers a retry may climb under `resolve-model --attempt` (failure-tier escalation). `0` turns escalation off. |
2536
+ | `cache.extra_files` | (not written; absent = no extra files) | Extra planning-root-relative docs to include in the cached context block, e.g. `["research/api-contract.md"]`. The built-in list is the *phase-model* spine (project/requirements/roadmap/state/standards), so a focus-model project measures an empty block. The block is what `cache prime`, `context-budget` and `hygiene scan` measure. It does not decide what the host caches: the host caches each agent's prompt prefix by itself. Entries are appended after the built-ins, so the `sha` of a project that sets nothing does not change; absolute paths, drive paths, and `..` segments are ignored. |
2537
+ | `brave_search` | auto-detected | Brave Search API availability |
2538
+
2539
+ ---
2540
+
2541
+ ### `config-get <key.path>`
2542
+
2543
+ Read a value from `.planning/config.json`. Supports dot-notation for nested keys.
2544
+
2545
+ ```bash
2546
+ pan-tools config-get model_profile [--raw]
2547
+ pan-tools config-get workflow.research [--raw]
2548
+ ```
2549
+
2550
+ **JSON output:** The value at the key path (could be string, boolean, number, object, or array).
2551
+
2552
+ **`--raw` output:** The value as a string; an object or array prints as JSON.
2553
+
2554
+ A key that is not set, including a default PAN applies without writing it (such as `cost.phase_budget_usd` or `workflow.phase_gate` in a config that never set them), prints `Error: Key not found: <key>` on stderr and exits 1; a project with no config.json prints `Error: No config.json found at <path>` and exits 1. The command reads the file, not the effective value.
2555
+
2556
+ ---
2557
+
2558
+ ### `config-set <key.path> <value>`
2559
+
2560
+ Write a value to `.planning/config.json`. Supports dot-notation for nested keys. Automatically parses `true`/`false` as booleans and numeric strings as numbers. Creates config.json if it does not exist (`.planning/` must exist already; `config-ensure-section` creates both), and refuses (exit 1, stderr) when the existing file is valid JSON but not an object.
2561
+
2562
+ ```bash
2563
+ pan-tools config-set model_profile quality [--raw]
2564
+ pan-tools config-set workflow.research false [--raw]
2565
+ pan-tools config-set parallelization true [--raw]
2566
+ ```
2567
+
2568
+ **JSON output:**
2569
+ ```json
2570
+ { "updated": true, "key": "workflow.research", "value": false }
2571
+ ```
2572
+
2573
+ **`--raw` output:** `key=value` string.
2574
+
2575
+ **Unknown keys.** A key path PAN does not read is still written, but the output gains a `warning` (also printed to stderr) naming it and the nearest known key: `unknown config key "workflow.auto_advnace": PAN does not read it; did you mean "workflow.auto_advance"?`. PAN checks the top-level key and, inside a known section such as `workflow` or `routing`, the key one level down. Sections that hold your own names (`model_overrides`, `effort_overrides`, `cost`, `cache`, `build`, `verification`, `concurrency`, `focus`, `memory`) are not checked below the top. `validate health` reports the same keys as `I006`. Keys that start with lowercase `x-` are user metadata and never warn.
2576
+
2577
+ ---
2578
+
2579
+ ## 16. Utility Commands
2580
+
2581
+ Standalone utility commands used across workflows.
2582
+
2583
+ ### `version [--check]`
2584
+
2585
+ The version of the PAN core that is running and where it lives (market item M29). An install reads its `pan-wizard-core/VERSION`; a source checkout reads `package.json`.
2586
+
2587
+ ```bash
2588
+ pan-tools version # {version, core, install_dir, node}
2589
+ pan-tools version --raw # the version
2590
+ pan-tools version --check # … plus update: {state, latest, checked_at, cache}
2591
+ pan-tools version --check --raw # e.g. "3.31.0 (available, npm latest 3.32.0)"
2592
+ ```
2593
+
2594
+ `--check` reads the result the update-check hook last cached (`<runtime home>/cache/pan-update-check.json`) and never touches the network, so it is instant and works offline. `state` is `available`, `current`, `ahead` (this core is newer than npm's latest, such as a prerelease or a source checkout), `offline` (the last check could not reach npm) or `unchecked` (no check has run yet). The cache comes from this install's runtime home (`~/.copilot` for a `.github` install, `~/.config/opencode` for `.opencode`), else the newest any runtime wrote.
2595
+
2596
+ ### `resolve-model <agent-type>`
2597
+
2598
+ Get the model name for an agent based on the current model profile in config. The model profile (`quality`/`balanced`/`budget`) determines which model tier each agent type uses.
2599
+
2600
+ ```bash
2601
+ pan-tools resolve-model pan-executor [--metadata '<json>'] [--attempt <n>] [--raw]
2602
+ # --metadata: {phaseNum, fileCount, waveCount, requirementCount, isArchitectural, context_estimate, needs_thinking, cache_warm}
2603
+ # enables the roadmap per-phase tier, complexity routing (routing.strategy: complexity) and the capability hints
2604
+ # --attempt: 1 for a first try; each later attempt follows a failure and climbs one tier
2605
+ ```
2606
+
2607
+ **Failure-tier escalation (`--attempt`):** a retry of work that already failed on a cheaper tier climbs one step up `fast → mid → reasoning` per failed attempt, capped by `routing.max_escalations` (default `1`; `0` turns it off) and never above the agent's own `quality` tier. Under `quality` and `balanced` every agent already runs on the reasoning tier, so the flag changes nothing there; it is the `budget` profile's retry path. A `model_overrides` entry or a roadmap per-phase tier is an explicit choice and is never escalated. With `--attempt`, a registered agent's JSON gains `attempt` and `escalated_from` (the tier it climbed from, or `null`); an unknown agent's carries neither. A value that is not a positive integer is an error. `/pan:exec-phase --gaps-only` resolves its executor as attempt 2.
2608
+
2609
+ **Agent types:** any key in `MODEL_PROFILES` (`core.cjs`) — the shipped `pan-*` agents, listed in the matrix below. The argument is not validated against an allowlist: a name that isn't in the table resolves to the mid tier and sets `unknown_agent: true` rather than erroring.
2610
+
2611
+ **JSON output:**
2612
+ ```json
2613
+ { "model": "inherit", "profile": "balanced", "strategy": "static", "effort": "high" }
2614
+ ```
2615
+
2616
+ For unknown agents: `{ "model": "sonnet", "profile": "balanced", "strategy": "static", "effort": "medium", "unknown_agent": true }` — unknown agents get the mid tier regardless of profile (`sonnet` under Anthropic; the detected provider's mid-tier id otherwise), and effort still follows the profile and `effort_overrides`. Invalid `--metadata` JSON is ignored (static routing), not an error.
2617
+
2618
+ **Under OpenCode:** the copy of PAN installed under `.opencode/` (or `~/.config/opencode/`) resolves provider-qualified ids — the provider, a slash, and the model's models.dev id, such as `openai/<model>` for an OpenAI tier (the table is `OPENCODE_MODELS` in `core.cjs`) — because OpenCode names every model `provider/model`. With `routing.provider` unset or `auto` (the default) and no `PAN_PROVIDER`, the provider is the one in OpenCode's own configured `model` (`opencode.json` at the project root, then `.opencode/opencode.json`) when it is `anthropic`, `openai` or `google`, and otherwise the runtime-directory detection's (`.opencode/` alone counts as OpenAI); the reasoning tier stays `inherit`.
2619
+
2620
+ **`--raw` output:** Model name string (e.g., `inherit`, `sonnet`, `haiku`). `inherit` means "use the model the session was launched with" — PAN does not name a model here, so it never goes stale as the lineup moves.
2621
+
2622
+ **Effort (2026-06):** alongside the tier, every agent resolves to a reasoning-effort level (`low`/`medium`/`high`/`xhigh`) — the primary within-model cost/intelligence dial on current models (it replaced fixed thinking budgets). Base levels per agent mirror the `effort:` frontmatter shipped in `agents/*.md`; the `budget` profile steps effort down one level (floor `low`); `quality`/`balanced` keep the base. Override per agent via `.planning/config.json` → `"effort_overrides": { "pan-planner": "xhigh" }`.
2623
+
2624
+ **Model profile matrix** (cost reset 2026-07; right-sized ceremony 2026-10):
2625
+
2626
+ | Agent | Quality | Balanced | Budget |
2627
+ |-------|---------|----------|--------|
2628
+ | planner, designer, roadmapper, executor, debugger, conductor, counterfactual | inherit | inherit | sonnet |
2629
+ | the two researchers (phase and project) and the plan-checker | inherit | sonnet | haiku |
2630
+ | every other registered agent — the doc writer, the verifier and the other checkers, and the review/support roster | inherit | inherit | haiku |
2631
+
2632
+ `quality` is `inherit` for **every** agent. `balanced` differs from it only on the plan-checker and the researchers, which it runs on the mid tier (the right-sized ceremony, `docs/specs/right-sized-ceremony.md`); `budget` down-tiers every agent. `inherit` → the reasoning tier, i.e. the model the session was launched with; `sonnet` → mid tier; `haiku` → fast tier (the `sonnet`/`haiku` names are the Anthropic mapping — `PROVIDER_MODELS` in `core.cjs` substitutes the equivalent for OpenAI/Google projects, and `inherit` is the reasoning tier on every provider). Derived from `MODEL_PROFILES` in `core.cjs` — that table is the source of truth.
2633
+
2634
+ ---
2635
+
2636
+ ### `commit <message> [--files f1 f2] [--amend] [--type TYPE] [--force] [--fail-on-error]`
2637
+
2638
+ Commit planning docs to git. Respects `commit_docs` config setting and `.gitignore`. Includes safety checks for deleted and sensitive files.
2639
+
2640
+ ```bash
2641
+ pan-tools commit "Phase 5 planning complete" [--raw]
2642
+ pan-tools commit "Update plans" --files .planning/phases/05-setup/05-01-plan.md
2643
+ pan-tools commit "" --amend
2644
+ pan-tools commit "add-feature" --type feat
2645
+ pan-tools commit "bugfix" --type fix --force
2646
+ ```
2647
+
2648
+ **Arguments:**
2649
+ - `<message>` — Commit message (required unless `--amend`)
2650
+ - `--files f1 f2 ...` — Specific files to stage (default: `.planning/`); the list ends at the next flag
2651
+ - `--amend` — Amend the previous commit instead of creating a new one
2652
+ - `--type TYPE` — Conventional commit type prefix. Valid: `feat`, `fix`, `docs`, `test`, `refactor`, `perf`, `chore`. Prepends `type: ` to message.
2653
+ - `--force` — Skip deleted-file safety check
2654
+ - `--fail-on-error` — Report a git refusal (e.g. missing identity) as a bare `error()` on **stderr** instead of a `commit_failed` JSON body on stdout. Both exit non-zero, so autonomous loops detect the silent-failure case where the artifact never actually landed either way; the flag only changes which stream carries the reason. (`nothing_to_commit` has no `error` key and is still a success at exit `0`.)
2655
+
2656
+ **Safety checks** (enabled by default via `config.commit.safety_checks`):
2657
+ - **Deleted files:** Blocks commit if `git status --porcelain` lists a deletion (` D` unstaged or `D` staged) anywhere in the repository, not only in what this commit stages (use `--force` to override)
2658
+ - **Sensitive files:** Blocks commit if a staged path matches a sensitive pattern: `.env`, `.pem` or `.key` at the end of the path, or `credentials`, `secret`, `password` or `token` anywhere in it (configurable via `config.commit.sensitive_patterns`). The match is on the path, not the content. PAN's own cost ledger, `.planning/metrics/tokens.jsonl`, is exempt
2659
+
2660
+ **JSON output:**
2661
+ ```json
2662
+ {
2663
+ "committed": true,
2664
+ "hash": "abc1234",
2665
+ "reason": "committed",
2666
+ "type": "feat",
2667
+ "safety_checks": { "deleted_files": [], "sensitive_files_blocked": [] }
2668
+ }
2669
+ ```
2670
+
2671
+ **Block reasons:** `deleted_files_detected`, `sensitive_file_detected`.
2672
+
2673
+ **Skip reasons:** `skipped_commit_docs_false`, `skipped_gitignored`, `nothing_to_commit`, `not_a_git_repo` (exit 0, with a `hint`). A blocked commit exits 1 with `error: commit_blocked`; PAN un-stages what it staged itself (`safety_checks.unstaged_by_pan`) and leaves anything you had staged beforehand untouched.
2674
+
2675
+ **`--raw` output:** The short hash, `skipped`, `blocked`, `nothing`, `not a git repo`, or `failed`.
2676
+
2677
+ ---
2678
+
2679
+ ### `rollback-snapshot <phase>`
2680
+
2681
+ Create a git tag snapshot before execution for easy rollback. Tag format: `pan-rollback-{phase}-{timestamp}` (dots in a decimal phase become dashes, e.g. `pan-rollback-5-1-…`; a colliding tag gets a `-1` suffix).
2682
+
2683
+ ```bash
2684
+ pan-tools rollback-snapshot 5 [--raw]
2685
+ pan-tools rollback-snapshot 5.1
2686
+ ```
2687
+
2688
+ **JSON output:**
2689
+ ```json
2690
+ { "tag": "pan-rollback-5-20260301T120000", "hash": "abc1234", "phase": "5" }
2691
+ ```
2692
+
2693
+ **Not a git repo (or no commits):** Returns `{ "tag": null, "hash": null, "phase": "5", "warning": "Not a git repository or no commits" }` (exit 0; `--raw` prints nothing).
2694
+
2695
+ **`--raw` output:** The tag name.
2696
+
2697
+ ---
2698
+
2699
+ ### `batch-commit <items-json>`
2700
+
2701
+ Commit the current `.planning/` changes in one commit that summarises a completed batch: `<items-json>` is an array of `{title}` objects and each title becomes a bullet in the commit body (`docs: focus-exec batch — N items completed`); per-item `files`/`message` are not read. Respects `commit_docs` config — returns `{ committed: false, reason: "skipped_commit_docs_false" }` when disabled.
2702
+
2703
+ **Module:** `commands.cjs`
2704
+
2705
+ ```bash
2706
+ pan-tools batch-commit '[{"title":"Fix login button"},{"title":"Add audit log"}]' [--raw]
2707
+ ```
2708
+
2709
+ **JSON output:**
2710
+ ```json
2711
+ { "committed": true, "hash": "abc1234", "reason": "committed", "items_count": 2 }
2712
+ ```
2713
+
2714
+ **Not a git repo:** Returns `{ "committed": false, "reason": "not_a_git_repo" }`.
2715
+
2716
+ ---
2717
+
2718
+ ### `estimate-cost`
2719
+
2720
+ Estimate relative cost multipliers for each model profile. Shows total and average cost across all agents.
2721
+
2722
+ **Module:** `commands.cjs`
2723
+
2724
+ ```bash
2725
+ pan-tools estimate-cost [--raw]
2726
+ ```
2727
+
2728
+ **JSON output (numbers shown are illustrative — actual counts depend on registered agents):**
2729
+ ```json
2730
+ {
2731
+ "estimates": [
2732
+ { "profile": "quality", "total": <N>, "average": 15.0, "agentCount": <N> },
2733
+ { "profile": "balanced", "total": <N>, "average": 15.0, "agentCount": <N> },
2734
+ { "profile": "budget", "total": <N>, "average": 1.6, "agentCount": <N> }
2735
+ ]
2736
+ }
2737
+ ```
2738
+
2739
+ `quality` and `balanced` report the same average by design — post cost reset both put every agent on the reasoning tier (15× baseline). `budget` is the only profile that down-tiers, so it's the only one that moves the number.
2740
+
2741
+ **`--raw` output:** One line per profile: `quality: ~15x baseline (<N> agents)`.
2742
+
2743
+ ---
2744
+
2745
+ ### `generate-slug <text>`
2746
+
2747
+ Convert arbitrary text to a URL-safe slug.
2748
+
2749
+ ```bash
2750
+ pan-tools generate-slug "Database Migration Layer" [--raw]
2751
+ ```
2752
+
2753
+ **JSON output:**
2754
+ ```json
2755
+ { "slug": "database-migration-layer" }
2756
+ ```
2757
+
2758
+ **`--raw` output:** The slug string.
2759
+
2760
+ **Rules:** Lowercase; every run of non-alphanumeric characters (spaces, underscores, punctuation) becomes a single hyphen; leading and trailing hyphens are trimmed.
2761
+
2762
+ ---
2763
+
2764
+ ### `current-timestamp [format]`
2765
+
2766
+ Get the current timestamp.
2767
+
2768
+ ```bash
2769
+ pan-tools current-timestamp # full (default)
2770
+ pan-tools current-timestamp date # YYYY-MM-DD
2771
+ pan-tools current-timestamp filename # YYYY-MM-DDTHH-MM-SS (no colons)
2772
+ ```
2773
+
2774
+ **Formats:**
2775
+
2776
+ | Format | Output | Example |
2777
+ |--------|--------|---------|
2778
+ | `full` | ISO 8601 | `2026-02-27T12:00:00.000Z` |
2779
+ | `date` | Date only | `2026-02-27` |
2780
+ | `filename` | Filesystem-safe | `2026-02-27T12-00-00` |
2781
+
2782
+ Any other format value is read as `full`.
2783
+
2784
+ **JSON output:**
2785
+ ```json
2786
+ { "timestamp": "2026-02-27T12:00:00.000Z" }
2787
+ ```
2788
+
2789
+ **`--raw` output:** The timestamp string.
2790
+
2791
+ ---
2792
+
2793
+ ### `verify-path-exists <path>`
2794
+
2795
+ Check whether a file or directory exists.
2796
+
2797
+ ```bash
2798
+ pan-tools verify-path-exists src/index.ts [--raw]
2799
+ ```
2800
+
2801
+ **JSON output:**
2802
+ ```json
2803
+ { "exists": true, "type": "file" }
2804
+ ```
2805
+
2806
+ Type is `file`, `directory`, or `other`. If not found: `{ "exists": false, "type": null }`.
2807
+
2808
+ **`--raw` output:** `true` or `false`.
2809
+
2810
+ ---
2811
+
2812
+ ### `history-digest`
2813
+
2814
+ Aggregate data from all summary.md files across current and archived phases. Extracts dependency graphs, decisions, patterns, and tech stack.
2815
+
2816
+ ```bash
2817
+ pan-tools history-digest [--raw]
2818
+ ```
2819
+
2820
+ **JSON output:**
2821
+ ```json
2822
+ {
2823
+ "phases": {
2824
+ "05-setup": {
2825
+ "name": "setup",
2826
+ "provides": ["auth-module", "db-schema"],
2827
+ "affects": ["api-routes"],
2828
+ "patterns": ["repository-pattern"]
2829
+ }
2830
+ },
2831
+ "decisions": [
2832
+ { "phase": "05-setup", "decision": "Use PostgreSQL for primary storage" }
2833
+ ],
2834
+ "tech_stack": ["typescript", "express", "postgresql"]
2835
+ }
2836
+ ```
2837
+
2838
+ Phases are keyed by each summary's `phase` frontmatter (`05-setup` in the summary templates; the directory's number when a summary has none), and `name` is the directory's name part with hyphens as spaces unless the summary sets `name:`. `provides` and `affects` read a nested `dependency-graph` list or the flat list the summary templates write.
2839
+
2840
+ **Note:** Uses the `@file:` protocol if the digest exceeds 50 KB.
2841
+
2842
+ ---
2843
+
2844
+ ### `summary-extract <path> [--fields f1,f2]`
2845
+
2846
+ Extract structured data from a summary.md file's frontmatter. `one_liner` comes from a `one-liner` frontmatter key when there is one, else from the bold line under the `# Phase … Summary` title, which is where the summary templates put it.
2847
+
2848
+ ```bash
2849
+ pan-tools summary-extract .planning/phases/05-setup/05-01-summary.md
2850
+ pan-tools summary-extract path/to/summary.md --fields key_files,decisions
2851
+ ```
2852
+
2853
+ **Arguments:**
2854
+ - `<path>` — Relative path to summary.md
2855
+ - `--fields f1,f2,...` — Comma-separated list of fields to extract (optional; returns all if omitted)
2856
+
2857
+ **Available fields:** `one_liner`, `key_files`, `tech_added`, `patterns`, `decisions`, `requirements_completed`
2858
+
2859
+ **JSON output:**
2860
+ ```json
2861
+ {
2862
+ "path": ".planning/phases/05-setup/05-01-summary.md",
2863
+ "one_liner": "Built authentication module with JWT support",
2864
+ "key_files": { "created": ["src/auth/index.ts"], "modified": ["src/auth/jwt.ts"] },
2865
+ "tech_added": ["jsonwebtoken"],
2866
+ "patterns": ["middleware-chain"],
2867
+ "decisions": [{ "summary": "Use JWT", "rationale": "Stateless auth for API" }],
2868
+ "requirements_completed": ["REQ-03"]
2869
+ }
2870
+ ```
2871
+
2872
+ ---
2873
+
2874
+ ### `websearch <query> [--limit N] [--freshness day|week|month|year]`
2875
+
2876
+ Search the web via the Brave Search API. Requires `BRAVE_API_KEY` environment variable. This is the only async command in pan-tools.
2877
+
2878
+ ```bash
2879
+ pan-tools websearch "Node.js stream backpressure" --limit 5 --freshness week
2880
+ ```
2881
+
2882
+ **Arguments:**
2883
+ - `<query>` — Search query string
2884
+ - `--limit N` — Max results (default: 10)
2885
+ - `--freshness day|week|month|year` — Recency filter, sent to Brave as `pd`/`pw`/`pm`/`py`; those codes and a `YYYY-MM-DDtoYYYY-MM-DD` range also pass through as given. Any other value is refused with an error (exit 1) before a request is made
2886
+
2887
+ **JSON output:**
2888
+ ```json
2889
+ {
2890
+ "available": true,
2891
+ "query": "Node.js stream backpressure",
2892
+ "count": 5,
2893
+ "results": [
2894
+ {
2895
+ "title": "Understanding Node.js Streams",
2896
+ "url": "https://example.com/...",
2897
+ "description": "A deep dive into...",
2898
+ "age": "2 days ago"
2899
+ }
2900
+ ]
2901
+ }
2902
+ ```
2903
+
2904
+ If `BRAVE_API_KEY` is not set: `{ "available": false, "reason": "BRAVE_API_KEY not set" }`.
2905
+
2906
+ ---
2907
+
2908
+ ## 17. Compound Init Commands
2909
+
2910
+ These commands gather all context needed for a specific workflow in a single call, avoiding multiple round trips. Each init command resolves models, checks file existence, reads state, and returns a comprehensive JSON object that the workflow's command `.md` file consumes.
2911
+
2912
+ All init commands accept `--raw` (no effect — init output is always JSON) and `--cwd <path>`.
2913
+
2914
+ ### `init execute-phase <phase> [--dry-run] [--budget N]`
2915
+
2916
+ All context for the execute-phase workflow. Includes tier classification, budget tracking, and execution mode.
2917
+
2918
+ ```bash
2919
+ pan-tools init execute-phase 5 [--raw]
2920
+ pan-tools init execute-phase 5 --dry-run
2921
+ pan-tools init execute-phase 5 --budget 30
2922
+ pan-tools init execute-phase 5 --dry-run --budget 25
2923
+ ```
2924
+
2925
+ **Arguments:**
2926
+ - `--dry-run` — Preview execution plan without spawning agents. Sets `dry_run: true` in output.
2927
+ - `--budget N` — Override budget points (default: `budget.default_points` from config.json, 50 when unset; below 1 is an error, above 200 is capped at 200). Plans exceeding budget set `budget_exceeded: true`.
2928
+
2929
+ **Key output fields:**
2930
+ - `executor_model`, `verifier_model`, `reviewer_model` — Resolved model names
2931
+ - `verifier_enabled` — `workflow.verifier`; when `false`, exec-phase and the native `pan-exec-waves` skip the verifier and say so
2932
+ - `commit_docs`, `parallelization`, `worktree_isolation`, `branching_strategy` — Config flags
2933
+ - `plan_check` — the phase's plan-check status (`verify plan-set`: `{status: ok|stale|failed|missing|legacy|disabled, …}`; `null` when the phase is not found); exec-phase stops on `stale`, `failed` or `missing`
2934
+ - `plan_drift` — the planned files changed since each plan's `planned_commit` (`verify plan-set`'s `drift`)
2935
+ - `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug` — Phase info
2936
+ - `plans`, `summaries`, `incomplete_plans`, `plan_count`, `incomplete_count` — Plan inventory
2937
+ - `branch_name` — Pre-computed branch name (if branching enabled)
2938
+ - `milestone_version`, `milestone_name`, `milestone_slug` — Milestone info
2939
+ - `state_exists`, `roadmap_exists`, `config_exists` — File existence checks
2940
+ - `phase_req_ids` — Requirement IDs extracted from roadmap.md
2941
+ - `plans_by_tier` — Tier breakdown: `{ micro: N, standard: N, full: N }`
2942
+ - `total_budget_points` — Budget limit for this execution
2943
+ - `estimated_points` — Total effort points (XS=1, S=2, M=4, L=10, XL=20)
2944
+ - `budget_exceeded` — `true` if estimated > budget
2945
+ - `execution_mode` — Execution ordering strategy (default: `wave_order`)
2946
+ - `dry_run` — Whether this is a preview-only run
2947
+ - `rollback_tag` — always `null` (the tag is created separately by `rollback-snapshot`)
2948
+
2949
+ ---
2950
+
2951
+ ### `init plan-phase <phase>`
2952
+
2953
+ All context for the plan-phase workflow.
2954
+
2955
+ ```bash
2956
+ pan-tools init plan-phase 5 [--raw]
2957
+ ```
2958
+
2959
+ **Key output fields:**
2960
+ - `researcher_model`, `planner_model`, `checker_model` — Resolved model names
2961
+ - `research_enabled`, `plan_checker_enabled`, `commit_docs` — Workflow flags
2962
+ - Phase info, existing artifacts (`has_research`, `has_context`, `has_plans`)
2963
+ - `context_path`, `research_path`, `verification_path`, `uat_path` — Existing file paths
2964
+ - `phase_req_ids` — Requirement IDs extracted from roadmap.md
2965
+
2966
+ ---
2967
+
2968
+ ### `init new-project`
2969
+
2970
+ All context for the new-project workflow. Includes brownfield detection.
2971
+
2972
+ ```bash
2973
+ pan-tools init new-project [--raw]
2974
+ ```
2975
+
2976
+ **Key output fields:**
2977
+ - `researcher_model`, `roadmapper_model` — Model names
2978
+ - `project_exists`, `has_codebase_map`, `planning_exists` — Existing state
2979
+ - `has_existing_code`, `has_package_file`, `is_brownfield`, `needs_codebase_map` — Brownfield detection
2980
+ - `has_git` — **runs `git init` when the project is not yet a repository** and reports the result; `brave_search_available` — environment detection; `shared_planning_tree` — `{tool, evidence}` when another tool shares the planning tree (planning-with-files), else `null`. A tree another tool owns (gsd-core) is refused instead: `{error, evidence, fix}` at exit 1.
2981
+
2982
+ ---
2983
+
2984
+ ### `init new-milestone`
2985
+
2986
+ All context for the new-milestone workflow.
2987
+
2988
+ ```bash
2989
+ pan-tools init new-milestone [--raw]
2990
+ ```
2991
+
2992
+ **Key output fields:**
2993
+ - `researcher_model`, `roadmapper_model` — Model names
2994
+ - `current_milestone`, `current_milestone_name` — Current milestone info
2995
+ - File existence checks for project.md, roadmap.md, state.md
2996
+
2997
+ ---
2998
+
2999
+ ### `init quick <description>`
3000
+
3001
+ All context for the quick task workflow.
3002
+
3003
+ ```bash
3004
+ pan-tools init quick "Fix login button alignment" [--raw]
3005
+ ```
3006
+
3007
+ **Key output fields:**
3008
+ - `planner_model`, `executor_model`, `checker_model`, `verifier_model` — Model names
3009
+ - `next_num`, `slug`, `description` — Quick task identifiers
3010
+ - `date`, `timestamp` — Current date/time
3011
+ - `quick_dir`, `task_dir` — Computed paths
3012
+
3013
+ ---
3014
+
3015
+ ### `init resume`
3016
+
3017
+ All context for the resume-project workflow.
3018
+
3019
+ ```bash
3020
+ pan-tools init resume [--raw]
3021
+ ```
3022
+
3023
+ **Key output fields:**
3024
+ - `state_exists`, `roadmap_exists`, `project_exists`, `planning_exists` — File existence
3025
+ - `has_interrupted_agent`, `interrupted_agent_id` — Agent state recovery
3026
+ - `commit_docs` — Config
3027
+
3028
+ ---
3029
+
3030
+ ### `init verify-work <phase>`
3031
+
3032
+ All context for the verify-work workflow.
3033
+
3034
+ ```bash
3035
+ pan-tools init verify-work 5 [--raw]
3036
+ ```
3037
+
3038
+ **Key output fields:**
3039
+ - `planner_model`, `checker_model` — Model names
3040
+ - Phase info, `has_verification` — Existing artifacts
3041
+
3042
+ ---
3043
+
3044
+ ### `init phase-op <phase>`
3045
+
3046
+ Generic phase operation context. Falls back to roadmap.md if no directory exists.
3047
+
3048
+ ```bash
3049
+ pan-tools init phase-op 5 [--raw]
3050
+ ```
3051
+
3052
+ **Key output fields:**
3053
+ - `commit_docs`, `brave_search` — Config flags
3054
+ - Phase info, existing artifacts (`has_research`, `has_context`, `has_plans`, `has_verification`)
3055
+ - `context_path`, `research_path`, `verification_path`, `uat_path` — File paths (if found)
3056
+
3057
+ ---
3058
+
3059
+ ### `init todos [area]`
3060
+
3061
+ All context for todo workflows.
3062
+
3063
+ ```bash
3064
+ pan-tools init todos [--raw]
3065
+ pan-tools init todos refactoring [--raw]
3066
+ ```
3067
+
3068
+ **Key output fields:**
3069
+ - `todo_count`, `todos[]` — Todo inventory
3070
+ - `area_filter` — Applied area filter
3071
+ - `pending_dir`, `completed_dir` — Directory paths
3072
+ - `pending_dir_exists`, `todos_dir_exists` — Directory existence
3073
+
3074
+ ---
3075
+
3076
+ ### `init milestone-op [--track <name>] [--all-tracks]`
3077
+
3078
+ All context for milestone operations. Backs `/pan:milestone-audit`.
3079
+
3080
+ ```bash
3081
+ pan-tools init milestone-op [--raw]
3082
+ pan-tools init milestone-op --track core
3083
+ pan-tools init milestone-op --all-tracks
3084
+ ```
3085
+
3086
+ **Key output fields:**
3087
+ - `planning_root`, `track`, `planning_root_source`, `planning_root_exists` — which tree this came from
3088
+ - `milestone_version`, `milestone_name`, `milestone_slug` — Current milestone
3089
+ - `milestone_status`, `milestone_basis`, `milestone_ambiguous`, `milestone_candidates` — **how** the milestone was decided
3090
+ - `phase_count`, `completed_phases`, `all_phases_complete` — Phase progress
3091
+ - `archived_milestones[]`, `archive_count` — Archive info
3092
+
3093
+ **`milestone_basis`** is one of `marked-current` (an explicit `(current)` / 🚧 marker), `first-unshipped`, `last-shipped` (the highest label when every milestone is shipped), `state` (no milestone heading, so the `milestone:` recorded in state.md's front matter), `default` (no roadmap), or `no-milestone-heading` (no heading and nothing recorded). It exists so a caller can tell a real `v1.0` from a fallback.
3094
+
3095
+ A milestone heading carries a version (`v4.1`) anywhere in its text, or a letter-series label (`R-2`, `AB-C`) as its **first word** — after status glyphs and an optional "Milestone". A heading repeated later in the roadmap (a traceability or archive section) counts once.
3096
+
3097
+ **`milestone_ambiguous: true`** means the roadmap marks more than one milestone current — a planning-state error. The audit workflow stops rather than picking one, because auditing a silently-chosen milestone is how a report ends up describing a milestone that does not exist.
3098
+
3099
+ **`--all-tracks`** returns `{all_tracks, track_count, ambiguous_tracks[], tracks[]}` — one full payload per planning tree, each labelled with its own `track` and `planning_root`. Use it to see every tree's milestone state before choosing which to audit.
3100
+
3101
+ ---
3102
+
3103
+ ### `init map-codebase`
3104
+
3105
+ All context for the map-codebase workflow.
3106
+
3107
+ ```bash
3108
+ pan-tools init map-codebase [--raw]
3109
+ ```
3110
+
3111
+ **Key output fields:**
3112
+ - `mapper_model` — Resolved model name
3113
+ - `search_gitignored`, `parallelization` — Config flags
3114
+ - `existing_maps[]`, `has_maps` — Existing codebase maps
3115
+
3116
+ ---
3117
+
3118
+ ### `init progress`
3119
+
3120
+ All context for the progress workflow.
3121
+
3122
+ ```bash
3123
+ pan-tools init progress [--raw]
3124
+ ```
3125
+
3126
+ **Key output fields:**
3127
+ - `executor_model`, `planner_model` — Model names
3128
+ - `milestone_version`, `milestone_name` — Milestone info
3129
+ - `phases[]`, `phase_count`, `completed_count`, `in_progress_count` — Phase overview
3130
+ - `current_phase`, `next_phase`, `paused_at`, `has_work_in_progress` — Current state
3131
+
3132
+ ---
3133
+
3134
+ ## 18. Focus Commands
3135
+
3136
+ Strategic project management: work item scanning, capacity budgeting, documentation sync, and execution pipelines.
3137
+
3138
+ **Module:** `focus.cjs`
3139
+
3140
+ ### `focus scan`
3141
+
3142
+ Collect, classify, and sort all work items from phases, todos, and error patterns.
3143
+
3144
+ ```bash
3145
+ pan-tools focus scan [--lean] [--raw]
3146
+ ```
3147
+
3148
+ **Sources scanned:**
3149
+ - Phase plans (roadmap.md + phase directory plan.md frontmatter)
3150
+ - Pending todos (`.planning/todos/pending/`)
3151
+ - Error patterns (`.planning/patterns.md`)
3152
+
3153
+ **Key output fields:**
3154
+ - `items[]` — Sorted work items with `id`, `title`, `source`, `priority`, `effort`, `points`, `realityScore`
3155
+ - `sources` — Count per source type: `{phases, todos, patterns}`
3156
+ - `total` — Total item count
3157
+ - `priorities` — Count per priority level: `{P0: N, P1: N, ...}`
3158
+ - `source_todos` — `{count, items[{file, line, tag, text}]}`: `TODO`/`FIXME`/`XXX`/`HACK` markers in `pan-wizard-core/bin/lib/*.cjs` under the project root, so `{count: 0, items: []}` in any project other than the PAN source tree. Not added to `items`
3159
+
3160
+ When no work item is found it prints `{items: [], sources, total: 0, message: "No work items found"}` instead, with no `priorities` or `source_todos`.
3161
+
3162
+ **Flags:**
3163
+ - `--lean` — Filter items with Reality Score < 1.5 (only affects P3-P6 items)
3164
+
3165
+ ### `focus plan`
3166
+
3167
+ Create a capacity-budgeted execution batch from scanned items.
3168
+
3169
+ ```bash
3170
+ pan-tools focus plan [--budget N] [--mode MODE] [--priority P0-P6] [--lean] [--raw]
3171
+ ```
3172
+
3173
+ **Modes:**
3174
+ | Mode | Budget | Algorithm |
3175
+ |------|--------|-----------|
3176
+ | `bugfix` | 40 pts | Single greedy pass over P0 → P4 in priority order (P5–P6 excluded; no item-type filter) |
3177
+ | `balanced` | 50 pts | Stability (60%) + Feature (40%) split |
3178
+ | `features` | 50 pts | P0 first (mandatory), then what that pass left splits 80% features (P3–P5) / 20% stability (P1–P2) |
3179
+ | `full` | 60 pts | One greedy pass over all priorities in priority order (P0 first, smallest effort first within a priority) |
3180
+
3181
+ **Key output fields:**
3182
+ - `mode`, `budget`, `allocated` — Budget allocation
3183
+ - `items_selected`, `items_remaining` — Batch sizing
3184
+ - `batch[]` — Selected items with a `tier` field, plus a `track` (`mandatory`, `stability` or `feature`) in `balanced` and `features` mode
3185
+ - `batch_file` — Path to saved batch JSON
3186
+
3187
+ **Writes:** `.planning/focus/batch-YYYY-MM-DD.json`
3188
+
3189
+ ### `focus sync`
3190
+
3191
+ Check documentation staleness by comparing actual file counts against documented counts.
3192
+
3193
+ ```bash
3194
+ pan-tools focus sync [--check-only] [--tests N] [--suites N] [--raw] # --tests/--suites supply live counts to reconcile against the docs
3195
+ ```
3196
+
3197
+ **Key output fields:**
3198
+ - `actuals` — `{commands, agents, modules}` actual counts
3199
+ - `stale[]` — Stale entries with `{file, entity, documented, actual}`
3200
+ - `current[]` — Current entries
3201
+ - `stale_count`, `needs_sync` — Summary
3202
+
3203
+ ### `focus exec`
3204
+
3205
+ Load the newest batch in `.planning/focus/` and classify its items by execution tier. Newest means the date in the file name (`batch-YYYY-MM-DD.json` from `focus plan`, or `batch-YYYY-MM-DD-<slug>.json`), then the time the file was written for batches from the same day. That is the batch `/pan:focus-auto --step exec` runs. The items come from a `batch` array, or from `items`, which `/pan:focus-auto` and agent-written batches use. A newest batch with neither list, or one that is not valid JSON, is refused with an error that names the file; an older batch never stands in.
3206
+
3207
+ ```bash
3208
+ pan-tools focus exec [--dry-run] [--force] [--raw] # refuses with dirty_working_tree on uncommitted changes unless --dry-run or --force
3209
+ ```
3210
+
3211
+ **Key output fields:**
3212
+ - `dry_run` — Whether this is a preview
3213
+ - `mode`, `budget`, `allocated` — From batch
3214
+ - `total_items` — Items in batch
3215
+ - `tiers` — `{micro, standard, full}` counts
3216
+ - `items[]` — Full batch items
3217
+ - `batch_file` — The batch file that was read
3218
+
3219
+ **Reads:** The newest `.planning/focus/batch-*.json`
3220
+
3221
+ ### `focus auto`
3222
+
3223
+ Auto-runner state management: initialize, status, update, stop.
3224
+
3225
+ ```bash
3226
+ pan-tools focus auto [--source scan|backlog] [--category CAT] [--mode MODE] [--budget N] [--max-cycles N] [--enforce-budget] [--verify-reserve F] [--deep-review]
3227
+ [--total-budget N] [--parallel-research] [--parallel-verify] [--clean-seal]
3228
+ [--status] [--stop] [--update] [--continue] [--dry-run] [--raw]
3229
+ ```
3230
+
3231
+ **Categories:** cleanup (P3-P5), tests (P2-P5), stability (P0-P2), features (P3-P5), docs (P5-P6), optimize (P1-P4), prompts (P0-P6), security (P0-P2), distill (P1-P5)
3232
+
3233
+ **Work source (ADR-0031):** `--source scan` (default) selects work by category code-scan; `--source backlog` ranks actionable `roadmap.md`/`requirements.md` items by value/effort. `--parallel-research`/`--parallel-verify` fan those stages out via the Workflow tool (implement/exec stays serial); `--clean-seal` runs one clean build + full verification after the last item. All default off.
3234
+
3235
+ **Operations:**
3236
+ - Default: Initialize new auto-run with category defaults. Defaults: `--max-cycles` 10 (1–50), `--total-budget` 500 (5–5000), `--budget` per category (5–100)
3237
+ - `--status`: Show current run state with computed budget/cycles remaining
3238
+ - `--update`: Record cycle results (pass `--items-completed`, `--items-failed`, `--points-used`, `--tests-before`, `--tests-after`, `--batch-file`, `--command`, and — for the `prompts` category — `--prompts-remaining N`). **Side effect:** in a git repository, each update runs the memory auto-optimize, regenerates the HTML reports when `workflow.phase_reports.enabled`, and checkpoint-commits `.planning/` (`commit_hash` in the output). None of the three happens when `focus.auto_commit` or `commit_docs` is false, or outside a git repository
3239
+ - `--stop`: Gracefully stop an active run
3240
+ - `--continue`: Resume a stopped/initialized run
3241
+ - `--dry-run`: Show what would be initialized without writing
3242
+
3243
+ **Stop conditions (auto-detected on --update):**
3244
+ - `regression` — tests decreased between cycles
3245
+ - `budget_cap` — cumulative points reached `--total-budget`; fires only when enforcement is on (`--enforce-budget` or config `budget.enforce`) — the budget is advisory otherwise
3246
+ - `budget_reserve_reached` — under enforcement, spend crossed into the verify reserve (`--verify-reserve`, default `budget.verify_reserve`)
3247
+ - `security_complete` / `distill_complete` — a cycle completed nothing in that category (the category-specific form of `zero_completed`)
3248
+ - `prompts_complete` — the `prompts` category has no prompts remaining
3249
+ - `max_cycles` — iteration limit reached
3250
+ - `zero_completed` — no items completed in a cycle
3251
+ - `diminishing_returns` — optimize only: cycle efficiency < 30% of previous cycle
3252
+ - `weekly_limit` — a fresh `cost limits` reading puts Claude Code's 7-day usage window at or past `cost.weekly_limit_stop_pct` (default 90; `0` turns it off). Like `regression`, the run is stopped rather than completed, and `--continue` resumes it
3253
+
3254
+ **State file:** `.planning/focus/auto-run.json`
3255
+
3256
+ ---
3257
+
3258
+ ### `focus design`
3259
+
3260
+ Workflow-only command that routes to the 10-phase investigation pipeline.
3261
+
3262
+ ```bash
3263
+ pan-tools focus design [--raw]
3264
+ ```
3265
+
3266
+ **Output:** JSON message directing to `/pan:focus-design` workflow.
3267
+
3268
+ **Modes** (flags of the `/pan:focus-design` command; the CLI verb itself parses none): `--full` (default), `--internal`, `--outward`, `--spike`
3269
+ **Modifiers:** `--gate`, `--audit`, `--mvp`
3270
+
3271
+ ---
3272
+
3273
+ ## 19. Standards Commands
3274
+
3275
+ Industry standards selection and advisory compliance. Select standards from a built-in catalog, get project-type recommendations, and track compliance via checklists.
3276
+
3277
+ **Module:** `config.cjs`
3278
+
3279
+ ### `standards list [--category <cat>]`
3280
+
3281
+ List all available standards from the built-in catalog.
3282
+
3283
+ ```bash
3284
+ pan-tools standards list [--raw]
3285
+ pan-tools standards list --category security
3286
+ ```
3287
+
3288
+ **Flags:**
3289
+ - `--category` — Filter by category (security, accessibility, quality, architecture, process)
3290
+
3291
+ **JSON output:**
3292
+ ```json
3293
+ {
3294
+ "standards": [
3295
+ { "id": "owasp-top10", "name": "OWASP Top 10 (2025)", "category": "security", "level": "foundational", "description": "...", "checklist_items": 10 }
3296
+ ],
3297
+ "count": <N>
3298
+ }
3299
+ ```
3300
+
3301
+ ### `standards select <id>`
3302
+
3303
+ Add a standard to the project. Creates/updates `.planning/standards.md`.
3304
+
3305
+ ```bash
3306
+ pan-tools standards select owasp-top10
3307
+ pan-tools standards select wcag-22
3308
+ ```
3309
+
3310
+ **JSON output:**
3311
+ ```json
3312
+ {
3313
+ "added": "owasp-top10",
3314
+ "project_standards": ["owasp-top10"],
3315
+ "standards_file": ".planning/standards.md"
3316
+ }
3317
+ ```
3318
+
3319
+ **Errors:** Unknown standard ID, duplicate selection.
3320
+
3321
+ ### `standards remove <id>`
3322
+
3323
+ Remove a standard from the project. Deletes standards.md when last standard removed.
3324
+
3325
+ ```bash
3326
+ pan-tools standards remove owasp-top10
3327
+ ```
3328
+
3329
+ **JSON output:**
3330
+ ```json
3331
+ {
3332
+ "removed": "owasp-top10",
3333
+ "project_standards": ["stride"],
3334
+ "standards_file": ".planning/standards.md"
3335
+ }
3336
+ ```
3337
+
3338
+ ### `standards status`
3339
+
3340
+ Report compliance status for all selected standards. Counts checked vs unchecked items.
3341
+
3342
+ ```bash
3343
+ pan-tools standards status [--raw]
3344
+ ```
3345
+
3346
+ **JSON output:**
3347
+ ```json
3348
+ {
3349
+ "project_standards": ["owasp-top10", "stride"],
3350
+ "checks": [
3351
+ { "standard_id": "owasp-top10", "standard_name": "OWASP Top 10 (2025)", "category": "security", "status": "partial", "checklist_items": 10, "verified_items": 3, "coverage": "30%" }
3352
+ ],
3353
+ "overall_status": "partial"
3354
+ }
3355
+ ```
3356
+
3357
+ **Status values:** `none` (no standards), `configured` (0% verified), `partial` (some verified), `complete` (all verified).
3358
+
3359
+ ### `standards recommend`
3360
+
3361
+ Recommend standards based on project.md content analysis. Detects project types (web, api, ai, agent, enterprise, cli) via keyword matching; falls back to `general` when none match.
3362
+
3363
+ ```bash
3364
+ pan-tools standards recommend [--raw]
3365
+ ```
3366
+
3367
+ **JSON output:**
3368
+ ```json
3369
+ {
3370
+ "project_types": ["web", "api"],
3371
+ "recommendations": [
3372
+ { "id": "owasp-top10", "name": "OWASP Top 10 (2025)", "reason": "web project detected", "priority": "high", "source_type": "web" }
3373
+ ]
3374
+ }
3375
+ ```
3376
+
3377
+ **Requires:** `.planning/project.md` must exist.
3378
+
3379
+ ### `standards phase-track`
3380
+
3381
+ Show which standards are relevant to a specific phase and their compliance state. Analyzes plan.md content for keywords that map to standards.
3382
+
3383
+ ```bash
3384
+ pan-tools standards phase-track <phase-number> [--raw]
3385
+ ```
3386
+
3387
+ **JSON output:**
3388
+ ```json
3389
+ {
3390
+ "phase": "1",
3391
+ "phase_name": "auth-setup",
3392
+ "relevant_standards": ["owasp-top10", "owasp-asvs-l1"],
3393
+ "compliance": [
3394
+ { "standard_id": "owasp-top10", "standard_name": "OWASP Top 10 (2025)", "selected": true, "status": "configured", "checklist_items": 10, "verified_items": 0, "coverage": "0%" },
3395
+ { "standard_id": "owasp-asvs-l1", "standard_name": "OWASP ASVS Level 1", "selected": false, "status": "not_selected", "action": "Consider selecting with: pan-tools standards select owasp-asvs-l1" }
3396
+ ]
3397
+ }
3398
+ ```
3399
+
3400
+ **Keyword detection:** Maps phase plan content keywords (auth, login, security, accessibility, llm, agent, etc.) to relevant standard IDs.
3401
+
3402
+ ### `standards tools`
3403
+
3404
+ List external scanning tools recommended for selected or specified standards.
3405
+
3406
+ ```bash
3407
+ pan-tools standards tools [standard-id] [--raw]
3408
+ ```
3409
+
3410
+ Without arguments, uses the project's selected standards. With a standard ID, shows tools for that specific standard.
3411
+
3412
+ **JSON output:**
3413
+ ```json
3414
+ {
3415
+ "standards_queried": ["owasp-top10"],
3416
+ "recommendations": [
3417
+ { "standard_id": "owasp-top10", "standard_name": "OWASP Top 10 (2025)", "tools": [
3418
+ { "name": "OWASP ZAP", "url": "https://www.zaproxy.org/", "description": "Dynamic application security scanner" },
3419
+ { "name": "Semgrep", "url": "https://semgrep.dev/", "description": "Static analysis with OWASP rule packs" }
3420
+ ]}
3421
+ ],
3422
+ "unique_tools": [
3423
+ { "name": "OWASP ZAP", "url": "https://www.zaproxy.org/", "description": "Dynamic application security scanner", "standards": ["owasp-top10"] }
3424
+ ]
3425
+ }
3426
+ ```
3427
+
3428
+ ---
3429
+
3430
+ ## 20. Operations Commands
3431
+
3432
+ Pre-flight checks, project dashboard, session learnings, and dependency validation. Based on competitive analysis of 6 AI coding tools (ADR-0013).
3433
+
3434
+ ### `preflight [target]`
3435
+
3436
+ Run pre-flight validation checks before execution. Validates state, blockers, git status, config, and error patterns.
3437
+
3438
+ **Module:** `verify-preflight.cjs` (re-exported by `verify.cjs`)
3439
+
3440
+ ```bash
3441
+ pan-tools preflight [target] [--raw]
3442
+ ```
3443
+
3444
+ **Arguments:**
3445
+ - `target` (optional) — `batch` to check for active batch file, or a phase number (e.g., `01`) to check phase directory exists
3446
+
3447
+ **Checks performed:**
3448
+ | Check | What it validates |
3449
+ |-------|-------------------|
3450
+ | `planning_dir` | `.planning/` directory exists |
3451
+ | `state_readable` | `state.md` can be parsed |
3452
+ | `no_blockers` | No open bullets under state.md's `### Blockers/Concerns` section (or a legacy `## Blockers` heading), where `state add-blocker` writes |
3453
+ | `git_clean` | Git working tree is clean |
3454
+ | `error_patterns` | Reports count of known error patterns |
3455
+ | `config_exists` | `config.json` exists |
3456
+ | `batch_exists` / `target_phase` | Target-specific check (if target provided) |
3457
+
3458
+ **JSON output:**
3459
+ ```json
3460
+ {
3461
+ "ready": true,
3462
+ "checks": [
3463
+ { "name": "state_readable", "passed": true, "detail": "..." }
3464
+ ],
3465
+ "blockers": [],
3466
+ "passed": 6,
3467
+ "total": 6
3468
+ }
3469
+ ```
3470
+
3471
+ ### `dashboard`
3472
+
3473
+ Aggregated project overview showing phase progress, the next phase, and the number of open blockers in state.md.
3474
+
3475
+ > **Not the HTML dashboard.** `pan-tools dashboard` emits a compact **JSON** project overview (this command, `state.cjs`). For the single-page **HTML** army dashboard, see the `hud` command (`hud.cjs`). The slash command `/pan:hud` is a discoverability **alias for `/pan:hud`** (the HTML view) — not for this JSON command.
3476
+
3477
+ **Module:** `state.cjs`
3478
+
3479
+ ```bash
3480
+ pan-tools dashboard [--raw]
3481
+ ```
3482
+
3483
+ **JSON output:**
3484
+ ```json
3485
+ {
3486
+ "project": "Project Name",
3487
+ "version": "1.0.0",
3488
+ "current_phase": { "number": "03", "name": "core", "status": "Executing" },
3489
+ "blockers": 0,
3490
+ "progress": { "phases_completed": 2, "phases_total": 5, "plans_total": 6, "plans_completed": 4 },
3491
+ "milestone": { "version": "v1.0", "name": "Initial Release" },
3492
+ "next_phase": { "number": "04", "name": "testing" },
3493
+ "last_activity": "2026-03-03"
3494
+ }
3495
+ ```
3496
+
3497
+ ### `learnings extract`
3498
+
3499
+ Auto-extract learnings from error patterns and phase summaries.
3500
+
3501
+ **Module:** `commands-learnings.cjs` (re-exported by `commands.cjs`)
3502
+
3503
+ ```bash
3504
+ pan-tools learnings extract [--raw]
3505
+ ```
3506
+
3507
+ **Sources:**
3508
+ - Error patterns (`.planning/patterns.md`) → error-resolution learnings
3509
+ - Phase summaries (`key-files` frontmatter: a flat list, or the summary templates' `{created, modified}`) → file co-change patterns (requires 2+ co-occurrences)
3510
+ - Patterns established (summary frontmatter) → pattern learnings
3511
+
3512
+ **Writes:** `.planning/learnings.md`
3513
+
3514
+ **Deduplication:** Skips learnings whose detail string already exists in the file.
3515
+
3516
+ **JSON output:**
3517
+ ```json
3518
+ {
3519
+ "extracted": 3,
3520
+ "total": 15,
3521
+ "by_type": { "error-resolution": 1, "co-change": 1, "pattern": 1 }
3522
+ }
3523
+ ```
3524
+
3525
+ ### `learnings list`
3526
+
3527
+ List all extracted learnings with type breakdown.
3528
+
3529
+ **Module:** `commands-learnings.cjs` (re-exported by `commands.cjs`)
3530
+
3531
+ ```bash
3532
+ pan-tools learnings list [--raw]
3533
+ ```
3534
+
3535
+ **JSON output:**
3536
+ ```json
3537
+ {
3538
+ "learnings": [
3539
+ { "id": "LEARN-001", "type": "error-resolution", "title": "Import fix", "detail": "old -> new", "files": [], "date": "2026-03-01" }
3540
+ ],
3541
+ "count": 1,
3542
+ "by_type": { "error-resolution": 1 }
3543
+ }
3544
+ ```
3545
+
3546
+ ### `learnings prune`
3547
+
3548
+ Remove learnings by age or specific ID.
3549
+
3550
+ **Module:** `commands-learnings.cjs` (re-exported by `commands.cjs`)
3551
+
3552
+ ```bash
3553
+ pan-tools learnings prune --days N [--raw]
3554
+ pan-tools learnings prune --id LEARN-NNN [--raw]
3555
+ ```
3556
+
3557
+ **Flags:**
3558
+ - `--days N` — Remove entries with dates older than N days
3559
+ - `--id LEARN-NNN` — Remove a specific entry by ID
3560
+
3561
+ **JSON output:**
3562
+ ```json
3563
+ {
3564
+ "pruned": 2,
3565
+ "remaining": 13
3566
+ }
3567
+ ```
3568
+
3569
+ ### `links validate`
3570
+
3571
+ Validate the doc–code link graph (ADR-0027). Walks `docs/`, `pan-wizard-core/{workflows,templates,references,learnings}/`, `commands/`, `agents/` for inline `[[<id>]]` refs, and `pan-wizard-core/`, `bin/`, `hooks/`, `scripts/` for `// @pan: <id>` source-comment anchors. Reports broken refs, stale anchors, and uncovered backlink contracts.
3572
+
3573
+ **Module:** `links.cjs`
3574
+
3575
+ ```bash
3576
+ pan-tools links validate [--strict] [--doc-root <p>] [--source-root <p>] [--raw]
3577
+ ```
3578
+
3579
+ **Flags:**
3580
+ - `--strict` — fail on real warnings, not only errors. (B-002 single-source informational warning is exempt.)
3581
+ - `--doc-root <path>` — override default doc roots; repeatable.
3582
+ - `--source-root <path>` — override default source roots; repeatable.
3583
+
3584
+ **Exit codes:**
3585
+ - `0` — pass
3586
+ - `1` — fail (errors present, or non-B-002 warnings under `--strict`)
3587
+
3588
+ **JSON output:** `{ ok, summary: { total_findings, errors, warnings, status, doc_files_scanned, source_files_scanned, anchors_found, forward_links_found, backlink_contracts_checked }, findings: [{ code, severity, source, source_line, target, detail }] }`
3589
+
3590
+ **Finding codes:** F-001/F-002 (broken inline / section refs, error), F-003/F-004 (key_links path / regex, warning), B-001 (uncovered `require-code-mention`, error), B-002 (single-source informational, warning), A-001/A-002/A-004 (stale anchor target / section / empty id).
3591
+
3592
+ See ADR-0027 and `docs/specs/doc_code_link_graph_featureai.md` for the wire-level spec.
3593
+
3594
+ ### `deps validate`
3595
+
3596
+ Cross-reference roadmap phases vs disk directories and detect orphaned requirements.
3597
+
3598
+ **Module:** `verify-preflight.cjs` (re-exported by `verify.cjs`)
3599
+
3600
+ ```bash
3601
+ pan-tools deps validate [--raw]
3602
+ ```
3603
+
3604
+ **Checks performed:**
3605
+ 1. Parse roadmap.md for phase headers
3606
+ 2. Scan `.planning/phases/` directories
3607
+ 3. Cross-reference: roadmap phases not on disk = `missing_phases`, disk dirs not in roadmap = `orphaned_dirs`
3608
+ 4. Parse requirements.md for REQ-NN IDs
3609
+ 5. Scan phase summaries for REQ-NN references
3610
+ 6. Unreferenced uncompleted requirements = `orphaned_reqs`
3611
+
3612
+ **JSON output:**
3613
+ ```json
3614
+ {
3615
+ "valid": true,
3616
+ "issues": [
3617
+ { "type": "info", "message": "Requirement REQ-09 not marked complete and not referenced in any summary" }
3618
+ ],
3619
+ "roadmap_phases": 5,
3620
+ "disk_phases": 5,
3621
+ "requirements_total": 10,
3622
+ "requirements_completed": 8,
3623
+ "orphaned_reqs": ["REQ-09"],
3624
+ "missing_phases": [],
3625
+ "orphaned_dirs": []
3626
+ }
3627
+ ```
3628
+
3629
+ ### `drift-check`
3630
+
3631
+ Check changed files against project conventions and produce a quantitative drift score.
3632
+
3633
+ **Module:** `verify-drift.cjs` (re-exported by `verify.cjs`)
3634
+
3635
+ ```bash
3636
+ pan-tools drift-check [--since <ref>] [--threshold <0.0-1.0>] [--files <path,...>] [--verbose] [--raw]
3637
+ ```
3638
+
3639
+ **Arguments:**
3640
+ - `--since <ref>` — Git ref to diff against (default: HEAD)
3641
+ - `--threshold <n>` — Pass/fail threshold (default: 0.5)
3642
+ - `--files <paths>` — Comma-separated specific files to check (bypasses git diff)
3643
+ - `--verbose` — Include `per_file` breakdown grouping violations by file
3644
+
3645
+ **Convention sources:** `.planning/codebase/conventions.md` (or an older map's `CONVENTIONS.md`) + `CLAUDE.md` + the built-in PAN rules (`BUILTIN_DRIFT_RULES` in `constants.cjs`).
3646
+
3647
+ **JSON output:**
3648
+ ```json
3649
+ {
3650
+ "drift_score": 0.35,
3651
+ "verdict": "low",
3652
+ "passed": true,
3653
+ "threshold": 0.5,
3654
+ "violations": [
3655
+ {"file": "lib/verify.cjs", "line": 45, "rule": "no-console-log", "message": "Use output() instead of console.log", "severity": "error"}
3656
+ ],
3657
+ "violation_count": 3,
3658
+ "files_checked": 8,
3659
+ "conventions_loaded": 12,
3660
+ "summary": "drift: 0.35 (low) — 3 violations in 8 files"
3661
+ }
3662
+ ```
3663
+
3664
+ **Verdicts:** clean (0-0.2), low (0.2-0.5), medium (0.5-0.8), high (0.8-1.0).
3665
+
3666
+ ---
3667
+
3668
+ ### `retro`
3669
+
3670
+ Milestone retrospective — analyze estimation accuracy, verification patterns, and common gap types.
3671
+
3672
+ **Module:** `verify-retro.cjs` (re-exported by `verify.cjs`)
3673
+
3674
+ ```bash
3675
+ pan-tools retro [--write-memory] [--max N] [--raw]
3676
+ ```
3677
+
3678
+ **Flags:**
3679
+ - `--write-memory` — append top-N gap patterns as lessons to `pan-planner` memory; write a verifier lesson when first-try rate < 60% over ≥3 runs. The entries are stored for `/pan:knowledge`; no workflow loads them into agents, and `/pan:retro` passes this flag only when the user gives it (ADR-0036, amended `2026-10-04`).
3680
+ - `--max N` — cap the `pan-planner` gap lessons written to memory (default 3, range 1–10); the verifier lesson has its own condition.
3681
+
3682
+ **JSON output:**
3683
+ ```json
3684
+ {
3685
+ "phases_planned": 8,
3686
+ "phases_completed": 6,
3687
+ "phases_decimal": 2,
3688
+ "estimation_accuracy_pct": 75,
3689
+ "verifications_total": 6,
3690
+ "verifications_passed_first_try": 4,
3691
+ "verifications_gaps_found": 2,
3692
+ "verifications_human_needed": 0,
3693
+ "first_try_rate_pct": 67,
3694
+ "common_gap_patterns": [
3695
+ {"pattern": "missing wiring between", "count": 3},
3696
+ {"pattern": "stub detected in", "count": 2}
3697
+ ],
3698
+ "memory": { "wrote": { "pan-planner": 2, "pan-verifier": 0 }, "max": 3 }
3699
+ }
3700
+ ```
3701
+
3702
+ The `memory` field is omitted unless `--write-memory` is set.
3703
+
3704
+ **Error:** `{"error": "roadmap.md not found"}` on stdout, **exit 1**, if no `.planning/roadmap.md` exists.
3705
+
3706
+ ---
3707
+
3708
+ ## 20.1 Capability-Aware Commands
3709
+
3710
+ Commands added for Spec A (frontier-model capability enhancements). All degrade gracefully rather than error on models and runtimes that lack the underlying capability: the installer turns each agent's `effort:` into `model_reasoning_effort` in Codex's agent TOML and into a prose preamble for OpenCode, Gemini CLI and Copilot CLI. Cache priming is withdrawn: `cache prime` only measures (ADR-0023, amended). Nothing here inspects the model at runtime — in particular `codebase estimate-size` picks single-shot vs sharded from repo size alone, so on a smaller-context model you get the sharded path by lowering `--threshold` yourself; there is no automatic fallback.
3711
+
3712
+ ### `memory read <agent>`
3713
+
3714
+ Read the append-only memory log for an agent. Returns parsed entries as JSON.
3715
+
3716
+ **Module:** `memory.cjs`
3717
+
3718
+ ```bash
3719
+ pan-tools memory read <agent> [--raw]
3720
+ ```
3721
+
3722
+ **JSON output when agent exists:**
3723
+ ```json
3724
+ {
3725
+ "agent": "pan-planner",
3726
+ "exists": true,
3727
+ "entries": [
3728
+ "2026-04-18: Prefer bulk Postgres writes",
3729
+ "2026-04-18: Recurring plan gap (3x): missing wiring between API and UI"
3730
+ ]
3731
+ }
3732
+ ```
3733
+
3734
+ Returns `{agent, exists: false, entries: []}` when no memory file exists.
3735
+
3736
+ ### `memory append <agent> <entry> [--cites <path[#symbol]>,...]`
3737
+
3738
+ Append a lesson to an agent's memory. Creates file + directory if missing. Entries are auto-prefixed with today's date if not already prefixed.
3739
+
3740
+ ```bash
3741
+ pan-tools memory append <agent> <entry text can have spaces>
3742
+ pan-tools memory append pan-executor "Batch inserts through the writer" --cites src/db/writer.js#bulkInsert
3743
+ ```
3744
+
3745
+ Agent name must match `^[a-zA-Z0-9_-]+$` (blocks path traversal), and may not be one of PAN's archives in the folder (`quarantine`, `state-archive`, `distill-patterns`). Newlines in the entry are collapsed to spaces.
3746
+
3747
+ **Citations** name the code a lesson rests on: a path relative to the project root, optionally with `#symbol`. They are stored at the end of the entry as `<!-- cites: src/db/writer.js#bulkInsert -->`. Each must hold when written: the file exists and, with a symbol, contains it (a whole word for an identifier). Otherwise the append is refused with `{ "error": "citation …" }`. `memory select` checks them again before every use.
3748
+
3749
+ ### `memory list`
3750
+
3751
+ List the agent logs in `.planning/memory/` and their entry counts — the files `memory select` reads. No workflow loads them into agents (ADR-0036, amended `2026-10-04`). Every other `.md` file there is under `not_loaded` with the reason. PAN's own archives are never memory: `quarantine.md` holds directives PAN refused to follow (ADR-0040), and `state-archive.md` is old state. A file with no `## Entries` list cannot be checked or expired, so it is not loaded either.
3752
+
3753
+ ```bash
3754
+ pan-tools memory list [--raw]
3755
+ ```
3756
+
3757
+ **JSON output:**
3758
+ ```json
3759
+ {
3760
+ "agents": [ {"agent": "pan-planner", "entries": 12}, {"agent": "pan-verifier", "entries": 4} ],
3761
+ "not_loaded": [ {"file": "quarantine.md", "reason": "PAN archive, never loaded as memory"}, {"file": "notes.md", "reason": "no `## Entries` list"} ],
3762
+ "usage": { "entries": 16, "with_evidence": 3, "cited": 5, "used": 12, "never_used": 4 }
3763
+ }
3764
+ ```
3765
+
3766
+ `usage` is the memory layer's telemetry across the agent logs. It counts entries that carry evidence of the failure they came from (`memory record`), entries that cite code, and entries ever used. `memory select --mark-used` counts each day an entry is selected that way (`uses` on its line). An entry never used is memory nobody reads.
3767
+
3768
+ ### `memory compact <agent> [max]`
3769
+
3770
+ Trim an agent's memory file to the last `max` entries (default 500). The entries it drops are appended to `.planning/memory/archive/<agent>.md` first, as `memory prune` does, so nothing a person recorded is lost.
3771
+
3772
+ ```bash
3773
+ pan-tools memory compact <agent> 50
3774
+ ```
3775
+
3776
+ **JSON output:**
3777
+ ```json
3778
+ { "compacted": true, "kept": 50, "removed": 42, "archived": 42, "archive": ".planning/memory/archive/pan-planner.md" }
3779
+ ```
3780
+
3781
+ ### `memory select <agent> [--cue <text>] [--token-budget N] [--recency-floor N] [--all] [--mark-used] [--days N]`
3782
+
3783
+ Return an agent's valid memory entries, for someone who hands them to an agent. No workflow does since `2026-10-04`: exec-phase and plan-phase stopped loading agent memory after the harness found no behavioural effect from it (ADR-0036, amended).
3784
+
3785
+ **Only valid entries are candidates.** An entry is left out, and reported, when:
3786
+ - **`stale`:** one of its citations no longer holds — the file is gone, or the symbol no longer appears in it.
3787
+ - **`expired`:** it has not been used for `--days` days (default `MEMORY_EXPIRE_DAYS` in `constants.cjs`; `0` turns expiry off). The clock runs from its last use, or from its date if it was never used. An undated entry never expires.
3788
+
3789
+ **Selection:** the newest `--recency-floor` valid entries come first, then the entries most relevant to `--cue`, until `--token-budget` is spent (both fall back to built-in defaults). The budget binds the newest entries too: one that does not fit is skipped. A log with a valid entry always returns at least one entry, its newest when no entry fits the budget. `--all` takes every valid entry with no budget.
3790
+
3791
+ **`--mark-used`** records today as the last use of each selected entry and adds the day to its count (`used: YYYY-MM-DD; uses: N` in the entry's `<!-- … -->` comment, beside any `cites`), which is what keeps it from expiring. Without it the command is read-only.
3792
+
3793
+ ```bash
3794
+ pan-tools memory select pan-executor --cue "auth refactor" --token-budget 2000 --recency-floor 5 [--raw]
3795
+ pan-tools memory select pan-executor --all --mark-used --raw
3796
+ ```
3797
+
3798
+ **JSON output:**
3799
+ ```json
3800
+ {
3801
+ "agent": "pan-executor", "cue": "", "mode": "all", "considered": 3,
3802
+ "selected": ["2026-10-01: Batch inserts through the writer <!-- cites: src/db/writer.js#bulkInsert -->"],
3803
+ "stale": [{ "entry": "2026-09-20: Use the legacy exporter <!-- cites: src/legacy.js -->", "missing": ["src/legacy.js (file not found)"] }],
3804
+ "expired": [{ "entry": "2026-06-01: An old lesson", "last_used": "2026-06-01" }],
3805
+ "total_tokens": 22, "dropped": 0, "marked_used": 1
3806
+ }
3807
+ ```
3808
+
3809
+ ### `memory record <agent> --lesson "<correction>" (--finding <id> | --trace <session>) --cites <path[#symbol]>,...`
3810
+
3811
+ The gated write path for agent memory. A lesson is recorded only from an observed failure that was corrected, with the correction, its evidence and a citation:
3812
+
3813
+ - **Evidence, one of:** `--finding <id>`, a finding in `.planning/findings.jsonl` whose status is `fixed` (a verifier gap a fix round closed); or `--trace <session>`, a trace session under `.planning/optimization/traces/` (the optimizer's recurring tool failures). The entry stores it as `evidence: finding:f_…` or `evidence: trace:<session>`.
3814
+ - **Citations:** `--cites`, at least one, each holding now (see `memory append`).
3815
+ - **The lesson:** one line of 20–300 characters that says the correction, what to do next time. It may not repeat the finding, carry an HTML comment, or read as a directive to bypass the process (ADR-0040).
3816
+ - **Once:** a finding gets one lesson, and a lesson already in the log is not recorded again.
3817
+
3818
+ A refusal is reported as `{ "recorded": false, "reason": "…" }` with exit 0: it is the gate working. A missing agent or two pieces of evidence is a usage error. A recorded lesson logs a trace event of type `decision` with category `memory_recorded`, opening the day's trace session when none is active. No workflow calls it since `2026-10-04` (exec-phase's `record_lessons` step was retired with the memory injection). `optimize apply` uses it for an older report's `memory_entry` action.
3819
+
3820
+ ```bash
3821
+ pan-tools memory record pan-executor --finding f_3c9a1e07b2 \
3822
+ --lesson "Give every token type an explicit expiry constant and test the expiry path" \
3823
+ --cites src/session.js#SESSION_TTL_MS
3824
+ ```
3825
+
3826
+ ### `memory prune [<agent>] [--apply] [--days N]`
3827
+
3828
+ Archive the entries `memory select` no longer returns: stale and expired ones, as it judges them. Covers one agent's log, or every agent log when no agent is given. The entries go to `.planning/memory/archive/<agent>.md`, each with the reason. Nothing is deleted: to restore an entry, move its line back under `## Entries`.
3829
+
3830
+ **Safety:** the archive is written first, and only then are those bullet lines taken out of the log, so an interruption can duplicate an entry but never lose one. Everything else in the log is left as it was, line endings included. Dry run by default.
3831
+
3832
+ ```bash
3833
+ pan-tools memory prune # what would move
3834
+ pan-tools memory prune pan-executor --apply
3835
+ pan-tools memory prune --days 0 --apply # stale only, no expiry
3836
+ ```
3837
+
3838
+ `hygiene scan` reports these entries as `memory-stale` with the `prune-memory` fix, and `hygiene clean --apply` runs it.
3839
+
3840
+ ### `memory budget`
3841
+
3842
+ Report the total token footprint of all agent memory files against the project's typical per-call **prompt** size (`median_prompt_tokens` — the median of `input + cache_read + cache_write` over the ledger's trustworthy rows): what the whole store would add to an agent's prompt if it were handed to one. No workflow hands it over since `2026-10-04`, and `validate health --full` no longer reports it. Under prompt caching the uncached `input` alone is tens of tokens, which is why the denominator is the whole prompt. Read-only.
3843
+
3844
+ ```bash
3845
+ pan-tools memory budget [--raw]
3846
+ ```
3847
+
3848
+ ### `memory optimize [--apply] [--keep N]`
3849
+
3850
+ Reconcile the always-loaded project memory so it stays small. Reconciles state.md's append-heavy bullet sections (Decisions / Blockers / Concerns / Todos / Pending Todos / Accumulated Context / Recent Activity / Session Continuity): dedupe, strip placeholders once real entries exist, and cap to the most-recent `N` (default 12), **archiving** the overflow to `.planning/memory/state-archive.md` (bullets that read like injected instructions go to `.planning/memory/quarantine.md` instead, reported as `quarantined`) — nothing is hard-deleted. Also consolidates any per-agent episodic log over the entry cap. Tables, prose, sub-bullets, and every non-target section are preserved byte-for-byte.
3851
+
3852
+ Dry-run by default (reports what *would* change); pass `--apply` to write. Idempotent — a second run on already-lean content is a no-op.
3853
+
3854
+ ```bash
3855
+ pan-tools memory optimize # dry-run: report what would change
3856
+ pan-tools memory optimize --apply # write the reconciled state.md + archive
3857
+ pan-tools memory optimize --apply --keep 20
3858
+ ```
3859
+
3860
+ **JSON output:**
3861
+ ```json
3862
+ {
3863
+ "apply": true,
3864
+ "state": { "changed": true, "sections_touched": ["Decisions"], "archived_entries": 3, "quarantined_entries": 0, "before_bytes": 18240, "after_bytes": 16975 },
3865
+ "agents": [],
3866
+ "archived": 3,
3867
+ "quarantined": 0
3868
+ }
3869
+ ```
3870
+
3871
+ > Auto-optimize also runs automatically inside the focus-auto checkpoint and the normal-flow session record, gated by `memory.auto_optimize` in `.planning/config.json` (default on). Running `memory optimize` by hand is only needed for an out-of-band pass or a custom `--keep`.
3872
+
3873
+ ### `memory rebuild [--apply]`
3874
+
3875
+ Regenerate PAN's *derived* tools-memory as an idempotent projection from source: the marker-fenced PAN section in `AGENTS.md` (read natively by Codex, OpenCode and Copilot CLI; Claude Code reads it through the `CLAUDE.md` bridge below; Gemini CLI reads `GEMINI.md` by default and PAN does not point it at `AGENTS.md`), PAN's block in `CLAUDE.md` (only when the Claude runtime is installed): the `@AGENTS.md` import and a `# Compact instructions` section that tells Claude Code's compaction summary what PAN work needs to resume. An older block holding only the import is brought up to date in place. It also re-derives state.md's YAML frontmatter from the body, keeping recorded values the body does not restate; a state.md with more than one front-matter block is reported as `action: "refused"` and left as it is. User content outside PAN's markers is never touched. Refuses to run inside the PAN source repository.
3876
+
3877
+ Dry-run by default; pass `--apply` to write. A second run changes nothing.
3878
+
3879
+ ```bash
3880
+ pan-tools memory rebuild # dry-run: report the derived targets
3881
+ pan-tools memory rebuild --apply # regenerate AGENTS.md / CLAUDE.md / state.md frontmatter
3882
+ ```
3883
+
3884
+ **JSON output:**
3885
+ ```json
3886
+ {
3887
+ "apply": true,
3888
+ "runtimes": ["claude", "copilot"],
3889
+ "rebuilt": [
3890
+ { "file": "AGENTS.md", "action": "update", "wrote": true },
3891
+ { "file": "CLAUDE.md", "action": "unchanged", "wrote": false },
3892
+ { "file": ".planning/state.md", "action": "update", "wrote": true }
3893
+ ],
3894
+ "changed_count": 2,
3895
+ "directive_warnings": []
3896
+ }
3897
+ ```
3898
+
3899
+ `directive_warnings` lists lines in `AGENTS.md` or `CLAUDE.md` that read like directives aimed at the agent (ADR-0040), each with `file`, `line` and `text`. They are reported for review and never edited.
3900
+
3901
+ ---
3902
+
3903
+ ### `cache prime [--summary]`
3904
+
3905
+ Build an ordered, cache-eligible context block list from stable `.planning/` files (project.md, requirements.md, roadmap.md, state.md, standards.md), plus any planning-root-relative paths listed in `config.json` → `cache.extra_files` (appended after the built-ins so existing cache keys stay stable).
3906
+
3907
+ It is a measurement and primes nothing. The host caches each agent's prompt prefix by itself, and a command's output cannot mark another agent's prompt for caching. The shipped commands used to call it on the belief that it did; they no longer do (ADR-0023, amended). To see how much every agent re-reads, use `context-budget` or `hygiene scan`.
3908
+
3909
+ **Module:** `core.cjs` (wrapper in dispatcher)
3910
+
3911
+ ```bash
3912
+ pan-tools cache prime [--summary] [--raw]
3913
+ ```
3914
+
3915
+ **Flags:**
3916
+ - `--summary` — omit full block content; return only `{path, bytes, cache}` metadata. Useful when the caller only needs the cache key / size estimate.
3917
+
3918
+ **JSON output (full):**
3919
+ ```json
3920
+ {
3921
+ "blocks": [
3922
+ { "path": ".planning/project.md", "content": "...", "cache": true },
3923
+ { "path": ".planning/requirements.md", "content": "...", "cache": true }
3924
+ ],
3925
+ "total_bytes": 42301,
3926
+ "sha": "0632f5e0399f582d"
3927
+ }
3928
+ ```
3929
+
3930
+ The `sha` is a stable 16-char prefix over file path + content, used for cache hit debugging.
3931
+
3932
+ ---
3933
+
3934
+ ### `codebase estimate-size [--threshold N] [--no-docs]`
3935
+
3936
+ Estimate total token size of a repository for single-shot vs sharded `/pan:map-codebase` mode selection.
3937
+
3938
+ **Module:** `codebase.cjs`
3939
+
3940
+ ```bash
3941
+ pan-tools codebase estimate-size [--threshold 700000] [--no-docs] [--raw]
3942
+ ```
3943
+
3944
+ **Flags:**
3945
+ - `--threshold N` — custom single-shot ceiling in tokens (default 700000).
3946
+ - `--no-docs` — skip `docs/*.md` top-level files in the estimate.
3947
+
3948
+ **JSON output:**
3949
+ ```json
3950
+ {
3951
+ "total_bytes": 1872704,
3952
+ "total_tokens": 468176,
3953
+ "threshold": 700000,
3954
+ "mode": "single-shot",
3955
+ "file_count": 111,
3956
+ "languages": { "javascript": 1170060, "python": 18398, "docs": 684246 }
3957
+ }
3958
+ ```
3959
+
3960
+ `mode` is `single-shot` when `total_tokens ≤ threshold`, else `sharded`. Selection is purely size-based — the module performs no model check. The default threshold assumes the consuming agent has a 1M-context window; pass a lower `--threshold` when mapping with a smaller-context model.
3961
+
3962
+ ---
3963
+
3964
+ ### `focus classify-stages [--stdin]`
3965
+
3966
+ Classify a focus-exec batch into parallel-tool-use waves + a `parallelism_hint` string. Used by `/pan:focus-auto --step exec` Stage 3.0 to decide whether to emit Reads/Greps in parallel.
3967
+
3968
+ **Module:** `focus.cjs`
3969
+
3970
+ ```bash
3971
+ pan-tools focus classify-stages [--stdin] [--raw]
3972
+ ```
3973
+
3974
+ **Flags:**
3975
+ - `--stdin` — read items JSON from stdin instead of the newest batch file (the one `focus exec` reads). Without `--stdin`, a newest batch that has no item list is refused with the same error `focus exec` gives.
3976
+
3977
+ **JSON output:**
3978
+ ```json
3979
+ {
3980
+ "waves": [
3981
+ [ {"id": "A", "tier": "MICRO"}, {"id": "B", "tier": "MICRO"} ],
3982
+ [ {"id": "C", "tier": "STANDARD"} ]
3983
+ ],
3984
+ "parallelism_hint": "emit-micro-in-parallel"
3985
+ }
3986
+ ```
3987
+
3988
+ Hint values: `emit-micro-in-parallel`, `emit-standard-in-parallel`, `sequential`.
3989
+
3990
+ ---
3991
+
3992
+ ### `focus reflection`
3993
+
3994
+ Emit a reflection prompt between focus-auto cycles. Reads `{run, cycle, batch, tier}` from stdin.
3995
+
3996
+ **Module:** `focus.cjs`
3997
+
3998
+ ```bash
3999
+ echo '{"run": {...}, "cycle": {...}, "batch": [...], "tier": "reasoning"}' \
4000
+ | pan-tools focus reflection [--raw]
4001
+ ```
4002
+
4003
+ **JSON output:**
4004
+ ```json
4005
+ { "reflect": true, "prompt": "Reflect before committing to cycle 2 of 5 in category \"cleanup\". ...", "reason": "ok" }
4006
+ ```
4007
+
4008
+ **Gate:** the reflection step is **reasoning-tier-only**. `run.reflection_enabled` wins if set — `true` forces reflection on at any tier, `false` forces it off. With the key absent, reflection is enabled only when the incoming `tier` is in `REFLECTION_THRESHOLD.enable_on_tiers` (`constants.cjs`), which is `['reasoning']`; `enabled_default` is `false`. So a `mid`/`fast` tier gets `{reflect: false}` even on a thinking-capable model — the gate reads the resolved tier, never the model's capabilities.
4009
+
4010
+ Returns `{reflect: false, reason: "reflection_disabled"}` when the gate is closed, and `{reflect: false, reason: "no_next_batch"}` when the proposed next batch is empty.
4011
+
4012
+ ---
4013
+
4014
+ ## 21. Codebase Commands
4015
+
4016
+ For `codebase estimate-size`, see Section 20.1 (Capability-Aware Commands) — it's the mode-selection entry point for `/pan:map-codebase` Stage 0.
4017
+
4018
+ ### `codebase detect-languages`
4019
+
4020
+ Detect programming languages used in a codebase via extension mapping and manifest scanning.
4021
+
4022
+ **Module:** `codebase.cjs`
4023
+
4024
+ ```bash
4025
+ pan-tools codebase detect-languages [--raw]
4026
+ ```
4027
+
4028
+ **JSON output:**
4029
+ ```json
4030
+ {
4031
+ "primary": "javascript",
4032
+ "secondary": ["python"],
4033
+ "files_by_language": {"javascript": ["src/index.js", "src/utils.js"], "python": ["scripts/deploy.py"]},
4034
+ "file_count": 3
4035
+ }
4036
+ ```
4037
+
4038
+ ### `codebase analyze-imports`
4039
+
4040
+ Build a dependency graph from import analysis with circular dependency detection.
4041
+
4042
+ **Module:** `codebase.cjs`
4043
+
4044
+ ```bash
4045
+ pan-tools codebase analyze-imports [--raw]
4046
+ ```
4047
+
4048
+ **JSON output:**
4049
+ ```json
4050
+ {
4051
+ "language": "javascript",
4052
+ "modules": 12,
4053
+ "imports": 18,
4054
+ "circular_deps": [],
4055
+ "entry_points": ["src/index.js"],
4056
+ "orphan_modules": ["src/unused.js"],
4057
+ "dependency_graph": "graph LR\n N0[index] --> N1[utils]"
4058
+ }
4059
+ ```
4060
+
4061
+ ### `codebase best-practices`
4062
+
4063
+ Detect best practices in these categories: Error Handling, Testing, Naming Conventions, Security, Performance.
4064
+
4065
+ **Module:** `codebase.cjs`
4066
+
4067
+ ```bash
4068
+ pan-tools codebase best-practices [--raw]
4069
+ ```
4070
+
4071
+ **JSON output:**
4072
+ ```json
4073
+ {
4074
+ "categories": [
4075
+ {"name": "Error Handling", "score": 7, "detected_patterns": ["try-catch ratio: 35%"], "recommendations": []},
4076
+ {"name": "Testing", "score": 8, "detected_patterns": ["12 test files found", "Test config present"], "recommendations": []}
4077
+ ],
4078
+ "score": 6.4,
4079
+ "recommendations": ["Add .env to .gitignore"]
4080
+ }
4081
+ ```
4082
+
4083
+ ---
4084
+
4085
+ ## 22. Spec B v2 Commands
4086
+
4087
+ Commands added across the five Spec B v2 waves. See [ADR-0024](decisions/ADR-0024-spec-b-v2-completion.md) for the design rationale. All modules are runtime-agnostic; agent quality varies by model capability.
4088
+
4089
+ ### `cost report [--format json|table|chart]` (Y-6)
4090
+
4091
+ Aggregate per-call cost across all PAN invocations in the project.
4092
+
4093
+ **Module:** `cost.cjs`
4094
+
4095
+ ```bash
4096
+ pan-tools cost report [--format json|table|chart] [--since YYYY-MM-DD] [--until YYYY-MM-DD]
4097
+ ```
4098
+
4099
+ Reads `.planning/metrics/tokens.jsonl` (append-only log — populated automatically since v3.4 by the `pan-cost-logger` SubagentStop hook, and manually via `cost append`). Aggregates by agent / command / tier / day, computes cache hit rate, and surfaces an overall USD estimate with the default rate table or `cost.rates` overrides. Three kinds of row never reach the totals and are counted instead: `suspect_excluded` (the oversum signature — more than 500M cache-read or 10M output tokens, a span over six hours, or, for a row the hook could not time, cache reads dwarfing input and output; rows measured from a single actor's own transcript, `token_source` `agent-transcript` or `session-transcript`, are exempt because they cannot carry anyone else's usage), `empty_excluded` (no tokens on any axis and no model — an unmeasured spawn), and `malformed_skipped` (unparseable lines). The table format prints them beside `Calls`.
4100
+
4101
+ Three formats:
4102
+ - `json` (default) — machine-readable, full payload
4103
+ - `table` — aligned columns with section-by-section breakdown
4104
+ - `chart` — ASCII bar chart of per-day cost
4105
+
4106
+ ### `cost append --model X --input-tokens N --output-tokens N ...` (Y-6)
4107
+
4108
+ Manually append a cost record. Used by explicit callers; the hook path handles automatic capture.
4109
+
4110
+ ```bash
4111
+ pan-tools cost append \
4112
+ [--agent <name>] [--command <name>] [--model <id>] [--tier reasoning|mid|fast] \
4113
+ [--input-tokens N] [--output-tokens N] \
4114
+ [--cache-read-tokens N] [--cache-write-tokens N] \
4115
+ [--phase <num>] [--session <id>]
4116
+ ```
4117
+
4118
+ Missing fields default to `null` or `0`. Cost is auto-computed when `model` or `tier` resolves to a known rate.
4119
+
4120
+ ### `cost clear` (Y-6)
4121
+
4122
+ Delete the cost log. Useful at the start of a billing cycle.
4123
+
4124
+ ### `cost rebuild [--apply] [--no-main-thread] [--claude-dir <path>]`
4125
+
4126
+ **Module:** `cost-rebuild.cjs`
4127
+
4128
+ Rebuild the ledger from Claude Code's own transcripts instead of trusting what the hooks wrote. Rows written before v3.29 were slices of the *parent* session transcript booked to whichever subagent stopped, and counted a turn once per content block; they cannot be corrected in place, but the transcripts they came from survive under `<Claude config dir>/projects/<encoded cwd>/` for Claude Code's retention window (`cleanupPeriodDays`, 30 by default). The folder is matched under the working directory as given and under its resolved real path — Claude Code names it after the real path, so a symlinked `--cwd` still finds it. The rebuild reads every session the ledger names plus every session of the project that has agent transcripts, writes one row per agent transcript (`<session>/subagents/agent-<id>.jsonl`, and `subagents/workflows/<run>/agent-<id>.jsonl` for Workflow-tool subagents) with its exact usage deduped by `message.id`, its type from the main thread's `Agent` tool call, its model and measured span, plus one row per session for the main thread's own usage (`agent: "(main thread)"`, `token_source: "session-transcript"`; `--no-main-thread` omits it). Hook rows of a rebuilt session are superseded; rows of sessions whose transcripts are gone, and caller-appended rows, are kept. A transcript folder reached only because the ledger names one of its sessions contributes that session alone (a ledger copied from another project must not import that project's history). Dry-run by default: reports each session's `superseded_rows` → `rebuilt_rows` and `old_cost_usd` → `new_cost_usd` (`--raw` prints them as `rows before → after`, `cost before → after`), priced as `cost report` prices (quarantined and empty rows at zero), and any `warnings` (an unreadable transcript contributes nothing and says so). `--apply` copies the current ledger to `tokens.jsonl.rebuilt-<date>` (`-2`, `-3`… when one exists — an earlier backup is never overwritten), carries forward any row a live hook appended while the plan was being read, writes through a temp file and rename, and writes nothing when the plan changes nothing; running it again is a no-op. `--claude-dir` overrides the Claude config directory (`CLAUDE_CONFIG_DIR`, else `~/.claude`). Plain chat sessions with neither agents nor ledger rows are left out, so the ledger stays a record of agent work and the sessions around it. Run it **before** `hygiene clean --apply` on a poisoned ledger — quarantine moves the whole file aside, and a rebuild afterwards has no rows left to keep. The main-thread row is dated to the session's last record with the whole session as its span, so a long session's own usage lands on its final day in the per-day view and in `--since`/`--until` windows; the cache-lifetime signal in `context-budget` ignores these rows. Transcripts whose last record is an interruption (`<synthetic>` model) take the model most of their records name.
4129
+
4130
+ ```bash
4131
+ pan-tools cost rebuild # dry run — what would change, per session
4132
+ pan-tools cost rebuild --apply # write it (previous ledger kept beside it)
4133
+ ```
4134
+
4135
+ ### `cost limits`
4136
+
4137
+ **Module:** `cost.cjs`
4138
+
4139
+ The host's usage-limit windows as PAN's status line last saw them: Claude Code passes `rate_limits.five_hour` and `rate_limits.seven_day` to the status line on a subscription, and the status line writes them, account-wide, to `<os-tmpdir>/pan-hooks-<uid>/claude-limits.json`.
4140
+
4141
+ ```bash
4142
+ pan-tools cost limits # JSON
4143
+ pan-tools cost limits --raw # weekly_limit_reached | ok | no_reading
4144
+ ```
4145
+
4146
+ Returns `{available, five_hour, seven_day, age_seconds, stale, weekly_limit_stop_pct, weekly_limit_reached}`. Each window is `{used_pct, resets_at, reset_since_read}`; a window whose `resets_at` has passed reads `used_pct: 0`. A reading older than 15 minutes is `stale`, and a stale reading never reaches the stop. `weekly_limit_reached` is true when a fresh 7-day reading is at or past `cost.weekly_limit_stop_pct` in `.planning/config.json` (default `90`; `0` or `false` turns it off; any other value that is not above 0 and at most 100 falls back to `90`). `/pan:focus-auto` stops on it with `weekly_limit` (the run is resumable), and `/pan:army` checks it between missions. Headless `claude -p` runs no status line, so there is nothing to read and nothing stops on it; other runtimes pass no such block.
4147
+
4148
+ ### `cost budget --phase <N>`
4149
+
4150
+ **Module:** `cost.cjs`
4151
+
4152
+ A phase's spend against its budget, `cost.phase_budget_usd` (default `20`; `0` or `false` turns it off; any other value that is not a positive number reads as `20`). The phase can also be given positionally.
4153
+
4154
+ ```bash
4155
+ pan-tools cost budget --phase 3 # JSON
4156
+ pan-tools cost budget 3 --raw # off | ok | near | over
4157
+ ```
4158
+
4159
+ Returns `{phase, budget_usd, spent_usd, pct, status, mode, warn_at_pct, unpriced}`. `spent_usd` sums the phase's priced ledger rows (suspect and empty rows left out, `phase: "03"` and `"3"` alike); `unpriced` counts its rows with no known rate. `status` is `near` from `warn_at_pct` (80) of the budget and `over` once spend reaches it. `mode` is always `warn`: exec-phase reports `near` and `over` after a wave and plan-phase in its final status, and nothing stops on it. Exits 0 whatever the status; exits 1 without a phase.
4160
+
4161
+ ### `models check`
4162
+
4163
+ Report whether the built-in model rate table is stale.
4164
+
4165
+ **Module:** `cost.cjs`
4166
+
4167
+ ```bash
4168
+ pan-tools models check [--raw]
4169
+ ```
4170
+
4171
+ Returns `{rates_verified_at, age_days, stale_after_days, stale, models, tiers, managed_model_pricing, managed_pricing_multiplier}`. The rate table carries the date it was last verified against published provider pricing; `stale` flips to `true` once that date is older than the threshold (sixty days, the cadence of PAN's ecosystem reviews). The calendar cannot see a default-model change inside that window; the suite's documented-default-models fixture test catches that instead. When stale, re-verify provider pricing, update `DEFAULT_RATES`, and bump `RATES_VERIFIED_AT` in `cost.cjs`. `--raw` prints a one-line human summary instead of JSON.
4172
+
4173
+ `managed_model_pricing` lists the model ids of the usable `overrides` rows in a Claude Code managed `modelPricing` block (contracted per-model rates an organisation deploys through managed settings; each row carries `input`, `output`, `cacheRead` and `cacheWrite`, and a row missing one is dropped, as Claude Code drops it), and `managed_pricing_multiplier` the block's `multiplier` — above 0 and at most 10, a discount below 1 and a markup above — or `null`. A multiplier PAN could not apply is reported as `managed_pricing_multiplier_ignored`. PAN prices with those rates, times the multiplier, when present, and applies the multiplier to the built-in rates of every model no row covers, as Claude Code does — see the `cost.rates` config key for the precedence. It reads them from the directory Claude Code documents for each OS; `PAN_MANAGED_SETTINGS_DIR` redirects the lookup. An empty list means no usable row was found, not that the setting is unsupported.
4174
+
4175
+ ### `bus publish <channel> <payload> [--source <name>]` (Y-7)
4176
+
4177
+ Append a message to a file-backed channel at `.planning/bus/<channel>.jsonl`.
4178
+
4179
+ **Module:** `bus.cjs`
4180
+
4181
+ Channel and source names validated against `^[a-zA-Z0-9_-]+$` (path-traversal safe). Payload is any JSON-serializable value; if not valid JSON, stored as a raw string. Returns `{published: true, ts, file, size}` (`file` is the channel file's absolute path, `size` its size in bytes after the append); an invalid channel or source name exits 1 with `error: "Invalid channel: …"` (or `"Invalid source: …"`).
4182
+
4183
+ ### `bus drain <channel> [--mode peek|consume|archive] [--limit N] [--offset N]` (Y-7)
4184
+
4185
+ Read messages from a channel.
4186
+
4187
+ - `peek` (default) — non-destructive read
4188
+ - `consume` — read, then remove only the drained window (`offset`..`offset+limit`) from the file; messages outside the window survive, and the file is emptied only when the window covers every line
4189
+ - `archive` — read the window, then rename the whole channel file (every message, not only the window) to `<channel>-<timestamp>.archive.jsonl`
4190
+
4191
+ Returns `{entries, total, offset, more, mode}`; `--limit` defaults to 1000. A malformed line comes back as `{malformed: true, raw, …}`. An unknown `--mode` on a non-empty channel exits 1, as does a failed write or rename (`drain_error`).
4192
+
4193
+ ### `bus list` (Y-7)
4194
+
4195
+ List all channels with message counts + byte sizes + active/archive flag.
4196
+
4197
+ ### `preview phase <N>` (Y-1)
4198
+
4199
+ Blast-radius analysis for a single phase.
4200
+
4201
+ **Module:** `preview.cjs`
4202
+
4203
+ ```bash
4204
+ pan-tools preview phase <N> [--raw]
4205
+ ```
4206
+
4207
+ Returns `{phase, phase_name, directory, status, plan_count, summary_count, goal, files_mentioned, test_files_mentioned, files_mentioned_count, test_files_count, risk_signals, risk_score, plans: [{file, bytes}], cost_forecast}`. Risk keywords checked: drop / delete / migrate / rename / breaking / auth. Score 1-10. `--raw` prints the same JSON.
4208
+
4209
+ `cost_forecast` (program P6) is `{agent, tier, plans, min_samples, per_plan, phase}`, from the project's cost ledger with this phase's own rows (and suspect or empty rows) left out. `per_plan` is `{status: "ok", n, p10, p50, p90}`, the dollars of a `pan-executor` call at one tier (the tier of the latest executor call); tiers are never pooled. `phase` is `{status: "ok", n_phases, p10, p50, p90}`: it resamples, 2,000 times and seeded, one past phase's other agents (planner, checkers, verifier, reviewer) plus one executor call per plan. Either is `{status: "insufficient", n, needed: 5}` when fewer than five observations back it (`phase` then also carries `n_phases` and a `reason`): no forecast is better than a guess from two runs.
4210
+
4211
+ ### `preview phases` (Y-1)
4212
+
4213
+ Cross-phase dependency graph with mermaid source + Kahn-style parallel batches + hidden-coupling detection.
4214
+
4215
+ ### `preview milestone` (Y-1)
4216
+
4217
+ Milestone completion ETA with velocity + confidence + bottleneck identification.
4218
+
4219
+ ### `review-deep merge <phase> --reviewer-file X --hardener-file Y [--meta-file Z]` (Y-2)
4220
+
4221
+ Merge reviewer + hardener + meta-reviewer findings into a consolidated `.planning/reviews/<phase>/deep-review.md` with verdict ladder (ok < ok_with_minor < fix_before_merge < review_required < block).
4222
+
4223
+ **Module:** `review-deep.cjs`
4224
+
4225
+ Publishes an audit entry to the `review-handoff` bus channel via `bus.cjs`.
4226
+
4227
+ ### `review-deep analyze <phase> ...` (Y-2)
4228
+
4229
+ Same merge logic as above but returns the payload without writing a file. Useful for piping.
4230
+
4231
+ ### `knowledge ask <question> [--max-sources N] [--recall-cue <text>]` (Y-3)
4232
+
4233
+ Retrieve candidate source files for a natural-language question, scored by keyword frequency across `CITATION_ROOTS`: `.planning/`'s project, requirements, roadmap, state, standards and patterns files and its `phases/`, `milestones/` and `memory/` folders, plus `docs/` and the top-level `README/CHANGELOG/CLAUDE.md`. PAN's own archives in `.planning/memory/` (`quarantine.md`, `state-archive.md`, `distill-patterns.md` and `archive/`) are never offered as sources.
4234
+
4235
+ **Module:** `knowledge.cjs`
4236
+
4237
+ ```bash
4238
+ pan-tools knowledge ask "why does phase 4 have a race condition fix?"
4239
+ pan-tools knowledge ask "how is auth wired?" --recall-cue "session tokens"
4240
+ ```
4241
+
4242
+ Returns `{question, sources: [{file, score, bytes}], total_candidates, returned}`. Keeps `project.md` and `requirements.md` among the sources even when they score zero, though they still count against `--max-sources` (default 20) and rank last at a zero score. `--recall-cue <text>` re-scores the same candidates against a second cue and adds a `recall_cue` + `recall_sources` view to the payload (no extra filesystem walk).
4243
+
4244
+ ### `knowledge discuss <phase> --subcmd read|append ...` (Y-3)
4245
+
4246
+ Multi-turn session state at `.planning/conversations/<phase>/session.json`.
4247
+
4248
+ - `--subcmd read` — load current session (empty if new)
4249
+ - `--subcmd append --role user|agent --content "..." [--cites a,b]` — append a turn
4250
+
4251
+ ### `knowledge playbook [--preview]` (Y-3)
4252
+
4253
+ Cluster the agent logs in `.planning/memory/` (those `memory list` lists; PAN's archives and files with no `## Entries` list are left out) into a categorized `.planning/playbook.md` with sections: Conventions / Gotchas / Decisions / Tool choices / Anti-patterns / Recurring gaps / General.
4254
+
4255
+ `--preview` returns the structured payload without writing the file.
4256
+
4257
+ ### `whatif prepare <phase> <scenario>` (Y-4)
4258
+
4259
+ Creates an isolated git worktree for counterfactual phase replay. Returns `{phase, scenario, slug, worktree: {worktree_path, branch, base}}`.
4260
+
4261
+ **Module:** `whatif.cjs`
4262
+
4263
+ Requires git repo. Worktree default location: sibling of main repo, branch prefixed `pan-whatif/`.
4264
+
4265
+ ### `whatif report <phase> <scenario> --comparison <json>` (Y-4)
4266
+
4267
+ Write the counterfactual comparison report to `.planning/counterfactuals/<phase>-<slug>.md` in the main tree (not the worktree — the worktree is disposable).
4268
+
4269
+ Comparison JSON shape: `{summary, differences[], recommendations[], risks[], verdict}`.
4270
+
4271
+ ### `whatif cleanup --worktree <path> [--branch <name>] [--force]` (Y-4)
4272
+
4273
+ Remove the worktree and, with `--branch`, delete that branch. It refuses (exit 1, nothing touched) anything what-if did not create: a `--branch` that does not start with `pan-whatif/`, or a registered worktree that is not on that branch (on any `pan-whatif/*` branch when `--branch` is omitted), or a cwd that is not a git repo. Past that check, removal is best-effort: git failures come back in `warnings[]` and the result is `{removed: true, warnings}`. `--force` applies to `git worktree remove`; the branch is always deleted with `-D`.
4274
+
4275
+ ---
4276
+
4277
+ ## 23. Self-Improvement Loop Commands
4278
+
4279
+ The autonomous external-build loop: scaffold an experiment folder, drive an external runtime against an idea.md, observe and harvest learnings. See ADR-0026 for design.
4280
+
4281
+ **Modules:** `experiment.cjs` (lifecycle), `runner.cjs` (execution); the `learn` verbs below are in `optimize.cjs` (`promote`, `unpromote`, `list-promoted`), `learn-lint.cjs` (`lint`) and `learn-index.cjs` (`build-index`, `topics-for`).
4282
+
4283
+ ### `experiment new <slug> --idea <path> [--root <dir>] [--runtime <name>] [--budget N] [--skip-installer]`
4284
+
4285
+ Scaffold a new experiment folder at `<root>/<slug>/`. Copies `<idea>` to `<root>/<slug>/.planning/idea.md`, writes the `experiment.json` manifest, initialises a git repository in the folder with a local commit identity, and (unless `--skip-installer`) runs the PAN installer for the chosen runtime inside the experiment dir (in an installed, non-source PAN the installer is unavailable and the result carries `installer_skipped`). Default root: `~/pan-experiments/`. Default runtime: `claude`. Hard `PAN_SOURCE_ROOT` guard refuses to scaffold inside the source repo.
4286
+
4287
+ ### `experiment list [--root <dir>] [--include-archived]`
4288
+
4289
+ Enumerate experiments under root with `{experiment_id, runtime, status, created_at, path, …}` per entry (the full `experiment.json` manifest plus `path`). By default archived experiments are omitted; pass `--include-archived` to list them too.
4290
+
4291
+ ### `experiment manifest <slug> [--root <dir>]`
4292
+
4293
+ Print the experiment's `experiment.json` manifest.
4294
+
4295
+ ### `experiment run <slug> [--root <dir>] [--prompt <text>] [--timeout <seconds>]`
4296
+
4297
+ Spawn the runtime adapter against the experiment via `spawnSync`, observe via `run-state.json`, return final status. Adapters live in `runner.cjs#RUNTIME_RUNNERS` — claude/codex/gemini/opencode supported, copilot is null. Default prompt: `/pan:new-project --auto @.planning/idea.md`. `--timeout` is in seconds; default 3600 (60 min).
4298
+
4299
+ **Incomplete-run detection:** When `exit_code: 0` but `state.md` shows `status != completed`, the runner returns `stop_reason: "incomplete"` (not `success`). Distinguishes real milestone-done from premature exits.
4300
+
4301
+ **Billing note (Claude runtime):** since June 15, 2026, headless `claude -p` / Agent SDK usage draws from a separate monthly **Agent SDK credit pool** on Claude subscriptions — it does not count against interactive session limits, and vice versa. The `billing_pool: "agent_sdk"` tag (and the rest of the `runState.metrics` envelope) is produced only by the runner module's capture-metrics API, **not** by the `experiment run` CLI path — a CLI run's `run-state.json` has no `metrics` key. Callers needing billing reconciliation must invoke the runner module directly with metrics capture enabled.
4302
+
4303
+ ### `experiment status <slug> [--root <dir>]`
4304
+
4305
+ Read snapshot from `<expPath>/.planning/run-state.json`: status, started_at, ended_at, pid, exit_code, stop_reason, elapsed_ms, events — and `metrics` only when the runner module captured them (a CLI run records none).
4306
+
4307
+ ### `experiment stop <slug> [--root <dir>]`
4308
+
4309
+ Finalize/record a stopped experiment in `run-state.json`. This **cannot** terminate an already-running synchronous experiment: the runner blocks while a run is in flight and no pid is recorded to signal, so `stop` only reconciles the run-state after the run has exited. If no pid is recorded (an in-flight run), it returns an error and writes nothing.
4310
+
4311
+ ### `experiment harvest <slug> [--root <dir>] [--source-root <dir>] [--force]`
4312
+
4313
+ Copy `.planning/idea.md`, `experiment.json`, `state.md`, `run-state.json`, `agent-history.json`, `optimization/` (traces + reports) and `phases/` back into a fixed destination at `<source-root>/experiments/<slug>/`. `--source-root` selects the PAN source root (destination base); `--force` overwrites an existing harvest at that path. Non-destructive — leaves the experiment folder intact for re-runs.
4314
+
4315
+ ### `experiment prune <slug> [--root <dir>] [--hard]`
4316
+
4317
+ Remove the experiment. Default is a soft prune — the experiment folder is archive-renamed (preserved, not deleted). With `--hard`, the folder is permanently deleted.
4318
+
4319
+ ### `learn promote --pattern <id> --scope <s> --topic <t> --summary <text> --rule <text> [--evidence <text>] [--applies-in <text>] [--source-experiments csv] [--cites <path[#symbol]>,...] [--source-root <path>]`
4320
+
4321
+ Append a promoted pattern into `pan-wizard-core/learnings/{scope}/{topic}.md`. Scope is `universal` (ships to installs) or `internal` (source-only, stripped at install). Refuses duplicate IDs within the same topic file. When `--scope universal` is used, classifies the rule via `classifyPatternKind()` and attaches a `warning` to the result if the rule looks prompt-fragment-shaped (per P-RES-007). `--cites` names the code the pattern rests on, relative to the source root (`path` or `path#symbol`); each citation must hold when promoted, is stored with the pattern, and `learn lint` keeps checking it (L-007). Returns `{promoted_to, pattern_id, scope, topic, promoted_at, warning?}`.
4322
+
4323
+ ### `learn unpromote --pattern <id> --scope <s> --topic <t>`
4324
+
4325
+ Remove a previously promoted pattern from a topic file. Used to undo a bad promote.
4326
+
4327
+ ### `learn list-promoted`
4328
+
4329
+ Walk both scopes; return inventory `{universal: [...], internal: [...], total}` with id/topic/source_experiments per pattern.
4330
+
4331
+ ### `learn lint [--scope universal|internal] [--strict]`
4332
+
4333
+ Validate the learnings store integrity. Checks: L-001 duplicate IDs across files, L-002 dangling pattern cross-references, L-003 empty `source_experiments` while body cites a known experiment name, L-004 universal-scope rule prose using PAN-internal terms, L-005 revision marker (`-rN`) without `superseded_by` frontmatter on the base, L-006 universal-scope pattern citing an internal pattern id (dangles after install), L-007 a pattern `cites` entry whose file or symbol is gone (an error). Exits non-zero on errors; warnings are advisory unless `--strict`. Wired into `/check`. Module: `learn-lint.cjs`.
4334
+
4335
+ ### `learn build-index`
4336
+
4337
+ Generate `pan-wizard-core/learnings/index.json` with topic→agent-relevance map, byte/token-est sizes, and pattern lists. Curated `RELEVANCE` table assigns `high|medium|low` per `(topic, agent_role)` for `planner / executor / verifier / reviewer`. Read by `topics-for`. Module: `learn-index.cjs`.
4338
+
4339
+ ### `learn topics-for --agent <role> [--cue <text>] [--min-relevance high|medium|low] [--token-budget N]`
4340
+
4341
+ Query the index for the topics to load within a token budget. It returns `{mode, selected: [...], dropped: [...], total_tokens}`. The workflow files (`plan-phase.md`, `exec-phase.md`, `verify-phase.md`, `execute-plan.md`) use it to load only relevant patterns instead of skimming the folder, which addresses the P-RES-002 distractor-density anti-pattern. Default budget 5000 tokens.
4342
+
4343
+ **`--cue` (mode `cue`)** is the task: the phase goal or plan objective, and the files it touches. The workflows pass it.
4344
+ - **Matching:** the cue's words are matched against each topic's name (counting double) and its patterns' summaries and rules. Common words are ignored, and a word meets its own inflections.
4345
+ - **What loads:** every topic scoring at least half the best match is a candidate, whatever its role relevance. Candidates are ranked by score, then relevance, then size; each selected topic carries its `cue_score`.
4346
+ - **Golden set:** on `tests/fixtures/learn-cue-golden.json` this loads about five topics per task, against thirteen for the role ranking, with precision 0.37 vs 0.06 and recall 0.76 vs 0.45.
4347
+
4348
+ **Without a cue, or when nothing matches it (mode `role`):** topics tagged relevant for the role at `--min-relevance` (default medium), most relevant first, then smallest first. A large topic the task needs can drop out that way, which is why the workflows pass the cue.
4349
+
4350
+ ---
4351
+
4352
+ ## 24. Doc-Lint Commands
4353
+
4354
+ Markdown frontmatter + structure linter, vendored from the whooo experiment. Validates PAN-shipped markdown against schemas in `pan-wizard-core/references/schemas/`.
4355
+
4356
+ **Module:** `doc-lint.cjs` (adapter) + `pan-wizard-core/bin/lib/doc-lint/{frontmatter,schema,validate,walk,reporter}.js`.
4357
+
4358
+ **Exit codes — every subcommand.** `0` clean · `1` violations found · `2` (`doc-lint <dir>` only) the schema itself is malformed. The verdict is identical on the `--raw` and JSON paths; gate on the exit code in either format.
4359
+
4360
+ > Corrected in this version: the JSON paths used to exit `0` unconditionally, because `output()` exits the process and the `process.exit(<verdict>)` line below each call was unreachable. `doc-lint --format json` therefore never failed, and `doc-lint schema-check` never failed in *either* format. Earlier revisions of this page documented the JSON path as merely "reporting `schema_errors` in the body" — that described the defect, not an intended design. If you pinned a version to that behaviour, the linter was not gating.
4361
+
4362
+ ### `doc-lint <dir> [--schema <path>] [--format human|json] [--strict] [--exclude <glob>]`
4363
+
4364
+ Walk `<dir>` for `.md` files, validate each against the named schema (default: `pan-command.schema.yml`, whatever the directory). Reports violations: missing required frontmatter fields, schema-type mismatches, structural issues. `--format` applies under `--raw` (a human report, or the violations as JSON); without `--raw` the result is always the JSON payload, suitable for CI gates. Exits `1` when any violation has `severity: error` (warnings alone exit `0`), `2` if the schema could not be parsed.
4365
+
4366
+ ### `doc-lint schema-check <path>`
4367
+
4368
+ Verify the YAML schema at `<path>` is syntactically valid before it's used to lint anything. `<path>` is a required positional argument. Schemas live at `pan-wizard-core/references/schemas/*.schema.yml`. Exits `1` when `ok` is false.
4369
+
4370
+ ### `doc-lint counts <dir> [--exclude <glob>]`
4371
+
4372
+ Scan `<dir>` for embedded numeric counts that violate the single-source-of-truth policy (counts belong only in `CLAUDE.md`). Repeat `--exclude` to skip paths. Reports each offending count with its file and line.
4373
+
4374
+ ### `doc-lint flags [--doc-dir <dir>]`
4375
+
4376
+ Detect aspirational or stale `--flags` documented for the PAN CLI that never appear as literals in the source (`pan-wizard-core/bin`, `bin`). `--doc-dir <dir>` (repeatable; a path relative to the project root, or an absolute path) replaces the default `docs`, so list `docs` again to keep it; a missing directory is skipped. Only lines that mention `pan-tools` are checked, and findings are reported relative to the project root. Exits 1 when any doc-only flag is found. Reports each doc-only flag with its file and line.
4377
+
4378
+ ---
4379
+
4380
+ ## 25. Skill-Aligned Decomposition Commands
4381
+
4382
+ SAD pass (ADR-0038, spec `docs/specs/skill-aligned-decomposition.md`): the planner drafts its task list, retrieves the skills (commands, templates, references, learnings topics) that loosely match each task, and realigns wording/granularity to what actually exists — SkillWeaver's decompose→retrieve→realign loop, keyword-scored, zero dependencies.
4383
+
4384
+ **Module:** `skill-align.cjs`
4385
+
4386
+ ### `skills index [--source-root <path>]` (ADR-0038)
4387
+
4388
+ Build (on the fly — nothing persisted) and print the skill index: `commands/pan/*.md`, `pan-wizard-core/templates/**` (recursive), `pan-wizard-core/references/*.md`, plus learnings topics via `learn-index.cjs`. Returns `{entries, total, by_kind, skipped_roots}`. Missing roots are skipped and reported, never thrown — partial installs and non-Claude runtime layouts degrade gracefully. Default root is the install root (resolved relative to the module); `--source-root` exists for tests.
4389
+
4390
+ ```bash
4391
+ pan-tools skills index --raw
4392
+ ```
4393
+
4394
+ ### `skills align (--draft "<text>" | --draft-file <path>) [--top <k>] [--min-score <n>] [--token-budget <n>] [--source-root <path>]` (ADR-0038)
4395
+
4396
+ Score each draft task (bullets/numbered/checkbox lines all accepted) against the skill index using `scoreRelevance`, after stripping planning glue words from the cue. Returns per-task top-k matches (names only), `coverage`, and a deduplicated `vocabulary` hint list ranked by aggregate score and greedy-packed into the token budget (default 1500) — overflow lands in `dropped`, never silently truncated. A draft that yields no tasks, or one over the max-task threshold, is reported as an `{error}` JSON body on stdout at **exit 1** (per "Error Shape"); omitting both `--draft` and `--draft-file` is a usage error on stderr, also exit 1.
4397
+
4398
+ ```bash
4399
+ pan-tools skills align --draft-file /tmp/draft-tasks.md --raw
4400
+ ```
4401
+
4402
+ Advisory and fail-open by design: no orchestrator step or checker dimension depends on it, and `pan-planner`'s `skill_alignment` step skips on any error.
4403
+
4404
+ ---
4405
+
4406
+ ## 26. Hygiene Commands
4407
+
4408
+ Project cleanup + version alignment (see `docs/FIELD-HARVEST-2026-07.md` follow-ups). Keeps a PAN-managed project aligned with the latest PAN version and free of accumulated history debris.
4409
+
4410
+ **Module:** `hygiene.cjs`
4411
+
4412
+ Both subcommands accept the planning-root flags (`--track`, `--planning-dir`, `--all-tracks`) documented under [Global Flags](#planning-root---track----planning-dir----all-tracks). Without them, only `.planning/` is scanned — and the scan says so, since a "clean" verdict is only meaningful alongside a statement of what was looked at.
4413
+
4414
+ ### `hygiene scan [--trace-age-days N] [--track <name>] [--all-tracks]`
4415
+
4416
+ Read-only findings report. Checks: per-runtime `pan-file-manifest.json` version vs the latest seen (including the executing core's own version); untracked installs (`pan-wizard-core` without a manifest); legacy uppercase planning filenames (pre-v2.2); orphaned atomic-write `.tmp` files older than 1h; per-agent memory logs past the compaction cap; **poisoned cost ledgers** — ≥50% suspect records *or* ≥50% of the token **mass** in suspect records, once the ledger holds at least 20 records (v3.27: a count-only gate passed a ledger whose 24% bad rows held 89% of the tokens); trace sessions older than retention (default 30d, newest 5 always kept); **optimization reports** past the same retention (v3.27 — traces aged out while the analysis JSON beside them never did); **cached context bloat** — the stable planning files `cache prime` lists, warned at 15k tokens and critical at 25k, with any single file over 6k called out: `state.md`, re-read on every agent call, carries the `compact-state` remedy while it has settled history to archive, and `roadmap.md`, read whole only by the roadmapper and milestone work (phase agents read `roadmap slice`), carries `compact-roadmap` while shipped phases can move to `roadmap-history.md`; fragment `.planning/` dirs with no workflow spine (phase, focus, and orchestration layouts all count as spines); a **`foreign-planning-tree`** warning when the tree carries markers of another tool (gsd-core's `HANDOFF.json`, `.gsd-allow-shrink`, two or more gsd-only directories, or its flat dotted `config.json` keys) — that tree gets the one warning and no per-tree checks, since its uppercase files would otherwise read as legacy PAN filenames; a **`shared-planning-tree`** info finding when another tool writes into the tree beside PAN (planning-with-files markers), after which PAN's own checks still run; and, under `cache-context` at `info` (`warn` once the re-written context is large), the prompt-cache lifetime recommendation that `context-budget` reports as `cache.ttl`. Memory: `memory-stale` (entries `memory select` leaves out because their cited code is gone or they went unused past the expiry window; fix `prune-memory`) and `memory-format` (a file in `.planning/memory/` with no `## Entries` list, or whose name is not a valid agent name, which `memory select` and `knowledge playbook` never read as memory (`knowledge ask` can still offer it as a source)). Once per project, read-only, `host-memory`: Claude Code's auto-memory index (`~/.claude/projects/<project>/memory/MEMORY.md`, under `CLAUDE_CONFIG_DIR` instead of `~/.claude` when that is set, or `autoMemoryDirectory`). It reports an index past the 200-line / 25 KB session-start load limit (`warn`) or near it (`info`), and index lines that hold content where a one-line pointer belongs (`info`). PAN never writes that store. Returns `{findings, installs, latest_version, host_memory, planning_exists, planning_root, track, planning_root_source, planning_root_exists, all_tracks, roots_scanned, summary}` — each finding has `check`, `severity` (`critical|warn|info`), `path`, `detail`, `fix` (the action `hygiene clean` would take, e.g. `{"action": "prune-memory", "agent": "pan-executor"}`, or `null`), `fixable`, and `track` (the tree it came from; `null` for the root tree or a project-wide check). `summary.by_track` breaks findings down per tree. Version alignment is a project property and is reported once no matter how many trees are swept.
4417
+
4418
+ ```bash
4419
+ pan-tools hygiene scan --raw
4420
+ ```
4421
+
4422
+ ### `hygiene clean [--apply] [--trace-age-days N] [--track <name>] [--all-tracks]`
4423
+
4424
+ Dry-run by default (lists what would change); `--apply` executes the safe subset: two-step case-hop renames of legacy filenames, `.tmp` orphan deletion, memory-log compaction (`compactMemory`), poisoned-ledger **quarantine-by-rename** (`tokens.jsonl.quarantined-<date>` — the poisoned rows are kept, but only the newest quarantine file survives; the hook's `.cost-cursor.json` is kept, since it tracks transcripts rather than the ledger), stale-trace and stale-report pruning, `state.md` compaction (`compact-state`, archiving settled sections to `.planning/state-history.md`, beside state.md), roadmap compaction (`compact-roadmap`), and archiving stale or expired memory entries (`prune-memory`). Version drift (remediation = re-run the installer) and fragment dirs (manual review) are never auto-fixed, and the legacy-filename rename refuses outright on a tree the scan flagged as `foreign-planning-tree` — PAN never renames another tool's state. Returns `{dry_run, applied, skipped, planning_root, track, planning_root_source, planning_root_exists, all_tracks, roots_scanned, summary}`. Under `--all-tracks` each fix is applied within its own tree's scope, so a track's bloated memory log is compacted in that track rather than in the root tree.
4425
+
4426
+ ```bash
4427
+ pan-tools hygiene clean --apply --raw
4428
+ ```
4429
+
4430
+ Wrapped by the `/pan:hygiene` command: scan → present by severity → version-drift remediation → dry-run clean → confirm → clean → report (it recommends re-running `/pan:hygiene` after the installer rather than re-scanning itself).
4431
+
4432
+ ---
4433
+
4434
+ ## 27. Optimization, Git and Distill Commands
4435
+
4436
+ Verbs the Command Index lists that had no section of their own. Wrapped by `/pan:learn --optimize`, `/pan:learn`, `/pan:git` and the `distill` focus-auto category.
4437
+
4438
+ ### `optimize trace <init|log|end|current|list|show|reconcile>` · `optimize learn` · `optimize apply` · `optimize revert` · `optimize list` · `optimize stats`
4439
+
4440
+ **Module:** `optimize.cjs`. Trace sessions live at `.planning/optimization/traces/<session>/` (`trace.jsonl` + `session.json`); reports at `.planning/optimization/reports/`. Bare `learn` is the documented alias for `optimize learn`, and takes its `--session` and `--sessions` flags.
4441
+
4442
+ ```bash
4443
+ pan-tools optimize trace init --description "exec phase 5" --command exec-phase --phase 5
4444
+ pan-tools optimize trace log --agent pan-executor --type decision --category deviation --impact minor --tokens-wasted 1200 --context '{"task":"05-01"}'
4445
+ pan-tools optimize trace current # the open session
4446
+ pan-tools optimize trace list # every session on disk (open and ended)
4447
+ pan-tools optimize trace show --session <id> # one session's events
4448
+ pan-tools optimize trace reconcile [--session <id> | --all] # rewrite session.json counters from trace.jsonl — the open session by default, one by id, or all
4449
+ pan-tools optimize trace end [--session <id>]
4450
+ pan-tools optimize learn [--session <id>] # analyse events → report
4451
+ pan-tools optimize learn --sessions <n> # pool the last n sessions into one analysis
4452
+ pan-tools optimize apply [--report <path>] # record the report's suggestions in suggestions.md; prints the apply_id
4453
+ pan-tools optimize revert <apply_id> | --last # undo one apply exactly
4454
+ pan-tools optimize list # reports on disk
4455
+ pan-tools optimize stats
4456
+ ```
4457
+
4458
+ The dispatcher parses these flags for `trace`: `--session`, `--all`, `--description`, `--command`, `--phase`, `--agent`, `--type` (`decision`, `error`, `gap`, `correction`, `redundancy`, `memory_hit`, `memory_miss` or `surprise`; anything else is stored as `unknown`), `--category`, `--impact` (`critical`, `major`, `minor` or `trivial`; anything else is stored as `minor`), `--correction`, `--tokens-wasted` (number), `--context` (JSON; invalid JSON is dropped). `learn` takes `--session`, or `--sessions <n>` (a whole number; `1` analyses the current session as plain `learn` does, and a larger number writes `pooled-<k>-<newest session>-analysis.json`, `k` being the sessions actually found). `apply` takes `--report` (default: the newest report). It writes `note` actions to `optimization/suggestions.md` and `planning_note` actions to `optimization/config-suggestions.md`; on an `-analysis.json` file it derives the notes itself and points them at the project's instructions (CLAUDE.md or AGENTS.md, outside PAN's section), except a judge's repeated failures, which point at its findings in `.planning/findings.jsonl`. An older report's memory actions (`memory`, `memory_append`, `memory_entry`) still write to `.planning/memory/`, a `memory_entry` only if `memory record` accepts it, and the result gives each such write a `warning` that PAN's workflows do not load that folder into agents. `revert` takes an `apply_id` or `--last`.
4459
+
4460
+ **What `learn` reads.** The analysis carries `tool_error_patterns`, failed tool calls the trace hook captured from subagent transcripts. They are grouped by agent, tool, class and message, and ranked by the number of `spawns` they recur in, then by occurrences; each also carries its `sessions` count. It also carries `verdict_stats`: each judge's `pass`/`fail`/`needs_human`, its `retries`, and the retries that resolved a failure. The legacy verdict categories that older sessions hold are counted too. A tool failure becomes a suggestion only when it recurs across spawns; memory misses, knowledge gaps and a judge failing twice or more also become notes.
4461
+
4462
+ **Revertible applies.** Every apply is logged in `optimization/applied.jsonl` with an `apply_id` and one record per action it wrote: the path, `created` or `appended`, the exact appended text, and a line-ending-insensitive hash of the file after the write. Applying the same report again skips each action with `already applied in <apply_id>`. `revert` deletes the files an apply created and cuts the text it appended, and it refuses in two cases:
4463
+ - a file that changed, or is gone, since the apply;
4464
+ - a file that a later unreverted apply also wrote (reverts go last-in, first-out per file).
4465
+
4466
+ A CRLF file is written back CRLF. The status is `reverted`, `partial`, `refused` or `nothing_to_revert`, with `refused[]` naming each file and why. Applies logged before these records existed report `not_revertible`. `stats` counts `apply_runs` and `reverted_runs` and names the `last_apply_id`.
4467
+
4468
+ ### `git <commit|branch|push|status|log|stash|diff|rollback|tag|sync> [...]`
4469
+
4470
+ **Module:** `git.cjs` — phase-aware git workflow; `commit` reuses `runCommitSafetyChecks` (deleted-file and sensitive-file gates). Works in any git repo, with or without `.planning/`.
4471
+
4472
+ ```bash
4473
+ pan-tools git commit --message "..." [--type feat] [--all] [--amend] [--force] [<file>...]
4474
+ pan-tools git branch <create|switch|list|delete|current> [--name <n>] [--phase <N>] [--force] # --phase 3 names it pan/phase-3
4475
+ pan-tools git push [--remote <r>] [--branch <b>] [--force] # remote validated; --force required for a force-push
4476
+ pan-tools git status
4477
+ pan-tools git log [--count <N>] # default 10
4478
+ pan-tools git stash <save|pop|list|drop> [--name <n>] [--index <i>]
4479
+ pan-tools git diff [--staged] [--file <path>]
4480
+ pan-tools git rollback [--tag <pan-rollback-*>] [--dry-run] # resets --hard to the given tag, or to the lexically last pan-rollback-* tag (`git tag -l` order — not necessarily the newest; pass --tag to be explicit); refuses on a dirty tree (dirty_working_tree);
4481
+ # --dry-run only reports the target — list snapshots with `pan-tools git tag list --pattern 'pan-rollback-*'`
4482
+ pan-tools git tag <list|create|delete> [--name <n>] [--message <m>] [--pattern <glob>] # --pattern for list; --name/--message for create; --name for delete
4483
+ pan-tools git sync [--remote <r>] [--branch <b>] [--rebase]
4484
+ ```
4485
+
4486
+ `git status` prints `{branch, clean, staged_count, unstaged_count, untracked_count, files[]}`. `files` are the `git status --porcelain` lines with their two-letter status column kept (` M src/a.js`, `M src/b.js`, `?? c.js`, `R old.js -> new.js`); an entry counts as staged when its first column is set and as unstaged when its second is, so a file staged and then edited again counts in both. `--raw` prints `<branch> (clean)` or `<branch> (<N> changes)`. Outside a git repository every `git` subcommand prints `{error: "not_a_git_repo", hint}` and exits 1.
4487
+
4488
+ ### `distill <scan|analyze|report> [--bloat-threshold N] [--touched-loc N]`
4489
+
4490
+ **Module:** `distill.cjs` — the deterministic passes of the AI code-bloat optimizer (phantom try/catch, unused imports, magic numbers, long functions, wide parameter lists, single-instance factories, deep nesting, repeated blocks, unreferenced exports); the `pan-distiller` agent judges only the flagged spans. Cross-session memory at `.planning/memory/distill-patterns.md`. The bloat-budget gate compares touched LOC against essential LOC; `--bloat-threshold` overrides the default ratio of `2.0`.
4491
+
4492
+ ---
4493
+
4494
+ ## 28. Evidence Loop Commands
4495
+
4496
+ **Module:** `findings.cjs` (with `verdict.cjs`). ADR-0049. PAN's judges put their verdict on record:
4497
+ - the plan checker, the reviewer and the design checker end their report with a fenced `pan-verdict` JSON block;
4498
+ - the verifier's verdict is its verification.md frontmatter.
4499
+
4500
+ The workflows save each report, record it with `findings record`, and branch on what the record prints. Whenever they continue past findings, they record why with `findings dispose`. The ledger is `.planning/findings.jsonl`: append-only, folded on read, committed with the other planning docs.
4501
+
4502
+ ```bash
4503
+ pan-tools findings record --phase 3 --file .planning/phases/03-auth/03-verification.md [--raw]
4504
+ pan-tools findings record --phase 3 --agent pan-reviewer --file .planning/phases/03-auth/03-review.md --raw
4505
+ pan-tools findings record --phase 3 --stdin < report.md
4506
+ pan-tools findings list [--phase N] [--agent A] [--status open|fixed|deferred|dismissed|decision] [--class C] [--milestone V]
4507
+ pan-tools findings dispose f_81ab3c9d20 --as dismissed --reason "generated file, not project code"
4508
+ pan-tools findings dispose --phase 3 --agent pan-reviewer --open --as deferred --reason "accepted at review: PASS_WITH_WARNINGS"
4509
+ pan-tools findings debt [--milestone v1.1] [--raw]
4510
+ pan-tools findings suppressions [--phase N] [--agent A] [--raw]
4511
+ pan-tools findings prove f_81ab3c9d20 --test .planning/reviews/3/proofs/catch.test.js [--timeout <ms>]
4512
+ ```
4513
+
4514
+ **`findings record`** takes exactly one of `--file`, `--text` or `--stdin`, plus `--phase` and optionally `--agent`.
4515
+ - **Input.** It reads the last `pan-verdict` block; without one, it reads a verification frontmatter (`status`, `gaps`, `human_verification`, `unrequested`, `test_gate`, `not_checked`). It prints `{contract, recorded, duplicate, verdict_id, agent, verdict, outcome, not_checked, source_kind, attempt, phase, findings, new, rematched, resolved, unaddressed, reopened, suppressed, returned, warnings}`, plus `ledger` when it wrote a verdict, and `agent_verdict` and `lowered_by` when a code fact lowered the judge's word.
4516
+ - **What was not checked.** `not_checked` lists `{check, reason}` for each check the judge could not run. A verification with `test_gate: skipped` always carries a `tests` entry, so a pass whose tests never ran is never recorded as a plain pass. The warnings name a verification that does not record `test_gate` (`test_gate_unrecorded`) and a failed gate under a passed status (`test_gate_failed_under_pass`). `--raw` prints the recorded verdict word (`gaps_found`, `NEEDS_FIXES`, `issues_found`, `GAPS` …): the judge's own word, or the lowered one when a code fact lowered it. It prints the outcome when there is no word.
4517
+ - **Exit codes.** A failed verdict is data: exit 0. An unreadable input exits 1 with `{error, reason}`, for example `no_verdict`, `invalid_json`, `unsupported_contract` or `phase_not_found`. That is what a workflow's `||` fallback runs on.
4518
+ - **Idempotency and attempts.** Recording the same artifact twice is a no-op that returns the first verdict. Each new verdict from an agent on a phase is the next `attempt`.
4519
+ - **Code facts only lower.** A verification with `status: passed` over `test_gate: failed` records as `gaps_found`, with a high finding at `code-fact:test_gate`; the judge's word is kept as `agent_verdict`.
4520
+ - **Closing on evidence.** An earlier open finding becomes `fixed` only when the new report answers it `resolved` with evidence (a `resolutions` entry, or a truth in the verifier's `re_verification.gaps_closed`), or when its code fact now passes. One the report does not repeat, or answers other than `resolved` with evidence, stays open and is listed in `unaddressed`. `still_present` reopens a `fixed` finding. A reworded report of an earlier finding keeps its id (`rematched`).
4521
+ - **Trace.** It logs `verdict_passed`, `verdict_failed` or `verdict_needs_human`, plus `verdict_retry` after a failed attempt.
4522
+
4523
+ **`findings list`** filtered by both `--phase` and `--agent` adds `churn`: `{reports, transitions, added, dropped, rate}`, how many ids appeared or disappeared between that judge's consecutive reports.
4524
+
4525
+ **`findings dispose`** takes finding ids, or `--phase N [--agent A] --open`, and records `fixed`, `deferred`, `dismissed` or `decision`. It refuses `deferred`, `dismissed` and `decision` without `--reason` (exit 1: never dispose silently). A later report does not override a `deferred` or `decision` disposition; a `fixed` one, deliberate or automatic, reopens when the finding is reported again. A `dismissed` one records the hash of the file its `where` names and becomes a learned suppression (below).
4526
+
4527
+ **Learned suppressions.** While the dismissed finding's file is unchanged, `findings record` filters the same judge's report of the same finding (same class family, same file, same truth or a similar summary). It is not one of the verdict's findings and is listed under `suppressed` as `{id, scope, summary}`. A suppression is a `candidate` in the phase it was dismissed in, and `promoted` to every phase once the same judge's finding was dismissed in two phases. Once the file changes, the suppression retires: a report of the finding reopens it, counted in `returned`. Logic findings (`defect`, `contradicts`, `missing`, `partial`), security findings (any `pan-hardener` finding, or a summary naming injection, auth, secrets, crypto and the like) and findings with no project file are never suppressed: a report of one reopens it. **`findings suppressions`** prints `{contract, suppressions[], active}`, each suppression `{id, agent, phase, class, file, summary, reason, scope, active}`; `--agent` keeps one judge's, `--phase` keeps that phase's candidates and every promoted one, and `--raw` prints the active count.
4528
+
4529
+ **`findings debt`** is what `/pan:milestone-audit` reads for tech debt. For one milestone (default: the current one), it groups the `deferred` findings, each with the reason recorded at the time, and the `open` ones nobody disposed, by phase: `{contract, milestone, phases: [{phase, deferred[], open[]}], deferred_count, open_count}` (`--raw`: `N deferred, M open`).
4530
+
4531
+ **`findings prove`** runs a catching test for a finding (program R5): it should pass at the phase's base and fail at HEAD. Prints `{id, status, test, base, command, base_exit, head_exit, detail?, confidence}`; `status` is `confirmed` (exit 0; `confidence: confirmed`), `fails_at_base`, `passes_at_head` or `not_checked` (exit 1; `confidence: candidate`). Each run appends a `proof` row to the ledger.
4532
+
4533
+ **Finding vocabulary.** `class` is one of `missing`, `partial`, `contradicts`, `unrequested`, `defect`, `risk`, `quality`, `human`. `severity` is one of `critical`, `high`, `medium`, `low`, `info`. The shipped reference `pan-wizard-core/references/verdict-contract.md` maps each judge's own terms onto them.
4534
+
4535
+ ---
4536
+
4537
+ ## 29. Bot Army and Report Commands
4538
+
4539
+ The bot-army registry, worktree isolation and campaign scheduling behind `/pan:army`, and the read-only HTML views (`hud`, `report`).
4540
+
4541
+ ### `squad list | show <name>` (ADR-0032)
4542
+
4543
+ Bot-army squad registry. `squad list` shows every squad (architecture / build / quality / release) with their model tier, tool-access contract, and member count; `squad show <name>` shows one squad's full roster. Read by `pan-conductor` campaign mode and `/pan:army` to resolve the roster at runtime instead of hardcoding it.
4544
+
4545
+ **Module:** `squads.cjs`
4546
+
4547
+ ### `worktree list | create <task> | remove <path> | cleanup` (ADR-0033)
4548
+
4549
+ Branch-per-agent git worktrees for the Build squad — each builder gets its own `army/<task>` branch + isolated tree so concurrent agents never collide. Worktrees are created as **siblings of the project directory** (`../pan-army-<slug>/`). `create` accepts `--base <ref>`; `remove` accepts `--branch <name>` `--force` and refuses to delete non-`army/` branches.
4550
+
4551
+ `worktree cleanup [--force]` is the campaign teardown sweep: removes every registered army worktree, prunes stale registrations, and deletes `army/*` branches — but a branch whose tip is **not reachable from HEAD** (squash-merged, or aborted work that exists nowhere else) is *kept* by default and listed with the exact command to delete it; a dirty worktree is likewise kept. `--force` sweeps both. The result reports `removed_worktrees`, `deleted_branches`, `kept` (each with a reason), `pruned`, and `clean`. Run it at campaign end and on any abort path; the per-task `worktree remove <path> --branch <name>` right after a squash-merge lands is the always-correct deletion, since at that moment the merge is the permanent artifact.
4552
+
4553
+ **Module:** `worktree.cjs`
4554
+
4555
+ ### `merge request <branch> [--phase N] [--ci-green]` · `merge confirm <branch>` (program L6a)
4556
+
4557
+ The human merge gate on the CLI, the same gate the MCP bridge uses. `request` stages the merge and prints the record without its approval token (`status: awaiting_approval`, `verify_pass`, `verify_source`, plus `gate` when `--phase` is given, in which case `verify_pass` is the phase gate's verdict, not the caller's word). `confirm` squash-merges only in an interactive terminal after the person types the branch name; without a TTY it exits 1 with `human_terminal_required`. There is no flag or environment variable that skips this.
4558
+
4559
+ **Module:** `merge.cjs`
4560
+
4561
+ ### `campaign schedule | status | due | record-run` (ADR-0034)
4562
+
4563
+ Scheduled, self-resuming bot-army campaigns. PAN is not a daemon: this module owns the schedule **descriptor** and the decision of whether a run is **due** — an external trigger (host scheduler, cron, `/loop`, or a human) polls `campaign due` and fires `/pan:army --continue`. The always-ask human merge gate is never affected by scheduling. Descriptor lives at `.planning/orchestration/schedule.json`.
4564
+
4565
+ **Module:** `campaign.cjs`
4566
+
4567
+ - `campaign schedule` — arm or update the schedule. Flags: `--cadence <hourly|daily|weekly|Nh|Nd>` (default `daily` when first armed; an update without it keeps the stored cadence), `--daily-budget <points>` (default 300), `--goal <text>`, `--source <name>` (default `backlog`), `--pause`, `--resume`, `--disable`, `--enforce-budget` (the daily budget pauses the day's run once spent) and `--advisory-budget` (back to the default: tracked, never a stop). Returns the written descriptor. There is no `--enable`: after `--disable`, set `"enabled": true` in `.planning/orchestration/schedule.json` by hand.
4568
+ - `campaign status` — `{scheduled, enabled, paused, cadence, daily_budget, next_due, last_run, runs, spent_today, due, reason, verify_reserve, into_verify_reserve}` (`{scheduled: false}` when nothing is armed; `goal`, `source` and `history` live only in `schedule.json` and the `campaign schedule` return). Also the default when `campaign` is run with no subcommand.
4569
+ - `campaign due` — host-scheduler gate: returns `{due, reason, next_due}`. `reason` is one of `no_schedule`, `disabled`, `paused`, `budget_exhausted_today` (only when the descriptor's `enforce_budget` is `true` — the daily budget is advisory otherwise), `due`, `not_yet`. **Exit codes:** `0` — due; `1` — not due. The payload has no `error` key, so the code is set explicitly; a `cron`/`&&` trigger can gate on it without parsing the body. "Not due" is a negative *answer*, not a failure, so pair the exit code with `reason` if you need to distinguish it from a broken descriptor.
4570
+ - `campaign record-run` — record a completed run and advance `next_due`. Flags: `--items <N>`, `--points <N>`.
4571
+
4572
+ ### `hud [--out <file>] [--open] [--stdout]` (ADR-0035)
4573
+
4574
+ Generates a single self-contained HTML dashboard of the project + bot army (default `.planning/hud.html`). Aggregates existing state — `state.md`, roadmap/phases, the squad registry, the campaign schedule, army worktrees, the cost ledger, `requirements.md`, verification artifacts, and git history — into one page of panels. It is a read-only **view**: it writes only its own rendered file and creates no new state. Army-only panels (command stack, campaign, safety harness, worktrees) self-hide on plain (non-army) projects. `--out` overrides the path, `--open` launches the default browser, `--stdout` prints HTML instead of writing. The file is dependency-free (inlined CSS, no `<script>`, no network) and identical on every runtime. Distinct from the JSON `pan-tools dashboard` command (`state.cjs`), which prints a compact project overview rather than HTML.
4575
+
4576
+ **Module:** `hud.cjs`
4577
+
4578
+ ### `report phase <N> [--out <file>] [--open] [--stdout] | index [--bundle] [--out <file>] [--open] [--stdout] | all [--open]`
4579
+
4580
+ `report all` writes every phase report plus the index to their default paths; its one flag, `--open`, opens the index in the default browser (only when the index was written or changed), and the result reports it as `opened`. Generates self-contained HTML reports for a project's phases, reusing the HUD's rendering. `report phase <N>` writes one phase's report (objective, roadmap position, what changed, verification verdict and gaps) to `.planning/phases/<NN-slug>/<NN>-report.html`; `report index` writes the project timeline to `.planning/report-index.html`, where each row links to a phase report; `report all` regenerates every phase report plus the index in one pass. Like `hud`, these are read-only **views** — every value is read from what PAN already tracks on disk (phase `plan`/`summary`/`verification` artifacts and their frontmatter, `roadmap.md`, and the cost ledger), and the command writes only its rendered file(s), so it can never corrupt planning data. Writes are deterministic: re-running with unchanged phase data rewrites nothing (the volatile generated-at timestamp is ignored when comparing), so reports produce no git churn. `--out` overrides the path, `--open` best-effort launches the default browser (only when the file was written or changed), `--stdout` prints HTML instead of writing (for `phase`/`index`). `--bundle` (on `index`) instead writes one self-contained `.planning/report-bundle.html` with every phase inlined under in-page anchors and no links to sibling files — the form to email or attach. A phase-less (focus-auto) project has nothing to report; `index` exits with a message pointing to `pan-tools hud` instead.
4581
+
4582
+ **Module:** `phase-report.cjs`