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,1546 @@
1
+ # PAN Wizard — Full E2E Audit (2026-08)
2
+
3
+ **Date:** 2026-08-05 · **Audited at:** `main` @ `c2926c1` (v3.22.0) · **Status:** findings only — no fixes applied
4
+
5
+ > This report is a point-in-time snapshot. All numbers below describe the audit run at the commit above, not the current tree — they are exempt from the live-counts policy (CLAUDE.md) and must not be "refreshed."
6
+
7
+ ---
8
+
9
+ ## Executive summary
10
+
11
+ **The system is fundamentally sound.** The full test suite is green (3348 tests / 717 suites, 0 failures, matching CLAUDE.md's counts table exactly), there are **zero Critical findings**, the zero-dependency rule is intact across the entire tree including pan-zcode, and the security posture is strong — a single Medium finding, against a long verified-clean list covering escapeRegex conventions, prototype-pollution guards, shell-safe child_process usage, mkdtempSync temp handling, pinned CI actions, and a clean secret scan.
12
+
13
+ The audit confirmed **137 findings**: 0 Critical, **9 High**, 80 Medium, 48 Low. Two additional candidate findings were refuted during adversarial verification and are documented in Appendix B.
14
+
15
+ ### The nine High findings
16
+
17
+ | ID | Location | Finding |
18
+ |---|---|---|
19
+ | H1 | `bin/install.js:2101` | Codex local install ships CommonJS hooks but deliberately skips the {"type":"commonjs"} package.json marker — all 4 Codex hooks crash in ESM projects |
20
+ | H2 | `pan-wizard-core/bin/lib/bus.cjs:161` | bus drain --mode consume permanently destroys unread messages beyond the read limit |
21
+ | H3 | `pan-wizard-core/bin/lib/verify.cjs:494` | verify reconcile always exits 0 — the anti-rubber-stamp auto-advance gate never fires |
22
+ | H4 | `agents/pan-verifier.md:96` | pan-verifier and pan-plan-checker invoke pan-tools via './.claude/…' which the agent installer never rewrites — broken on all non-Claude runtimes and global installs |
23
+ | H5 | `pan-wizard-core/references/model-profiles.md:55` | model-profiles.md documents pre-COST-RESET profile table — code makes balanced=reasoning for ALL agents |
24
+ | H6 | `pan-wizard-core/workflows/learn.md:19` | learn.md and optimize.md use bare `.claude/pan-wizard-core/` paths the installer never rewrites — broken on Codex/Gemini/OpenCode/Copilot and global installs |
25
+ | H7 | `docs/EXAMPLES.md:354` | EXAMPLES.md shows /pan:insert-phase renumbering subsequent phases — the workflow explicitly never renumbers (decimal insertion) |
26
+ | H8 | `README.md:191` | README 'Development Installation' instructs running the installer from the cloned repo root, which the installer hard-refuses |
27
+ | H9 | `README.md:814` | README troubleshooting tells Codex users to verify skills in .codex/skills/, a location the installer actively deletes |
28
+
29
+ ### Themes
30
+
31
+ 1. **Runtime-parity breakage is the dominant High-severity theme (H1, H4, H6).** The installer's path-rewrite step only recognizes the `~/.claude/` (and, for hooks, `./.claude/`) prefix. Shipped content authored with any other prefix — `./.claude/` in pan-verifier/pan-plan-checker, bare `.claude/` in learn.md/optimize.md — ships verbatim and breaks on the four non-Claude runtimes and on global installs. The Codex hooks ESM crash (H1) is the same class: one runtime excluded from a protection every other runtime gets. A single hardening pass on the installer's rewrite/marker logic plus a content-lint rule (only `~/.claude/` allowed in shipped content) closes the whole class.
32
+
33
+ 2. **Dead gates.** The anti-rubber-stamp `verify reconcile` exit code (H3) is dead code because `output()` unconditionally exits 0 first — the auto-advance gate in exec-phase can never stop a rubber-stamped verification, and CHANGELOG's claim to the contrary is false. The installer's model-capability warning is similarly unreachable (M-class, `finishInstall` references an undefined variable inside a swallowed try/catch). Both were missed because tests cover the pure functions but never the CLI exit path.
34
+
35
+ 3. **Docs that actively mislead survived audit pass 1 (H5, H7–H9).** README's development-install instructions cannot succeed (the installer hard-refuses its own repo root); Codex troubleshooting points at a directory the installer deletes; EXAMPLES.md promises phase renumbering that the workflow explicitly never does; and model-profiles.md still documents the pre-COST-RESET profile table, silently demoting agents to cheaper models when orchestrators follow it.
36
+
37
+ 4. **One real data-loss bug in core (H2).** `bus drain --mode consume` truncates the whole channel file after a limited read — unread messages beyond `--limit`/`--offset` (or beyond the 1000-message default on large channels) are destroyed.
38
+
39
+ 5. **The Medium tail (80) is mostly drift and edge-case robustness**, concentrated in shipped content (stale cross-references, renamed-command leftovers, contradictory instructions) and core-module edge paths (crashes on malformed input, wrong exit codes, silent no-ops). The Low tail (48) is hygiene: embedded counts that violate the counts policy, minor link rot, cosmetic drift.
40
+
41
+ 6. **Test coverage is broad but has named holes**: pan-context-monitor.js (134 lines of threshold/debounce logic) has zero behavioral tests, pan-check-update.js's logic lives in an untestable inline `node -e` string, and global-install mode has no positive coverage on any runtime.
42
+
43
+ ### Highest-leverage fixes
44
+
45
+ 1. **Installer parity pass** — write the `{"type":"commonjs"}` marker for Codex (H1), extend path rewriting or fix the offending sources (H4, H6), and add a shipped-content lint asserting the canonical `~/.claude/` prefix. Fixes 3 of 9 Highs and prevents recurrence.
46
+ 2. **Give `output()` an exit-code path** and add CLI-level exit-code tests for `verify reconcile` (H3) — this un-deadens the auto-advance gate and the same pattern likely applies to other gates routed through `output()`.
47
+ 3. **Fix `bus drain` consume-mode truncation** (H2) — small, contained, real data loss.
48
+ 4. **Docs correction batch** (H5, H7–H9 plus the Medium/Low doc tail) — mechanical once enumerated; every item below carries a concrete fix.
49
+
50
+ ---
51
+
52
+ ## Methodology
53
+
54
+ - **12 independent reviewers** across the 8 requested dimensions (core modules split across 3 reviewers, shipped content and docs each split in 2). All 12 returned results; no dimension failed.
55
+ - **Live installer test**: fresh 5-runtime install (`--claude --codex --gemini --opencode --copilot --local`) into `d:\pantesting\audit-e2e-2026-08`, exit 0, manifest cross-checked against disk in both directions, re-run for the upgrade path, learnings/internal strip verified. The `PAN_SOURCE_ROOT` guard was verified by code inspection and its existing scenario tests only — never by executing the installer from the source repo.
56
+ - **Full test suite executed**: `npm run test:all` → 3348 tests / 717 suites / 0 failures / 0 skipped (≈35 s).
57
+ - **Adversarial verification**: 142 raw findings were deduplicated to 139, then every finding was independently verified by an agent instructed to refute it; Critical/High candidates got **two** independent verifiers (a code-skeptic and a reproducer) and required unanimous confirmation. 137 confirmed, 2 refuted (Appendix B), 0 split verdicts. All High findings were confirmed with live end-to-end reproduction in sandboxes (OS temp dirs or fresh `d:\pantesting` subdirectories).
58
+ - **Constraints honored**: findings only — no repo files were modified; no installs into the source repository; live artifacts remain in `d:\pantesting` (subdirectories `audit-e2e-2026-08` and `verify-codex-esm-2126`), which is the designated manual-test area.
59
+
60
+ ---
61
+
62
+ ## Critical findings
63
+
64
+ _No Critical findings._
65
+
66
+
67
+ ## High findings
68
+
69
+ ### Installer & runtimes
70
+
71
+ #### H1. Codex local install ships CommonJS hooks but deliberately skips the {"type":"commonjs"} package.json marker — all 4 Codex hooks crash in ESM projects
72
+
73
+ `bin/install.js:2101` · Installer & runtimes
74
+
75
+ **Failure scenario:** User project has "type": "module" in its root package.json (any modern ESM project). Run `node bin/install.js --codex --local`. The installer copies CommonJS hooks (pan-check-update.js etc., all using require()) into .codex/hooks/ and registers them in .codex/hooks.json, but the marker write is guarded by `if (!isCodex)`, so .codex/ gets no package.json. Node resolves module type by walking up from .codex/hooks/pan-check-update.js to the project's ESM package.json. Reproduced live: spawning the installed hook in a {"type":"module"} project exits 1 with `ReferenceError: require is not defined in ES module scope`. Every registered hook (SessionStart, PostToolUse, SubagentStop x2) fails on every trigger — update check, context monitor, cost logger, and trace logger are all dead for Codex local installs in ESM projects. The other four runtimes get the marker precisely to prevent this (the installer's own comment: "Prevents 'require is not defined' errors when project has 'type': 'module'").
76
+
77
+ **Evidence:**
78
+
79
+ ```
80
+ install.js:2101-2113: `if (!isCodex) { // Write package.json to force CommonJS mode for PAN scripts // Prevents "require is not defined" errors when project has "type": "module" ... fs.writeFileSync(pkgJsonDest, '{"type":"commonjs"}\n');` — while 2115 `if (!isOpencode)` installs hooks for Codex and 2271-2277 registers them: `const hooksJsonPath = path.join(targetDir, 'hooks.json'); ... lib.mergeCodexHooksConfig(...)`. Live repro output: `exit: 1 / const fs = require('fs'); ^ [ReferenceError]`.
81
+ ```
82
+
83
+ **Suggested fix:** Drop the `!isCodex` exclusion (write {"type":"commonjs"} into .codex/ too), or rename the shipped hooks to .cjs for the Codex target and register the .cjs paths in hooks.json.
84
+
85
+ ### Core modules
86
+
87
+ #### H2. bus drain --mode consume permanently destroys unread messages beyond the read limit
88
+
89
+ `pan-wizard-core/bin/lib/bus.cjs:161` · Core modules
90
+
91
+ **Failure scenario:** Publish 5 messages to a channel, then run `pan-tools bus drain ch --mode consume --limit 2` (both flags documented in CLI-REFERENCE.md §bus drain). Verified live: drain returns 2 entries with more:true, then truncates the whole file — readChannel afterwards shows 0 remaining; the 3 unread messages are unrecoverable. Same loss occurs with no flags on any channel holding more than DEFAULT_DRAIN_LIMIT (1000) messages: the first consume silently deletes the tail. Also occurs with --offset (entries before offset are destroyed).
92
+
93
+ **Evidence:**
94
+
95
+ ```
96
+ drain(): `if (mode === 'consume') { try { fs.writeFileSync(file, '', 'utf-8'); }` — unconditional truncation after `const read = readChannel(cwd, channel, opts);` which honors `limit`/`offset`. Empirical: drained entries: 2, total: 5, more: true → left in channel after consume: 0.
97
+ ```
98
+
99
+ **Suggested fix:** In drain(), when mode==='consume' and read.more is true (or offset>0), rewrite the file with the un-drained lines instead of truncating to empty (read all lines, splice out the consumed window, write back the remainder). Alternatively refuse consume+limit/offset combinations.
100
+
101
+ #### H3. verify reconcile always exits 0 — the anti-rubber-stamp auto-advance gate never fires
102
+
103
+ `pan-wizard-core/bin/lib/verify.cjs:494` · Core modules
104
+
105
+ **Failure scenario:** A phase has verification.md with 'status: passed' but its plan's must_haves artifacts fail substance checks (e.g. a 2-line stub where min_lines: 30 is required). exec-phase.md's auto-advance gate runs 'pan-tools verify reconcile ${PHASE_NUMBER} --raw; RECONCILE_EXIT=$?' and stops only when RECONCILE_EXIT is non-zero. Because output() in core.cjs unconditionally calls process.exit(0) (core.cjs:193), the subsequent 'process.exit(r.reconciled ? 0 : 1)' is dead code and RECONCILE_EXIT is always 0. Empirically confirmed in a fixture: reconcile printed 'invalid' (contradiction detected) yet exited 0 in both --raw and JSON modes. Result: a rubber-stamped verification auto-advances to the next phase — the exact failure ADR-0036 says this gate closes. CHANGELOG.md:178 falsely states 'a claimed pass over failing checks exits non-zero'. Tests (tests/verify.test.cjs) only cover the pure reconcilePhase() function, never the CLI exit code, so this was never caught.
106
+
107
+ **Evidence:**
108
+
109
+ ```
110
+ verify.cjs:493-494: `output(r, raw, r.reconciled ? 'valid' : 'invalid');\n process.exit(r.reconciled ? 0 : 1);` — core.cjs output() ends with `process.exit(0);`. Fixture run: stdout 'invalid', RECONCILE_EXIT=0. exec-phase.md:679-681 gates solely on `RECONCILE_EXIT=$?`.
111
+ ```
112
+
113
+ **Suggested fix:** In cmdVerifyReconcile, don't rely on code after output(): write the payload to stdout directly and call process.exit(r.reconciled ? 0 : 1), or add an exitCode parameter to output(). Add a CLI-level test asserting exit code 1 on contradiction.
114
+
115
+ ### Shipped commands & agents
116
+
117
+ #### H4. pan-verifier and pan-plan-checker invoke pan-tools via './.claude/…' which the agent installer never rewrites — broken on all non-Claude runtimes and global installs
118
+
119
+ `agents/pan-verifier.md:96` · Shipped commands & agents
120
+
121
+ **Failure scenario:** User installs PAN for a single non-Claude runtime (e.g. `npx pan-wizard --gemini --local`). /pan:verify-phase spawns pan-verifier, whose instructions run `node ./.claude/pan-wizard-core/bin/pan-tools.cjs roadmap get-phase …`. No `.claude/` directory exists in a Gemini-only install, so every one of the agent's 9 pan-tools calls (roadmap get-phase, verify artifacts, verify key-links, summary-extract, verify commits, standards phase-track/status/tools) fails with MODULE_NOT_FOUND. Same for pan-plan-checker's 6 calls during /pan:plan-phase verification. Confirmed in the real v3.22.0 install at d:/pantesting: .gemini/agents/pan-verifier.md, .codex/agents/pan-verifier.toml, .opencode/agents/pan-verifier.md (9 occurrences) and .github/agents/pan-plan-checker.agent.md (6 occurrences) all contain the literal `./.claude/` path. Also broken for `--global` Claude installs (core lives in ~/.claude, not ./.claude). The agents copy step in bin/install.js (lines 2016-2018) rewrites only `~/.claude/` — the repo's declared source-of-truth prefix — never `./.claude/`; every other agent uses `~/.claude/…` and is rewritten correctly.
122
+
123
+ **Evidence:**
124
+
125
+ ```
126
+ agents/pan-verifier.md:96 `node ./.claude/pan-wizard-core/bin/pan-tools.cjs roadmap get-phase "$PHASE_NUM"`; bin/install.js:2016-2018 `// Always replace ~/.claude/ as it is the source of truth in the repo\n const dirRegex = /~\/\.claude\//g;` (no ./.claude regex in the agents step); installed proof: /d/pantesting/.gemini/agents/pan-verifier.md:103 `node ./.claude/pan-wizard-core/bin/pan-tools.cjs roadmap get-phase "$PHASE_NUM"`
127
+ ```
128
+
129
+ **Suggested fix:** In agents/pan-verifier.md (lines 96, 174, 217, 266, 376, 381, 407, 420, 448) and agents/pan-plan-checker.md (lines 423, 535, 546, 559, 577, 620), change `./.claude/pan-wizard-core/bin/pan-tools.cjs` to `~/.claude/pan-wizard-core/bin/pan-tools.cjs` to match the convention the installer rewrites.
130
+
131
+ ### Shipped workflows, templates & references
132
+
133
+ #### H5. model-profiles.md documents pre-COST-RESET profile table — code makes balanced=reasoning for ALL agents
134
+
135
+ `pan-wizard-core/references/model-profiles.md:55` · Shipped workflows, templates & references
136
+
137
+ **Failure scenario:** model-profile-resolution.md tells orchestrators to look up each agent's model in this table and pass it to Task spawns (research-phase.md step 0 uses exactly this path). In the default `balanced` profile the doc says pan-executor/researchers/verifier=mid and reviewer/document_code=fast, so an orchestrator passes model="sonnet"/"haiku" — but core.cjs deliberately resolves balanced to `reasoning` for every agent since the 2026-07 COST RESET. Agents get silently demoted to cheaper models, exactly the behavior the reset removed; users also get wrong cost expectations. settings.md:47-49 repeats the same stale scheme ("Balanced: Opus for planning, Sonnet for execution/verification").
138
+
139
+ **Evidence:**
140
+
141
+ ```
142
+ Doc: "| pan-executor | reasoning | mid | mid |" and "**balanced** (default) — Reasoning only for planning... Mid for execution." vs core.cjs:50-54: "COST RESET (2026-07): quality + balanced (the default) both resolve to the `reasoning` tier for EVERY agent" and core.cjs:65 "'pan-executor': { quality: 'reasoning', balanced: 'reasoning', budget: 'mid' }"
143
+ ```
144
+
145
+ **Suggested fix:** Rewrite the Profile Definitions table, Profile Philosophy, and Design Rationale sections to match core.cjs MODEL_PROFILES (quality+balanced = reasoning for all agents; budget = mid/fast), and fix settings.md option descriptions.
146
+
147
+ #### H6. learn.md and optimize.md use bare `.claude/pan-wizard-core/` paths the installer never rewrites — broken on Codex/Gemini/OpenCode/Copilot and global installs
148
+
149
+ `pan-wizard-core/workflows/learn.md:19` · Shipped workflows, templates & references
150
+
151
+ **Failure scenario:** Installer path rewriting only matches `~/.claude/` and `./.claude/` (bin/install.js:712-715). The bare `.claude/...` form in learn.md:19,24,34,70 and optimize.md:26,36,66,79,89,100 passes through verbatim. Confirmed in the real install at d:/pantesting/.codex/pan-wizard-core/workflows/learn.md:19, which still says `node .claude/pan-wizard-core/bin/pan-tools.cjs` — a path that does not exist in a Codex install. A Codex user running /pan:learn gets MODULE_NOT_FOUND on every instructed command. learn.md:85 also points all runtimes at `.claude/settings.json` for hook registration.
152
+
153
+ **Evidence:**
154
+
155
+ ```
156
+ learn.md:19 "node .claude/pan-wizard-core/bin/pan-tools.cjs optimize trace current"; installed artifact /d/pantesting/.codex/pan-wizard-core/workflows/learn.md:19 identical (unrewritten) while line 17 of update.md in the same install was correctly rewritten to "./.codex/..."
157
+ ```
158
+
159
+ **Suggested fix:** Change all invocations in learn.md and optimize.md to the canonical `~/.claude/pan-wizard-core/bin/pan-tools.cjs` form so the installer rewrites them per runtime/install-type.
160
+
161
+ ### User & developer docs
162
+
163
+ #### H7. EXAMPLES.md shows /pan:insert-phase renumbering subsequent phases — the workflow explicitly never renumbers (decimal insertion)
164
+
165
+ `docs/EXAMPLES.md:354` · User & developer docs
166
+
167
+ **Failure scenario:** A user runs `/pan:insert-phase 4 "Webhook Integration"` expecting the doc's shown result (integer 'Phase 4: Webhook Integration' with Notifications→5, Admin→6). Actual behavior inserts a DECIMAL phase after phase 4 (e.g. 4.1) and never renumbers — the roadmap ends up in a completely different shape than the example promises, and the urgent work lands after Notifications rather than before it.
168
+
169
+ **Evidence:**
170
+
171
+ ```
172
+ Doc 354-366: '/pan:insert-phase 4 "Webhook Integration" ... PAN inserts the new phase at position 4, shifting Notifications to 5 and Admin Panel to 6.' pan-wizard-core/workflows/insert-phase.md:2 'Uses decimal numbering (72.1, 72.2, etc.) ... without renumbering the entire roadmap' and line 115: 'Don't renumber existing phases'; phase.cjs cmdPhaseInsert 'Calculate next decimal using existing logic'.
173
+ ```
174
+
175
+ **Suggested fix:** Rewrite Example 6 to show decimal insertion (e.g. `/pan:insert-phase 3` → 'Phase 3.1: Webhook Integration', no renumbering), consistent with USER-GUIDE ('decimal numbering') and the workflow.
176
+
177
+ #### H8. README 'Development Installation' instructs running the installer from the cloned repo root, which the installer hard-refuses
178
+
179
+ `README.md:191` · User & developer docs
180
+
181
+ **Failure scenario:** A contributor follows the doc exactly: `git clone .../PanWizard.git && cd PanWizard && node bin/install.js --claude --local`. bin/install.js line 1776 compares cwd to PAN_SOURCE_ROOT (repo root) and exits 1 with 'Refusing to install PAN into its own source repository.' The documented step can never succeed.
182
+
183
+ **Evidence:**
184
+
185
+ ```
186
+ README lines 189-194: 'git clone https://github.com/oharms/PanWizard.git\ncd PanWizard\nnode bin/install.js --claude --local ... Installs to ./.claude/'. bin/install.js:1776: 'if (normPath(path.resolve(process.cwd())) === normPath(PAN_SOURCE_ROOT)) { console.error(...Refusing to install PAN into its own source repository...); process.exit(1); }'
187
+ ```
188
+
189
+ **Suggested fix:** Rewrite the section to install into a separate test directory (e.g. `cd /path/to/test-project && node /path/to/PanWizard/bin/install.js --claude --local`), matching the installer's own error hint and CLAUDE.md's d:\pantesting rule.
190
+
191
+ #### H9. README troubleshooting tells Codex users to verify skills in .codex/skills/, a location the installer actively deletes
192
+
193
+ `README.md:814` · User & developer docs
194
+
195
+ **Failure scenario:** A Codex user verifying an install checks `~/.codex/skills/pan-*/SKILL.md` / `./.codex/skills/pan-*/SKILL.md` per the doc, finds nothing (Codex skills install to `~/.agents/skills/` or `./.agents/skills/`, and the installer removes legacy pan-* dirs from .codex/skills), concludes the install failed, and loops on reinstalling. README line 139's note repeats the wrong path shape for Codex.
196
+
197
+ **Evidence:**
198
+
199
+ ```
200
+ README:814 'For Codex, verify skills exist in `~/.codex/skills/pan-*/SKILL.md` (global) or `./.codex/skills/pan-*/SKILL.md` (local)'. bin/install.js:117-121 getCodexSkillsRoot → `~/.agents/skills` or `./.agents/skills`; lines 1892-1898: '// Upgrade path: remove pan-* skills left in the legacy .codex/skills/ location by older installs (dead directory for current Codex)' with fs.rmSync.
201
+ ```
202
+
203
+ **Suggested fix:** Change line 814 to `~/.agents/skills/pan-*/SKILL.md` (global) / `./.agents/skills/pan-*/SKILL.md` (local) and qualify line 139 (Copilot: skills/pan-*/SKILL.md under .github|~/.copilot; Codex: shared .agents/skills tree).
204
+
205
+
206
+ ## Medium findings
207
+
208
+ ### Installer & runtimes
209
+
210
+ #### M1. Copilot agent converter extracts tools from `allowed_tools:` block-list frontmatter that zero PAN agents use — all 24 Copilot agents ship without tool restrictions
211
+
212
+ `bin/install-lib.cjs:539` · Installer & runtimes
213
+
214
+ **Failure scenario:** Every agent in agents/*.md declares tools inline as `tools: Read, Grep, Glob, Bash` (verified: all 24 files use this form; grep for `allowed_tools` in agents/ returns nothing). convertClaudeToCopilotAgent only matches `allowed_tools:` followed by a YAML block list, so copilotTools is always empty and the emitted .agent.md frontmatter contains only name+description. Verified live: installed .github/agents/pan-conductor.agent.md has no tools field. Consequence: read-only-by-design agents (e.g. pan-distiller: Read, Grep, Glob; pan-optimizer: Read, Glob, Grep) run on Copilot CLI with the default full toolset including write/bash — the claudeToCopilotTools mapping table and convertCopilotToolName are effectively dead code for agents. Contrast: the Gemini converter (install-lib.cjs:272) and OpenCode converter (336) both parse the inline `tools:` form.
215
+
216
+ **Suggested fix:** Parse the same frontmatter shapes as the Gemini/OpenCode converters (`tools:` inline comma list and `allowed-tools:` block list) in convertClaudeToCopilotAgent, mapping via convertCopilotToolName.
217
+
218
+ #### M2. --copilot flag missing from --help output even though Copilot CLI is a fully supported 5th runtime
219
+
220
+ `bin/install.js:222` · Installer & runtimes
221
+
222
+ **Failure scenario:** User runs `npx pan-wizard --help` to discover install options. The Options section lists --claude, --opencode, --gemini, --codex, --all but not --copilot (verified live), while the banner two lines above advertises 'Claude Code, OpenCode, Gemini, Codex, and Copilot CLI' and the interactive prompt offers Copilot as option 5. A user scripting a Copilot install from --help has no way to learn the flag exists and will either use --all (installing 4 unwanted runtimes) or conclude Copilot needs the interactive path. The quality-audit pass 1 (commit ccc4b99, 'fix broken flags') missed this. --skip-warnings (used at 2479/2504/2511) is also undocumented in help.
223
+
224
+ **Suggested fix:** Add `--copilot Install for GitHub Copilot CLI only` (and optionally --skip-warnings) to the help string at line 222.
225
+
226
+ #### M3. cleanupOrphanedFiles uses unguarded fs.unlinkSync — a locked legacy file aborts the entire install with an unhandled exception
227
+
228
+ `bin/install.js:770` · Installer & runtimes
229
+
230
+ **Failure scenario:** Upgrading a pre-v1.9 install on Windows where hooks/statusline.js or hooks/pan-notify.sh exists and is locked (open editor, antivirus scan, or the statusline hook currently executing). cleanupOrphanedFiles is called directly at install.js:1804 outside any try/catch, and its `fs.unlinkSync(fullPath)` at 770 has no guard — EBUSY/EPERM throws, the installer dies with a raw stack trace before any copy stage, and neither the failures[] path nor INSTALL_WARNINGS reporting runs. Every other deletion in the installer is wrapped (`try { fs.unlinkSync(...) } catch (err) { pushInstallWarning(...) }` at 335, or bare catch elsewhere); this is the one remaining unguarded delete on the install path.
231
+
232
+ **Suggested fix:** Wrap the unlinkSync in try/catch and route failures through pushInstallWarning('staleCleanup', relPath, err).
233
+
234
+ #### M4. Upgrade path never sweeps stale Claude skill shims (skills/pan-*.md) — shims for removed/renamed commands persist forever and point at nonexistent commands
235
+
236
+ `bin/install.js:1937` · Installer & runtimes
237
+
238
+ **Failure scenario:** User installed a PAN version whose command set included a command later renamed or removed (the v15→v16 rename map in pan-wizard-core constants.cjs COMMAND_RENAME_MAP shows this happens), then upgrades by re-running the installer. The shim loop (1942-1951) only writes shims for current commands; unlike every other command surface (OpenCode flat commands delete pan-*.md first at 333-336, Codex/Copilot skills sweep pan-* dirs at 390-395/630-634, commands/pan is wiped wholesale by copyWithPathReplacement), skills/ is never cleaned. Reproduced live: planted .claude/skills/pan-oldcommand.md, re-ran --claude --local, file survived (60 files where 59 expected). Claude Code's skill discovery then surfaces a skill whose body says 'run /pan:oldcommand' — a command that no longer exists. Same defect class: .claude/workflows/ (2156-2168) writes pan-*.js scripts without sweeping stale ones. Being untracked in the manifest (see separate finding), the orphans are also invisible to verifyInstall.
239
+
240
+ **Suggested fix:** Before the shim-writing loop, delete existing skills/pan-*.md files (mirroring copyFlattenedCommands' stale cleanup); do the same for workflows/pan-*.js.
241
+
242
+ #### M5. Model-capability warning (E-9) is dead code: finishInstall references undefined `targetDir`, ReferenceError swallowed by bare catch — warning can never fire
243
+
244
+ `bin/install.js:2481` · Installer & runtimes
245
+
246
+ **Failure scenario:** User has `"model": "claude-3-haiku-20240307"` (or any model detectModelCapabilities flags as lacking 1M ctx / extended thinking) in .claude/settings.json and runs the installer. The advertised warning ("PAN's multi-agent workflows are tuned for frontier reasoning models... lacks: ...") never prints, because line 2481 reads `targetDir` which is not a parameter of finishInstall() and not module-scoped (every other targetDir is a local of install()/uninstall()/sweepProprietaryCommandSurfaces()); the ReferenceError is silently eaten by the bare `catch {}` at 2498. Reproduced live: set model to claude-3-haiku in the sandbox's .claude/settings.json, re-ran `--claude --local`, no warning printed. All of detectModelCapabilities' installer-facing logic (including the comment about preventing a 'FALSE warning' on claude-opus-5) is unreachable from the installer.
247
+
248
+ **Suggested fix:** Use the `settingsPath`/`settings` parameters already passed into finishInstall (and remove the shadowing re-declarations inside the try block) instead of re-deriving from the nonexistent targetDir.
249
+
250
+ #### M6. Statusline overwrite guard only inspects the first statusline-capable runtime — existing custom statuslines in Gemini/Copilot are silently clobbered
251
+
252
+ `bin/install.js:2658` · Installer & runtimes
253
+
254
+ **Failure scenario:** User has a custom statusLine in .gemini/settings.json (or .github/copilot/settings.json) but none in .claude/settings.json, and runs `node bin/install.js --claude --gemini --local`. installAllRuntimes picks the FIRST statusline runtime's result as `primaryStatuslineResult` (claude), handleStatusline sees no existing statusline there and calls back true, and finalize applies shouldInstallStatusline=true to every statusline runtime — finishInstall unconditionally sets `settings.statusLine = {command: pan-statusline}` for Gemini without checking Gemini's existing entry and without the prompt/--force-statusline gate that protects the primary runtime. Reproduced live: planted `{"command":"node my-custom-statusline.js"}` in .gemini/settings.json, removed claude's statusLine, ran --claude --gemini --local; gemini statusLine afterwards was `node .gemini/hooks/pan-statusline.js` with no prompt or warning.
255
+
256
+ **Suggested fix:** Evaluate handleStatusline per runtime (check each result's settings.statusLine) instead of a single primary, or skip the overwrite in finishInstall when settings.statusLine already exists and differs from PAN's, unless --force-statusline.
257
+
258
+ ### Core modules
259
+
260
+ #### M7. CLI-REFERENCE.md and pan-tools.cjs header document '--type plan|summary' but code only accepts 'plans'/'summaries'
261
+
262
+ `docs/CLI-REFERENCE.md:782` · Core modules
263
+
264
+ **Failure scenario:** A user copies the canonical reference example 'pan-tools phases list --type plan --phase 5' (CLI-REFERENCE.md:782; signature at :776 and :787 also say plan|summary, as does the dispatcher's own header comment pan-tools.cjs:36). The command fails: 'Error: Unknown type: plan. Expected 'plans' or 'summaries'' (empirically confirmed, exit 1). cmdPhasesList (phase.cjs:74-79) matches only the plural forms; the actual workflow caller (execute-plan.md:141) uses '--type summaries', so the docs and header comment are wrong, not the code.
265
+
266
+ **Suggested fix:** Fix CLI-REFERENCE.md:776/782/787 and the pan-tools.cjs:36 header comment to 'plans|summaries' (or make cmdPhasesList accept both singular and plural).
267
+
268
+ #### M8. isDreamDue contradicts its own contract (permanently true after first run) and has no consumer
269
+
270
+ `pan-wizard-core/bin/lib/campaign.cjs:155` · Core modules
271
+
272
+ **Failure scenario:** Docstring says the dream/retro step is due "once per calendar day that had activity — curate memory between missions, not after every cycle". But `!sameUtcDay(last, at) || (schedule.history || []).length > 0` returns true whenever history is non-empty — and recordRun always appends to history, which is capped at 50 but never emptied — so after the first recorded run isDreamDue is true on every call, same-day or not, forever. Grep shows no caller anywhere in pan-tools.cjs, commands/, agents/, or workflows (only the unit test), so this is dead exported code whose behavior would fire the retro after every cycle if ever wired as documented.
273
+
274
+ **Suggested fix:** Either fix the predicate (e.g. `return !sameUtcDay(last, at) && (schedule.history || []).length > 0;` or track last_dream_at) and wire a consumer, or delete the export.
275
+
276
+ #### M9. campaign due claims exit-coded gating (0 = due, 1 = not due) but always exits 0
277
+
278
+ `pan-wizard-core/bin/lib/campaign.cjs:193` · Core modules
279
+
280
+ **Failure scenario:** A host scheduler wired per the code's own documented contract — e.g. cron running `pan-tools campaign due && claude "/pan:army --continue"` — fires the bot-army run on every poll regardless of dueness, because output() unconditionally calls process.exit(0). Verified live: `campaign due` with no schedule prints {"due": false, "reason": "no_schedule"} and exits 0. Combined with advisory-by-default budgets (enforce_budget: false), an exit-code-gated trigger runs missions every poll interval.
281
+
282
+ **Suggested fix:** Either implement the contract (set process.exitCode = d.due ? 0 : 1 and bypass output()'s exit(0) for this command) or delete the comment and document that callers must parse the JSON/raw output.
283
+
284
+ #### M10. scaffold context/uat/verification without --phase crashes with raw TypeError instead of usage error
285
+
286
+ `pan-wizard-core/bin/lib/commands.cjs:776` · Core modules
287
+
288
+ **Failure scenario:** Running `pan-tools scaffold context` (flag forgotten) crashes: "TypeError [ERR_INVALID_ARG_TYPE]: The 'path' argument must be of type string. Received null at Object.join ... commands.cjs:776" — verified live in a temp dir. The guard `if (phase && !phaseDir && type !== 'phase-dir')` only fires when phase is truthy, so phase=undefined falls through to `path.join(phaseDir, ...)` with phaseDir=null. Same for scaffold uat / scaffold verification.
289
+
290
+ **Suggested fix:** Add `if (!phase && type !== 'phase-dir') error('--phase required for ' + type + ' scaffold');` before the switch (or change the guard to `if (!phaseDir && type !== 'phase-dir')`).
291
+
292
+ #### M11. config-set with a missing value reports updated:true but writes nothing
293
+
294
+ `pan-wizard-core/bin/lib/config.cjs:139` · Core modules
295
+
296
+ **Failure scenario:** Verified live: `pan-tools config-set workflow.research` (value forgotten) exits 0 with {"updated": true, "key": "workflow.research"} while config.json ends up as {"workflow": {}} — the assignment of undefined is dropped by JSON.stringify. An agent or user believes the toggle was set (e.g. disabling research) when nothing changed; intermediate empty objects are still created as a side effect.
297
+
298
+ **Suggested fix:** Add `if (value === undefined) error('Usage: config-set <key.path> <value>');` alongside the existing keyPath check.
299
+
300
+ #### M12. getRoadmapPhaseInternal does not normalize the phase number — padded '01' misses the conventional 'Phase 1:' heading while findPhaseInternal accepts both
301
+
302
+ `pan-wizard-core/bin/lib/core.cjs:597` · Core modules
303
+
304
+ **Failure scenario:** Roadmaps use unpadded headings ('### Phase 1: Setup' — the shipped roadmap template and pan-roadmapper examples), but phase directories are zero-padded ('01-setup') and state.md/agents frequently carry the padded form. Verified live: with roadmap '### Phase 1: Setup', findPhaseInternal(cwd,'01') and (cwd,'1') both succeed, but getRoadmapPhaseInternal(cwd,'01') returns null while (cwd,'1') succeeds. Callers (init.cjs cmdInitExecutePhase/cmdInitPlanPhase, preview.cjs, and getPhaseModelTier used by resolve-model) then silently lose the phase goal, **Requirements** IDs, and any <!-- model_tier --> override whenever the padded identifier is used — e.g. `pan-tools init execute-phase 01` yields empty requirement IDs while still finding the phase directory.
305
+
306
+ **Suggested fix:** In getRoadmapPhaseInternal, build the pattern from both the raw and normalized forms (or strip leading zeros): e.g. match `Phase\s+0*${unpadded}:` so '1' and '01' both hit 'Phase 1:' and 'Phase 01:'.
307
+
308
+ #### M13. cost.rates config overrides silently ignored for versioned model ids — only DEFAULT_RATES gets prefix matching
309
+
310
+ `pan-wizard-core/bin/lib/cost.cjs:101` · Core modules
311
+
312
+ **Failure scenario:** User sets `.planning/config.json` → cost.rates['claude-opus-4-7'] = {input:1, output:1, ...} (the documented family-key override). Hook-captured records carry versioned ids like 'claude-opus-4-7-20260301'. resolveRate checks configRates only by exact key, misses, then prefix-matches against DEFAULT_RATES and returns the built-in $5/$25 — verified live: resolveRate('claude-opus-4-7-20260301', null, {'claude-opus-4-7':{input:1,...}}) returns {input:5, output:25, ...}. Every cost report and HUD figure prices those records at defaults, defeating the exact override the v3.x fix comment (appendRecord) says should reach cost.cjs.
313
+
314
+ **Suggested fix:** Apply the same longest-prefix family matching to configRates keys before falling back to DEFAULT_RATES.
315
+
316
+ #### M14. distill flags safe JSON.parse try/catch wrappers as removable 'phantom try/catch — does not throw in this form'
317
+
318
+ `pan-wizard-core/bin/lib/distill.cjs:26` · Core modules
319
+
320
+ **Failure scenario:** `pan-tools distill scan` on any project using the standard defensive pattern `try { return JSON.parse(raw); } catch { return null; }` (the exact pattern PAN's own modules use throughout) emits confidence-0.9 finding "Phantom try/catch around JSON.parse() — does not throw in this form" — verified live. JSON.parse throws SyntaxError on malformed input and JSON.stringify throws on circular structures/BigInt, so the claim is false for two of the seven listed callees; the /pan:distill and focus-auto --category distill loops feed these findings to a distiller agent as bloat-removal candidates, steering it to strip legitimate error handling.
321
+
322
+ **Suggested fix:** Remove JSON.parse and JSON.stringify from the phantom-try-catch callee alternation (keep Number/String/Boolean/parseInt/parseFloat, which genuinely never throw).
323
+
324
+ #### M15. distill 'REGRESSED — last resolved' labels are false: memory stores detections, but analyze treats them as resolutions
325
+
326
+ `pan-wizard-core/bin/lib/distill.cjs:378` · Core modules
327
+
328
+ **Failure scenario:** Verified live: on a project with one long function, `pan-tools distill report` writes the finding to .planning/memory/distill-patterns.md, then an immediate `pan-tools distill analyze` reports regressed_count: 1 with message "Function big() is 61 LOC (limit 50) — decompose (REGRESSED — last resolved 2026-08-05)" — for a finding that was never resolved. writePatternsMemory persists CURRENT scan findings (i.e. still-unresolved issues), while detectRegressedPatterns interprets any memory entry as 'previously_resolved'. Every persistent finding is falsely branded a regression from the second run onward, corrupting the distill telemetry the workflows report to the user.
329
+
330
+ **Suggested fix:** Only mark regressed when a pattern existed in memory AND was absent in an intermediate scan (store a resolved flag, or diff consecutive scans: memory entries not present in the previous scan's findings are resolutions).
331
+
332
+ #### M16. experiment new: installer step always fails in installed projects — PAN_SOURCE_ROOT resolves to .claude/, where bin/install.js is never shipped
333
+
334
+ `pan-wizard-core/bin/lib/experiment.cjs:198` · Core modules
335
+
336
+ **Failure scenario:** In any installed project (verified against the real install at d:/pantesting/audit-e2e-2026-08), `pan-tools experiment new audit-probe --idea idea.md` returns installer_error "Cannot find module 'D:\\pantesting\\audit-e2e-2026-08\\.claude\\bin\\install.js'" and the experiment is stuck at status 'scaffolded' — never 'ready'. experiment.cjs lives at .claude/pan-wizard-core/bin/lib in installs, so `path.resolve(__dirname,'..','..','..')` is `.claude/`, and the installer only ships pan-wizard-core/bin (no top-level bin/install.js). The shipped, documented `/pan:experiment` + CLI `experiment new` (CLI-REFERENCE.md commands 154-161) therefore deterministically fails its install step outside the PAN source repo, so `experiment run` drives an external session against a folder with no PAN installed.
337
+
338
+ **Suggested fix:** Resolve the installer relative to an explicit source root: detect the installed layout (e.g. check for bin/install.js existence and fall back to a packaged installer path or npx pan-wizard), or make experiment new default to skipInstaller with a clear message when bin/install.js is absent.
339
+
340
+ #### M17. focus sync computes 'actual' counts from PAN-source-repo layout — in installed host projects every documented count is reported stale against actual 0
341
+
342
+ `pan-wizard-core/bin/lib/focus.cjs:410` · Core modules
343
+
344
+ **Failure scenario:** The shipped /pan:focus-sync command targets "the host project's documentation" and says "Output via `pan-tools focus sync`" (commands/pan/focus-sync.md line 94). But checkDocStaleness derives actuals from `commands/pan/`, `agents/`, and `pan-wizard-core/bin/lib/` under the project cwd — directories that exist only in the PAN source repo (installs put them under .claude/). In any host project those counts are 0, so a README line like "ships 18 commands" (about the user's own CLI) is reported `stale {documented: 18, actual: 0}`, steering the sync step to 'fix' correct documentation to zero. checkCount's generic `(\d+)\s+commands` regex matches any such phrase.
345
+
346
+ **Suggested fix:** Gate the commands/agents/modules count checks on the PAN source layout actually existing (skip an entity when its directory is absent instead of treating it as 0), or scope this staleness check to the PAN repo only.
347
+
348
+ #### M18. prompts_complete stop reason is unreachable: focus auto --update never parses or records prompts_remaining, though focus-auto.md instructs it
349
+
350
+ `pan-wizard-core/bin/lib/focus.cjs:928` · Core modules
351
+
352
+ **Failure scenario:** commands/pan/focus-auto.md line 490 instructs: "Record `prompts_remaining` count in cycle update — when 0, `prompts_complete` stop fires". But focusAutoUpdate builds the cycle exclusively from --items-completed/--items-failed/--points-used/--tests-before/--tests-after/--batch-file/--command; there is no --prompts-remaining flag and nothing sets cycle.prompts_remaining. determineStopReason's `cycle.prompts_remaining === 0` is therefore always `undefined === 0` = false on every CLI-recorded cycle, so a prompts-category auto-run never gets its documented completion stop — it only ends via zero_completed or max_cycles, burning extra cycles. The unit tests (tests/focus.test.cjs:1839) hand-build cycle objects with prompts_remaining, masking the gap.
353
+
354
+ **Suggested fix:** Parse `--prompts-remaining` in focusAutoUpdate's cycle construction (Number(getVal('--prompts-remaining', undefined)) preserving undefined when absent), or drop the prompts_complete branch and the instruction from focus-auto.md.
355
+
356
+ #### M19. Frontmatter inline-array serialization is lossy: items containing ', ' are split into multiple items on re-parse
357
+
358
+ `pan-wizard-core/bin/lib/frontmatter.cjs:161` · Core modules
359
+
360
+ **Failure scenario:** Run `pan-tools frontmatter set plan.md --field tags --value '["auth, sessions"]'` (or merge, or ANY frontmatter set on a file whose existing array field has a comma-containing item — spliceFrontmatter re-renders all fields, so untouched fields corrupt too). Verified: `{tags: ['auth, sessions']}` (1 item) serializes to `tags: [auth, sessions]` and extractFrontmatter reads it back as ['auth','sessions'] (2 items). Comma-laden strings are natural in fields like key_decisions ('Use CJS, not ESM'). The multi-line path quotes via quoteIfNeeded (and quoteIfNeeded itself doesn't quote comma-only strings either), so only arrays with ≤3 items under 60 joined chars — the common case — corrupt.
361
+
362
+ **Suggested fix:** In renderYamlEntry, exclude items containing ',' from the inline-render condition (force multi-line), or quote inline items via quoteIfNeeded extended to treat ',' as a quote-trigger and make extractFrontmatter's inline-array split respect quotes.
363
+
364
+ #### M20. `git commit` dispatcher leaks the subcommand token into the files list — silently stages and commits any repo path named 'commit'
365
+
366
+ `pan-wizard-core/bin/lib/git.cjs:347` · Core modules
367
+
368
+ **Failure scenario:** pan-tools.cjs:369 calls `git.cmdGit(cwd, subcommand, args.slice(1), raw)`, so args[0] is always the literal 'commit'. The files filter never excludes it, so `files` is never empty and cmdGitCommit runs `execGit(cwd, ['add', 'commit'])` on EVERY `pan-tools git commit` where --all is absent. Verified empirically: in a repo containing an untracked file named `commit`, `pan-tools git commit --message "add intended file"` (user named no files) committed BOTH intended.txt and the stray `commit` file — silent inclusion of unrequested content in a commit. When no such path exists, the stray `git add commit` fails silently (exit code ignored) on every commit — wasted subprocess and masked semantics (the 'add named files' branch always runs instead of 'commit staged only').
369
+
370
+ **Suggested fix:** Filter on args.slice(1) (drop the subcommand token), e.g. `files: args.slice(1).filter((a, i, arr) => !a.startsWith('--') && arr[i-1] !== '--type' && arr[i-1] !== '--message')`, and add a test asserting `pan-tools git commit --message x` in a repo containing a path named 'commit' does not stage it.
371
+
372
+ #### M21. `memory optimize` agent-log consolidation is dead code — iterates listMemoryAgents() return object, TypeError silently swallowed
373
+
374
+ `pan-wizard-core/bin/lib/memory-optimize.cjs:266` · Core modules
375
+
376
+ **Failure scenario:** Project has .planning/memory/pan-executor.md with 600 entries (over the 500 cap). Run `pan-tools memory optimize --apply`. Expected per the module doc ('consolidate over-budget agent logs') and the output summary field: log compacted to 500. Actual (verified empirically): `for (const a of listMemoryAgents(cwd))` throws TypeError ('return value is not iterable') because listMemoryAgents returns `{agents: [...]}`, the surrounding `catch { /* agent sweep is best-effort */ }` swallows it, result.agents stays [], and the 600-entry log remains 600 entries. Even if iteration were fixed, `readMemory(cwd, a)` is passed an `{agent, entries}` object instead of a name and `parseEntries(rawMem)` is passed readMemory's object instead of a string — the whole sweep has never worked and is untested on this path.
377
+
378
+ **Suggested fix:** Use `for (const a of listMemoryAgents(cwd).agents)`, call `readMemory(cwd, a.agent)`, and count via `rawMem.entries.length` (parseEntries expects the raw string, not the readMemory object). Add a unit test with an over-cap agent log asserting result.agents is non-empty after optimize.
379
+
380
+ #### M22. Regex injection in `requirements mark-complete`: requirement IDs interpolated into new RegExp() unescaped — crashes on metacharacters
381
+
382
+ `pan-wizard-core/bin/lib/milestone.cjs:52` · Core modules
383
+
384
+ **Failure scenario:** Run `pan-tools requirements mark-complete "REQ-01)"` (or any ID containing a regex metachar — IDs are agent-interpolated from roadmap text via ${REQ_IDS} in pan-wizard-core/workflows/execute-plan.md:404). Verified: process exits 1 with an uncaught SyntaxError ('Unmatched )') from `new RegExp` at milestone.cjs:52 instead of reporting the ID as not_found. IDs containing `.` or `+` (e.g. 'REQ.01') silently build wrong patterns that can match/modify unintended lines in requirements.md. This is exactly the class the 2026-07 hardening ('regex-injection→escapeRegex everywhere') fixed elsewhere — knowledge.cjs, hud.cjs, phase-remove.cjs all import escapeRegex; milestone.cjs was missed (it imports from core.cjs but not escapeRegex).
385
+
386
+ **Suggested fix:** Import escapeRegex from core.cjs and wrap reqId at all three interpolation sites (checkbox pattern line 52, both table patterns lines 59 and 63), matching the convention used in phase-remove.cjs and knowledge.cjs.
387
+
388
+ #### M23. `optimize apply` writes files at report-supplied relative paths with no containment check — agent/report-authored action.path can escape the project
389
+
390
+ `pan-wizard-core/bin/lib/optimize.cjs:633` · Core modules
391
+
392
+ **Failure scenario:** An optimizer report (markdown auto-apply block or JSON analysis — content authored by the pan-optimizer agent, i.e. model output, the same trust tier ADR-0040 quarantines in memory) contains `{"type":"memory_append","path":"../../.claude/settings.json","content":"..."}`. applyReportRecommendations does `path.join(cwd, action.path)` then mkdirSync+appendFileSync — writing outside .planning and outside the project for enough `../` segments. The memory-injection defense (memory-optimize.cjs DIRECTIVE_PATTERNS) guards standing memory, but this write path has no path containment and no content screening.
393
+
394
+ **Suggested fix:** Resolve the joined path and require it to be inside a fixed allowroot (e.g. path.resolve(cwd, '.planning') prefix check on path.resolve(memPath)), skipping the action with a reason otherwise.
395
+
396
+ #### M24. phases list --type plans --include-archived crashes with ENOENT when any archived milestone exists
397
+
398
+ `pan-wizard-core/bin/lib/phase.cjs:71` · Core modules
399
+
400
+ **Failure scenario:** A project has completed a milestone with --archive-phases (phase dirs moved to .planning/milestones/v1.0-phases/). Running the documented combination 'pan-tools phases list --type plans --include-archived' (CLI-REFERENCE.md:776 documents all three flags in one signature) pushes archived entries into dirs as decorated strings like '01-old [v1.0]' (line 48), then the type branch calls fs.readdirSync(path.join(phasesDir, dir)) on that non-existent decorated path under .planning/phases/. Empirically confirmed: 'Error: Failed to list phases: ENOENT: no such file or directory, scandir ...\.planning\phases\01-old [v1.0]' exit 1. Archived phases can never be listed with a type filter.
401
+
402
+ **Suggested fix:** Track archived entries as objects carrying their real fullPath (getArchivedPhaseDirs already returns fullPath) instead of decorated name strings, and read files from fullPath in the type branch; or skip archived entries when a type filter is active and document that.
403
+
404
+ #### M25. review-deep resolves --reviewer-file/--hardener-file/--meta-file against process.cwd(), ignoring --cwd
405
+
406
+ `pan-wizard-core/bin/lib/review-deep.cjs:251` · Core modules
407
+
408
+ **Failure scenario:** A sandboxed subagent runs 'pan-tools --cwd /path/to/project review-deep merge 07 --reviewer-file .planning/reviews/07/reviewer.md' from a different working directory. Every other file-taking command resolves relative paths against cwd (e.g. verify.cjs:125 'path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath)'), but cmdReviewDeepMerge/Analyze pass the raw path to safeReadFile, which resolves against the process's real cwd — the files aren't found and the command errors 'No input files provided or readable' (or silently merges only the files that happen to resolve).
409
+
410
+ **Suggested fix:** Resolve each opts.*File with path.isAbsolute(p) ? p : path.join(cwd, p) before safeReadFile, matching the convention used elsewhere.
411
+
412
+ #### M26. experiment stop can never stop a running experiment — pid is only persisted after the child has already exited
413
+
414
+ `pan-wizard-core/bin/lib/runner.cjs:442` · Core modules
415
+
416
+ **Failure scenario:** User starts 'pan-tools experiment run <slug>' (blocks synchronously for up to 60 min) and from a second terminal runs 'pan-tools experiment stop <slug>'. runExperiment writes run-state.json before spawning with pid:null (runState initialized at line 218, written at line 225), then blocks in spawnSync; runState.pid is assigned only at line 293 from the completed spawnSync result — i.e. after the child is already dead. So while status==='running', state.pid is always null and stopExperiment returns { error: 'experiment "<slug>" has no recorded pid' } — the SIGTERM path is unreachable for any live run. The docstring's promised graceful-stop ('send SIGTERM. If still alive after a short grace period, SIGKILL', lines 420-424) is doubly false: no live pid ever exists, and no grace-period/SIGKILL code exists at all.
417
+
418
+ **Suggested fix:** Switch runExperiment to async child_process.spawn (persist child.pid immediately after spawn, then wait), or document experiment stop as inoperative for in-flight runs and remove the SIGTERM/SIGKILL claims.
419
+
420
+ #### M27. state resolve-blocker reports resolved:true even when no blocker matched the given text
421
+
422
+ `pan-wizard-core/bin/lib/state.cjs:467` · Core modules
423
+
424
+ **Failure scenario:** User runs 'pan-tools state resolve-blocker --text "typo-blocker-name"' where no blocker line contains that text. The filter removes nothing, the unchanged section is rewritten, and the command still outputs { resolved: true, blocker: "typo-blocker-name" } / 'true', so a workflow believes the blocker was cleared while it still blocks preflight (cmdPreflight counts active blockers and refuses 'ready').
425
+
426
+ **Suggested fix:** Compare filtered.length against lines.length; output { resolved: false, reason: 'no matching blocker' } when nothing was removed.
427
+
428
+ #### M28. dashboard --raw prints JSON — the human-readable raw branch discards its own output by passing raw=false
429
+
430
+ `pan-wizard-core/bin/lib/state.cjs:1015` · Core modules
431
+
432
+ **Failure scenario:** User runs 'pan-tools dashboard --raw' expecting the condensed human summary the code builds ('Project: ...', 'Current Phase: ...', etc.). The raw branch calls output(result, false, lines.join('\n')) — with raw=false, output() ignores the third argument and prints full JSON. Empirically confirmed: dashboard --raw emitted the JSON object, identical to non-raw mode; the entire lines-building block (1006-1014) is dead code.
433
+
434
+ **Suggested fix:** Change to output(result, true, lines.join('\n')) (or output(result, raw, lines.join('\n'))).
435
+
436
+ #### M29. validate deployment hook-path integrity check silently validates nothing for the hooks PAN itself installs
437
+
438
+ `pan-wizard-core/bin/lib/verify-deploy.cjs:94` · Core modules
439
+
440
+ **Failure scenario:** The installer writes Claude/Gemini/Codex hooks in nested groups: settings.hooks.SessionStart = [{ hooks: [{type:'command', command:'node .claude/hooks/pan-check-update.js'}] }] (install.js:2367-2374, confirmed in the real d:\pantesting\.claude\settings.json). validateRuntimeInstall iterates hookArr entries and reads hook.command directly — but each entry is the wrapper group {hooks:[...]} with no .command property, so zero hook commands are collected and the 'hook paths resolve to real files' check runs on an empty list. Empirically confirmed: fixture with settings.json referencing four hook files, none of which exist on disk → settings_ok:true, settings_issues:[]. Only the flat statusLine.command is actually validated (deleting pan-statusline.js was correctly flagged). Missing or wrongly-pathed PAN hooks pass deployment validation.
441
+
442
+ **Suggested fix:** Descend into the nested shape: for (const group of hookArr) { if (group.command) hookCommands.push(group.command); for (const h of (Array.isArray(group.hooks) ? group.hooks : [])) if (h.command) hookCommands.push(h.command); }
443
+
444
+ #### M30. verify stubs --gate is a no-op: exits 0 even with blocking high-severity stub findings
445
+
446
+ `pan-wizard-core/bin/lib/verify.cjs:542` · Core modules
447
+
448
+ **Failure scenario:** A handoff script or workflow runs 'pan-tools verify stubs --gate' expecting a non-zero exit to block on fake implementations (CHANGELOG.md:179: 'high-severity is the blocking set (--gate)'). Empirically confirmed: with a changed file containing `throw new Error("not implemented")` the command reports blocking:1 and prints 'invalid', but exits 0, so any shell gate (`&&`, `set -e`, CI step) passes. The 'if (opts.gate) process.exit(...)' line is unreachable because output() at line 541 already called process.exit(0).
449
+
450
+ **Suggested fix:** Compute the gate exit before emitting: when opts.gate, write output without output()'s implicit exit(0) and process.exit(r.blocking > 0 ? 1 : 0).
451
+
452
+ #### M31. validate health --repair writes a legacy flat config.json that silently disables the workflow.verifier gate
453
+
454
+ `pan-wizard-core/bin/lib/verify.cjs:933` · Core modules
455
+
456
+ **Failure scenario:** A user with a missing or corrupt .planning/config.json runs 'pan-tools validate health --repair'. repairIssues writes flat defaults {research:true, plan_checker:true, verifier:true, ...} while the canonical schema created by config-ensure-section is nested ({workflow:{research, plan_check, verifier}} — config.cjs:50-58; note also the key-name drift plan_checker vs plan_check). checkVerificationGate reads raw JSON 'config.workflow.verifier !== true' (verify.cjs:1046) and returns early on the flat file, and exec-phase.md's gate reads 'config-get workflow.auto_advance'/'workflow.verifier' style nested paths. Empirically confirmed: identical project with a phase that has summaries but no verification.md emits VERIFICATION_GATE_MISSING once with nested config, zero times with the exact flat config --repair writes. So the repair intended to restore health permanently disables the verification-gate warning despite verifier defaulting to true.
457
+
458
+ **Suggested fix:** Have repairIssues build the config via config.cjs buildConfigDefaults() (nested schema) instead of its own flat literal, or at minimum write {workflow:{research:true, plan_check:true, verifier:true}}.
459
+
460
+ #### M32. learn lint never exits non-zero on failure, but ARCHITECTURE.md and USER-GUIDE.md claim it does
461
+
462
+ `pan-wizard-core/bin/pan-tools.cjs:1473` · Core modules
463
+
464
+ **Failure scenario:** A user or CI wires 'pan-tools learn lint' as a gate per USER-GUIDE.md:770 ('exits non-zero on errors') or ARCHITECTURE.md:126 ('Exits non-zero on errors'). Both output() calls at pan-tools.cjs:1469/1471 unconditionally process.exit(0) before the fail check runs, so a learnings store with L-001 duplicate IDs or other errors still exits 0 and the gate passes. CHANGELOG.md:457 already acknowledged this exact latent issue ('same latent issue exists for learn lint... filed as follow-up'), yet quality-audit pass 1 (ccc4b99) left the false 'exits non-zero' claims in both docs uncorrected.
465
+
466
+ **Suggested fix:** Either fix the exit semantics (write stdout directly, then process.exit(1) on fail — the pattern cmdLinksValidate already uses per CHANGELOG:457), or delete the 'exits non-zero' claims from ARCHITECTURE.md:126 and USER-GUIDE.md:770 until fixed.
467
+
468
+ ### Shipped commands & agents
469
+
470
+ #### M33. pan-executor's blocker example omits the required --text flag — blockers are silently never recorded
471
+
472
+ `agents/pan-executor.md:462` · Shipped commands & agents
473
+
474
+ **Failure scenario:** Executor hits a blocker mid-plan and follows its own instructions: `node …/pan-tools.cjs state add-blocker "Blocker description"`. The dispatcher only reads `--text`/`--text-file` (pan-tools.cjs:328-332 `text: getArgValue(args, '--text')`), so text resolves to null, readTextArgOrFile throws, and cmdStateAddBlocker returns `{added: false, reason: …}` with exit code 0. The blocker never lands in state.md, and downstream consumers (pan-conductor's 'check for new blockers in state.md', /pan:resume blocker surfacing) see nothing. All sibling examples in the same block correctly use flags (record-metric, add-decision, record-session).
475
+
476
+ **Suggested fix:** Change line 462 to `node ~/.claude/pan-wizard-core/bin/pan-tools.cjs state add-blocker --text "Blocker description"`.
477
+
478
+ #### M34. /pan:audit-deployment validates config keys ('mode', 'depth') and phase filenames (PLAN.md, plan_*.md) that PAN never produces — false CRITICALs on every healthy deployment
479
+
480
+ `commands/pan/audit-deployment.md:114` · Shipped commands & agents
481
+
482
+ **Failure scenario:** User audits a healthy, freshly-installed v3.22.0 project. Step 2.1 (line 114) requires `.planning/config.json` to have 'required keys (mode, depth, model_profile, workflow)' and step 2.5 (lines 141-143) validates `mode: "yolo"|"interactive"` and `depth: "quick"|"standard"|"comprehensive"` — but buildConfigDefaults in config.cjs writes neither `mode` nor `depth` (keys: model_profile, commit_docs, search_gitignored, branching_strategy, workflow, budget, commit, execution, routing…); 'yolo' appears nowhere in core except Gemini's CLI flag. Step 2.2 (lines 123-124) requires per-phase `PLAN.md` and `plan_*.md` files, but template.cjs names plans `NN-MM-plan.md` (template.cjs:137 `${padded}-${planId}-plan.md`) and verification files `{N}-verification.md`. An agent following this checklist reports missing required config keys and missing PLAN.md for every phase of a perfectly healthy project.
483
+
484
+ **Suggested fix:** Rewrite step 2.1/2.5 to validate the real config schema (model_profile, workflow booleans, commit_docs, budget) and step 2.2 to check `*-plan.md` / `*-summary.md` / `*-verification.md` per the actual naming convention.
485
+
486
+ #### M35. /pan:experiment stale status: ADR-0026 marked 'pending' and shipped W4 features listed as 'coming soon'
487
+
488
+ `commands/pan/experiment.md:20` · Shipped commands & agents
489
+
490
+ **Failure scenario:** Line 20 says '**ADR:** ADR-0026 (pending W4)' — docs/decisions/ADR-0026-self-improvement-loop.md exists. Lines 166-176 ('Subcommands (W4 — coming soon)') list `pan-tools learn promote/unpromote/list-promoted` and `/pan:learn --experiment <slug>` as future work, but all are shipped: the dispatcher routes learn promote/unpromote/list-promoted (pan-tools.cjs:1370-1413) and commands/pan/learn.md:27 documents `--experiment` as '(v3.7.0+, W3)'. A user reading experiment.md skips a working feature believing it unreleased.
491
+
492
+ **Suggested fix:** Update line 20 to reference the landed ADR-0026 and move the shipped items out of the 'coming soon' section (leaving only archive/delete aliases, which genuinely don't exist).
493
+
494
+ #### M36. /pan:experiment refers users to '/pan:check' — no such shipped command exists
495
+
496
+ `commands/pan/experiment.md:34` · Shipped commands & agents
497
+
498
+ **Failure scenario:** A user deciding whether the experiment loop is overkill follows line 34's advice: 'Validating a single-file change. The experiment loop is heavy — use `npm test` and `/pan:check`.' There is no commands/pan/check.md among the 59 shipped commands (verified against the full directory listing); `/check` exists only as a dev-only skill in the source repo's .claude/commands/, which is never installed. The user gets 'unknown command' in every runtime.
499
+
500
+ **Suggested fix:** Replace `/pan:check` with a real shipped alternative (e.g. `npm test` alone, or `/pan:quick`), or drop the reference.
501
+
502
+ #### M37. /pan:learn points to '.claude/workflows/learn.md' — path doesn't exist in any install and leaks the Claude dir to all runtimes
503
+
504
+ `commands/pan/learn.md:75` · Shipped commands & agents
505
+
506
+ **Failure scenario:** The command's final instruction is 'Follow the workflow at `.claude/workflows/learn.md` (or `pan-wizard-core/workflows/learn.md`).' In an installed project, `.claude/workflows/` contains only JS workflow files (verified at d:/pantesting: pan-map-codebase.js, pan-review-pipeline.js) — the markdown workflow lives at `.claude/pan-wizard-core/workflows/learn.md`. Both cited paths 404. Because the path lacks the `~/` or `./` prefix, neither installer regex (`/~\/\.claude\//` or `/\.\/\.claude\//`) rewrites it, so a Gemini/Codex/OpenCode/Copilot install ships a literal `.claude/workflows/learn.md` reference pointing at another runtime's directory.
507
+
508
+ **Suggested fix:** Change line 75 to `@~/.claude/pan-wizard-core/workflows/learn.md` so the installer rewrites it per runtime and the path resolves.
509
+
510
+ #### M38. /pan:links defaults are PAN-source-repo layout — in user projects it scans (almost) nothing and reports 'pass'
511
+
512
+ `commands/pan/links.md:13` · Shipped commands & agents
513
+
514
+ **Failure scenario:** The shipped command tells every installed project's agent that /pan:links 'Walks `docs/`, `pan-wizard-core/`, `commands/`, and `agents/`' — directories that exist at the PAN source repo root, not in a user project (where the equivalents sit under `.claude/pan-wizard-core/…`). links.cjs DEFAULT_DOC_ROOTS/DEFAULT_SOURCE_ROOTS hardcode the same layout. Confirmed by running `pan-tools links validate --cwd <empty project>`: result is `status: "pass", doc_files_scanned: 0, source_files_scanned: 0` — a user believes their doc-code link graph was validated when nothing was scanned.
515
+
516
+ **Suggested fix:** Have links.md instruct passing --doc-root/--source-root for user projects (or make links.cjs fall back to sensible user-project roots), and flag a 0-files-scanned pass as a warning rather than a clean pass.
517
+
518
+ #### M39. /pan:optimize documents 'trace status' — subcommand does not exist (actual name: 'trace current')
519
+
520
+ `commands/pan/optimize.md:26` · Shipped commands & agents
521
+
522
+ **Failure scenario:** Agent or user follows the command doc and runs `pan-tools optimize trace status` (documented twice: usage block line 26 and section '### trace status' line 70, described as 'Show the active trace session ID and event count'). optimize.cjs cmdOptimizeTrace supports only init/log/end/current/list/show/reconcile and returns `{ error: 'Unknown trace subcommand. Available: init, log, end, current, list, show, reconcile' }`. The quality-audit pass 1 (commit ccc4b99) that fixed broken flags missed this one.
523
+
524
+ **Suggested fix:** Rename both mentions to `trace current` (or add a `status` alias in optimize.cjs, but the doc fix is the counts-policy-consistent change).
525
+
526
+ #### M40. /pan:retro claims 'does not modify any files' but the --write-memory mode (used by /pan:army) writes agent memory
527
+
528
+ `commands/pan/retro.md:17` · Shipped commands & agents
529
+
530
+ **Failure scenario:** army.md Phase 6 and pan-conductor's campaign_mode both instruct `/pan:retro --write-memory` to persist lessons ('Dreaming' step). verify-retro.cjs:129 implements it: `if (argsList.includes('--write-memory'))` → appendMemory writes lessons into pan-planner/pan-verifier memory files. retro.md states 'This is a reflection command — it does not modify any files' and documents no flags (argument-hint: ""), so an agent resolving the army's instruction against retro.md sees a contradiction, and a user granting the command 'read-only' trust gets files written.
531
+
532
+ **Suggested fix:** Document the --write-memory flag in retro.md and qualify the read-only claim (read-only by default; --write-memory appends agent memory entries).
533
+
534
+ #### M41. /pan:settings references the workflow via '@./.claude/…' — broken on --global installs of every runtime
535
+
536
+ `commands/pan/settings.md:24` · Shipped commands & agents
537
+
538
+ **Failure scenario:** User installs globally (`npx pan-wizard --claude --global`, a documented first-class path) and runs /pan:settings in a project. The command's only execution context is `@./.claude/pan-wizard-core/workflows/settings.md`; the command copier rewrites `./.claude/` unconditionally to the project-relative `./<runtime-dir>/` (bin/install.js:713-715), but a global install puts pan-wizard-core in the home config dir, so the referenced workflow file does not exist relative to the project cwd and the settings logic never loads. Every other command uses `~/.claude/…`, which the installer maps to the absolute global path or the local relative path as appropriate. settings.md is the only command file using the `./.claude/` form (confirmed by repo-wide grep).
539
+
540
+ **Suggested fix:** Change both occurrences (lines 24 and 28) to `@~/.claude/pan-wizard-core/workflows/settings.md`, matching all other commands.
541
+
542
+ #### M42. /pan:what-if claims report filenames include a timestamp preventing overwrites — they don't; reruns silently overwrite the report
543
+
544
+ `commands/pan/what-if.md:116` · Shipped commands & agents
545
+
546
+ **Failure scenario:** User runs `/pan:what-if 7 "Use Redis instead of Memcached"` twice (e.g. re-exploring after code changed), trusting the doc's claim that 'Filename + branch include a timestamp so running what-if multiple times on the same phase+scenario produces distinct reports without overwriting.' whatif.cjs writeCounterfactualReport names the file `${phaseNum}-${slug}.md` with no timestamp and no existence check, so the second run silently overwrites the first comparison report — the exact data loss the doc promises can't happen. Only the worktree path and branch carry timestamps.
547
+
548
+ **Suggested fix:** Either fix the doc (only branch/worktree are timestamped; the report is overwritten per phase+slug) or add a timestamp/uniquifier to the report filename in whatif.cjs.
549
+
550
+ #### M43. /pan:help documents a focus-plan '--dry-run' flag that neither the CLI nor the command supports
551
+
552
+ `pan-wizard-core/workflows/help.md:151` · Shipped commands & agents
553
+
554
+ **Failure scenario:** help.md's focus-plan section lists '`--dry-run` — show what would be selected without writing'. cmdFocusPlan parses only --budget/--mode/--priority/--lean (focus.cjs:315-338) and unconditionally writes the batch file; commands/pan/focus-plan.md's own flags list (--mode, --priority, --lean, --no-spec-check) gives the orchestrating agent no dry-run behavior either. A user passing --dry-run expecting no writes still gets `.planning/focus/batch-<date>.json` written/overwritten.
555
+
556
+ **Suggested fix:** Remove the --dry-run bullet from help.md's focus-plan section (dry-run genuinely exists on focus-exec and focus-auto, not focus-plan).
557
+
558
+ #### M44. /pan:help's 'All Commands (42)' table omits 17 of the 59 shipped commands and embeds a filesystem count
559
+
560
+ `pan-wizard-core/workflows/help.md:211` · Shipped commands & agents
561
+
562
+ **Failure scenario:** User runs /pan:help (the command instructs 'Output ONLY the reference content… no additions or modifications'), so the reference is displayed verbatim as the complete command list. The table titled 'All Commands (42)' is missing 17 shipped commands: army, cost, dashboard, design-phase, experiment, git, hud, hygiene, knowledge, learn, links, mcp-bridge, optimize, preview, report, review-deep, what-if — including design-phase which just shipped in the ADR-0042 design-parity PR and the entire Observability group. Users relying on /pan:help cannot discover a third of the command surface. The '(42)' header is also a filesystem-derived count outside CLAUDE.md, violating the counts policy (fix is to delete the number, not update it).
563
+
564
+ **Suggested fix:** Delete the '(42)' count from the heading per counts policy, and add the 17 missing commands to the table (or state explicitly that the table is a curated subset and name where the rest live).
565
+
566
+ ### Shipped workflows, templates & references
567
+
568
+ #### M45. diagnose-issues.md spawns general-purpose agents with a context-only template whose own contract requires pan-debugger
569
+
570
+ `pan-wizard-core/workflows/diagnose-issues.md:83` · Shipped workflows, templates & references
571
+
572
+ **Failure scenario:** debug-subagent-prompt.md:3 states "The agent contains all debugging expertise - this template provides problem context only" and both its usage examples pass subagent_type="pan-debugger". diagnose-issues.md fills that same template but spawns subagent_type="general-purpose" with no instruction to read pan-debugger.md — so after UAT gaps, debug agents run with symptoms but zero debugging methodology, and the expected "## ROOT CAUSE FOUND" return contract and .planning/debug/{slug}.md session structure are unspecified for them. The workflow also lists placeholders ({truth}) that don't exist in the template (which has {issue_id}/{issue_summary}) and references "DEBUG-{slug}.md" (lines 67, 203) instead of the actual `.planning/debug/{slug}.md`.
573
+
574
+ **Suggested fix:** Spawn subagent_type="pan-debugger" (or prepend "First, read ~/.claude/agents/pan-debugger.md" as plan-phase does), and align placeholder names and the debug filename.
575
+
576
+ #### M46. exec-phase.md checkpoint flow references nonexistent "continuation-prompt.md template" with 5 placeholders defined nowhere
577
+
578
+ `pan-wizard-core/workflows/exec-phase.md:289` · Shipped workflows, templates & references
579
+
580
+ **Failure scenario:** During /pan:exec-phase, when a plan hits a checkpoint (autonomous:false) the orchestrator is told to "Spawn continuation agent (NOT resume) using continuation-prompt.md template" and fill {completed_tasks_table}, {resume_task_number}, {resume_task_name}, {user_response}, {resume_instructions}. No file named continuation-prompt.md exists anywhere in the shipped tree (templates/ has continue-here.md; references/ has continuation-format.md, which is a "Next Up" display format, not a continuation prompt), and the five placeholders appear only on these two lines in the whole repo — the orchestrator has no template to follow at the checkpoint-resume moment.
581
+
582
+ **Suggested fix:** Either add the template file or inline the continuation-prompt structure into exec-phase.md's checkpoint_handling step.
583
+
584
+ #### M47. execute-plan.md deletes current-agent-id.txt before checking it — interrupted-agent resume detection is dead code
585
+
586
+ `pan-wizard-core/workflows/execute-plan.md:97` · Shipped workflows, templates & references
587
+
588
+ **Failure scenario:** init_agent_tracking runs `rm -f .planning/current-agent-id.txt` and immediately after tests `if [ -f .planning/current-agent-id.txt ]`. The file is always gone by the time the check runs, so "Found interrupted agent" and the documented "If interrupted: ask user to resume ... or start fresh" branch can never execute. A session killed mid-plan leaves current-agent-id.txt; on the next /pan:exec-phase run the interruption is silently discarded instead of offering resume.
589
+
590
+ **Suggested fix:** Move the `rm -f` after the existence check (check → offer resume → then clear), matching the tracking protocol described at line 106.
591
+
592
+ #### M48. help.md claims to be "the complete PAN command reference" but omits 17 of 59 shipped commands
593
+
594
+ `pan-wizard-core/workflows/help.md:2` · Shipped workflows, templates & references
595
+
596
+ **Failure scenario:** The purpose block says "Display the complete PAN command reference" and instructs outputting ONLY that reference. Diffing help.md's /pan: mentions against commands/pan/*.md shows 17 shipped commands never mentioned: army, cost, dashboard, design-phase, experiment, git, hud, hygiene, knowledge, learn, links, mcp-bridge, optimize, preview, report, review-deep, what-if. A user running /pan:help never learns these exist — including the /pan:learn and /pan:optimize self-improvement loop that other workflows (exec-phase trace logging) depend on.
597
+
598
+ **Suggested fix:** Add the missing commands to the reference (or drop the "complete" claim).
599
+
600
+ #### M49. milestone-audit.md tells the auditor to create `.planning/v{version}-v{version}-milestone-audit.md` — doubled prefix breaks every downstream consumer
601
+
602
+ `pan-wizard-core/workflows/milestone-audit.md:132` · Shipped workflows, templates & references
603
+
604
+ **Failure scenario:** Every other reference (same file lines 189, 212, 243, 255, 294; commands/pan/milestone-done.md:46; milestone.cjs:158 archive rename; milestone-gaps.md:15 glob) expects `.planning/v{version}-milestone-audit.md`. An auditor following line 132 literally writes v1.0-v1.0-milestone-audit.md; /pan:milestone-done then reports "No milestone audit found", and cmdMilestoneComplete's archive rename of `${version}-milestone-audit.md` silently misses the file (ENOENT swallowed).
605
+
606
+ **Suggested fix:** Fix line 132 to `.planning/v{version}-milestone-audit.md`.
607
+
608
+ #### M50. milestone-new.md cites `/pan:discuss-milestone` as the producer of MILESTONE-context.md — command does not exist and nothing produces the file
609
+
610
+ `pan-wizard-core/workflows/milestone-new.md:20` · Shipped workflows, templates & references
611
+
612
+ **Failure scenario:** commands/pan/ has no discuss-milestone.md (59 commands checked), and repo-wide grep shows MILESTONE-context.md only ever consumed (milestone-new.md lines 3, 20, 24, 71, 370), never produced. A user following the doc runs /pan:discuss-milestone and gets an unknown-command error; the entire "If MILESTONE-context.md exists" branch is dead code unless the file is hand-authored.
613
+
614
+ **Suggested fix:** Remove the /pan:discuss-milestone attribution (or point at the real context source, e.g. conversation/idea.md), or ship the command.
615
+
616
+ #### M51. milestone-new.md researcher FILE column uses uppercase STACK.md/FEATURES.md/ARCHITECTURE.md/PITFALLS.md — contradicts synthesizer reads and actual template filenames
617
+
618
+ `pan-wizard-core/workflows/milestone-new.md:154` · Shipped workflows, templates & references
619
+
620
+ **Failure scenario:** The dimension table tells each pan-project-researcher "Write to: .planning/research/{FILE}" and "Use template: ~/.claude/pan-wizard-core/templates/research-project/{FILE}" with FILE=STACK.md etc. On Linux/case-sensitive macOS the template path templates/research-project/STACK.md does not exist (files are lowercase stack.md...), and if researchers write STACK.md the synthesizer spawned at lines 163-166 reads .planning/research/stack.md (lowercase) and finds nothing — summary.md loses all four dimensions. new-project.md uses lowercase for the identical flow (line 548).
621
+
622
+ **Suggested fix:** Change the FILE row to lowercase stack.md / features.md / architecture.md / pitfalls.md.
623
+
624
+ #### M52. new-project.md uses local-only `./.claude/` paths for every pan-tools/agent/template reference — broken in global installs
625
+
626
+ `pan-wizard-core/workflows/new-project.md:66` · Shipped workflows, templates & references
627
+
628
+ **Failure scenario:** copyWithPathReplacement rewrites `./.claude/` to `./<runtime-dir>/` unconditionally, while `~/.claude/` becomes the absolute global dir on global installs (install.js:1765-1767). new-project.md is the only major workflow written with `./.claude/` throughout (lines 66, 157, 163, 307, 459, 513, 549, ... 1061). User installs `npx pan-wizard --claude --global`, starts a fresh project, runs /pan:new-project: the workflow instructs `node ./.claude/pan-wizard-core/bin/pan-tools.cjs init new-project` — no local .claude/pan-wizard-core exists → MODULE_NOT_FOUND on the very first onboarding command, while every other workflow in the same install carries correct absolute paths.
629
+
630
+ **Suggested fix:** Switch new-project.md to the canonical `~/.claude/` prefix used by the other 30+ workflows.
631
+
632
+ #### M53. resume-project.md incomplete-plan detection uses `${plan/PLAN/SUMMARY}` — case-mismatched substitution is a no-op, detection never fires
633
+
634
+ `pan-wizard-core/workflows/resume-project.md:70` · Shipped workflows, templates & references
635
+
636
+ **Failure scenario:** Plan files are lowercase `*-plan.md`; the substitution replaces literal uppercase "PLAN". Verified: for plan=".planning/phases/02-auth/02-01-plan.md", ${plan/PLAN/SUMMARY} returns the unchanged plan path, so `[ ! -f "$summary" ]` tests the plan file itself (always exists) and "Incomplete: $plan" never prints. A user resuming a project with an executed-but-unsummarized plan is told nothing is incomplete, and the "Complete the incomplete plan" routing in determine_next_action is unreachable via this check.
637
+
638
+ **Suggested fix:** Use `summary="${plan%-plan.md}-summary.md"`.
639
+
640
+ #### M54. Global defaults path mismatch: workflows read/write `~/.pan-wizard-core/defaults.json` (plus stray `mkdir -p ~/.gsd`), code reads `~/.pan-wizard/defaults.json`
641
+
642
+ `pan-wizard-core/workflows/settings.md:163` · Shipped workflows, templates & references
643
+
644
+ **Failure scenario:** User runs /pan:settings, answers "Yes" to save global defaults → the agent writes `~/.pan-wizard-core/defaults.json` (after a leftover `mkdir -p ~/.gsd` from the GSD lineage at line 166). config.cjs buildConfigDefaults reads user defaults exclusively from `~/.pan-wizard/defaults.json` (config.cjs:117), so any config.json created by the CLI (config-ensure-section, health --repair) silently ignores the saved defaults. new-project.md:314-332 reads the same wrong `~/.pan-wizard-core/` path.
645
+
646
+ **Suggested fix:** Standardize settings.md and new-project.md on `~/.pan-wizard/defaults.json` and delete the `mkdir -p ~/.gsd` line.
647
+
648
+ #### M55. update.md global-install detection uses `$HOME/.claude/` which the installer's rewrite regexes don't match
649
+
650
+ `pan-wizard-core/workflows/update.md:19` · Shipped workflows, templates & references
651
+
652
+ **Failure scenario:** Lines 19-20 hardcode GLOBAL_VERSION_FILE="$HOME/.claude/pan-wizard-core/VERSION" under a comment claiming "Paths templated at install time". The rewrite regexes only match `~/.claude/` and `./.claude/`, so `$HOME/.claude/` ships verbatim to every runtime — confirmed in /d/pantesting/.codex/.../update.md:19. A Gemini/Codex user with a global PAN install runs /pan:update: the global check looks in $HOME/.claude (empty), reports UNKNOWN, and the workflow proceeds as a 0.0.0 fresh install, skipping version comparison and the changelog confirmation gate.
653
+
654
+ **Suggested fix:** Use `~/.claude/pan-wizard-core/VERSION` (rewritable form) for the global paths, or detect via the installed pan-wizard-core location.
655
+
656
+ #### M56. verify-phase.md prior-verification check globs `.planning/phase-N*/` — wrong directory and unpadded number, never matches
657
+
658
+ `pan-wizard-core/workflows/verify-phase.md:66` · Shipped workflows, templates & references
659
+
660
+ **Failure scenario:** Phases live in `.planning/phases/NN-slug/` (zero-padded). The check_prior_verification step computes PREV=$((phase_number - 1)) and globs `.planning/phase-${PREV}*/*-verification.md` — wrong parent dir (`phase-` vs `phases/`) and unpadded number (2 vs 02). With `.planning/phases/02-auth/02-verification.md` on disk, verifying phase 3 always logs the false warning "Phase 2 has no verification record" and the gaps_found/human_needed awareness branch never runs.
661
+
662
+ **Suggested fix:** Glob `.planning/phases/*/` and match the padded phase (or use `pan-tools find-phase "$PREV"` like exec-phase does).
663
+
664
+ ### Tests
665
+
666
+ #### M57. Global install mode (--global/--config-dir/env-var dir resolution) has no positive test coverage across all 5 runtimes
667
+
668
+ `bin/install.js:56` · Tests
669
+
670
+ **Failure scenario:** Exactly one test in the entire suite passes --global (tests/agents-md-layer.test.cjs:97), and it only asserts what a global install does NOT create ('no AGENTS.md or CLAUDE.md is created in the working directory'). Nothing verifies a global install actually lays down a working installation: not the config-dir precedence chain (getOpencodeGlobalDir: OPENCODE_CONFIG_DIR > OPENCODE_CONFIG > XDG_CONFIG_HOME > ~/.config/opencode; CLAUDE_CONFIG_DIR/CODEX_HOME per the help notes), not the global `~/.claude/` path rewriting (bin/install.js:358, 417, 657, 712, 1763), not global uninstall cleanup (line 1322 '# For global uninstalls, clean up ~/.config/opencode/opencode.json'). A regression in any of these ships to npm unseen and breaks `npx pan-wizard --claude --global` — a literal example in the --help text — for every user, while the fully-covered --local path keeps CI green.
671
+
672
+ **Suggested fix:** Add tests/scenarios/global-install.test.cjs: for each runtime, run install.js with --global --config-dir <tmp fake home dir>, assert pan-wizard-core + commands + hooks land in the config dir, manifest is written, installed pan-tools runs from that path (mirroring runtime-matrix.test.cjs), path references are rewritten from ~/.claude/, and --global --uninstall removes everything including the opencode global config entry.
673
+
674
+ #### M58. pan-check-update.js update-check logic lives in an untestable inline `node -e` string with zero coverage
675
+
676
+ `hooks/pan-check-update.js:25` · Tests
677
+
678
+ **Failure scenario:** The version-resolution and cache-write logic (project VERSION vs global VERSION precedence, `npm view pan-wizard version`, cache JSON shape consumed by the statusline) is JavaScript embedded inside a template-string passed to `spawn(process.execPath, ['-e', ...])`. It is never parsed, linted, or executed by any test — a syntax error or field rename inside the string (e.g. `update_available` → `updateAvailable`) crashes the detached child silently on every SessionStart, the cache file goes stale/absent, and the update indicator never fires; CI stays green. Verified: no test file executes pan-check-update.js — all grep hits in tests/ are installer-registration string assertions.
679
+
680
+ **Suggested fix:** Extract the inline script body into a required module (e.g. hooks/lib/check-update-core.js or an exported function invoked via a child argv flag) so it is at least syntax-checked at load and unit-testable; add a test covering project-VERSION-over-global precedence and the cache record shape ({update_available, installed, latest, checked}).
681
+
682
+ #### M59. pan-context-monitor.js hook (134 lines of threshold/debounce/escalation logic) has zero behavioral tests
683
+
684
+ `hooks/pan-context-monitor.js:1` · Tests
685
+
686
+ **Failure scenario:** A regression in the debounce or threshold logic (e.g. flipping the comparison at line 99 `if (!firstWarn && warnData.callsSinceWarn < DEBOUNCE_CALLS && !severityEscalated)` or the threshold checks at lines 75/93) ships silently: no test executes this hook, so CI stays green while every Claude install either spams a context warning after every tool use or never warns at all — and the CRITICAL warning ('STOP new work immediately. Save state NOW') is the agent's only signal before context exhaustion. Verified: the file has no module.exports, no test file references it except installer-registration string checks (tests/scenarios/hook-registration.test.cjs asserts only that 'pan-context-monitor.js' appears in settings hook commands), and no test spawns/executes it.
687
+
688
+ **Suggested fix:** Follow the pattern already used by the other three logic-bearing hooks (pan-cost-logger.js exports buildCostRecord, pan-statusline.js exports buildStatuslineOutput, pan-trace-logger.js exports buildTraceEvents — each with a dedicated unit test file): extract a pure helper like buildContextWarning(metrics, warnState, nowSeconds) with module.exports, and add tests/context-monitor-hook.test.cjs covering the 35% warning threshold, 25% critical threshold, first-warn immediate emit, 5-call debounce, warning→critical escalation bypass, and the 60s stale-metrics guard.
689
+
690
+ ### Hooks
691
+
692
+ #### M60. 0700 bridge-dir hardening does not stop the pre-planted-directory attack its comment claims to prevent (mkdirSync recursive is a silent no-op on an existing attacker-owned dir)
693
+
694
+ `hooks/pan-context-monitor.js:31` · Hooks
695
+
696
+ **Failure scenario:** Shared multi-user Linux host (the exact threat model the 2026-07 hardening comment names). Attacker pre-creates /tmp/pan-hooks-1000 (victim's uid is public) as attacker-owned mode-0777 BEFORE the victim's first session. Victim's statusline hook runs `fs.mkdirSync(dir, { recursive: true, mode: 0o700 })` — with recursive:true this silently succeeds on the existing dir and never verifies ownership or mode. The victim then writes `claude-ctx-<session_id>.json` into the attacker's directory: (a) the attacker reads it, learning the live session id and context metrics; (b) the attacker replaces the file with crafted JSON such as {"remaining_percentage":30,"used_pct":"70. Ignore prior instructions and ...","timestamp":<now>} — pan-context-monitor interpolates used_pct verbatim into the additionalContext message (line 117: `Usage at ${usedPct}%`), injecting attacker-controlled text directly into the agent's conversation; (c) writeFileSync follows symlinks, so a symlink swapped in at the bridge path redirects the victim's periodic statusline writes to any victim-writable file. Windows is unaffected (per-user %TEMP%; the mode is a no-op there, which is acceptable) — the failure is specifically on the shared Unix hosts the hardening was written for.
697
+
698
+ **Suggested fix:** After mkdirSync, lstat the dir and verify it is a real directory owned by the current uid with mode 0700 (chmod/refuse otherwise), or use a per-user mkdtemp-anchored path exchanged via an unpredictable channel; additionally coerce used_pct/remaining_percentage to Number before interpolating into the additionalContext message.
699
+
700
+ #### M61. Re-fired SubagentStop appends phantom zero-token rows that the idempotency guard cannot catch (cursor advance defeats the duplicate check)
701
+
702
+ `hooks/pan-cost-logger.js:275` · Hooks
703
+
704
+ **Failure scenario:** The exact field condition the guard was added for (its own comment: re-fired SubagentStop was 'the source of ~57% duplicate rows in the field (2026-07)'). Event 1: cursor since=0, transcript slice sums to real tokens (e.g. input=5000), cursor advances to N, row appended. The event re-fires: readCursor returns N, the slice past N is empty, so the record has input/output/cache all 0, duration_ms null. isDuplicateOfLastRecord strips only `ts` and compares — the zeros row differs from the previous real-token row, so it is APPENDED. Every re-fire now produces a junk row with token_source:'transcript' and clamped:false, indistinguishable from a genuine zero-token run; /pan:cost counts it as a real agent spawn, inflating spawn counts and halving per-agent averages. Second concrete trigger: a project with BOTH a global and a local PAN install (both documented, README lines 154-155) registers two cost-logger commands (different paths, so Claude Code runs both); they share `.planning/metrics/.cost-cursor.json`, so the second process sees the advanced cursor and appends a zeros row on EVERY SubagentStop. pan-trace-logger.js has the identical defect (isDuplicateCompletion, lines 400-416, plus its own cursor).
705
+
706
+ **Suggested fix:** When the transcript slice is empty (lineCount === since) AND the previous ledger row has the same session/agent/transcript, skip the append entirely (a re-fire), or extend the duplicate check to treat an all-zero slice row following a real row for the same agent+session as a duplicate. Apply the same fix to pan-trace-logger.
707
+
708
+ #### M62. Global-install hooks silently create .planning/ metrics and trace artifacts in every project, including non-PAN repos
709
+
710
+ `hooks/pan-trace-logger.js:429` · Hooks
711
+
712
+ **Failure scenario:** User runs `npx pan-wizard --claude --global` (documented in README lines 154 and 172). Hooks are registered in ~/.claude/settings.json (bin/install.js:2339-2436), which Claude Code applies to ALL projects. The user then opens any unrelated repo and any Task subagent completes: pan-trace-logger's stdin driver calls ensureSessionId(cwd) unconditionally, creating `.planning/optimization/traces/sess_auto_YYYYMMDD/session.json` and `.planning/optimization/current-session` in that repo; pan-cost-logger's appendRecord does `fs.mkdirSync(dir, { recursive: true })` and appends `.planning/metrics/tokens.jsonl`. The repo now contains untracked PAN artifacts with no .gitignore entry — easily committed by accident into a project that never installed PAN. The trace-logger's own header (lines 4-6) says the opposite: 'If a trace session is active (.planning/optimization/current-session exists), this hook appends' — i.e. the code contradicts its documented gate.
713
+
714
+ **Suggested fix:** Gate both loggers on evidence of a PAN project (e.g. `.planning/` or `<configDir>/pan-wizard-core/` already exists in cwd) before creating anything; alternatively only auto-mint sessions when `.planning/optimization/` already exists. At minimum, fix the pan-trace-logger header comment to match the always-create behavior.
715
+
716
+ ### User & developer docs
717
+
718
+ #### M63. ARCHITECTURE.md documents .planning/research/ and .planning/codebase/ outputs with legacy UPPERCASE filenames — actual outputs are lowercase
719
+
720
+ `docs/ARCHITECTURE.md:440` · User & developer docs
721
+
722
+ **Failure scenario:** On a case-sensitive filesystem (Linux), a user or script following the doc reads `.planning/codebase/STACK.md` or `.planning/research/FEATURES.md` → file not found; the mapper agents actually write stack.md, integrations.md, architecture.md, structure.md, conventions.md, testing.md, concerns.md, relationships.md, best-practices.md and research/{stack,features,architecture,pitfalls,summary}.md. Hygiene even classifies uppercase planning filenames as pre-v2.2 legacy.
723
+
724
+ **Suggested fix:** Lowercase all research/ and codebase/ filenames in the Layer 5 tree (lines 434-449) and File Lifecycle references.
725
+
726
+ #### M64. ARCHITECTURE.md 'File Format by Runtime' table claims Codex hooks are 'Not supported' — contradicted by the installer and the same doc's Hooks section
727
+
728
+ `docs/ARCHITECTURE.md:799` · User & developer docs
729
+
730
+ **Failure scenario:** A Codex user reading this table concludes cost/trace logging and update checks won't work on Codex and skips hook troubleshooting, when the installer actually writes and registers all hooks in .codex/hooks.json (confirmed by ARCHITECTURE.md line 532, HOOKS.md line 270, and install.js line 2278). Same table also lists Codex config as `config.toml` while USER-GUIDE line 1186 says `config.json`.
731
+
732
+ **Suggested fix:** Update the row to 'hooks/*.js in .codex/hooks.json (since 2026-06)' and reconcile the Codex config cell with USER-GUIDE.
733
+
734
+ #### M65. CLI-REFERENCE documents experiment run flags --timeout-ms and --capture-metrics that the dispatcher never parses, and a wrong default timeout
735
+
736
+ `docs/CLI-REFERENCE.md:3405` · User & developer docs
737
+
738
+ **Failure scenario:** `pan-tools experiment run x --timeout-ms 300000` is silently ignored (dispatcher parses exact flag `--timeout` in SECONDS) so the run uses the default; `--capture-metrics` is also silently dropped — the dispatcher builds runOpts of only {root, prompt, timeoutMs, runtimeOverride}, so runner.cjs's captureMetrics support is unreachable from the CLI and no metrics envelope is ever captured despite the doc's detailed billing_pool/metrics promises. The stated 'Default timeout: 30 min' is also wrong (60 min).
739
+
740
+ **Suggested fix:** Document the real surface: `--timeout <seconds>` (default 3600s/60 min), and either wire `--capture-metrics` into the dispatcher or delete that flag paragraph from the doc.
741
+
742
+ #### M66. CLI-REFERENCE documents experiment harvest [--out <dir>] — flag does not exist; harvest destination is fixed
743
+
744
+ `docs/CLI-REFERENCE.md:3423` · User & developer docs
745
+
746
+ **Failure scenario:** `pan-tools experiment harvest my-idea --out /d/harvests` silently ignores --out and writes to `<sourceRoot>/experiments/my-idea/`; a second run then errors 'harvest already exists ... (use --force to overwrite)' with --force undocumented in this section.
747
+
748
+ **Suggested fix:** Replace `[--out <dir>]` with the real flags `[--source-root <dir>] [--force]` and state the fixed destination `<source-root>/experiments/<slug>/`.
749
+
750
+ #### M67. CLI-REFERENCE documents experiment prune [--keep-harvest] — flag doesn't exist and the described semantics are inverted
751
+
752
+ `docs/CLI-REFERENCE.md:3427` · User & developer docs
753
+
754
+ **Failure scenario:** Doc says prune deletes the folder and `--keep-harvest` preserves harvested artifacts. Actually default prune SOFT-archives (renames to `<slug>-archived-<ts>`), only the undocumented `--hard` deletes, and `--keep-harvest` is silently ignored. A user passing --keep-harvest expecting deletion gets a rename; a user wanting deletion doesn't learn about --hard.
755
+
756
+ **Suggested fix:** Document the real behavior: default = soft archive-rename, `--hard` = permanent delete; remove --keep-harvest.
757
+
758
+ #### M68. CLI-REFERENCE documents `doc-lint schema-check [--schemas-dir <path>]` but the command requires a positional path and has no --schemas-dir flag
759
+
760
+ `docs/CLI-REFERENCE.md:3467` · User & developer docs
761
+
762
+ **Failure scenario:** Running the documented form `pan-tools doc-lint schema-check` (path optional per doc) exits with error 'doc-lint schema-check <path> required'; `--schemas-dir X` would be taken as the missing positional check ('--schemas-dir'.startsWith('--') → error). The documented invocation never runs.
763
+
764
+ **Suggested fix:** Document as `doc-lint schema-check <path>` (positional, required). Also add the undocumented `doc-lint counts <dir> [--exclude ...]` and `doc-lint flags [--doc-dir ...]` subcommands.
765
+
766
+ #### M69. DEVELOPMENT.md 'Local Testing' commands are refused by the installer's source-root guard
767
+
768
+ `docs/DEVELOPMENT.md:93` · User & developer docs
769
+
770
+ **Failure scenario:** After the doc's own 'Development Setup' (`cd PanWizard`), running the documented `node bin/install.js --claude --local` or `--global` exits 1 — the guard fires on cwd == repo root regardless of --local/--global. Neither documented command works from where the doc leaves the reader.
771
+
772
+ **Suggested fix:** Change the Local Testing snippet to run from a scratch project directory (`cd ../pan-test && node ../PanWizard/bin/install.js --claude --local`), and note the source-root guard.
773
+
774
+ #### M70. EXAMPLES Example 1 runs /pan:milestone-done after Phase 1 of a 3-phase milestone, then continues to Phase 2
775
+
776
+ `docs/EXAMPLES.md:102` · User & developer docs
777
+
778
+ **Failure scenario:** A new user mimicking the example runs `/pan:milestone-done` with phases 2-3 still pending; milestone-done archives the roadmap/requirements and tags a release (README: 'All phases verified' is the precondition), leaving the user's remaining phases archived mid-milestone. The example's claim that it 'updates roadmap.md to mark Phase 1 as complete' is also not what milestone-done does (phase completion happens in the exec/verify flow).
779
+
780
+ **Suggested fix:** Move Step 6 (milestone-done) after all three phases complete, or re-label it as phase completion.
781
+
782
+ #### M71. FAQ cost table repeats the false 'balanced = Opus for planning, Sonnet for execution' claim
783
+
784
+ `docs/FAQ.md:26` · User & developer docs
785
+
786
+ **Failure scenario:** Same mismatch as README:724 — a user budgeting from this table underestimates balanced-profile cost because execution actually runs at the reasoning/inherit tier, not Sonnet.
787
+
788
+ **Suggested fix:** Reword to 'balanced: every agent at the session model (reasoning tier); budget: Sonnet/Haiku mix'.
789
+
790
+ #### M72. FAQ tells Codex users to check ~/.codex/skills/pan-*/SKILL.md — wrong location (skills live in ~/.agents/skills/)
791
+
792
+ `docs/FAQ.md:236` · User & developer docs
793
+
794
+ **Failure scenario:** Same failure as README:814 — the FAQ 'Commands not found after install' checklist step 3 points Codex users at a directory the installer deliberately empties, so a healthy install looks broken.
795
+
796
+ **Suggested fix:** Change to `~/.agents/skills/pan-*/SKILL.md` (global) / `./.agents/skills/pan-*/SKILL.md` (local).
797
+
798
+ #### M73. MIGRATION guide's 'Full rollback to v2.10.0' is impossible — the v2.10.0 tag does not exist in the public repo and pre-3.13.1 versions were never published
799
+
800
+ `docs/MIGRATION-v2-to-v3.md:196` · User & developer docs
801
+
802
+ **Failure scenario:** A user following the rollback runs `git checkout v2.10.0` in a fresh clone of github.com/oharms/PanWizard → 'pathspec v2.10.0 did not match'; the repo history starts at the sanitized genesis commit and the earliest tag (local and on origin) is v3.13.1. There is no working rollback path to any v2.x, so the documented recovery procedure dead-ends.
803
+
804
+ **Suggested fix:** Remove or rewrite the v2.10.0 rollback section (e.g. 'rollback = uninstall; v2.x artifacts are not publicly available'), since public history begins at v3.13.1.
805
+
806
+ #### M74. TROUBLESHOOTING claims PAN 'does not implement locking' — state writes have serialized through lock.cjs since ADR-0030
807
+
808
+ `docs/TROUBLESHOOTING.md:686` · User & developer docs
809
+
810
+ **Failure scenario:** A reader debugging concurrent-session symptoms is told there is no locking at all and may skip inspecting `.planning/*.lock` / stale-lock behavior; ARCHITECTURE.md documents the opposite ('writes serialize through lock.cjs (ADR-0030)'), and state.cjs wraps writes in withFileLock with atomic temp-rename.
811
+
812
+ **Suggested fix:** Reword to 'state.md writes are serialized via advisory file locking + atomic writes (ADR-0030), but PAN still does not coordinate whole-session concurrency — keep to one session per project.'
813
+
814
+ #### M75. USER-GUIDE map-codebase description is stale: 4 mapper agents and UPPERCASE output filenames vs actual 6 agents writing 9 lowercase documents
815
+
816
+ `docs/USER-GUIDE.md:1258` · User & developer docs
817
+
818
+ **Failure scenario:** The brownfield diagram (lines 281-291) shows 4 mappers producing codebase/STACK.md, ARCHITECTURE.md, CONVENTIONS.md, CONCERNS.md; the workflow spawns 6 pan-document_code agents producing 9 lowercase files. A user looking for `.planning/codebase/CONVENTIONS.md` per line 1258 on Linux finds nothing (actual file is conventions.md) and assumes mapping failed.
819
+
820
+ **Suggested fix:** Update the diagram to 6 mappers / 9 documents and lowercase all filenames in the diagram and the File Details row.
821
+
822
+ #### M76. README Model Profiles table says `balanced` runs Execution/Verification on Sonnet — code runs every balanced agent at the reasoning (inherit/Opus) tier
823
+
824
+ `README.md:724` · User & developer docs
825
+
826
+ **Failure scenario:** A cost-conscious user keeps the default `balanced` profile expecting Sonnet-priced execution per the table. MODEL_PROFILES maps every agent's balanced tier to 'reasoning' (= inherit — the session's model, typically Opus), and agent files carry no `model:` frontmatter, so executors actually run at full session-model cost. USER-GUIDE (line 604) states the opposite of README: 'quality and balanced — Every agent runs on the reasoning tier'.
827
+
828
+ **Suggested fix:** Fix the balanced row to reasoning/inherit across all three columns (or drop the simplified table and link the User Guide breakdown).
829
+
830
+ ### Specs & ADRs
831
+
832
+ #### M77. ADR-0027 status is 'Proposed' while its feature shipped and its own spec's frontmatter says 'status: accepted'
833
+
834
+ `docs/decisions/ADR-0027-doc-code-link-graph.md:8` · Specs & ADRs
835
+
836
+ **Failure scenario:** A contributor auditing what is shipped from the ADR index reads ADR-0027 'Proposed' and concludes the `links validate` lint does not exist — then re-proposes it, or omits it from user docs. The two linked documents directly contradict each other: docs/specs/doc_code_link_graph_featureai.md:4 says 'status: accepted' and declares 'adr: ADR-0027', while the ADR says Proposed. The feature is demonstrably shipped: pan-wizard-core/bin/lib/links.cjs implements validateAll (line 471), pan-tools.cjs requires lib/links.cjs (line 221), and commands/pan/links.md exists.
837
+
838
+ **Suggested fix:** Update ADR-0027 Status to Accepted with the ship version/date, matching the convention used by ADR-0023..0042.
839
+
840
+ #### M78. GLM/z.ai provider spec has no status marker and its feature (Surface A) was never shipped — nothing marks it deferred or superseded
841
+
842
+ `docs/specs/glm_zai_provider_featureai.md:32` · Specs & ADRs
843
+
844
+ **Failure scenario:** The shipped pan_zcode spec links here as a live 'Companion' (pan_zcode_mcp_bridge_featureai.md:4: "Companion: the [GLM/z.ai provider spec](glm_zai_provider_featureai.md) covers GLM *through PAN's existing runtimes*"). A reader following that link finds SC-1..SC-10 with no status header and concludes `pan-tools provider set glm` / `routing.provider: "zhipu"` exists. A user who then sets routing.provider to "zhipu" in .planning/config.json hits the exact failure SC-1 says is prevented: core.cjs detectProvider (line 736: `return PROVIDER_MODELS[p] ? p : 'default'`) silently downgrades to 'default' because PROVIDER_MODELS (core.cjs:35-41) has only anthropic/openai/google/default — no zhipu entry. None of the spec's 'Files to Create' exist: pan-wizard-core/bin/lib/provider.cjs, commands/pan/provider.md, pan-wizard-core/references/glm-provider.md, tests/provider.test.cjs, tests/scenarios/provider-install.test.cjs are all absent, and pan-tools.cjs has no 'provider' verb.
845
+
846
+ **Suggested fix:** Add a status header (e.g. **Status:** Proposed — not implemented; ZCode Surface B superseded by docs/specs/pan_zcode_mcp_bridge_featureai.md / ADR-0041) so the spec stops reading as shipped behavior.
847
+
848
+ ### Security & hygiene
849
+
850
+ #### M79. saveLocalPatches path-traversal guard only blocks forward-slash '..', bypassable by backslash on Windows
851
+
852
+ `bin/install.js:1687` · Security & hygiene
853
+
854
+ **Failure scenario:** saveLocalPatches iterates manifest.files keys and, for each relPath, reads path.join(configDir, relPath) and copies it to path.join(patchesDir, relPath). The traversal guard is `if (relPath.split('/').includes('..')) continue;` — it splits on '/' only. On Windows path.join treats backslash as a separator, so a manifest key like `..\..\..\secret` is a single element that does NOT include('..') and slips past the guard; path.join then resolves it outside configDir (verified: path.join('C:\\work\\clonedrepo\\.claude','..\\..\\..\\Users\\victim\\.ssh\\id_rsa') === 'C:\\Users\\victim\\.ssh\\id_rsa'). Threat model: a victim runs `pan install`/update inside a cloned untrusted repo that ships a poisoned `.claude/pan-file-manifest.json` with backslash-'..' keys; saveLocalPatches (called before files are overwritten) then reads a file outside the target dir and writes a copy to a backslash-'..' path that also escapes patchesDir. Impact is bounded (source and dest are yoked to the same relPath, so it is a one-hop file relocation of an already-readable file, not arbitrary-content write) which is why this is Low, but the guard is specifically meant to stop '..' escape and is incomplete for Windows separators.
855
+
856
+ **Suggested fix:** Normalise separators before the check, e.g. test `relPath.split(/[\\/]/).includes('..')` (or reject any key where path.relative(configDir, path.join(configDir, relPath)) starts with '..'). Apply the same normalisation to the equivalent guard used for the Codex ../.agents keys.
857
+
858
+ ### pan-zcode (experimental)
859
+
860
+ #### M80. [zcode] Shipped merge-approval instruction tells the operator to use the wrong value (request id, not the nonce), so no gated merge can ever be approved
861
+
862
+ `pan-zcode/bin/install-zcode.js:51` · pan-zcode (experimental)
863
+
864
+ **Failure scenario:** After the M0 spike, an operator registers pan-mcp and the agent calls pan_request_merge on branch 'army/task-x'. requestMerge returns a record with TWO distinct fields: id: 'army-task-x' (a slug of the branch) and approval_token: '9507d9d3d813dddd37' (a random crypto nonce). The generated INSTALL-ZCODE.md step 3 (from this INSTRUCTIONS constant) says: 'Approve a staged merge by setting PAN_MERGE_APPROVAL=<request-id>'. The tool description in pan-zcode/mcp/native-tools.cjs:46 corroborates it: 'env PAN_MERGE_APPROVAL equal to the request id'. Following that literally, the operator sets PAN_MERGE_APPROVAL=army-task-x. The agent then calls pan_confirm_merge; evaluateMerge compares env token 'army-task-x' against record.approval_token '9507d9d3d813dddd37' (merge-gate.cjs:90), they differ, it pushes 'no_human_approval', and the merge is refused (merged:false). I reproduced this live: approving with the id yields {allowed:false, reasons:['no_human_approval']} while approving with approval_token yields {allowed:true, reasons:[]}. Net effect: an operator who follows the only documented approval procedure can never approve any merge — the entire human merge-gate feature is non-functional. (The implementation's own docstring at merge-gate.cjs:54-55 is correct — 'sets PAN_MERGE_APPROVAL to record.approval_token' — so the two operator-facing surfaces contradict the code and the module doc.) It fails closed (never an unsafe merge) and is in the experimental preview subsystem, hence Medium rather than High.
865
+
866
+ **Suggested fix:** In the INSTALL-ZCODE.md INSTRUCTIONS string change 'PAN_MERGE_APPROVAL=<request-id>' to 'PAN_MERGE_APPROVAL=<approval_token>' (the value of the record's approval_token field), and fix the pan_confirm_merge tool description in native-tools.cjs:46 from 'equal to the request id' to 'equal to the request's approval_token'.
867
+
868
+
869
+ ## Low findings
870
+
871
+ ### Installer & runtimes
872
+
873
+ #### L1. writeManifest never records the 59 Claude skill shims (skills/pan-*.md) — verifyInstall can't catch shim write failures and saveLocalPatches won't back up edits
874
+
875
+ `bin/install.js:1625` · Installer & runtimes
876
+
877
+ **Failure scenario:** After a --claude install, the manifest tracks pan-wizard-core (197), commands/pan (59), agents (24), hooks (5), workflows (2) = 287 entries, but the 59 skills/pan-*.md shim files written at 1942-1951 match no writeManifest branch: the skills branch (1625) is gated on `(isCodex || isCopilot || unifiedSkills)` and only lists SKILL.md directories via listCodexSkillNames, not flat shim files. Confirmed live: 59 untracked skills/*.md under .claude. Consequences: (a) a silent write failure in the shim loop is caught only by the loop's own console warning, never by the post-install verifyInstall pass that exists specifically to catch silent copy failures (IMPROVEMENT-TODO P0); (b) a user-modified shim is not detected/backed up to pan-local-patches on upgrade, contradicting docs/AGENTS.md:906 ('The installer backs up modified files'); (c) the 'Verified N installed files' count under-reports the install surface. Copilot's hooks/pan.json and Codex's hooks.json are also untracked (written after writeManifest and not matching the pan-*.js hook filter), though both are removed by uninstall's hardcoded lists.
878
+
879
+ **Suggested fix:** Add a manifest branch for runtime==='claude' that records skills/pan-*.md hashes (and consider moving the codex/copilot hook-config writes before writeManifest or tracking them explicitly).
880
+
881
+ #### L2. PAN_SOURCE_ROOT guard only exact-matches the repo root — installing from any subdirectory of the source repo plants un-ignored install artifacts inside the repo
882
+
883
+ `bin/install.js:1776` · Installer & runtimes
884
+
885
+ **Failure scenario:** A developer runs `cd d:/PanWizard/docs && node ../bin/install.js --claude --local` (e.g. absent-mindedly testing from a subfolder). The guard compares `normPath(path.resolve(process.cwd())) === normPath(PAN_SOURCE_ROOT)` — an exact match against the repo root only — so the install proceeds, creating docs/.claude/, docs/AGENTS.md, docs/CLAUDE.md and (with --codex) docs/.agents/ inside the source repository. The .gitignore self-install patterns (`.claude/pan-wizard-core/`, `.claude/pan-file-manifest.json`, etc.) contain slashes and are therefore root-anchored, so docs/.claude/** is fully committable — defeating the CLAUDE.md rule the guard implements ('NEVER create .claude/pan-wizard-core ... in this repo'). The guard is also bypassable via a symlink/junction to the repo since path.resolve does not canonicalize (fs.realpathSync would).
886
+
887
+ **Suggested fix:** Change the guard to a prefix/containment check (resolved cwd inside PAN_SOURCE_ROOT, using fs.realpathSync on both), matching the isInsideSourceRepo() helper the memory-rebuild module already has.
888
+
889
+ #### L3. Codex agent TOML conversion never converts /pan:command mentions to Codex's $pan-command syntax — 21 of 24 installed agents instruct with invalid invocations
890
+
891
+ `bin/install.js:2025` · Installer & runtimes
892
+
893
+ **Failure scenario:** Install with --codex. The agent copy path applies path replacement + attribution + convertClaudeAgentToCodexToml(stripSubTags(content)) but — unlike the Codex command/skill path (copyCommandsAsCodexSkills calls convertClaudeCommandToCodexSkill → convertSlashCommandsToCodexSkillMentions) and unlike the OpenCode/Copilot agent paths (both convert slash mentions) — never rewrites /pan:name to $pan-name. Verified live: 21 of 24 .codex/agents/*.toml contain /pan: references (e.g. pan-planner.toml has 4x `/pan:plan-phase`, 3x `/pan:exec-phase`). A Codex sub-agent following these developer_instructions will tell the user to run /pan:plan-phase, which is not a valid Codex invocation ($pan-plan-phase is, per the installer's own Done! message and skill adapters).
894
+
895
+ **Suggested fix:** In the isCodex agent branch, apply convertSlashCommandsToCodexSkillMentions(content) before convertClaudeAgentToCodexToml.
896
+
897
+ #### L4. OpenCode install writes a spurious empty settings.json; uninstall leaves it and an emptied opencode.json ({}) behind
898
+
899
+ `bin/install.js:2465` · Installer & runtimes
900
+
901
+ **Failure scenario:** Run `--opencode --local` in a clean project. install() returns settings={} (readSettings of nonexistent file), and finishInstall's write gate `if (!isCodex && !(isCopilot && Object.keys(settings).length === 0))` only special-cases Copilot, so writeSettings creates .opencode/settings.json containing `{}` — a file OpenCode doesn't use (its config is opencode.json). On `--opencode --local --uninstall`, the settings.json cleanup finds no PAN entries (settingsModified stays false) so the file survives, and opencode.json — created by PAN's configureOpencodePermissions — is stripped of PAN permission keys but left behind as `{}`. Confirmed live: after full 5-runtime uninstall the only leftovers besides pan-local-patches were .opencode/settings.json = {} and .opencode/opencode.json = {} (plus empty runtime dirs), despite the closing message 'Your other files and settings have been preserved' — these aren't user files.
902
+
903
+ **Suggested fix:** Extend the empty-settings write guard to OpenCode (or all runtimes): skip writeSettings when Object.keys(settings).length === 0; in uninstall, remove PAN-created settings.json/opencode.json when they end up as {}.
904
+
905
+ ### Core modules
906
+
907
+ #### L5. bridge recommend always returns phase_name: null — reads phaseInfo.name but findPhaseInternal returns phase_name
908
+
909
+ `pan-wizard-core/bin/lib/bridge.cjs:219` · Core modules
910
+
911
+ **Failure scenario:** Verified live: with phase dir 01-setup-auth and a cached tool list, `pan-tools bridge recommend 1` outputs "phase_name": null even though the phase name is 'setup-auth'. searchPhaseInDir returns {phase_number, phase_name, ...} — there is no .name property — so the field can never be populated.
912
+
913
+ **Suggested fix:** Change to `phase_name: phaseInfo.phase_name || null`.
914
+
915
+ #### L6. detectLanguages folds all JavaScript under TypeScript whenever any TS signal exists — comment claims the merge is tsconfig-gated but it isn't
916
+
917
+ `pan-wizard-core/bin/lib/codebase.cjs:102` · Core modules
918
+
919
+ **Failure scenario:** Verified live: a JS-only project (app.js, util.js) containing a bare tsconfig.json (common for editor type-checking of JS) reports primary: 'typescript' with both .js files listed under typescript and no javascript entry — because the manifest promotion creates `typescript: []` (truthy) and the merge condition checks only key presence. Likewise a 500-file .js repo with one stray .ts file and NO tsconfig gets fully relabeled typescript, since the condition never actually consults tsconfig.json despite the comment. This mislabels the language summary that pan-tools codebase detect-languages / map-codebase feed into project docs.
920
+
921
+ **Suggested fix:** Gate the merge on an actual tsconfig.json check (fs.accessSync) or on typescript having a meaningful share of files (e.g. ts files > 0 and tsconfig present), and don't merge when typescript is an empty manifest-promoted array.
922
+
923
+ #### L7. Dead code in codebase.cjs: unused patterns array and cmdAnalyzeImports args parameter documented as supporting --files
924
+
925
+ `pan-wizard-core/bin/lib/codebase.cjs:549` · Core modules
926
+
927
+ **Failure scenario:** detectTestingPractices declares `const patterns = ['*.test.*', '*.spec.*', '__tests__/**'];` and never reads it — the actual detection hardcodes suffix checks, so the globs are decorative. cmdAnalyzeImports' JSDoc advertises `@param {string[]} args - Additional arguments (--files f1,f2)` but the function body never touches args; a caller passing --files gets a full-repo scan silently instead of a scoped one.
928
+
929
+ **Suggested fix:** Delete the unused patterns constant and either implement --files filtering in cmdAnalyzeImports or remove the parameter from the JSDoc and signature.
930
+
931
+ #### L8. standards phase-track drops phase_name from output — same nonexistent .name property
932
+
933
+ `pan-wizard-core/bin/lib/config.cjs:581` · Core modules
934
+
935
+ **Failure scenario:** Verified live: `pan-tools standards phase-track 1` against phase dir 01-setup-auth outputs only {phase, relevant_standards, compliance} — the phase_name key vanishes entirely because `phase.name` is undefined and JSON.stringify drops it (same in the no-plan-files branch at line 561). Consumers expecting the documented shape get a missing field.
936
+
937
+ **Suggested fix:** Use `phase.phase_name` in both output sites of cmdStandardsPhaseTrack.
938
+
939
+ #### L9. Dead `os` import in init.cjs
940
+
941
+ `pan-wizard-core/bin/lib/init.cjs:6` · Core modules
942
+
943
+ **Failure scenario:** No runtime failure — `const os = require('os');` is imported and never referenced anywhere in the file (verified: zero `os.` usages). Pure dead code from an earlier revision; misleads readers into thinking the module touches OS temp/home paths.
944
+
945
+ **Suggested fix:** Delete the `const os = require('os');` line.
946
+
947
+ #### L10. scanAllPhases uses a local phase-dir regex that omits the letter suffix the canonical PHASE_DIR_RE supports — letter phases misparsed in `init progress`
948
+
949
+ `pan-wizard-core/bin/lib/init.cjs:771` · Core modules
950
+
951
+ **Failure scenario:** A project uses a letter phase directory like `06A-hotfix` (letter phases are first-class: constants.cjs PHASE_DIR_RE is /^(\d+[A-Z]?(?:\.\d+)*)-?(.*)/i and core.cjs comparePhaseNum documents '12 < 12A < 12B'). `pan-tools init progress` (cmdInitProgress → scanAllPhases) matches it against the local regex /^(\d+(?:\.\d+)*)-?(.*)/ which lacks [A-Z]?, reporting number '06' and name 'A-hotfix'. With both `06-foo` and `06A-hotfix` present, the progress output lists two phases numbered '06', one with a mangled name — wrong current/next-phase derivation feeding the resume/progress workflow.
952
+
953
+ **Suggested fix:** Replace the local `dirName.match(/^(\d+(?:\.\d+)*)-?(.*)/)` in scanAllPhases with the shared parsePhaseDir()/PHASE_DIR_RE from utils.cjs/constants.cjs, as hud.cjs and phase-report.cjs already do.
954
+
955
+ #### L11. `knowledge discuss` phase argument is unvalidated — path traversal writes session.json outside .planning/conversations
956
+
957
+ `pan-wizard-core/bin/lib/knowledge.cjs:155` · Core modules
958
+
959
+ **Failure scenario:** Run `pan-tools knowledge discuss "../../../outside" --subcmd append --role user --content x`. sessionFile builds `path.join(conversationsDir(cwd), String(phaseNum), 'session.json')` with no validation of phaseNum, and appendTurn's `fs.mkdirSync(path.dirname(file), {recursive:true})` + writeFileSync then create directories and write session.json outside the project tree. Contrast with memory.cjs, which validates agent names against /^[a-zA-Z0-9_-]+$/ precisely to keep constructed paths inside .planning — the same 2026-07 hardening convention was not applied here. Local single-user CLI, so impact is accidental writes from an agent-passed malformed phase id rather than a remote exploit.
960
+
961
+ **Suggested fix:** Validate phaseNum against a phase-shaped allowlist (e.g. /^[0-9]+(\.[0-9]+)*[A-Z]?$/i, mirroring memory.cjs's validateAgentName pattern) in appendTurn/loadSession and return {error} on mismatch.
962
+
963
+ #### L12. Stale-lock steal race in acquireLock: two contenders can both hold the lock, and releaseLock can delete a successor's lock
964
+
965
+ `pan-wizard-core/bin/lib/lock.cjs:52` · Core modules
966
+
967
+ **Failure scenario:** A lock file is >10s old (holder crashed or a long-running holder stalls past staleMs). Processes A and B call acquireLock concurrently: A stats the stale lock, unlinks it, and creates its own via wx — A holds. B, having statted the SAME old lock before A's unlink completed, also decides it is stale and unlinks — removing A's FRESH lock — then creates its own via wx. Both A and B execute the critical section simultaneously: the exact lost-update the lock exists to prevent, on the contended path (concurrent fleets) where it matters. Additionally, if a slow live holder is stolen from, its releaseLock (unconditional unlinkSync of lockPath) deletes the thief's lock, letting a third process in. The module documents best-effort fallback on TIMEOUT, but the steal path silently converts single-holder into double-holder.
968
+
969
+ **Suggested fix:** Steal via rename to a unique name (rename is atomic and fails for the second stealer) before unlinking, or after wx-create stat the created file and verify ownership (pid content) before treating the lock as held; make releaseLock verify the lock file still contains this process's pid before unlinking.
970
+
971
+ #### L13. whatif prepare payload phase_name is always null — reads phaseInfo.name but findPhaseInternal returns phase_name (same bug in preview.cjs:142)
972
+
973
+ `pan-wizard-core/bin/lib/whatif.cjs:82` · Core modules
974
+
975
+ **Failure scenario:** Running 'pan-tools whatif prepare 07 "use SQLite instead"' on any project: buildCounterfactualContext returns phase_name: null even though the phase directory encodes the name, because findPhaseInternal's return object (core.cjs:482-494) has phase_name/phase_slug, never a .name property. The pan-counterfactual agent's context payload therefore always lacks the phase name. preview.cjs:142 has the same typo ('phaseInfo.name ||') but degrades to the roadmap fallback; whatif has no fallback.
976
+
977
+ **Suggested fix:** Use phaseInfo.phase_name in whatif.cjs:82 and preview.cjs:142.
978
+
979
+ #### L14. pan-tools no-arg usage text omits ~29 routed commands (git, experiment, memory, optimize, learn, hygiene, cost, worktree, ...)
980
+
981
+ `pan-wizard-core/bin/pan-tools.cjs:286` · Core modules
982
+
983
+ **Failure scenario:** The default error 'Unknown command: X. Run pan-tools without arguments to see available commands.' (line 1507) points users at the usage list, but that list stops at 'standards' and omits estimate-cost, git, distill, experiment, batch-commit, hud, report, drift-check, memory, bridge, whatif, knowledge, skills, hygiene, review-deep, preview, cost, models, squad, worktree, campaign, bus, cache, retro, codebase, optimize, doc-lint, learn, and links — all of which are routed in the switch. A user told to discover commands this way cannot find roughly half of them.
984
+
985
+ **Suggested fix:** Regenerate the usage string from the switch cases (or append the missing command names).
986
+
987
+ #### L15. verify unknown-subcommand error omits routed subcommands reconcile and stubs
988
+
989
+ `pan-wizard-core/bin/pan-tools.cjs:533` · Core modules
990
+
991
+ **Failure scenario:** User typos 'pan-tools verify reconciled 07' and gets 'Unknown verify subcommand. Available: plan-structure, phase-completeness, references, commits, artifacts, key-links' — hiding the two newest subcommands (reconcile, stubs) that the same switch routes at lines 528-531, steering users away from features that exist.
992
+
993
+ **Suggested fix:** Append 'reconcile, stubs' to the error message.
994
+
995
+ #### L16. batch-commit swallows malformed JSON and reports misleading 'no_items' instead of a parse error
996
+
997
+ `pan-wizard-core/bin/pan-tools.cjs:790` · Core modules
998
+
999
+ **Failure scenario:** A workflow invokes pan-tools batch-commit '<json>' where the JSON is malformed (e.g. shell quoting stripped the quotes). The catch block discards the parse error, items stays [], and cmdBatchCommit outputs { committed: false, reason: 'no_items' } with exit 0 — the caller is told there was nothing to commit rather than that its payload was garbled, so planned doc commits are silently skipped.
1000
+
1001
+ **Suggested fix:** Report the parse failure: catch (e) { error(`Invalid JSON for batch-commit: ${e.message}`); } (parseJsonOrError already exists for this).
1002
+
1003
+ #### L17. optimize trace options object has a duplicate 'description' key (and a redundant --tokens-wasted double-read)
1004
+
1005
+ `pan-wizard-core/bin/pan-tools.cjs:1309` · Core modules
1006
+
1007
+ **Failure scenario:** No behavioral break today (both keys compute the same getArgValue call; last wins), but the duplicate silently masks the first assignment — if either line is later edited to differ (e.g. adding a default to one), the other silently overrides it, a classic drift trap in a 15-line options literal.
1008
+
1009
+ **Suggested fix:** Delete the duplicate 'description:' line at 1309 (keep 1301), and hoist getArgValue(args, '--tokens-wasted') into a variable instead of calling it twice on line 1311.
1010
+
1011
+ ### Shipped commands & agents
1012
+
1013
+ #### L18. pan-previewer cites bare 'pan-wizard-core/templates/preview-report.md' — unreachable relative path in installed projects
1014
+
1015
+ `agents/pan-previewer.md:58` · Shipped commands & agents
1016
+
1017
+ **Failure scenario:** The agent is told 'Use the template at `pan-wizard-core/templates/preview-report.md` as the skeleton.' Relative to a user project's cwd that path doesn't exist (actual: `.claude/pan-wizard-core/templates/…` locally, or the home config dir globally), and because it lacks the `~/.claude/` prefix, the installer's per-runtime rewrite never fixes it — the agent either errors on the Read or skips the template skeleton. The orchestrating /pan:preview command does pass the correct `@~/.claude/…` template in its execution_context, which mitigates but does not fix the agent-side reference (the agent is also spawned by focus flows).
1018
+
1019
+ **Suggested fix:** Change agents/pan-previewer.md:58 to `~/.claude/pan-wizard-core/templates/preview-report.md` so the installer rewrites it per runtime.
1020
+
1021
+ #### L19. Contradictory model gates for hierarchical exec: exec-phase.md says 'Opus 4.7 only', pan-conductor.md and army.md say 'Opus 4.8'
1022
+
1023
+ `commands/pan/exec-phase.md:65` · Shipped commands & agents
1024
+
1025
+ **Failure scenario:** exec-phase.md:65 gates `--hierarchical` as '(v3.4+, Claude + Opus 4.7 only)' while the agent it spawns declares 'Claude + Opus 4.8 only' (pan-conductor.md:3) and army.md's tier table says 'Opus 4.8'. An orchestrating model deciding whether to honor --hierarchical on an Opus 4.7 session gets opposite answers from the command and the agent it would spawn; a user on 4.7 is told the feature both is and isn't available.
1026
+
1027
+ **Suggested fix:** Pick one gate (presumably the current 'Opus 4.8'/latest-reasoning-model phrasing) and align exec-phase.md:65 with pan-conductor.md and army.md.
1028
+
1029
+ #### L20. /pan:links 'See also' cites 'pan-tools verify-key-links' — the command is 'verify key-links'; as spelled it errors
1030
+
1031
+ `commands/pan/links.md:102` · Shipped commands & agents
1032
+
1033
+ **Failure scenario:** A user exploring the legacy verifier runs `pan-tools verify-key-links` exactly as written (the line even asserts 'both still ship'). The dispatcher has no `verify-key-links` case and errors `Unknown command: verify-key-links`. The real invocation is `pan-tools verify key-links <plan-file>`. The same wrong spelling is propagated into docs/SKILLS-FULL-TEXT.md:6647.
1034
+
1035
+ **Suggested fix:** Change to `pan-tools verify key-links` in links.md:102 (and the generated docs/SKILLS-FULL-TEXT.md copy).
1036
+
1037
+ ### Shipped workflows, templates & references
1038
+
1039
+ #### L21. git-integration.md commit example stages `{phase}-{plan}-PLAN.md` / `-SUMMARY.md` — uppercase filenames that don't exist
1040
+
1041
+ `pan-wizard-core/references/git-integration.md:132` · Shipped workflows, templates & references
1042
+
1043
+ **Failure scenario:** Actual artifacts are lowercase `-plan.md`/`-summary.md` (execute-plan.md:414 uses them correctly). An executor following this reference's literal example on a case-sensitive filesystem stages nonexistent paths; git add fails per-file and the plan-completion metadata commit ends up empty/skipped. Stale GSD-era naming also appears at lines 126, 208-209.
1044
+
1045
+ **Suggested fix:** Lowercase the artifact names in the examples.
1046
+
1047
+ #### L22. guardrails.md instructs running `/pan:sync` — no such shipped command exists
1048
+
1049
+ `pan-wizard-core/references/guardrails.md:16` · Shipped workflows, templates & references
1050
+
1051
+ **Failure scenario:** guardrails.md is @-loaded by exec-phase, execute-plan, plan-phase, verify-phase and new-project in every user project. Its doc-drift rule says "Run `/pan:sync` (or the equivalent doc-sync step) before phase completion" — /sync exists only as a PAN-development skill in the source repo's .claude/commands (not shipped); commands/pan/ has no sync.md. An agent or user attempting the named command gets an unknown-command error on every phase completion where the rule triggers.
1052
+
1053
+ **Suggested fix:** Replace `/pan:sync` with a real instruction (e.g. update CHANGELOG/state via pan-tools commit) since the hedge "or the equivalent doc-sync step" is the only actionable part.
1054
+
1055
+ #### L23. Pre-rename command names survive in shipped reference/template prose: "complete-milestone" (planning-config.md ×4, milestone-archive.md ×2) and "verify-work" (uat.md)
1056
+
1057
+ `pan-wizard-core/references/planning-config.md:116` · Shipped workflows, templates & references
1058
+
1059
+ **Failure scenario:** COMMAND_RENAME_MAP (constants.cjs:168-176) renamed complete-milestone→milestone-done and verify-work→verify-phase, but planning-config.md:104,116,127,133,175 still document merge behavior "at `complete-milestone`", milestone-archive.md:3,101 say the template "is used by the complete-milestone workflow", and templates/uat.md:109 says "User runs diagnosis (from verify-work offer...)". A user looking for the named workflow/command finds nothing — /pan:complete-milestone is not a command.
1060
+
1061
+ **Suggested fix:** Update the prose to milestone-done and verify-phase.
1062
+
1063
+ #### L24. Shipped-but-orphaned template set (templates/codebase/* ×9, debug.md, milestone.md, planner-subagent-prompt.md, discovery.md, standards.md, retrospective.md, continue-here.md, config.json) — several carry instructions contradicting the live system
1064
+
1065
+ `pan-wizard-core/templates/codebase/stack.md:3` · Shipped workflows, templates & references
1066
+
1067
+ **Failure scenario:** Repo-wide grep finds no shipped workflow/command/agent/module referencing any of these files (pan-document_code embeds its own inline templates; milestone.cjs builds milestones.md entries itself; pause.md inlines the continue-here format; config.cjs builds defaults in code). Worse, their content contradicts current behavior: all 9 codebase templates self-describe as "Template for `.planning/codebase/STACK.md`" (uppercase) against map-codebase.md's explicit lowercase rule, and templates/discovery.md:3 claims it's used "during mandatory discovery in plan-phase" — plan-phase.md contains no discovery step. An agent that stumbles on these while exploring the installed tree gets authoritative-looking but wrong instructions.
1068
+
1069
+ **Suggested fix:** Delete the orphaned templates or wire them to their consumers; at minimum fix the uppercase target paths and the stale discovery.md claim.
1070
+
1071
+ #### L25. templates/playbook.md targets `.planning/PLAYBOOK.md` but knowledge.cjs writes `.planning/playbook.md`
1072
+
1073
+ `pan-wizard-core/templates/playbook.md:2` · Shipped workflows, templates & references
1074
+
1075
+ **Failure scenario:** The template (loaded by commands/pan/knowledge.md:28) says output goes to `.planning/PLAYBOOK.md`, while the module that actually generates it uses PLAYBOOK_FILE='playbook.md' (knowledge.cjs:23) and the command doc says `.planning/playbook.md`. On case-sensitive filesystems an agent following the template header looks for/creates the wrong file alongside the CLI-generated one.
1076
+
1077
+ **Suggested fix:** Lowercase the path in the template header.
1078
+
1079
+ #### L26. exec-phase.md documents a manual `pan-tools report --open` invocation that errors — report requires a phase|index|all subcommand
1080
+
1081
+ `pan-wizard-core/workflows/exec-phase.md:605` · Shipped workflows, templates & references
1082
+
1083
+ **Failure scenario:** The phase-reports note says browser-opening "is reserved for a manual `pan-tools report --open`". Running that literally makes args[1]='--open', which matches none of phase/index/all in the dispatcher → error "Unknown report subcommand. Available: phase <N>, index, all". A user copying the documented command gets an error instead of an opened report.
1084
+
1085
+ **Suggested fix:** Document `pan-tools report index --open` (or `report phase <N> --open`).
1086
+
1087
+ #### L27. execute-plan.md codebase-map update step names uppercase STRUCTURE.md/STACK.md/CONVENTIONS.md/INTEGRATIONS.md — contradicts the lowercase convention map-codebase enforces
1088
+
1089
+ `pan-wizard-core/workflows/execute-plan.md:426` · Shipped workflows, templates & references
1090
+
1091
+ **Failure scenario:** map-codebase.md:26 states "All output documents use lowercase filenames (stack.md, not STACK.md)" and creates stack.md/structure.md/etc. An executor following execute-plan.md's update_codebase_map step on a case-sensitive filesystem (Linux CI, most macOS dev containers) writes .planning/codebase/STACK.md as a new file; the real stack.md goes stale and subsequent planners read outdated maps.
1092
+
1093
+ **Suggested fix:** Lowercase the four filenames in line 426.
1094
+
1095
+ #### L28. pause.md phase detection globs `*/plan.md` (files are `*-plan.md`) and uses non-portable `grep -oP`
1096
+
1097
+ `pan-wizard-core/workflows/pause.md:16` · Shipped workflows, templates & references
1098
+
1099
+ **Failure scenario:** `ls -lt .planning/phases/*/plan.md` matches nothing in any real project (plans are `{phase}-{plan}-plan.md`), so auto-detection of the phase being paused always fails and the workflow always falls back to asking the user. On macOS the `grep -oP` (PCRE) would fail even if the glob matched, since BSD grep lacks -P.
1100
+
1101
+ **Suggested fix:** Glob `*-plan.md` and use a portable extraction (sed/awk).
1102
+
1103
+ #### L29. phase-tests.md reads `${phase_dir}/context.md` (actual: `NN-context.md`) and its commit message cites the retired "add-tests" command
1104
+
1105
+ `pan-wizard-core/workflows/phase-tests.md:50` · Shipped workflows, templates & references
1106
+
1107
+ **Failure scenario:** Context files are written as `${phase_dir}/${PADDED_PHASE}-context.md` (discuss-phase.md:217), so priority-2 artifact loading in phase-tests silently finds nothing and test generation loses acceptance criteria/decisions context. Line 344's commit template says "from add-tests command" — add-tests was renamed to phase-tests (constants.cjs COMMAND_RENAME_MAP:170), so user projects get commits referencing a command that no longer exists.
1108
+
1109
+ **Suggested fix:** Use `${phase_dir}/*-context.md` and update the commit message to phase-tests.
1110
+
1111
+ #### L30. research-phase.md existing-research check globs `*/research.md` — actual files are `NN-research.md`, so dedup never triggers
1112
+
1113
+ `pan-wizard-core/workflows/research-phase.md:29` · Shipped workflows, templates & references
1114
+
1115
+ **Failure scenario:** Research artifacts are written as `{phase_dir}/{phase_num}-research.md` (plan-phase.md:269, research-phase.md:60 itself). Step 2's `ls .planning/phases/${PHASE}-*/research.md` never matches (wrong basename, plus unpadded ${PHASE} vs zero-padded dir names), so the "If exists: Offer update/view/skip" branch is dead — re-running /pan:research-phase silently re-researches and overwrites existing research instead of offering the choice.
1116
+
1117
+ **Suggested fix:** Glob `*-research.md` in the resolved (padded) phase directory, or use `has_research` from `init phase-op` (already available in step 3).
1118
+
1119
+ #### L31. transition.md references "create-meta-prompts" — a workflow that exists nowhere in the shipped tree
1120
+
1121
+ `pan-wizard-core/workflows/transition.md:161` · Shipped workflows, templates & references
1122
+
1123
+ **Failure scenario:** archive_prompts says "The `completed/` subfolder pattern from create-meta-prompts handles archival." — repo-wide grep finds create-meta-prompts only on this line, and no other file mentions a completed/ subfolder pattern. The step is a no-op justified by a nonexistent mechanism, so phase prompts are never actually archived and the reader is pointed at a phantom workflow (GSD-lineage leftover).
1124
+
1125
+ **Suggested fix:** Delete the step or describe the real (non-)archival behavior.
1126
+
1127
+ #### L32. Claude-Code-specific UI text ships unconverted to other runtimes ("Restart Claude Code", Claude Code "Deny" permissions)
1128
+
1129
+ `pan-wizard-core/workflows/update.md:187` · Shipped workflows, templates & references
1130
+
1131
+ **Failure scenario:** Confirmed in the Codex install at d:/pantesting/.codex/pan-wizard-core/workflows/update.md:187 — after /pan:update on Codex the user is told "Restart Claude Code to pick up the new commands". map-codebase.md:318 likewise tells Gemini/OpenCode/Codex/Copilot users to add files to Claude Code "Deny" permissions when secrets are found. Wrong-product guidance at security- and update-relevant moments (the runtime-agnostic content rule says shipped workflows must not hardcode one runtime).
1132
+
1133
+ **Suggested fix:** Use runtime-neutral wording ("Restart your AI coding tool") or add these strings to the installer's per-runtime conversion.
1134
+
1135
+ #### L33. update.md changelog link `https://github.com/pan-wizard-core/blob/main/CHANGELOG.md` is malformed (no owner/repo)
1136
+
1137
+ `pan-wizard-core/workflows/update.md:189` · Shipped workflows, templates & references
1138
+
1139
+ **Failure scenario:** Every successful /pan:update ends by displaying this link; GitHub URLs need github.com/<owner>/<repo>/blob/..., so "pan-wizard-core" is parsed as an owner with no repo → 404 for every user who clicks it.
1140
+
1141
+ **Suggested fix:** Point at the real repository CHANGELOG (github.com/oharms/PanWizard/blob/main/CHANGELOG.md) or the npm package page.
1142
+
1143
+ #### L34. verify-phase.md requirements grep `"^| ${phase_number}"` is an empty-alternation regex that matches every line
1144
+
1145
+ `pan-wizard-core/workflows/verify-phase.md:53` · Shipped workflows, templates & references
1146
+
1147
+ **Failure scenario:** In ERE, `^| 3` is alternation between `^` (matches at start of every line) and ` 3`, so the intended "table rows for this phase" filter is a no-op that dumps the entire requirements.md into the verifier's context on every /pan:verify-phase run. Verified: 3-line sample input → 3 lines matched.
1148
+
1149
+ **Suggested fix:** Escape the pipe: `grep -E "^\\| *${phase_number} "` (and same pattern where reused).
1150
+
1151
+ ### Tests
1152
+
1153
+ #### L35. handleStatusline existing-config-preservation and --force-statusline branches are untested
1154
+
1155
+ `bin/install.js:2535` · Tests
1156
+
1157
+ **Failure scenario:** handleStatusline has four branches (no existing statusline → install; existing + --force-statusline → replace; existing + non-interactive → skip with warning; existing + interactive → readline prompt). No test seeds a pre-existing user statusLine and re-runs the installer to assert it is preserved, and no test passes --force-statusline (grep for 'force-statusline' in tests/ returns nothing). A regression that inverts the non-interactive branch (calling callback(true) when hasExisting) would silently overwrite a user's custom statusline command on every scripted upgrade — user-config loss that no test would catch. Existing statusline tests cover only fresh-install registration and uninstall cleanup (tests/copilot-install.test.cjs:188-199, 355-362).
1158
+
1159
+ **Suggested fix:** In tests/claude-install.test.cjs (Claude is the primary statusline consumer, currently with zero statusLine assertions): seed .claude/settings.json with a foreign statusLine, run the installer non-interactively, assert the foreign command survives; run again with --force-statusline and assert it is replaced with the pan-statusline command.
1160
+
1161
+ #### L36. package.json test:watch script uses the exact shell-glob pattern the repo documents as broken on Windows node 18/20
1162
+
1163
+ `package.json:71` · Tests
1164
+
1165
+ **Failure scenario:** A developer on Windows with node 18 or 20 runs `npm run test:watch`. npm invokes the script via cmd.exe, which does not expand globs, and node <22 does not expand test-path globs itself, so `node --test --watch tests/*.test.cjs` receives the literal pattern, matches no file, and exits 1 having run nothing. scripts/run-tests.cjs was created for exactly this failure and fixed test/test:scenarios/test:all — test:watch was missed.
1166
+
1167
+ **Suggested fix:** Point test:watch at the glob-free runner or an explicit directory, e.g. `node --test --watch tests/` (node --test accepts directories) or extend scripts/run-tests.cjs with a --watch passthrough.
1168
+
1169
+ ### Hooks
1170
+
1171
+ #### L37. Copilot hook templating uses one token for both home-relative and project-relative dirs, producing a stray ~/.github dir (local) and a dead project-VERSION check (global)
1172
+
1173
+ `bin/install-lib.cjs:87` · Hooks
1174
+
1175
+ **Failure scenario:** The installer rewrites every `'.claude'` literal in the hooks with a single replacement (install.js:2134), but pan-check-update.js uses `'.claude'` in two different roles: home-anchored (cacheDir, globalVersionFile) and project-anchored (projectVersionFile). (1) `npx pan-wizard --copilot --local`: replacement is `'.github'`, so the hook's cacheDir becomes `path.join(homeDir, '.github', 'cache')` — the first session creates a confusing `~/.github/` directory in the user's HOME (verified by running the actual replace: `const cacheDir = path.join(homeDir, '.github', 'cache');`). (2) `npx pan-wizard --copilot --global`: replacement is `'.copilot'`, so projectVersionFile becomes `cwd/.copilot/pan-wizard-core/VERSION`, a path that never exists because project-level Copilot installs live in `.github/` (getDirName returns '.github') — the documented project-first version check is dead code for Copilot global installs, so a stale project install is never reported by the update badge.
1176
+
1177
+ **Suggested fix:** Template the home-anchored and project-anchored occurrences with separate tokens (e.g. two placeholders, or template projectVersionFile with getDirName(runtime) and home paths with the global config dir).
1178
+
1179
+ #### L38. Update check uses inequality, so an installed version NEWER than npm latest permanently shows the update badge and invites a downgrade
1180
+
1181
+ `hooks/pan-check-update.js:49` · Hooks
1182
+
1183
+ **Failure scenario:** A user installs PAN from a source checkout whose VERSION is ahead of the npm 'latest' dist-tag (README documents source installs: `node bin/install.js --claude --local`), or a release tag is pushed and the registry/dist-tag lags. `update_available: latest && installed !== latest` evaluates true for ANY difference, so the statusline permanently shows '⬆ /pan:update' (pan-statusline.js:134) — nudging the user to 'update' to an older published version. Also, if no VERSION file is found at all, installed stays '0.0.0' and the badge shows whenever npm is reachable, even though the hook can't tell what (if anything) is installed.
1184
+
1185
+ **Suggested fix:** Compare semver components and set update_available only when latest > installed (a ~10-line zero-dep semver compare); skip the badge entirely when installed is the '0.0.0' sentinel.
1186
+
1187
+ #### L39. Context-monitor warning mixes the statusline's 80%-rescaled used_pct with raw remaining_percentage, feeding the agent self-contradictory numbers
1188
+
1189
+ `hooks/pan-context-monitor.js:113` · Hooks
1190
+
1191
+ **Failure scenario:** Statusline computes used_pct on a rescaled axis where 80% real usage displays as 100% (`used = Math.min(100, Math.round((rawUsed / 80) * 100))`, pan-statusline.js:38) and writes that into the bridge file. The context monitor prints that scaled figure next to the RAW remaining figure: at the warning threshold (remaining=35%), rawUsed=65 → used_pct=81, so the agent receives 'CONTEXT MONITOR WARNING: Usage at 81%. Remaining: 35%.' — 81+35=116%. At the critical threshold (remaining=25%): 'Usage at 94%. Remaining: 25%.' A message designed to make the agent reason accurately about remaining context hands it arithmetic that doesn't add up, and an agent computing budget from 'Usage at 94%' will under- or over-estimate depending on which number it trusts.
1192
+
1193
+ **Suggested fix:** Report both numbers on the same scale — either write raw used (100 - remaining) into the bridge as a separate field for the monitor, or omit the used figure and state only remaining.
1194
+
1195
+ #### L40. Per-transcript cursor files grow without bound — keys for dead transcript paths are never pruned and the whole map is rewritten on every SubagentStop
1196
+
1197
+ `hooks/pan-cost-logger.js:74` · Hooks
1198
+
1199
+ **Failure scenario:** Every Claude Code session has a fresh transcript path, and both loggers add one key per transcript to `.planning/metrics/.cost-cursor.json` / `.planning/optimization/.trace-cursor.json` (absolute paths as keys, ~100+ bytes each). Nothing anywhere prunes them: grep for 'cost-cursor|trace-cursor' across pan-wizard-core/ and commands/ returns zero hits, and hygiene.cjs handles ledger quarantine and trace pruning but never touches the cursor files. A long-lived project used daily accumulates thousands of entries for transcripts that no longer exist, and the entire JSON map is read and rewritten synchronously on every SubagentStop event in both hooks.
1200
+
1201
+ **Suggested fix:** Cap the map (e.g. keep the most recent N entries, or drop keys whose transcript file no longer exists) when writing the cursor; the hooks already own both read and write paths.
1202
+
1203
+ ### User & developer docs
1204
+
1205
+ #### L41. CLI-REFERENCE omits several shipped dispatcher surfaces (verify reconcile/stubs, memory select/budget, doc-lint counts/flags, commit --fail-on-error; module table missing 4 modules)
1206
+
1207
+ `docs/CLI-REFERENCE.md:131` · User & developer docs
1208
+
1209
+ **Failure scenario:** Two-way diff of the Command Index + detail sections vs pan-tools.cjs: existing-but-undocumented routes are `verify reconcile`, `verify stubs [--gate]`, `memory select --cue/--token-budget/--recency-floor`, `memory budget`, `doc-lint counts <dir> [--exclude]`, `doc-lint flags [--doc-dir]`, plus flags `commit --fail-on-error` (claimed shipped in ARCHITECTURE.md v3.7.10 gates but absent from the commit section), `knowledge ask --recall-cue`, `experiment list --include-archived`, and `cost report --since/--until` (documented in USER-GUIDE/FAQ but not in the cost section header). The Module Architecture table also omits constants.cjs, links.cjs, lock.cjs, utils.cjs.
1210
+
1211
+ **Suggested fix:** Add the missing rows/flags to the Command Index and relevant sections, and the 4 missing modules to the module table.
1212
+
1213
+ #### L42. CLI-REFERENCE `focus auto` categories list omits security and distill
1214
+
1215
+ `docs/CLI-REFERENCE.md:2461` · User & developer docs
1216
+
1217
+ **Failure scenario:** A reader of the CLI reference concludes `focus auto --category security|distill` is invalid, though FOCUS_CATEGORIES includes both and USER-GUIDE/MIGRATION document them as v3.5 additions. The reference contradicts sibling docs and the code.
1218
+
1219
+ **Suggested fix:** Append 'security (P0-P2), distill (P1-P4/P5)' to the categories line.
1220
+
1221
+ #### L43. MIGRATION verify step suggests `pan-tools focus scan --help`, which runs a real scan instead of printing help
1222
+
1223
+ `docs/MIGRATION-v2-to-v3.md:172` · User & developer docs
1224
+
1225
+ **Failure scenario:** pan-tools has no --help handling; `focus scan --help` passes '--help' as an ignored arg to cmdFocusScan and executes a full work-item scan of the project — surprising output, no usage text, and the user learns nothing about the command surface.
1226
+
1227
+ **Suggested fix:** Replace with a harmless smoke test that exists, e.g. `pan-tools focus scan --raw` with a note it runs a scan, or `pan-tools` (no args) which prints the command list.
1228
+
1229
+ #### L44. USER-GUIDE cites stale plan-checker dimension numbers (nyquist as '8th', standards as 'Dimension 9')
1230
+
1231
+ `docs/USER-GUIDE.md:536` · User & developer docs
1232
+
1233
+ **Failure scenario:** A user cross-referencing the plan-checker output ('Dimension 9: SKIPPED (nyquist_validation disabled)', 'Dimension 10: SKIPPED (no standards selected)') against the guide's numbering (nyquist = 8th dimension at line 536; standards = Dimension 9 at line 428) maps the wrong dimensions to the wrong toggles.
1234
+
1235
+ **Suggested fix:** Update to Dimension 9 (Nyquist) and Dimension 10 (Standards Awareness), or drop the numbers and use names only.
1236
+
1237
+ ### Specs & ADRs
1238
+
1239
+ #### L45. 17 additional ADRs still carry status 'Proposed' although their decisions demonstrably shipped (quality-audit pass 1 missed the status backfill)
1240
+
1241
+ `docs/decisions/ADR-0007-ship-v1.md:4` · Specs & ADRs
1242
+
1243
+ **Failure scenario:** The project maintains ADR statuses elsewhere (ADR-0001 'Partially Accepted', ADR-0002 'Accepted — fully implemented', ADR-0023..0042 all annotated with ship dates), so a reader reasonably trusts Status fields — and gets a false picture of what shipped for the 2026-03/04 range. Verified shipped-while-'Proposed': ADR-0005 (COMMAND_RENAME_MAP in constants.cjs:166), ADR-0006 (commands/pan/focus-*.md exist), ADR-0007 (v1.0.0 ship — package now at v3.22.0 on npm), ADR-0010 (`validate health --standards` flag in pan-tools.cjs:676), ADR-0015 (commands/pan/focus-auto.md + focus.cjs), ADR-0016 (focus-sync.md), ADR-0017 (focus-design.md), ADR-0018 (tests/scenarios/feedback-*.test.cjs), ADR-0019 (tests/scenarios/user-workflow-e2e.test.cjs), ADR-0020 (`case 'drift-check'` pan-tools.cjs:931 + verify-drift.cjs), ADR-0021 (codebase.cjs + opus-47-map-codebase-modes.test.cjs), ADR-0022 (retro.md, verify-retro.cjs, review-deep.cjs). Also Proposed and likely shipped but not individually verified: 0004, 0008, 0009, 0011, 0014.
1244
+
1245
+ **Suggested fix:** One backfill pass over ADR-0004..0022: set Status to Accepted (with ship version) for the implemented ones, or explicitly mark Rejected/Superseded where applicable (e.g. ADR-0008 → Superseded by ADR-0021).
1246
+
1247
+ #### L46. ADR-0021 declares it supersedes ADR-0008, but ADR-0008 has no 'Superseded by' back-link and still reads 'Proposed'
1248
+
1249
+ `docs/decisions/ADR-0008-map-codebase-mermaid-togaf.md:4` · Specs & ADRs
1250
+
1251
+ **Failure scenario:** A reader opening ADR-0008 (e.g. from the ADR listing or a spec link) has no way to learn the decision was folded into the mapper-v2 redesign; the supersession is only discoverable from ADR-0021's Links section (line 72). This is the exact dangling-reference failure ADR-0027 cites as motivation ('an ADR superseded, no tooling notices'). Cross-links are required in both directions per the ADR convention.
1252
+
1253
+ **Suggested fix:** Change ADR-0008 Status to 'Superseded by ADR-0021' with a link.
1254
+
1255
+ #### L47. comp_analysis_markdown_v1.md embeds the stale filesystem count '12 agents' as PAN's current capability, with no status or date header (counts policy)
1256
+
1257
+ `docs/specs/comp_analysis_markdown_v1.md:27` · Specs & ADRs
1258
+
1259
+ **Failure scenario:** The doc opens in present tense ('PAN Wizard occupies a rare and defensible position...') with no Generated/Date/Status header, so a reader takes the capability table as current; it states PAN has '12 agents' while the tree ships 24 (agents/*.md; CLAUDE.md counts table). The same stale count recurs at line 101 ('PAN's 12 agents should support configurable model assignment'). Per the project's counts policy, filesystem-derived counts outside CLAUDE.md are prohibited and the fix is deletion, not update.
1260
+
1261
+ **Suggested fix:** Delete the '12 agents' numbers (use qualitative phrasing per CLAUDE.md counts policy) and/or add a Generated/Status header marking the analysis as point-in-time.
1262
+
1263
+ #### L48. focus-auto-optimize-category.md is dated 2025-07-25 — a date that predates the project's existence
1264
+
1265
+ `docs/specs/focus-auto-optimize-category.md:3` · Specs & ADRs
1266
+
1267
+ **Failure scenario:** Anyone ordering specs chronologically (or a doc-lint/link-graph pass keying on dates) places this spec ~7 months before PAN Wizard's first sessions (2026-02) and before the focus-auto feature it extends (ADR-0015, 2026-03-03). Its own downstream superplan (docs/specs/focus-auto-optimize-superplan.md:3) was 'Generated: 2026-03-18 · Source spec: docs/specs/focus-auto-optimize-category.md' against baseline v2.8.0, so the source spec cannot be from July 2025. The year is a typo that misdates the design record.
1268
+
1269
+ **Suggested fix:** Correct the date field to the real 2026 authoring date (on or before 2026-03-18).
1270
+
1271
+
1272
+ ---
1273
+
1274
+ ## Appendix A — What we checked and found clean
1275
+
1276
+ Coverage claims below are the reviewers' own, verified where marked; they are listed so the absence of findings in an area reads as "checked, clean" rather than "not looked at."
1277
+
1278
+ ### Installer & runtimes
1279
+
1280
+ - PAN_SOURCE_ROOT self-install guard: present for both install (install.js:1776) and uninstall (install.js:861), case-insensitive on win32 via normPath; covered by tests/e2e-install.test.cjs:444-472 ('installer/uninstaller refuses to run from source repository') — note the test lives in tests/, not tests/scenarios/
1281
+ - Live 5-runtime local install (d:/pantesting/audit-e2e-2026-08, --claude --codex --gemini --opencode --copilot --local): exit 0; all 5 runtime dirs plus shared .agents/skills created; 59 commands per runtime in the correct per-runtime format (claude nested .md, opencode flat pan-*.md, gemini .toml, codex+copilot SKILL.md dirs), 24 agents per runtime in correct formats (.md / .toml / .agent.md)
1282
+ - learnings/internal strip: absent from all 5 installed pan-wizard-core copies, learnings/universal present in all 5; copySharedCore path has the same strip
1283
+ - Manifest integrity both directions: 0 manifest entries missing on disk for all 5 runtimes (287/285/285/280/285 entries); codex manifest correctly tracks the shared tree via ../.agents/skills keys resolvable on Windows; agents (24), hooks (5), workflows (2), gemini .toml commands all tracked; only known-untracked surfaces are settings/config files (by design) and the Claude skill shims + hook-config files (reported as findings)
1284
+ - Upgrade path idempotence: re-running the identical 5-runtime install produced no duplicate hook registrations (claude/gemini settings.json SessionStart:1 PostToolUse:1 SubagentStop:2; codex hooks.json same; copilot pan.json same), no file duplication, identical manifest counts, exit 0
1285
+ - Local-patch flow: modifying a tracked file (pan-tools.cjs) before reinstall was detected, backed up to .claude/pan-local-patches/ with correct backup-meta.json (from_version, file list) and reported with the right per-runtime reapply command
1286
+ - Uninstall (--all 5, --local): removed exactly the PAN surfaces — commands/skills/shims/agents/hooks/workflows/pan-wizard-core/manifest/CommonJS package.json marker, PAN entries in settings.json (statusline, SessionStart/PostToolUse/SubagentStop hooks), gemini experimental.enableAgents, codex hooks.json (deleted when only PAN remained), copilot hooks/pan.json + statusline in copilot/settings.json, opencode permission entries in opencode.json; AGENTS.md/CLAUDE.md PAN sections stripped only after the last runtime; shared .agents/skills ref-counting swept the tree exactly once (at the codex uninstall) and pruned empty dirs
1287
+ - Hook wiring per runtime matches documented surfaces: claude/gemini settings.json PascalCase events; codex .codex/hooks.json PascalCase (mergeCodexHooksConfig non-destructive + dedup by pan-* marker); copilot .github/hooks/pan.json version:1 camelCase; opencode none — consistent with HOOK_EVENT_MAP (install-lib.cjs:841)
1288
+ - Zero runtime dependencies: bin/install.js requires only fs/path/os/readline/crypto + ./install-lib.cjs + ../package.json; install-lib.cjs requires only path/os/fs + ../pan-wizard-core/bin/lib/agents-md.cjs (relative); package.json files field ships bin/, pan-wizard-core/, commands/, agents/, hooks/dist so all relative requires resolve in the published package
1289
+ - Windows path handling: pathPrefix, manifest keys, and codexSkillsPrefix normalized to forward slashes via replace(/\\/g,'/'); verifyInstall resolves ../ manifest keys via path.join — all verified live on win32
1290
+ - copyWithPathReplacement wipes destDir before copy (orphan cleanup for commands/pan and pan-wizard-core on upgrade); OpenCode legacy singular command/ dir cleaned on both upgrade (1869-1876) and uninstall (900-915); codex legacy .codex/skills swept on upgrade (1893-1900)
1291
+ - verifyInstall (install-lib.cjs:1111): checks the pan-tools.cjs dispatcher anchor plus every manifest entry for existence AND non-emptiness; wired at install.js:2213 with exit 1 on failure — sound design and passed live (287/285/285/280/285 files verified)
1292
+ - Arg-parsing guards: --global+--local conflict rejected, --config-dir+--local rejected, --config-dir without value rejected, --uninstall without location flag rejected, non-TTY defaults to claude project-level, interactive prompt offers all 5 runtimes + All
1293
+ - convertClaudeToGeminiAgent and convertClaudeToOpencodeFrontmatter correctly parse the inline `tools:` frontmatter form all 24 agents actually use (verified in installed .gemini/agents output: tools mapped to snake_case, Task/mcp__ excluded); codex TOML agents correctly map effort→model_reasoning_effort ('max' would be silently dropped but no agent uses it — shipped efforts are low/medium/high/xhigh only)
1294
+ - getCommitAttribution / processAttribution, parseJsonc (JSONC with BOM/comments/trailing commas), buildHookCommand quoting, saveLocalPatches ../-key skip (documented, prevents path escape), mergeCodexHooksConfig/removeCodexPanHooks mutation handling (fresh JSON.parse copy at install.js:1089) — all read clean
1295
+ - CHANGELOG.md and VERSION are written into pan-wizard-core before writeManifest, so they are manifest-tracked; VERSION upgrade/reinstall/downgrade detection message logic correct
1296
+
1297
+ ### Core modules
1298
+
1299
+ - Zero-dep compliance: all 16 assigned modules require() only Node builtins (fs, path, os, child_process, crypto) and relative ./ modules — verified by grep across every file; no external packages anywhere.
1300
+ - config.cjs prototype-pollution guard (2026-07 hardening): cmdConfigSet rejects __proto__/constructor/prototype on every path segment before any write — verified live that both `config-set __proto__.polluted 1` and `config-set constructor.prototype.polluted 1` error out. Other merge paths in the file are safe: buildConfigDefaults merges ~/.pan-wizard/defaults.json via object spread (CreateDataProperty semantics — a JSON '__proto__' key becomes an own property, never touches the prototype), and cmdConfigGet/loadConfig only read.
1301
+ - core.cjs loadConfig: no deep-merge writes from parsed JSON (sections returned as-is), flat/nested key fallback logic correct, malformed config intentionally falls back to documented defaults.
1302
+ - core.cjs output() large-payload path keeps the hardened pattern: mkdtempSync private dir + writeFileSync with flag 'wx', truncation fallback on failure.
1303
+ - agents-md.cjs: upsert/remove marker-fence logic preserves user content around the PAN block, bridge insert is idempotent (skips when PAN markers or a user-authored @AGENTS.md import exist), and the null-when-empty removal contract is consistent.
1304
+ - bus.cjs input validation: channel/source names restricted to ^[a-zA-Z0-9_-]+$ blocks path traversal; malformed JSONL lines are surfaced as {malformed:true, raw} rather than silently dropped; publish append + error paths return structured errors (the consume-truncation data loss reported separately).
1305
+ - constants.cjs shared g-flag regex PHASE_HEADER_RE: both consumers (roadmap.cjs, verify.cjs) reset lastIndex before exec loops — no state-leak bug; other exported regexes are non-global or used safely.
1306
+ - core.cjs comparePhaseNum / normalizePhaseName: ordering across integer, letter-suffix, and multi-level decimal phases (1 < 2 < 2A < 2A.1 < 3) verified sound by inspection; searchPhaseInDir plan/summary/incomplete-plan inventory and toPosix on emitted directory paths correct.
1307
+ - cost.cjs: isSuspectRecord quarantine thresholds and aggregate() suspect/malformed counting sound; readRecords tolerates torn JSONL rows and surfaces the count; renderTable/renderChart handle empty windows; checkRatesStaleness math correct.
1308
+ - commands.cjs commit path: staging happens before safety checks so staged deletions are caught; sensitive-pattern regexes compiled with per-pattern try/catch; nothing-to-commit detected on stdout+stderr; failOnError escalation (P-EXP-001) works as described; cmdRollbackSnapshot disables tag.gpgsign and handles tag collision with suffix retry.
1309
+ - commands-learnings.cjs: PAT-NNN/LEARN-NNN auto-increment, dedup-by-detail in cmdLearningsExtract, and the session-history keep-last-20 split logic all correct on close reading; prune by --days/--id rewrites cleanly.
1310
+ - context-budget.cjs: token estimation, warning/critical thresholds, idle/critical/warning status ladder, and the P-RES-002 relevance heuristic are internally consistent; cache-metrics block is fully guarded so buildCachedContext failures degrade to null.
1311
+ - doc-lint.cjs: vendored doc-lint/{frontmatter,schema,validate,walk,reporter}.js all present under pan-wizard-core/bin/lib/doc-lint/ (relative requires resolve); count-lint code-fence skipping, allowlist (CLAUDE.md/CHANGELOG/decisions/specs/...) and the aspirational-flag checker scoped to pan-tools lines behave as documented.
1312
+ - codebase.cjs graph layer: walkSourceFiles skip-dirs + dot-dir exclusion, resolveImport (relative-only, extension/index probing), findCircularDeps handling of neighbors missing from adjacency, entry-point/orphan derivation, and estimateRepoTokenSize threshold/mode logic all sound; all emitted paths go through toPosix.
1313
+ - experiment.cjs: slug validation (SLUG_RE + length), runtime validation, source-repo guard case-insensitive on win32 (normPath), archived-experiment skip in listExperiments, and harvest/prune force/soft flows correct (the installed-layout PAN_SOURCE_ROOT issue reported separately).
1314
+ - focus.cjs allocation: allocatePass/allocateBudget mode logic (bugfix/balanced/features/full) respects budgets and exclusion sets; focusAutoInit validates category/source/mode/budget/max-cycles/total-budget bounds and clamps verify-reserve to 0..0.5; verifyTestCount uses fixed execFile('node',['--test']) with NODE_TEST_* env stripping — no injection surface; focusAutoCheckpointCommit honors commit_docs and focus.auto_commit gates.
1315
+ - campaign.cjs schedule mechanics: parseCadence, UTC-day spend accounting, isRunDue ordering (disabled/paused/budget/next_due), recordRun history cap at 50, and verify-reserve status indicators all verified sound (tests pass these paths); only the exit-code comment and isDreamDue reported as findings.
1316
+ - Zero-dep rule: all 17 audited files require only Node builtins (fs, path, os, child_process) and relative ./ modules — verified by grep across the full set; no external packages
1317
+ - hud.cjs openInBrowser (2026-07 hardening): still a POSITIVE allowlist (/^[A-Za-z0-9 _.:\\/()-]+$/) applied to path.resolve output, preceded by a statSync isFile check, with execFileSync (no shell) — and the deliberate byte-copy in phase-report.cjs carries the identical barrier
1318
+ - hud.cjs rendering: every project-derived value in every renderer flows through esc() HTML-escaping (checked renderMission, renderNowBuilding, renderCommandStack, renderCampaign, renderRoadmap, renderPlanningActivity, renderHarness, renderTelemetry, renderWorktrees, renderQuality, renderActivity); ledgerReliability correctly refuses to quote dollar figures for poisoned (suspect>calls) or wholly-unpriced (unknown>=calls) ledgers; scanPlanningActivity walk is depth- and entry-bounded
1319
+ - lock.cjs writeFileAtomic: same-directory pid-suffixed temp + rename, with a Windows rename-over-open-file fallback and temp cleanup — sound; sleepSync via Atomics.wait needs no flags on supported Node versions
1320
+ - hygiene.cjs: case-only renames use a two-step .case-hop (correct for Windows); checkLegacyUppercase avoids the existsSync case-insensitivity trap by checking the literal directory listing; poisoned-ledger fix is quarantine-by-rename (never deletes user content); compareVersions tolerates missing segments; version drift and fragment dirs are correctly report-only
1321
+ - memory.cjs: agent names validated against /^[a-zA-Z0-9_-]+$/ before any path construction (no traversal); scoreEntry escapes cue tokens before RegExp; selectMemory's Infinity-score recency floor works (NaN comparator result deliberately falls through || to the index tiebreak) and guarantees non-empty output on non-empty logs; soft auto-compaction in appendMemory surfaces auto_compacted rather than silently trimming
1322
+ - memory-optimize.cjs state.md path: parseSections/joinSections/reconcileBullets are byte-preserving when unchanged; quarantine of directive-like bullets happens before dedupe/cap; archive and quarantine files are append-only and dated; maybeAutoOptimizeMemory never throws into calling flows (the bug found is confined to the per-agent sweep in cmdMemoryOptimize)
1323
+ - memory-rebuild.cjs: isInsideSourceRepo guard correctly distinguishes source checkout (bin/install.js + pan-wizard-core present) from install layouts, case-insensitive on win32; rebuild only writes when desired differs; user content outside PAN markers preserved; directive scan warns rather than auto-edits
1324
+ - learn-lint.cjs / learn-index.cjs: pattern IDs escaped before RegExp in extractPatternBody; index file paths normalized to forward slashes via replace(/\\/g,'/'); topicsForAgent budget packing and L-001..L-005 checks are logically sound; readIndex falls back to a fresh build on missing/corrupt index
1325
+ - links.cjs: parseInlineLinks correctly skips leading YAML frontmatter, fenced code blocks, and inline code spans; resolveDocId ADR shortcut handles missing/ambiguous ADRs; cmdLinksValidate's process.exit(1)-on-fail bypass of core.output() is deliberate and documented; B-002 exclusion from --strict matches the spec reference
1326
+ - phase-report.cjs: relative hrefs are correct in both directions (index at .planning/report-index.html → phases/<dir>/<N>-report.html; report crumbs ../../report-index.html; stepper ./../<dir>/); writeIfChanged timestamp-stripping gives zero-churn idempotence; bundle uses in-page anchors so links never dangle; collectPhaseData/collectIndexData are pure and never throw on missing artifacts
1327
+ - optimize.cjs trace lifecycle: session reuse window logic sound; endTraceSession clears the current-session pointer only when it points at the ended session; reconcile-on-read overlays prevent stale-zero counters; generateSessionId format correct; token double-counting avoided by restricting sums to agent_completion events
1328
+ - init.cjs: padded_phase padStart is safe (phase_number is always a string from searchPhaseInDir/getRoadmapPhaseInternal); branch templates always defined via loadConfig defaults; brownfield scan is bounded (depth 3, SKIP_DIRS) and cross-platform
1329
+ - git.cjs (beyond the files-leak finding): tag creation disables tag.gpgsign for non-interactive runs; rollback refuses a dirty working tree and validates the tag against the pan-rollback-* list; push/sync validate the remote exists before running; status porcelain counting is correct
1330
+ - milestone.cjs cmdMilestoneComplete: archive steps are ENOENT-tolerant with real errors surfaced as warnings; commit/tag path disables gpgsign; state update is best-effort by design
1331
+ - frontmatter.cjs CRUD error paths: missing file → structured {error} not a throw; invalid --data JSON → clean error(); parseMustHavesBlock indentation grouping handles string items, kv items, continuation lines, and nested arrays correctly
1332
+ - knowledge.cjs ask/playbook: scoreRelevance escapes question tokens via escapeRegex; playbook entries are strings from parseEntries (no [object Object] risk); recall_score correctly stripped from the sources shape
1333
+ - Zero-dep compliance: all 18 audited files (17 lib modules + pan-tools.cjs dispatcher) require only Node builtins (fs, path, os, crypto, child_process) and relative ./ modules — no external packages (verified via grep -P over every file)
1334
+ - Dispatcher routing integrity: all 171 unique module.fn references in pan-tools.cjs resolve to exported functions on their required modules (verified by loading all 38 lib modules and cross-checking against the dispatcher source); directly-imported core functions (output, error, buildCachedContext, readStateSafe) also resolve
1335
+ - git dispatcher help text accurate: all 10 advertised git subcommands (commit, branch, push, status, log, stash, diff, rollback, tag, sync) have matching cases in git.cjs cmdGit
1336
+ - --cwd handling in the dispatcher: both --cwd=VALUE and --cwd VALUE forms validated, missing value and non-directory rejected with clean errors; --raw/--verbose flags spliced out before subcommand parsing
1337
+ - execGit (core.cjs) trims stdout consistently, so both verifyCommitHashes (verify.cjs:63) and cmdVerifyCommits (verify.cjs:298) commit-type comparisons are correct (initially suspected inconsistency, refuted)
1338
+ - roadmap.cjs: cmdRoadmapGetPhase/cmdRoadmapAnalyze/cmdRoadmapUpdatePlanProgress — user input consistently passed through escapeRegex before RegExp construction; missing roadmap.md handled gracefully in every path; PHASE_HEADER_RE lastIndex reset before each global exec
1339
+ - utils.cjs: pure helpers sound — classifyPhaseStatus precedence correct, fileAccessible/readJsonFile fail-closed, lazy require of core.cjs in listPhaseDirs avoids a circular-dependency initialization hazard
1340
+ - squads.cjs: static frozen registry internally consistent; cmdSquadShow rejects unknown squads with the full available list; validateRoster logic correct
1341
+ - worktree.cjs: git worktree list --porcelain parsing correct; branch deletion guarded to army/ prefix only; force flag opt-in; errors surfaced as warnings not thrown
1342
+ - whatif.cjs worktree lifecycle: branch/path slugs sanitized (scenarioSlug bounds to [a-z0-9-], 50 chars); cleanupWorktree only deletes the named branch after a --list existence check
1343
+ - verify-preflight.cjs: cmdPreflight and cmdDepsValidate handle missing .planning/, missing state.md, non-git dirs, and unreadable phase dirs gracefully; blocker parsing skips 'None' placeholder correctly
1344
+ - verify-retro.cjs: division-by-zero guarded in first_try_rate and estimation accuracy; --write-memory bounded (--max clamped 1..10); gap-pattern grouping bounded to top 10
1345
+ - verify-drift.cjs: parseConventionRules escapes user regex fragments and skips invalid regexes; file scan bounded by DRIFT_MAX_FILES/DRIFT_MAX_FILE_SIZE; binary extensions excluded; getChangedFiles degrades to [] outside git
1346
+ - skill-align.cjs: buildSkillIndex fail-open (missing roots reported in skipped_roots, never thrown); alignTasks clamps topK/minScore/tokenBudget; budget overflow reported in 'dropped', not silent; resolveSkillRoot 3-levels-up matches both source and install layouts
1347
+ - template.cjs: cmdTemplateFill writes with flag 'wx' and handles EEXIST explicitly (no silent overwrite of existing plans/summaries/verifications); decimal phase ids survive the padded split
1348
+ - state.cjs concurrency: every state.md write funnels through writeStateMd → withFileLock + writeFileAtomic (ADR-0030); frontmatter rebuilt on each write via syncStateFrontmatter
1349
+ - preview.cjs: computeParallelBatches has a cycle guard (iteration cap + dump-remainder batch); generateMermaid strips double-quotes from labels; buildPhasePreview in_progress detection works (findPhaseInternal does return incomplete_plans — initially suspected, refuted)
1350
+ - runner.cjs spawn path: Windows .cmd shim shell handling with whitespace-quoting of args, ETIMEDOUT/SIGTERM timeout detection, and claude JSON envelope parsing are all coherent; runtime overrides correctly bypass shell mode
1351
+ - No dispatcher route mutates the PAN source repo as a side effect: write-capable commands only touch <cwd>/.planning/ (by design, project-local); experiment.cjs routes carry the PAN_SOURCE_ROOT guard; nothing writes into the module's own install directory
1352
+ - verify.cjs checkArtifacts/checkKeyLinks pure checkers: regex patterns from plans wrapped in try/catch (invalid regex reported, not thrown); string items skipped; totals computed consistently
1353
+
1354
+ ### Shipped commands & agents
1355
+
1356
+ - File census matches CLAUDE.md's counts table: 59 files in commands/pan/*.md, 24 in agents/*.md — every one was enumerated and inspected (full reads for all agents' frontmatter and ~30 command files; targeted greps + section reads for the rest).
1357
+ - Cross-references to core docs all resolve: every workflows/*.md (29 distinct), templates/*.md (13 distinct incl. research-project/summary.md), and references/*.md (6 distinct) path cited in shipped commands/agents exists on disk; all @~/.claude/… execution_context references resolve after installer rewrite (verified against the real v3.22.0 five-runtime install at d:/pantesting).
1358
+ - Agent name references: every pan-* agent named in commands (pan-planner, pan-executor, pan-verifier, pan-plan-checker, pan-designer, pan-design-checker, pan-conductor, pan-release, pan-hardener, pan-meta-reviewer, pan-counterfactual, pan-previewer, pan-knowledge, pan-debugger, pan-phase-researcher, pan-project-researcher, pan-research-synthesizer, pan-experiment-runner, pan-optimizer, pan-distiller, pan-document_code, pan-integration-checker, pan-reviewer, pan-roadmapper) has a matching agents/*.md file; /pan:<cmd> cross-references all resolve except the reported /pan:check.
1359
+ - pan-tools invocations: every subcommand+flag cited in commands/agents was checked against pan-tools.cjs routing and target modules and found supported — git commit/branch/push/status/log/stash/diff/rollback/tag/sync with all documented flags (git.cjs:340-390); focus scan/plan/sync/exec/auto/classify-stages/reflection incl. --source, --category, --mode, --budget, --max-cycles, --total-budget, --enforce-budget, --verify-reserve, --parallel-research, --parallel-verify, --clean-seal, --deep-review, --update telemetry flags (focus.cjs:958-1046, 782-790); cost report --format json|table|chart, cost append/clear; codebase estimate-size --threshold; distill scan/analyze/report --touched-loc/--bloat-threshold; campaign schedule/status/due/record-run incl. --pause/--resume/--disable; report phase/index/all incl. --bundle/--out/--open/--stdout; hud flags; whatif prepare/report/cleanup flags; knowledge ask/discuss/playbook flags; learn promote/unpromote/list-promoted flags; skills align --draft-file; resolve-model --metadata; config-get dotted keys; state advance-plan/update-progress/record-metric/add-decision/record-session; bus publish; cache prime --summary; requirements mark-complete; verify plan-structure/commits/artifacts/key-links; experiment new/list/manifest/run/status/stop flags; squad list/show; worktree create; review-deep merge flags; hygiene scan/clean flags; preview phase/phases/milestone.
1360
+ - COMMAND_RENAME_MAP (v15→v16): no old slash-command names (/pan:execute-phase, /pan:verify-work, /pan:pause-work, /pan:new-milestone, /pan:complete-milestone, etc.) appear anywhere in commands/pan or agents (the init execute-phase / init verify-work / init new-milestone strings that do appear are legitimate pan-tools init subcommands).
1361
+ - Counts policy: no filesystem-derived counts embedded in any commands/pan/*.md or agents/*.md file (the one violation found lives in workflows/help.md and is reported).
1362
+ - Runtime leakage of ~/.claude/ paths is by design: the installer rewrites ~/.claude/ per runtime/mode in all command and agent copiers — verified end-to-end in d:/pantesting where .gemini/.codex/.opencode/.github copies carry correctly rewritten paths (the ./.claude/ and unprefixed .claude/ deviations that escape this are reported as findings). The .claude/.gemini/.codex/.opencode/.github mentions in focus-* scope-boundary lists are intentional runtime-dir enumerations.
1363
+ - Frontmatter: all 24 agents carry structurally consistent name/description/tools/color/effort (model: opus pinned only on the three security agents, matching the installer's documented strip behavior for other runtimes); all 59 commands have parseable frontmatter with description (the only field the Gemini/Codex/Copilot/unified skill converters read — skill names derive from filenames, so the pan:-prefix inconsistency in name: fields of army/experiment/focus-* is cosmetic and unparsed).
1364
+ - focus-auto.md's flag table matches focusAutoInit exactly (source/category/mode/budget/max-cycles/total-budget/enforce-budget/verify-reserve/continue/stop/status/dry-run/deep-review/parallel-research/parallel-verify/clean-seal); army.md's caps and tier structure are consistent with pan-conductor's safety harness (depth 2, 12 spawns, abort file, advisory budgets) and with campaign.cjs/focus.cjs indicators (into_verify_reserve, budget_reserve_reached, new_work_budget_remaining).
1365
+ - exec-phase.md, plan-phase.md, verify-phase.md, quick.md, debug.md, git.md, cost.md, hud.md, report.md, preview.md, review-deep.md, knowledge.md, hygiene.md, mcp-bridge.md, dashboard.md, phase-budget.md, milestone-done.md, patches.md, update.md and the thin router commands (add/insert/remove-phase, progress, pause, resume, profile, todo-add/check, health, assumptions, phase-tests, milestone-new/audit/cleanup/gaps, map-codebase, new-project, discuss-phase) were read and their tool invocations, flags, and referenced artifacts check out.
1366
+
1367
+ ### Shipped workflows, templates & references
1368
+
1369
+ - All @-references and pan-wizard-core-relative cross-references in workflows/, templates/, references/ resolve to files on disk (checked every ~/.claude/... and pan-wizard-core/... .md reference against the tree; all workflows/templates/references/agents targets exist)
1370
+ - All subagent_type names used in workflows/templates (pan-executor, pan-planner, pan-plan-checker, pan-verifier, pan-reviewer, pan-debugger, pan-document_code, pan-phase-researcher, pan-project-researcher, pan-research-synthesizer, pan-roadmapper, pan-integration-checker, general-purpose) match agents/*.md on disk
1371
+ - Every pan-tools subcommand+flag combination used across the three dirs exists in pan-tools.cjs routing (state update/patch/advance-plan/record-metric/update-progress/add-decision/add-blocker/record-session/load, roadmap get-phase/analyze/update-plan-progress, phase add/insert/remove/complete/next-decimal, phases list --type/--raw, verify artifacts/key-links/reconcile, validate health --repair, optimize trace init/log/end/current/list + learn/apply --report/list/stats, memory select --cue/budget, learn topics-for --agent --token-budget/promote, standards select/recommend, init execute-phase/plan-phase/new-project/new-milestone/quick/resume/phase-op/todos/milestone-op/map-codebase/progress, report phase/index, doc-lint <dir>, skills align, codebase detect-languages/analyze-imports/best-practices, summary-extract --fields, progress bar, frontmatter get --field, retro, estimate-cost, generate-slug, current-timestamp, config-get/config-set/config-ensure-section, requirements mark-complete, phase-plan-index, state-snapshot, resolve-model) — sole exception reported: prose `report --open`
1372
+ - health.md error-code table (E001-E005, W001-W007, I001) and repair actions match verify.cjs cmdValidateHealth implementation exactly
1373
+ - retro.md's parsed JSON fields all exist in verify-retro.cjs output (phases_planned/completed/decimal, estimation_accuracy_pct, verifications_*, first_try_rate_pct, common_gap_patterns)
1374
+ - cmdCommit contract matches workflow usage: message optional only with --amend (execute-plan.md:429 amend-without-message is valid), empty --files defaults to staging .planning/ (plan-phase.md:303 valid), commit_docs+gitignore checks internal
1375
+ - templates/validation.md frontmatter placeholders ({N}, {phase-slug}, {date}) match plan-phase.md step 5.5 fill instructions
1376
+ - phase-report.cjs output names ({phase}-report.html, .planning/report-index.html) match exec-phase.md:611-613 commit paths
1377
+ - milestone.cjs archive filenames (v{X.Y}-roadmap.md, v{X.Y}-requirements.md, v{X.Y}-milestone-audit.md) match milestone-cleanup.md reads and milestone-audit.md consumers (except the line-132 typo reported)
1378
+ - verification-patterns.md:634 pointer to checkpoints.md <automation_reference> section resolves (tag exists at checkpoints.md:381-533)
1379
+ - references/schemas/pan-command.schema.yml matches doc-lint.cjs default schema path and field model
1380
+ - Counts policy: no CLAUDE.md-style filesystem-derived counts embedded anywhere in workflows/templates/references (map-codebase's '9 documents' is an internal contract matching its own explicit list of 9; researcher '4 agents' etc. are workflow-internal)
1381
+ - progress.md's state-snapshot field usage matches state.cjs cmdStateSnapshot output (current_phase, decisions[], blockers[], session)
1382
+ - milestone-gaps.md jq path .directories[-1] matches cmdPhasesList JSON shape
1383
+ - templates/summary.md frontmatter fields match summary-extract and execute-plan create_summary instructions; summary-minimal/standard/complex all exist and match template.cjs cmdTemplateSelect returns
1384
+ - templates/preview-report.md placeholders and mode contract match pan-previewer.md and commands/pan/preview.md
1385
+ - planning-config.md nested planning.commit_docs schema is genuinely supported by config.cjs loadConfig (handles both flat and nested forms); auto-detection claim (.planning gitignored → commit_docs false) matches code
1386
+ - discuss-phase auto_mode_bypass (P-1803), plan-phase P-1802, exec-phase auto-advance and --no-transition chains are internally consistent, and the auto_advance step referenced by the bypass exists (discuss-phase.md:547)
1387
+ - ~/.claude/ and ./.claude/ rewriting verified against the real installed artifact at d:/pantesting/.codex — canonical-form paths in workflows are correctly converted per runtime (the four non-canonical forms found are reported as findings)
1388
+ - help.md's listed /pan: commands all exist on disk (no phantom commands in the reference; the gap is omissions, reported); all other /pan: references across the three dirs resolve except /pan:discuss-milestone and /pan:sync (reported) and the ui-brand.md /pan:alternative-N format placeholders (intentional examples)
1389
+ - pause.md/continue-here template formats are consistent with each other and with resume-project/transition handoff cleanup (.continue-here*.md globs)
1390
+ - assumptions.md, add-phase.md, insert-phase.md, remove-phase.md, todo-add.md, todo-check.md, milestone-cleanup.md, quick.md, profile.md, settings.md question flow, questioning.md, tdd.md, handoff-decisions.md, checkpoints.md, ui-brand.md, decimal-phase-calculation.md, phase-argument-parsing.md, git-planning-commit.md, design-methodology.md checked for broken refs/stale subcommands — no additional issues beyond those reported
1391
+
1392
+ ### Tests
1393
+
1394
+ - Full suite green: npm run test:all → tests 3348, suites 717, pass 3348, fail 0, cancelled 0, skipped 0, todo 0, duration_ms 34950 — exactly matching CLAUDE.md's counts table (3348/717), so no drift finding applies
1395
+ - All 50 pan-wizard-core/bin/lib/*.cjs modules have behavioral coverage — verified each of the 9 modules with zero direct test requires is exercised another way: init.cjs and milestone.cjs via runPanTools CLI (tests/init.test.cjs, tests/milestone.test.cjs), phase-remove.cjs via tests/phase.test.cjs (removePhaseFromDisk) + dispatcher.test.cjs, verify-drift.cjs via tests/drift-check.test.cjs, verify-preflight.cjs via tests/preflight.test.cjs, verify-retro.cjs via 'retro' command tests in tests/verify.test.cjs:650+, verify-deploy.cjs via 'validate deployment' tests in tests/verify.test.cjs:897+ and detectInstalledRuntimes in tests/verify-health.test.cjs:913, commands-learnings.cjs via learnings extract/list/prune CLI tests (tests/learnings.test.cjs), agents-md.cjs via install-lib re-export tests (tests/installer-functions.test.cjs:1164+) plus the agents-md-layer E2E suite
1396
+ - tests/module-surface.test.cjs pins the export surface (names AND types) of every lib module against a fixture, so decomposition/API drift is caught
1397
+ - tests/claude-md-counts.test.cjs recomputes every file-based CLAUDE.md count from disk and asserts the table matches — the counts SSOT is self-auditing
1398
+ - PAN_SOURCE_ROOT self-install guard is scenario-tested for both install and uninstall (tests/e2e-install.test.cjs:444 'installer refuses to run from source repository' and :459 uninstaller variant)
1399
+ - learnings/internal strip has explicit negative-assertion coverage across all 5 runtimes (tests/scenarios/learnings-installed.test.cjs:42-45: 'learnings/internal/ MUST NOT ship')
1400
+ - All 5 runtime flags covered: dedicated local-install unit suites per runtime (claude/codex/gemini/opencode/copilot-install.test.cjs) plus tests/scenarios/runtime-matrix.test.cjs which installs and runs pan-tools from the INSTALLED path for each of ['claude','opencode','gemini','codex','copilot']
1401
+ - Uninstall covered per runtime, including the skills/-orphan regression (tests/claude-install.test.cjs) and Copilot legacy config.json migration (tests/copilot-install.test.cjs:367+)
1402
+ - Release cannot publish with failing tests: scripts/release-check.js Gate 2/7 runs npm run test:all, and release.yml's npm publish triggers it via prepublishOnly
1403
+ - CI test invocation is glob-safe: ci.yml runs npm test / npm run test:scenarios which use the glob-free scripts/run-tests.cjs (deterministic readdir+sort), across ubuntu/windows/macos × node 18/20/22
1404
+ - No .only or unconditional .skip in the suite; only conditional git-unavailable skips in tests/whatif.test.cjs:218-220 (run showed skipped 0)
1405
+ - No network access in any test; every non-relative require across tests/ is a node builtin — the require('lodash')/require('nope') grep hits are string fixtures written to temp files for codebase/distill scanners
1406
+ - Flaky-pattern review: tests/helpers.cjs cleanup uses bounded fs.rmSync retries (maxRetries:5, retryDelay:100) that warn rather than fail (documented macOS git-gc race); tests/scenarios/feedback-performance.test.cjs wall-clock thresholds were already re-tuned (2000-5000ms) after documented pre-2026-05 flakes and are annotated as regression canaries; tests/runner.test.cjs timeout test allows 4000ms for an 800ms abort — generous margins, no unmitigated real-time dependency found
1407
+ - 3 of 5 hooks have real behavioral unit tests exercising their logic via exported pure helpers: pan-cost-logger.js (tests/cost-logger-hook.test.cjs), pan-statusline.js (tests/statusline.test.cjs with injected fake fs), pan-trace-logger.js (tests/trace-logger.test.cjs)
1408
+
1409
+ ### Hooks
1410
+
1411
+ - Source-to-dist parity: all 5 hooks byte-identical between hooks/*.js and hooks/dist/*.js (diff -q each pair); no stale or extra dist files; scripts/build-hooks.js is genuinely copy-only and release-check.js Gate 1 reruns build:hooks under prepublishOnly, so published tarballs (files whitelist ships hooks/dist) cannot carry stale dist
1412
+ - Cross-platform safety of registered hook commands: every registration is a plain `node <path>` invocation — global installs use buildHookCommand (install-lib.cjs:623) which forward-slashes and double-quotes the absolute path (space-safe on Windows), local installs use relative `node .claude/hooks/...`; no bash-isms, no 'sh'/'bash' spawns, no shell operators, no signals, no shebang-dependent execution anywhere
1413
+ - No POSIX-only paths in hooks: temp via os.tmpdir(), home via os.homedir(); no /tmp or ~ literals; no reliance on HOME (USERPROFILE fallback unnecessary); Windows uid fallback (process.env.USERNAME || 'win') is derived identically in statusline and context-monitor so the IPC channel stays consistent; on Windows the ignored 0o700 mode is backstopped by the per-user ACL'd %TEMP%
1414
+ - pan-check-update.js spawns its network check via process.execPath (not PATH-dependent 'node'), detached+unref'd with windowsHide and stdio:'ignore', execSync('npm view ...') has a 10s timeout — SessionStart cannot stall the host runtime; loggers and monitor wrap all work in try/catch with silent exit 0, so malformed/empty stdin JSON never blocks or errors
1415
+ - Hook protocol output discipline: pan-context-monitor emits only the documented PostToolUse hookSpecificOutput/additionalContext JSON on stdout; cost/trace loggers write nothing to stdout; statusline writes only the rendered line (empty on parse failure)
1416
+ - Zero runtime dependencies confirmed in all 5 hooks: only fs/path/os/child_process builtins required
1417
+ - Installer wiring consistency per runtime: copy step (install.js:2116-2151) targets <configDir>/hooks and registration paths match it for Claude/Gemini (settings.json SessionStart/PostToolUse/SubagentStop with idempotent has-hook checks), Codex (mergeCodexHooksConfig merges non-destructively into shared .codex/hooks.json, dedupes by pan-* marker, preserves foreign hooks), Copilot (.github/hooks/pan.json version:1 camelCase config plus legacy config.json cleanup), and OpenCode correctly gets no hooks copied and none registered; hook files are recorded in the manifest (install.js:1646); the '.claude' templating regex touches only the five intended path.join literals (verified by enumerating all .claude occurrences and simulating the replace for copilot/gemini/opencode)
1418
+ - Uninstall path: panHooks list (install.js:1049) covers all 5 current hooks plus legacy names (pan-check-update.sh, pan.json) and settings/hooks.json/config.json entries are stripped per runtime, including deleting emptied event arrays
1419
+ - Plugin packaging: buildPluginHooksConfig registers all four event hooks via quoted ${CLAUDE_PLUGIN_ROOT} paths and scripts/build-plugin.js copies from hooks/dist (untemplated '.claude' is correct for the Claude-only plugin)
1420
+ - Day-rollover of auto trace sessions (sess_auto_YYYYMMDD regex, finalizeSession recount) behaves as tested; explicit sessions stay sticky; 63 hook-related unit tests pass (cost-logger-hook, cost-logger, trace-logger, statusline)
1421
+ - Context-monitor debounce logic (first-warn immediate, 5-call debounce, severity-escalation bypass) is internally consistent; stale-metrics (60s) and missing-session guards exit silently as intended
1422
+
1423
+ ### User & developer docs
1424
+
1425
+ - CLI-REFERENCE Command Index (191 rows) vs pan-tools.cjs dispatcher, both directions: every indexed command/subcommand exists (state, phase(s), roadmap, requirements, milestone, validate, verify*, progress, context-budget, todo, scaffold, frontmatter, template, config-*, utility set, init x12, focus x8, standards x7, operations, memory, cache, codebase, cost, models, bus, preview, review-deep, knowledge, whatif, bridge, optimize, git, distill, squad, worktree, campaign, hud, report, skills, hygiene, learn, links, doc-lint, experiment); no phantom top-level commands
1426
+ - All commands named in README/USER-GUIDE tables exist as commands/pan/*.md (59 files, matching CLAUDE.md's count); SKILLS-REFERENCE.md and SKILLS-FULL-TEXT.md cover exactly the 59 shipped commands, are marked auto-generated, defer counts to CLAUDE.md, and spot-checked line counts (map-codebase 113, new-project 107, add-phase 44, army 176) and embedded full text match the source files verbatim
1427
+ - Relative markdown links in README + all 16 audited docs resolve (scripted existence check) — zero broken link targets
1428
+ - Counts policy: no embedded filesystem counts (command/agent/module/test totals) found in any audited doc outside CLAUDE.md
1429
+ - Installer claims in README/FAQ/USER-GUIDE verified against bin/install.js: flags --claude/--opencode/--gemini/--codex/--copilot/--all, --global/-g, --local/-l, --uninstall/-u (requires --global|--local), --config-dir, --unified-skills, --force-statusline; CLAUDE_CONFIG_DIR handling; install dirs (OpenCode ~/.config/opencode XDG chain, Copilot ~/.copilot global vs .github local, Codex CODEX_HOME); Copilot skills at skills/pan-*/SKILL.md
1430
+ - Per-runtime command prefixes (/pan: Claude+Gemini, /pan- OpenCode+Copilot, $pan- Codex) verified against bin/install-lib.cjs converters
1431
+ - HOOKS.md and context-monitor.md verified against hooks/*.js and installer: 5 hooks with correct events, thresholds WARNING<=35/CRITICAL<=25, debounce 5 calls, stale 60s, bridge file path <os-tmpdir>/pan-hooks-<uid>/claude-ctx-{session_id}.json, hook registration in settings.json / .codex/hooks.json / .github/hooks/pan.json, build:hooks copy-only pipeline
1432
+ - Config reference: nested doc keys planning.commit_docs, planning.search_gitignored, parallelization.enabled, workflow.research/plan_check/verifier/nyquist_validation, git.* are all readable — core.cjs loadConfig() explicitly resolves flat keys with nested-section fallback (initially suspected mismatch, disproved); config defaults (default_points 50, verify_reserve 0.15, memory.auto_optimize) match code
1433
+ - Model documentation in USER-GUIDE/INTERNALS verified against core.cjs and agents/: MODEL_PROFILES (all-reasoning for quality+balanced, budget mid/fast per agent), tier mapping (anthropic/openai/google), COST_MULTIPLIERS {reasoning:15, mid:3, fast:1}, complexity thresholds downgrade_max 2 / upgrade_min 6, PAN_PROVIDER + directory-based provider detection, three agents pinning model: opus (hardener, meta-reviewer, reviewer), legacy tier aliases
1434
+ - AGENTS.md: agent inventory covers all 24 shipped agents; tool lists spot-checked for 9 agents against agents/*.md frontmatter — all match; squad table and pan-tools squad/worktree invocations valid
1435
+ - Focus system docs verified against focus.cjs/constants.cjs: scan --lean, plan --budget/--mode/--priority (4 modes bugfix/balanced/features/full), sync --check-only, exec --dry-run, auto flags (--source/--category/--mode/--budget/--max-cycles/--total-budget/--parallel-research/--parallel-verify/--clean-seal/--status/--stop/--update/--continue/--dry-run and update metrics flags); FOCUS_CATEGORIES includes security+distill as USER-GUIDE/MIGRATION claim
1436
+ - Bot-army docs verified: army.md flag surface (--source/--max-cycles/--total-budget/--verify-reserve/--squads/--no-build-worktrees/--push/--clean-seal/--schedule/--daily-budget/--dry-run/--continue/--stop/--status), campaign.cjs default daily_budget 300 and schedule/status/due/record-run flags, worktree create --base / remove --branch --force, conductor hard caps (nesting 2, 12 spawns, abort file) as documented in FAQ/USER-GUIDE/ARCHITECTURE
1437
+ - Other CLI sections verified against modules: retro --write-memory/--max (verify-retro.cjs), drift-check --since/--threshold/--files/--verbose, progress json|table|bar|health, quick --full, exec-phase --gaps-only, hygiene scan/clean --apply/--trace-age-days, skills index/align flags, learn promote/unpromote/list-promoted/lint/build-index/topics-for flags, codebase estimate-size --threshold/--no-docs, experiment new/list/manifest/status/stop flags and ~/pan-experiments default root, learnings prune --days/--id, cache prime --summary, hud/report flags
1438
+ - USER-GUIDE self-improvement loop section: tests/scenarios/learnings-installed.test.cjs exists, /pan:learn --experiment documented in learn.md, runner adapter map (claude/codex/gemini/opencode supported, copilot null) matches runner.cjs, 700K single-shot threshold matches codebase.cjs
1439
+ - SECURITY-HARDENING.md referenced artifacts all exist: .github/workflows/release.yml, .github/workflows/codeql.yml, .gitleaks.toml, scripts/install-git-hooks.js, SECURITY.md, docs/decisions/ADR-0040-memory-injection-defense.md
1440
+ - TROUBLESHOOTING/MIGRATION VERSION-file claims verified (installer writes pan-wizard-core/VERSION; pan-check-update.js reads it); all pan-tools diagnostic invocations in TROUBLESHOOTING are valid dispatcher routes
1441
+ - README asset URLs (pan-readme-hero.png, pan-orchestration.png, terminal.svg, pan-developer.png) exist in assets/ and assets/ is in package.json files array; DEVELOPMENT.md Node >=16.7.0 matches package.json engines
1442
+ - ARCHITECTURE.md module dependency graph and module inventory: all 46 listed modules exist on disk (no phantom modules); workflow-name mapping table (resume-project, diagnose-issues, transition, execute-plan) matches pan-wizard-core/workflows/; COMPARISON.md's 'map-codebase (6 parallel agents)' matches the workflow
1443
+
1444
+ ### Specs & ADRs
1445
+
1446
+ - ADR numbering: contiguous ADR-0001..ADR-0042, no gaps or duplicates; every ADR has a Status section; file counts match CLAUDE.md's counts table (42 ADRs, 44 specs). ADR-9999 in doc_code_link_graph_featureai.md is an intentional error-payload example, not a broken reference.
1447
+ - Deep-read the 10 most recent ADRs (0033-0042) and verified their implementation claims against code: worktree.cjs/campaign.cjs/squads.cjs/hud.cjs and commands/pan/army.md + agents/pan-release.md (0033-0035); memory.cjs selectMemory FW-2 (0036); exactly 3 agents pinned model: opus — pan-hardener, pan-meta-reviewer, pan-reviewer (0037); skill-align.cjs + pan-tools 'skills' verb, spec marked Shipped v3.13.0 (0038); 0039 is a design rule with no implementation as stated; memory-optimize.cjs quarantine (QUARANTINE_FILE, directive detection) + memory-rebuild.cjs scanForDirectives warnings (0040); pan-zcode/mcp/server.cjs SUPPORTED_VERSIONS_LIST ['2026-07-28','2025-06-18','2025-03-26','2024-11-05'], server/discover handler, -32022 error, resultType stamping gated on _meta, plus tests/pan-zcode-mcp.test.cjs and pan-zcode/KNOWN-BETA-RISKS.md all present (0041); design-methodology.md, templates/design.md, pan-designer/pan-design-checker agents, commands/pan/design-phase.md, and focus-design's 5.4 independent-verification gate all present and wired (0042).
1448
+ - ADR-0030 verified: lock.cjs exports withFileLock/writeFileAtomic and state.cjs wires them into the state write (state.cjs:872-873). ADR-0029's acceptance criterion 'no implementation until accepted' is honored — no evaluator/LLM-judge code in experiment.cjs or runner.cjs. ADR-0012 fully implemented (CONTRIBUTORS.md + ATTRIBUTION.md exist, README links both). ADR-0013's four features exist (verify-preflight.cjs, commands/pan/dashboard.md, learn verbs, pan-tools 'deps' case).
1449
+ - Client-identifying content: scanned all 86 spec/ADR files for client codenames, emails, and company strings (plusb, Loans, client names, @-emails). Nothing identifying found: ADR-0037's 'the lending project' and FIELD-REPORT-style generic domain names are the sanitized replacement vocabulary per the go-public scrub policy; ADR-0031's 'MontyHall compiler project' is a personal project reference; local d:\ paths in specs reference only d:\pantesting (the documented test dir) and d:\googlecli\agents-cli (Google's public ADK CLI).
1450
+ - Cross-reference sweep: extracted and existence-checked every repo-path-shaped reference in all specs and ADRs (211 raw misses triaged). All load-bearing references in Accepted/Implemented docs resolve: docs/ECOSYSTEM-REVIEW-2026-06.md, docs/FIELD-HARVEST-2026-07.md, pan-wizard-core/references/{guardrails,model-profiles,design-methodology}.md, templates/design.md, agents/pan-plan-checker.md, tests/fixtures/module-surface.json. Remaining misses are install-relative shorthand (references/... = pan-wizard-core/references/...), planned-file names inside dated Proposed specs (e.g. ADR-0015's test table, opus_47 v2's planned scenario-test names — coverage shipped as unit tests preview/knowledge/whatif/bridge.test.cjs), or renamed dev files in the dated 2026-03-01 custom-skills-assessment — point-in-time records, not current-behavior claims.
1451
+ - Supersession hygiene elsewhere is correct: opus_47_extended_features_featureai.md (Spec B v1) carries frontmatter status: superseded + superseded_by and a banner pointing to v2, exactly as ADR-0024 claims.
1452
+ - multi_model_routing_featureai.md (no status header) checked against code: shipped as routing.strategy static/complexity in core.cjs (line 708-710) + config.cjs defaults + per-agent/per-phase overrides; the unshipped cascade mode is explicitly labeled 'Reserved for future cascade routing' in the shipped pan-wizard-core/references/model-profiles.md:224, so the divergence is marked in shipped docs — no finding.
1453
+ - Spot-checked status-less but dated specs (codebase_mapper_v2, documentation_alignment_cleanup, googlecli_adoption, help_commands_rewrite, lifecycle_completeness, map_codebase_mermaid_togaf, production_*, user_reality_testing, end_to_end UAT, focus_auto_runner, focus_sync_v2, custom-skills-assessment, focus-auto-optimize-superplan): all carry Generated/Date headers and read as point-in-time proposals; googlecli spec's optional #3 `pan-tools info` is correctly marked 'prototype-first, optional' and never claimed shipped.
1454
+
1455
+ ### Security & hygiene
1456
+
1457
+ - escapeRegex convention: phase.cjs, roadmap.cjs, phase-remove.cjs, phase-report.cjs, hud.cjs, knowledge.cjs, focus.cjs, optimize.cjs, verify.cjs (line 1100/1106/1157) and state.cjs write path (stateReplaceField, cmdStateGet) all correctly route dynamic IDs/labels through escapeRegex — milestone.cjs is the only exception.
1458
+ - state.cjs stateExtractField/extractFieldsFromState and install-lib.cjs extractFrontmatterField/convertClaudeAgentToCodexToml build RegExp from a `fieldName`/`key` argument WITHOUT escaping, but every caller passes a hardcoded string literal ('Current Plan','Status','name','description','thinking'), so no untrusted input reaches them — not exploitable.
1459
+ - commands.cjs:311 (commit sensitive_patterns) and verify.cjs:393 / links.cjs:346 (must_haves link.pattern) compile RegExp from config/plan-authored strings but are each wrapped in try/catch that returns null/skips on invalid pattern — no crash surface.
1460
+ - child_process usage is shell-safe: core.cjs/git.cjs/worktree.cjs (execGit), focus.cjs, phase-report.cjs, hud.cjs, experiment.cjs, install-git-hooks.js, release-check.js, run-tests.cjs, and pan-zcode/mcp/server.cjs all use execFileSync/spawnSync with argv ARRAYS and shell:false — no string-built commands with interpolated input.
1461
+ - runner.cjs uses shell:true but ONLY on win32 (adapter.shell==='win32') to resolve .cmd shims, quotes each whitespace-bearing arg with doubled-quote cmd.exe escaping, and the spawned prompt/args originate from PAN's own experiment manifest, not external attacker input.
1462
+ - pan-zcode/mcp/server.cjs runVerb enforces reg.FORBIDDEN_VERB (/(^|-)(push|reset|rebase|force)($|-)/) as defense-in-depth and spawns via shell-less execFileSync('node', argv); resolveOverflow honours only tmpdir paths basenamed out.json.
1463
+ - config.cjs cmdConfigSet has an inline literal prototype-pollution guard rejecting __proto__/constructor/prototype key segments before walking; buildConfigDefaults merges userDefaults via explicit per-section spreads over a fixed hardcoded skeleton (no recursive attacker-key merge).
1464
+ - frontmatter.cjs cmdFrontmatterMerge Object.assign(fm, mergeData) over parsed JSON has no __proto__ guard, but fm is spliced back into YAML text (spliceFrontmatter) and never used as a JS lookup object / never reaches Object.prototype in a way that pollutes global state — not a reachable pollution sink.
1465
+ - Temp dirs use fs.mkdtempSync everywhere: core.cjs (os.tmpdir()/'pan-'), scripts/release-check.js (two mkdtempSync sites) — no hand-rolled Math.random/Date.now temp names.
1466
+ - Hook IPC bridge dir created at mode 0o700 in both pan-context-monitor.js bridgeDir() and pan-statusline.js, keyed by uid, with direct read-no-existsSync-gap; hooks/ sources are byte-identical to hooks/dist/ copies.
1467
+ - Zero-dependency rule intact: no require() of any non-builtin, non-relative module across bin/, pan-wizard-core/, hooks/, pan-zcode/, scripts/.
1468
+ - Secret scan of git-tracked files found no live token/key/credential values (no ghp_/npm_/sk-/AKIA/PEM literals); .gitleaks.toml + .gitleaksignore allowlists are narrowly scoped to manifest SHA-256 hashes and test-fixture filenames; hasBraveSearchKey/websearch read BRAVE_API_KEY only from env/user home and send it via X-Subscription-Token header to the fixed Brave endpoint (no key logging, no key in prose/state).
1469
+ - install-zcode.js assertNotInSourceRepo resolves symlinks (realpathNearest) and case-folds on win32/darwin before the source-repo guard — junction/case-variant bypass covered.
1470
+ - CI/workflow hygiene (.github/workflows ci.yml, codeql.yml, release.yml): top-level permissions default contents:read; every third-party action pinned by full commit SHA with version comment; release publishes via OIDC trusted publishing (no NPM_TOKEN) with id-token:write scoped to the publish job; gitleaks installed via checksum-verified official tarball; no untrusted input (PR title/branch name) interpolated into any run: block.
1471
+
1472
+ ### pan-zcode (experimental)
1473
+
1474
+ - Zero-dependency rule holds across the whole pan-zcode/ subtree: grep for any require() of a non-builtin, non-relative module returns nothing; every require is fs/os/path/child_process/crypto or a relative .cjs.
1475
+ - install-zcode.js has its own source-root guard equivalent to PAN_SOURCE_ROOT: assertNotInSourceRepo() resolves symlinks/junctions via realpathNearest() and case-folds on win32/darwin. Live-tested from /d/pantesting/audit-e2e-2026-08-zcode: it refuses a repo subdir (D:/PanWizard/pan-zcode/out) and a case-variant (d:/panwizard/x), and builds a valid bundle (24 agents + pan-mcp.json + manifest + INSTALL-ZCODE.md) into a sandbox target. Never run from the repo root.
1476
+ - MCP spawn is shell-less and injection-safe: defaultSpawn uses execFileSync('node', argvArray) with NO shell; the verb is always chosen from the registry allowlist; runVerb re-checks FORBIDDEN_VERB (/(^|-)(push|reset|rebase|force)($|-)/) as defense-in-depth; each tool arg is validated by str()/AGENT_RE/PHASE_RE/QUERY_RE before becoming an argv element. Input-safety tests confirm 'a; rm -rf /', '$(whoami)', '../etc/passwd', '3; ls', over-length and empty inputs are all rejected with -32602 and zero spawns.
1477
+ - merge-gate git execution is safe: makeDefaultGit uses execFileSync('git', argvArray, {cwd}) with no shell; confirmMerge only ever issues ['merge','--squash',branch] then ['commit','--no-verify',...]; never push/reset/rebase/force (asserted by tests). validateBranch rejects hostile branch names via BRANCH_RE.
1478
+ - The @file: overflow protocol in server.cjs resolveOverflow is confined: only paths under os.tmpdir() whose basename is exactly out.json are read (the path originates from PAN's own engine, not tool input); '/etc/passwd' and a non-out.json basename are returned unread. Verified by test and by reading core.cjs output() which writes via mkdtempSync + flag:'wx'.
1479
+ - JSON-RPC/MCP protocol handling is robust: malformed envelope -> -32600, JSON parse error -> -32700, unknown method -> -32601, unknown tool/resource/bad params -> -32602, engine failure on resources/read -> -32603; notifications (id null/undefined, including request-methods sent id-less) correctly get no response, while id=0 is treated as a valid request. tool-execution failures stay in-band as isError:true rather than protocol errors.
1480
+ - Dual-era negotiation (ADR-0041) matches the code: legacy initialize never echoes the modern 2026-07-28 revision (falls back to 2025-06-18); modern _meta requests on an unsupported version return -32022 with data.supported listing SUPPORTED_VERSIONS_LIST; server/discover is reachable in both eras; modern results are stamped resultType:'complete' while legacy results are not. ADR-0041's claims are consistent with server.cjs.
1481
+ - bin/install.js does NOT ship or reference pan-zcode into any of the 5 runtimes (grep for zcode/pan-z in bin/ is empty). pan-zcode is carried only via the package.json files array as a separate opt-in subsystem with its own installer.
1482
+ - Docs consistently present pan-zcode as experimental/preview and explicitly NOT a sixth runtime of bin/install.js: README.md ('experimental, separate subsystem', beta caveat), docs/ARCHITECTURE.md ('deliberately not a sixth target of bin/install.js'), CONTRIBUTING.md ('Experimental: ZCode subsystem'), CHANGELOG.md ('opt-in subsystem, not wired into the five-runtime installer'). No embedded filesystem-derived counts found in pan-zcode docs.
1483
+ - convert-agent.cjs correctly drops Task (no nesting) and mcp__* tools, maps PAN model tiers to 'inherit' with a preserved pan-tier hint comment, and preserves the body; converting the real agents/ tree (24 agents) never leaks a PAN tier (e.g. 'model: opus') as a ZCode model id.
1484
+ - All three pan-zcode test files pass: node --test tests/pan-zcode-*.test.cjs reports 52 tests / 11 suites / 0 fail.
1485
+ - The merge-gate security model is coherent as documented: the approval_token nonce is disclosed to the agent in the pan_request_merge tool result, but this is not exploitable because the agent cannot set the MCP server's PAN_MERGE_APPROVAL environment variable (evaluateMerge reads it from the server process env, never from agent tool input), and KNOWN-BETA-RISKS.md correctly names server-side branch protection as the non-bypassable second lock. Token is single-use (cleared from env on successful merge) and bound to the exact staged branch (branch_mismatch on slug-colliding look-alikes).
1486
+
1487
+
1488
+ ---
1489
+
1490
+ ## Appendix B — Refuted findings
1491
+
1492
+ Two candidate findings were killed during adversarial verification; recorded here so they aren't re-reported by future audits without new evidence.
1493
+
1494
+ #### R1. transition.md yolo route invokes SlashCommand("/pan:milestone-done") — contradicts its own and plan-phase's rule that SlashCommand/Skills don't resolve inside Task subagents
1495
+
1496
+ `pan-wizard-core/workflows/transition.md:564` · Shipped workflows, templates & references
1497
+
1498
+ > [REFUTED] The quoted line exists (transition.md:564), but the claimed failure path is unreachable. The reviewer asserts transition.md runs inline inside the exec-phase orchestrator that plan-phase.md step 14 spawned as a Task subagent. That spawn passes ARGUMENTS='${PHASE} --auto --no-transition' (plan-phase.md:574) and its prompt explicitly says 'Do NOT run transition.md' (plan-phase.md:580); exec-phase.md:643-662 implements the guard — with --no-transition present the subagent returns PHASE COMPLETE and 'STOP. Do not proceed to auto-advance or transition.' Every documented Task-spawn of exec-phase carries the flag (plan-phase.md:574; discuss-phase.md:595), and no command or agent invokes transition.md. Therefore Route B's SlashCommand is never executed inside a Task subagent per the shipped instructions. The only contexts where transition.md's yolo Route B runs are top-level: exec-phase.md:717-719 executes transition inline only when --no-transition is absent (i.e., direct /pan:exec-phase X --auto in the main session) and resume-project.md:244 routing — both places where SlashCommand does resolve, so the 'Skills don't resolve inside Task subagents' rule (plan-phase.md:557, transition.md:412) does not apply. Surviving kernel is much smaller: Route B still carries the exact 'Exit skill and invoke SlashCommand(...)' pattern that P-1701 (CHANGELOG.md:693, v3.7.4) removed from the yolo branch because SlashCommand fails in headless claude -p; a headless yolo run hitting a milestone boundary at top level could misfire. But that mechanism differs from the one claimed, and headless multi-phase autonomous runs are documented as blocked upstream by the TTY issue anyway (P-1501-r3, CHANGELOG.md:685-689; P-1701 marked 'UNTESTABLE without P-1501 working'). That residual inconsistency plus the inaccurate recursion description at transition.md:435 amount to hygiene/drift (Low), not the claimed Medium stall via subagent Skill resolution.
1499
+
1500
+ #### R2. Client-context codenames (lending/compliance/forecasting/platform/spec-factory) present in public git-tracked docs
1501
+
1502
+ `docs/FIELD-HARVEST-2026-07.md:14` · Security & hygiene
1503
+
1504
+ > [REFUTED] The cited strings exist where claimed, but the finding inverts the policy it invokes. (1) The policy is stated in CHANGELOG.md's v3.13.1 entry: source projects are to be referenced "by neutral codenames (e.g. 'the lending project', 'the compliance project') instead of private project directory names" — these labels ARE the sanctioned sanitization applied during the go-public sweep, not a violation of it. There is no repo-stated 'no-codename policy'; the private memory note records the approved codename set with the real-name mapping kept off-repo. (2) The claimed disclosure of "client engagements" does not exist publicly: the word "client" appears 0 times in docs/FIELD-HARVEST-2026-07.md, docs/FIELD-REPORT-army-2026-06.md, CHANGELOG.md, and ADR-0037; public framing is "past PAN-driven work" / "a real project". The client linkage exists only in private context outside the repo. (3) Grep for the real client name across tracked files: zero hits; the only proper noun is "Fineract" (Apache open-source upstream, no-yield row). Sensitive roots are placeholder-bracketed; FIELD-HARVEST:8 documents shipped content is machine-path-free. No client identifier appears in the public repo, so the failure scenario is not real; the suggested fix would re-sanitize already-neutral labels.
1505
+
1506
+
1507
+ ---
1508
+
1509
+ ## Appendix C — Reviewer coverage notes
1510
+
1511
+ ### Installer & runtimes
1512
+
1513
+ Read fully: bin/install.js (2704 lines) and bin/install-lib.cjs (1416 lines). Skimmed: tests/e2e-install.test.cjs (guard + CommonJS-marker + reinstall groups), .gitignore, package.json, agents/*.md frontmatter (grep across all 24), hooks/dist listing, pan-wizard-core learnings path usage (commands-learnings.cjs/optimize.cjs — user-project learnings write to .planning/, not the runtime dir, so the pan-wizard-core wipe on upgrade does not destroy user learnings). Not exercised: --global installs, --config-dir, --unified-skills (static review only — its manifest ../ keys and ref-counted uninstall logic read consistent), interactive prompt path, npm test suites (other reviewers' dimension). Live testing done in d:/pantesting/audit-e2e-2026-08 (left in place, currently holding a full fresh 5-runtime install plus the pan-local-patches dir from the patch-flow test): fresh 5-runtime install (exit 0), manifest cross-check both directions via script, upgrade re-run (idempotent), patch backup flow, full 5-runtime uninstall + leftover audit, then reinstall; plus targeted repros for the dead capability warning, the gemini statusline clobber, the stale skill-shim orphan, and (in os.tmpdir) the Codex ESM hook crash. All 11 findings carry live or exact-quote evidence; severities follow the given rubric (the Codex ESM hook breakage is High as a user-visible breakage on a common path — ESM projects — for a supported runtime's whole hook surface).
1514
+
1515
+ ### Core modules
1516
+
1517
+ Coverage: all 16 assigned files in pan-wizard-core/bin/lib/ (agents-md, bridge, bus, campaign, codebase, commands-learnings, commands, config, constants, context-budget, core, cost, distill, doc-lint, experiment, focus — 8,928 LOC total) read in full. Callers traced through pan-wizard-core/bin/pan-tools.cjs dispatch plus commands/pan/*.md (army, experiment, focus-auto, focus-sync), agents/pan-roadmapper.md, templates/roadmap.md, and docs/CLI-REFERENCE.md. Skimmed for contract verification only: utils.cjs (planningPath), roadmap.cjs/verify.cjs (PHASE_HEADER_RE lastIndex resets), init.cjs/preview.cjs (getRoadmapPhaseInternal call sites), bin/install.js (what ships), tests/campaign.test.cjs and tests/focus.test.cjs (codified behavior). 10 findings confirmed by live execution: pan-tools invocations against os.tmpdir() fixtures (scaffold crash, campaign due exit code, bridge recommend, standards phase-track, config-set guard + missing value, distill report→analyze regression labeling, bus drain consume loss, detectLanguages tsconfig, roadmap padded-phase miss, resolveRate override gap) plus one run of the already-installed copy at d:/pantesting/audit-e2e-2026-08 (experiment new installer failure, root pointed at scratchpad, artifacts cleaned up). No files under D:\\PanWizard were modified; no installs performed. Not run: full npm test suite (out of scope for this dimension; unit tests consulted as behavioral documentation only)."
1518
+
1519
+ Read all 17 assigned files in pan-wizard-core/bin/lib/ in FULL (frontmatter, git, hud, hygiene, init, knowledge, learn-index, learn-lint, links, lock, memory-optimize, memory-rebuild, memory, milestone, optimize, phase-remove, phase-report), plus targeted reads of core.cjs (searchPhaseInDir/findPhaseInternal/loadConfig), utils.cjs (parsePhaseDir), constants.cjs (PHASE_DIR_RE), and pan-tools.cjs dispatch sites for git/requirements/knowledge to confirm reachability. Four findings were verified by executing the actual module code in isolated scratchpad temp dirs (node -e, no repo files touched): (1) memory-optimize agent sweep — 600-entry over-cap log unchanged after --apply, result.agents []; (2) milestone regex crash — exit 1 SyntaxError on 'REQ-01)'; (3) git commit files leak — a repo path named 'commit' was silently staged+committed by cmdGit; (4) frontmatter inline-array round-trip — 1 comma-containing item re-parses as 2. Remaining findings (lock steal race, knowledge traversal, scanAllPhases regex divergence, optimize apply path containment, dead os import) verified by code inspection with dispatcher/caller traces. Zero-dep rule checked mechanically across all 17 files (only builtins + relative requires). npm test was not run (not needed — verifications were targeted). No files under D:\\PanWizard were created, modified, or deleted; scratchpad test artifacts remain in the session scratchpad (cleanup command was denied by the permission system).
1520
+
1521
+ Read in full (8,901 lines): pan-tools.cjs (1511) plus phase, preview, review-deep, roadmap, runner, skill-align, squads, state, template, utils, verify-deploy, verify-drift, verify-preflight, verify-retro, verify, whatif, worktree .cjs. Traced supporting code in core.cjs (output/error/execGit/findPhaseInternal/searchPhaseInDir/getArchivedPhaseDirs/getRoadmapPhaseInternal/getMilestoneInfo), config.cjs (buildConfigDefaults/cmdConfigEnsureSection), commands.cjs (cmdBatchCommit), bin/install.js + bin/install-lib.cjs (hook settings shape), workflows/exec-phase.md (reconcile gate), tests/verify.test.cjs (coverage gap: pure functions only, no CLI exit-code assertions). Skimmed only: phase-remove.cjs, git.cjs, memory.cjs (out of assigned scope, touched for reachability). Ran 6 read-only empirical fixtures in the session scratchpad (never writing to D:\\PanWizard or d:\\pantesting): reconcile exit code (0 on 'invalid'), stubs --gate exit code (0 with blocking:1), phases list archived+type ENOENT crash, --type plan doc example failure, dashboard --raw JSON output, validate-deployment settings_ok:true with all four hook files missing, and flat-vs-nested config VERIFICATION_GATE_MISSING (0 vs 1). Also verified zero-dep compliance by grep and full dispatcher route resolution by loading all modules. The three dead-exit-code findings share one root cause (core.cjs output() unconditionally process.exit(0)) — a single output(exitCode) refactor fixes all three.
1522
+
1523
+ ### Shipped commands & agents
1524
+
1525
+ Coverage: listed all 59 commands/pan/*.md and 24 agents/*.md, then read every agent frontmatter plus full text of the executable/high-risk files (pan-verifier, pan-plan-checker sections, pan-executor state block, pan-conductor, pan-release, links, experiment, settings, patches, update, report, hud, preview, what-if, knowledge, review-deep, cost, optimize, retro, mcp-bridge, hygiene, git, debug, quick, verify-phase, milestone-done, exec-phase, plan-phase, research-phase, design-phase, army, focus-auto/design/plan head sections, audit-deployment, learn, dashboard, discord, phase-budget, help + its workflow); remaining files were skimmed via targeted greps for pan-tools invocations, @-references, /pan: refs, .claude paths, counts, and stale-status markers — those greps covered 100% of both directories. Verified dispatcher routing by reading pan-tools.cjs in full and confirming flags in focus/git/cost/codebase/distill/optimize/state/whatif/campaign/links/config/init/template modules. Ground-truthed installer rewrite behavior against the real v3.22.0 five-runtime install at d:/pantesting (read-only). Ran one read-only CLI probe (links validate against an empty scratch dir). Did not run npm test (not needed for this dimension; no files were modified). Not checked: the Discord invite URL (network), and Copilot CLI's default-tool semantics for agents without a tools field (finding framed accordingly). Borderline items deliberately left out as refutable: research-phase.md step 2's `ls …/research.md` example misses the `{N}-research.md` naming but INIT's has_research field supplies the correct signal; milestone-done.md's {{version}} placeholders and self-referential 'load workflow' rule are harmless; 'amber' agent color missing from the OpenCode hex map only drops a cosmetic color line.
1526
+
1527
+ ### Shipped workflows, templates & references
1528
+
1529
+ Read fully: all 33 workflows (exec-phase, execute-plan, plan-phase, verify-phase, discuss-phase, transition, milestone-new/audit/cleanup/gaps, new-project (targeted skim of 1150 lines via greps + key sections), map-codebase, health, update, learn, optimize, retro, settings, profile, progress, research-phase, resume-project, pause, phase-tests, quick, diagnose-issues, assumptions, add/insert/remove-phase, todo-add/check, help (command-list diffed)); all 16 references plus schemas/pan-command.schema.yml (model-profiles, model-profile-resolution, planning-config, continuation-format, git-integration, git-planning-commit read fully; guardrails, checkpoints, verification-patterns, questioning, tdd, ui-brand, handoff-decisions, decimal-phase-calculation, phase-argument-parsing, design-methodology skimmed with targeted greps for refs/commands/stale names); all 43 templates enumerated with reference-count analysis, ~15 read fully (summary, validation, debug-subagent-prompt, continue-here, phase-prompt head, preview-report, playbook, discovery, standards, retrospective, milestone, debug, config.json, codebase/* headers, research-project/*). Every pan-tools invocation across the three dirs was extracted and checked against the full pan-tools.cjs dispatcher (read in full, 1511 lines) and lib modules (verify.cjs health codes, verify-retro.cjs, state.cjs, phase.cjs, milestone.cjs, knowledge.cjs, phase-report.cjs, optimize.cjs, config.cjs, core.cjs MODEL_PROFILES, template.cjs, commands.cjs cmdCommit). Path-rewrite findings were verified against the REAL installed artifact at d:/pantesting/.codex (read-only). Two shell-behavior claims (case-sensitive ${plan/PLAN/SUMMARY} no-op; `grep -E \"^| 3\"` match-all) verified by execution in scratchpad-safe shell. No files under D:\\PanWizard were modified. 29 findings: 2 High, 13 Medium, 14 Low.
1530
+
1531
+ ### Tests
1532
+
1533
+ Run output (npm run test:all from /d/PanWizard, node --test via scripts/run-tests.cjs): tests 3348, suites 717, pass 3348, fail 0, cancelled 0, skipped 0, todo 0, duration_ms 34950. This exactly matches CLAUDE.md's counts table (3348 tests / 717 suites), so the prompted 'mismatch → Low finding' does not apply. Coverage method: listed all 50 lib modules, grepped tests/ for direct requires (9 had none), then traced each of those 9 through CLI dispatch (runPanTools) or facade re-exports to confirm real behavioral coverage — read the relevant test files rather than trusting filename matching. Read fully: hooks/pan-context-monitor.js, hooks/pan-check-update.js, scripts/run-tests.cjs, scripts/release-check.js (gate list), tests/module-surface.test.cjs header, tests/claude-md-counts.test.cjs header, feedback-performance.test.cjs, relevant slices of bin/install.js (flag parsing, help template, handleStatusline), agents-md-layer.test.cjs global suite, runtime-matrix.test.cjs. Skimmed: per-runtime install test suites, e2e-install describes, hook-registration scenario, ci.yml/release.yml. Not exercised: interactive installer prompt path (needs TTY; untested in suite — acknowledged limitation, not filed as a finding since it requires stdin simulation and the non-interactive default is the covered common path), tests/e2e Playwright VSCode suite (separate npm script, not part of test:all), pan-zcode subsystem tests beyond confirming pan-zcode-*.test.cjs exist and pass within the suite. Two findings (help omits --copilot; test:watch glob) are adjacent to the tests dimension but were confirmed incidentally with hard evidence and left in for the merged report.
1534
+
1535
+ ### Hooks
1536
+
1537
+ Read all 5 hook sources in full (hooks/pan-check-update.js, pan-context-monitor.js, pan-cost-logger.js, pan-statusline.js, pan-trace-logger.js) and diffed each against hooks/dist. Read installer hook wiring end-to-end: bin/install.js lines 2100-2160 (copy+templating), 2249-2439 (per-runtime registration), 1040-1300 (uninstall), and bin/install-lib.cjs getDirName/getConfigDirFromHome/buildHookCommand/buildCopilotHooksConfig/mergeCodexHooksConfig/buildPluginHooksConfig. Simulated the '.claude' templating with the real install-lib functions for copilot(global/local)/gemini/opencode. Ran hook unit tests: 63/63 pass. Skimmed only: cost.cjs/hygiene.cjs (for ledger rotation/cursor pruning evidence), release-check.js, build-plugin.js. Not verified externally: vendor claims about Gemini/Codex/Copilot hook event support (installer comments self-acknowledge SubagentStop may not fire on some hosts). Deliberately NOT reported as a finding: a possible double-count race when two SubagentStop events sharing one transcript fire concurrently (both read the cursor before either writes) — reachability of actual token double-counting depends on whether transcript entries carry per-subagent session_ids (which the in-loop session filter would then neutralize), which I could not confirm from the repo. Test-coverage gap noted but not filed: pan-context-monitor.js and pan-check-update.js have no unit tests (only registration-level scenario tests).
1538
+
1539
+ ### User & developer docs
1540
+
1541
+ Read fully: README.md, USER-GUIDE.md, ARCHITECTURE.md, DEVELOPMENT.md, EXAMPLES.md, FAQ.md, HOOKS.md, INTERNALS.md, MIGRATION-v2-to-v3.md, COMPARISON.md, SECURITY-HARDENING.md, context-monitor.md. CLI-REFERENCE.md (3521 lines): Command Index + module table read fully and diffed both directions against the pan-tools.cjs dispatcher (read in full); flag-bearing detail sections verified against modules, remaining prose skimmed. TROUBLESHOOTING.md: all CLI invocations verified + sections 640-800 read; remainder skimmed via targeted greps. AGENTS.md: inventory/structure verified, deep-dive prose spot-checked (9 agents' tool lists). SKILLS-REFERENCE.md and SKILLS-FULL-TEXT.md (auto-generated): verified fresh via full inventory diff (59/59 commands) plus content/line-count spot checks rather than line-by-line reading. No files were modified. Cross-dimension observation for the code reviewers (out of scope here): verify-drift.cjs (drift-check) still reads `.planning/codebase/CONVENTIONS.md` (uppercase) at lines 22/187 while workflows/map-codebase.md writes lowercase conventions.md — on case-sensitive filesystems drift-check silently loads zero project conventions; CLI-REFERENCE line 2869 documents the uppercase path so the doc matches the code but not the pipeline. Also noted: agents/pan-conductor.md says 'Claude + Opus 4.8 only' while all docs say 'Opus 4.7' — trivial naming drift, not reported as a finding.
1542
+
1543
+ ### Specs & ADRs
1544
+
1545
+ Coverage: read every ADR's title and full Status block (42/42) and skimmed bodies; deep-read ADR-0033..0042 plus ADR-0008/0012/0013/0020/0021/0027/0007 in full. Read headers of all 44 specs and identified status markers; deep-read the status-less ones (glm_zai_provider in full; comp_analysis, multi_model_routing + superplan, custom-skills-assessment, focus-auto-optimize pair, contributors_attribution, googlecli_adoption in relevant sections). Verification was read-only: grep/ls/sed plus git log for dating; implementation claims checked directly against pan-wizard-core/bin/lib/*.cjs, pan-tools.cjs dispatcher, pan-zcode/mcp/server.cjs, agents/, commands/pan/, and tests/. No files were modified and no installer was run. Not exhaustively verified: bodies of ADR-0004/0009/0011/0014 (grouped in the stale-Proposed cluster as 'likely shipped but not individually verified') and the prose of ~15 dated mid-2026-03 specs beyond their headers and file references.
1546
+