@sienklogic/plan-build-run 2.19.0 → 2.19.2

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 (951) hide show
  1. package/CHANGELOG.md +1287 -303
  2. package/CLAUDE.md +74 -39
  3. package/LICENSE +2 -1
  4. package/README.md +412 -177
  5. package/bin/install.js +2752 -0
  6. package/dashboard/bin/cli.cjs +96 -0
  7. package/dashboard/bin/stop.cjs +129 -0
  8. package/dashboard/eslint.config.js +37 -0
  9. package/dashboard/index.html +20 -0
  10. package/dashboard/package.json +27 -25
  11. package/dashboard/server/index.js +151 -0
  12. package/dashboard/server/lib/frontmatter.js +92 -0
  13. package/dashboard/server/middleware/static.js +35 -0
  14. package/dashboard/server/package.json +16 -0
  15. package/dashboard/server/routes/agents.js +234 -0
  16. package/dashboard/server/routes/config.js +64 -0
  17. package/dashboard/server/routes/health.js +98 -0
  18. package/dashboard/server/routes/incidents.js +78 -0
  19. package/dashboard/server/routes/intel.js +69 -0
  20. package/dashboard/server/routes/memory.js +107 -0
  21. package/dashboard/server/routes/planning.js +234 -0
  22. package/dashboard/server/routes/progress.js +77 -0
  23. package/dashboard/server/routes/projects.js +36 -0
  24. package/dashboard/server/routes/requirements.js +40 -0
  25. package/dashboard/server/routes/roadmap.js +69 -0
  26. package/dashboard/server/routes/sessions.js +70 -0
  27. package/dashboard/server/routes/status.js +25 -0
  28. package/dashboard/server/routes/telemetry.js +233 -0
  29. package/dashboard/server/services/file-watcher.js +105 -0
  30. package/dashboard/server/services/planning-reader.js +727 -0
  31. package/dashboard/server/test/cli.test.js +34 -0
  32. package/dashboard/server/test/frontmatter.test.js +104 -0
  33. package/dashboard/server/test/isolation.test.js +32 -0
  34. package/dashboard/server/test/planning-reader.test.js +151 -0
  35. package/dashboard/server/test/routes.test.js +91 -0
  36. package/dashboard/server/test/ws.test.js +81 -0
  37. package/dashboard/server/ws.js +96 -0
  38. package/dashboard/src/App.jsx +165 -0
  39. package/dashboard/src/components/charts/BudgetBars.jsx +42 -0
  40. package/dashboard/src/components/charts/ContextRadar.jsx +34 -0
  41. package/dashboard/src/components/charts/PhaseDonut.jsx +66 -0
  42. package/dashboard/src/components/charts/SuccessTrend.jsx +45 -0
  43. package/dashboard/src/components/charts/TokenChart.jsx +55 -0
  44. package/dashboard/src/components/charts/index.js +5 -0
  45. package/dashboard/src/components/config/CfgSection.jsx +93 -0
  46. package/dashboard/src/components/layout/Header.jsx +89 -0
  47. package/dashboard/src/components/layout/ProjectSwitcher.jsx +160 -0
  48. package/dashboard/src/components/layout/Sidebar.jsx +161 -0
  49. package/dashboard/src/components/ui/AutoModeBanner.jsx +138 -0
  50. package/dashboard/src/components/ui/BackButton.jsx +27 -0
  51. package/dashboard/src/components/ui/Badge.jsx +27 -0
  52. package/dashboard/src/components/ui/Card.jsx +23 -0
  53. package/dashboard/src/components/ui/ChartTooltip.jsx +48 -0
  54. package/dashboard/src/components/ui/CheckpointBox.jsx +110 -0
  55. package/dashboard/src/components/ui/CodeBlock.jsx +27 -0
  56. package/dashboard/src/components/ui/ConfidenceBadge.jsx +20 -0
  57. package/dashboard/src/components/ui/ConfirmModal.jsx +161 -0
  58. package/dashboard/src/components/ui/ConnectionBanner.jsx +60 -0
  59. package/dashboard/src/components/ui/ErrorBoundary.jsx +106 -0
  60. package/dashboard/src/components/ui/ErrorBox.jsx +107 -0
  61. package/dashboard/src/components/ui/KeyValue.jsx +33 -0
  62. package/dashboard/src/components/ui/LoadingSkeleton.jsx +84 -0
  63. package/dashboard/src/components/ui/MetricCard.jsx +58 -0
  64. package/dashboard/src/components/ui/NextUpBlock.jsx +92 -0
  65. package/dashboard/src/components/ui/NumberInput.jsx +44 -0
  66. package/dashboard/src/components/ui/PBRBanner.jsx +47 -0
  67. package/dashboard/src/components/ui/PipelineView.jsx +130 -0
  68. package/dashboard/src/components/ui/ProgressBar.jsx +28 -0
  69. package/dashboard/src/components/ui/ProgressDisplay.jsx +47 -0
  70. package/dashboard/src/components/ui/QualityGateBadge.jsx +15 -0
  71. package/dashboard/src/components/ui/SectionTitle.jsx +35 -0
  72. package/dashboard/src/components/ui/SelectInput.jsx +45 -0
  73. package/dashboard/src/components/ui/StatusDot.jsx +51 -0
  74. package/dashboard/src/components/ui/StatusSymbol.jsx +49 -0
  75. package/dashboard/src/components/ui/TabBar.jsx +41 -0
  76. package/dashboard/src/components/ui/TextInput.jsx +42 -0
  77. package/dashboard/src/components/ui/Toast.jsx +117 -0
  78. package/dashboard/src/components/ui/Toggle.jsx +70 -0
  79. package/dashboard/src/components/ui/index.js +29 -0
  80. package/dashboard/src/hooks/useDocumentTitle.js +16 -0
  81. package/dashboard/src/hooks/useFetch.js +50 -0
  82. package/dashboard/src/hooks/useToast.jsx +43 -0
  83. package/dashboard/src/hooks/useWebSocket.js +103 -0
  84. package/dashboard/src/lib/api.js +112 -0
  85. package/dashboard/src/lib/configSchema.js +189 -0
  86. package/dashboard/src/lib/constants.js +22 -0
  87. package/dashboard/src/main.jsx +15 -0
  88. package/dashboard/src/pages/AgentsPage.jsx +191 -0
  89. package/dashboard/src/pages/ConfigPage.jsx +298 -0
  90. package/dashboard/src/pages/HooksPage.jsx +412 -0
  91. package/dashboard/src/pages/IncidentsPage.jsx +135 -0
  92. package/dashboard/src/pages/IntelPage.jsx +193 -0
  93. package/dashboard/src/pages/LiveFeed.jsx +274 -0
  94. package/dashboard/src/pages/MemoryPage.jsx +107 -0
  95. package/dashboard/src/pages/OnboardingPage.jsx +117 -0
  96. package/dashboard/src/pages/Overview.jsx +360 -0
  97. package/dashboard/src/pages/PhaseDetailView.jsx +216 -0
  98. package/dashboard/src/pages/PlanningPage.jsx +181 -0
  99. package/dashboard/src/pages/ProgressPage.jsx +249 -0
  100. package/dashboard/src/pages/ResearchPage.jsx +129 -0
  101. package/dashboard/src/pages/RoadmapPage.jsx +251 -0
  102. package/dashboard/src/pages/SessionsPage.jsx +117 -0
  103. package/dashboard/src/pages/Telemetry.jsx +166 -0
  104. package/dashboard/src/pages/planning/DecisionsTab.jsx +153 -0
  105. package/dashboard/src/pages/planning/FilesTab.jsx +420 -0
  106. package/dashboard/src/pages/planning/MilestoneDetail.jsx +319 -0
  107. package/dashboard/src/pages/planning/MilestonesTab.jsx +151 -0
  108. package/dashboard/src/pages/planning/NotesTab.jsx +251 -0
  109. package/dashboard/src/pages/planning/PhasesTab.jsx +218 -0
  110. package/dashboard/src/pages/planning/QuickTab.jsx +50 -0
  111. package/dashboard/src/pages/planning/ResearchTab.jsx +103 -0
  112. package/dashboard/src/pages/planning/TodosTab.jsx +297 -0
  113. package/dashboard/src/theme/ThemeProvider.jsx +38 -0
  114. package/dashboard/src/theme/tokens.js +17 -0
  115. package/dashboard/tests/components/ConfirmModal.test.jsx +179 -0
  116. package/dashboard/tests/components/ConnectionBanner.test.jsx +37 -0
  117. package/dashboard/tests/components/ErrorBoundary.test.jsx +59 -0
  118. package/dashboard/tests/components/LoadingSkeleton.test.jsx +46 -0
  119. package/dashboard/tests/components/ToastContainer.test.jsx +47 -0
  120. package/dashboard/tests/components/Toggle.test.jsx +61 -0
  121. package/dashboard/tests/hooks/useFetch.test.jsx +77 -0
  122. package/dashboard/tests/hooks/useToast.test.jsx +78 -0
  123. package/dashboard/tests/hooks/useWebSocket.test.jsx +128 -0
  124. package/dashboard/tests/pages/ConfigPage.test.jsx +199 -0
  125. package/dashboard/tests/pages/PlanningPage.test.jsx +119 -0
  126. package/dashboard/tests/pages/planning/FilesTab.test.jsx +198 -0
  127. package/dashboard/tests/pages/planning/NotesTab.test.jsx +178 -0
  128. package/dashboard/tests/pages/planning/TodosTab.test.jsx +188 -0
  129. package/dashboard/tests/performance.test.jsx +46 -0
  130. package/dashboard/tests/routes/config.test.js +98 -0
  131. package/dashboard/tests/routes/health.test.js +40 -0
  132. package/dashboard/tests/routes/planning.test.js +112 -0
  133. package/dashboard/tests/routes/roadmap.test.js +91 -0
  134. package/dashboard/tests/routes/status.test.js +131 -0
  135. package/dashboard/tests/server/planning-reader.test.js +153 -0
  136. package/dashboard/tests/setup.js +7 -0
  137. package/dashboard/vite.config.js +41 -0
  138. package/package.json +55 -40
  139. package/plan-build-run/bin/config-schema.json +1298 -0
  140. package/plugins/pbr/.claude-plugin/plugin.json +1 -1
  141. package/plugins/pbr/CLAUDE.md +19 -0
  142. package/plugins/pbr/UI-CONSISTENCY-GAPS.md +1 -1
  143. package/plugins/pbr/agents/advisor-researcher.md +101 -0
  144. package/plugins/pbr/agents/audit.md +205 -89
  145. package/plugins/pbr/agents/codebase-mapper.md +158 -23
  146. package/plugins/pbr/agents/debugger.md +212 -34
  147. package/plugins/pbr/agents/dev-sync.md +206 -0
  148. package/plugins/pbr/agents/executor.md +717 -39
  149. package/plugins/pbr/agents/general.md +71 -6
  150. package/plugins/pbr/agents/integration-checker.md +146 -30
  151. package/plugins/pbr/agents/intel-updater.md +332 -0
  152. package/plugins/pbr/agents/nyquist-auditor.md +253 -0
  153. package/plugins/pbr/agents/plan-checker.md +265 -65
  154. package/plugins/pbr/agents/planner.md +440 -42
  155. package/plugins/pbr/agents/researcher.md +219 -36
  156. package/plugins/pbr/agents/roadmapper.md +397 -0
  157. package/plugins/pbr/agents/synthesizer.md +166 -26
  158. package/plugins/pbr/agents/ui-checker.md +203 -0
  159. package/plugins/pbr/agents/ui-researcher.md +224 -0
  160. package/plugins/pbr/agents/verifier.md +495 -47
  161. package/plugins/pbr/commands/add-phase.md +75 -0
  162. package/plugins/pbr/commands/add-todo.md +8 -0
  163. package/plugins/pbr/commands/audit-fix.md +5 -0
  164. package/plugins/pbr/commands/audit-milestone.md +8 -0
  165. package/plugins/pbr/commands/autonomous.md +5 -0
  166. package/plugins/pbr/commands/backlog.md +6 -0
  167. package/plugins/pbr/commands/check-todos.md +8 -0
  168. package/plugins/pbr/commands/complete-milestone.md +8 -0
  169. package/plugins/pbr/commands/config.md +1 -1
  170. package/plugins/pbr/commands/discuss-phase.md +6 -0
  171. package/plugins/pbr/commands/do.md +5 -0
  172. package/plugins/pbr/commands/execute-phase.md +6 -0
  173. package/plugins/pbr/commands/fast.md +6 -0
  174. package/plugins/pbr/commands/forensics.md +6 -0
  175. package/plugins/pbr/commands/import.md +1 -1
  176. package/plugins/pbr/commands/insert-phase.md +65 -0
  177. package/plugins/pbr/commands/intel.md +5 -0
  178. package/plugins/pbr/commands/join-discord.md +11 -0
  179. package/plugins/pbr/commands/list-phase-assumptions.md +5 -0
  180. package/plugins/pbr/commands/map-codebase.md +6 -0
  181. package/plugins/pbr/commands/milestone-summary.md +6 -0
  182. package/plugins/pbr/commands/new-milestone.md +8 -0
  183. package/plugins/pbr/commands/new-project.md +6 -0
  184. package/plugins/pbr/commands/pause-work.md +5 -0
  185. package/plugins/pbr/commands/plan-milestone-gaps.md +7 -0
  186. package/plugins/pbr/commands/plan-phase.md +6 -0
  187. package/plugins/pbr/commands/plant-seed.md +6 -0
  188. package/plugins/pbr/commands/profile-user.md +5 -0
  189. package/plugins/pbr/commands/profile.md +5 -0
  190. package/plugins/pbr/commands/progress.md +6 -0
  191. package/plugins/pbr/commands/quick.md +1 -1
  192. package/plugins/pbr/commands/reapply-patches.md +47 -0
  193. package/plugins/pbr/commands/release.md +6 -0
  194. package/plugins/pbr/commands/remove-phase.md +66 -0
  195. package/plugins/pbr/commands/research-phase.md +59 -0
  196. package/plugins/pbr/commands/resume-work.md +5 -0
  197. package/plugins/pbr/commands/seed.md +6 -0
  198. package/plugins/pbr/commands/session-report.md +5 -0
  199. package/plugins/pbr/commands/set-profile.md +6 -0
  200. package/plugins/pbr/commands/settings.md +5 -0
  201. package/plugins/pbr/commands/setup.md +1 -1
  202. package/plugins/pbr/commands/ship.md +5 -0
  203. package/plugins/pbr/commands/stats.md +6 -0
  204. package/plugins/pbr/commands/test.md +5 -0
  205. package/plugins/pbr/commands/thread.md +6 -0
  206. package/plugins/pbr/commands/todo.md +1 -1
  207. package/plugins/pbr/commands/ui-phase.md +5 -0
  208. package/plugins/pbr/commands/ui-review.md +5 -0
  209. package/plugins/pbr/commands/undo.md +5 -0
  210. package/plugins/pbr/commands/update.md +37 -0
  211. package/plugins/pbr/commands/validate-phase.md +5 -0
  212. package/plugins/pbr/commands/verify-work.md +6 -0
  213. package/plugins/pbr/dashboard/package-lock.json +6 -0
  214. package/plugins/pbr/dist/architecture-guard.js +59 -0
  215. package/plugins/pbr/dist/audit-dimensions.js +556 -0
  216. package/plugins/pbr/dist/auto-continue.js +277 -0
  217. package/plugins/pbr/dist/block-skill-self-read.js +124 -0
  218. package/plugins/pbr/dist/check-agent-state-write.js +63 -0
  219. package/plugins/pbr/dist/check-config-change.js +155 -0
  220. package/plugins/pbr/dist/check-cross-plugin-sync.js +93 -0
  221. package/plugins/pbr/dist/check-dangerous-commands.js +193 -0
  222. package/plugins/pbr/dist/check-direct-state-write.js +37 -0
  223. package/plugins/pbr/dist/check-doc-sprawl.js +102 -0
  224. package/plugins/pbr/dist/check-phase-boundary.js +191 -0
  225. package/plugins/pbr/dist/check-plan-format.js +209 -0
  226. package/plugins/pbr/dist/check-read-first.js +345 -0
  227. package/plugins/pbr/dist/check-roadmap-sync.js +507 -0
  228. package/plugins/pbr/dist/check-skill-workflow.js +354 -0
  229. package/plugins/pbr/dist/check-state-sync.js +658 -0
  230. package/plugins/pbr/dist/check-subagent-output.js +396 -0
  231. package/plugins/pbr/dist/check-summary-gate.js +188 -0
  232. package/plugins/pbr/dist/context-bridge.js +425 -0
  233. package/plugins/pbr/dist/context-budget-check.js +442 -0
  234. package/plugins/pbr/dist/context-quality.js +271 -0
  235. package/plugins/pbr/dist/enforce-context-budget.js +138 -0
  236. package/plugins/pbr/dist/enforce-pbr-workflow.js +277 -0
  237. package/plugins/pbr/dist/event-handler.js +202 -0
  238. package/plugins/pbr/dist/event-logger.js +125 -0
  239. package/plugins/pbr/dist/feedback-loop.js +155 -0
  240. package/plugins/pbr/dist/graph-update.js +422 -0
  241. package/plugins/pbr/dist/hook-logger.js +114 -0
  242. package/plugins/pbr/dist/hook-server-client.js +361 -0
  243. package/plugins/pbr/dist/hook-server.js +658 -0
  244. package/plugins/pbr/dist/hooks-schema.json +87 -0
  245. package/plugins/pbr/dist/instructions-loaded.js +173 -0
  246. package/plugins/pbr/dist/intercept-plan-mode.js +81 -0
  247. package/plugins/pbr/dist/log-notification.js +131 -0
  248. package/plugins/pbr/dist/log-subagent.js +349 -0
  249. package/plugins/pbr/dist/log-tool-failure.js +140 -0
  250. package/plugins/pbr/dist/milestone-learnings.js +519 -0
  251. package/plugins/pbr/dist/pbr-tools.js +1961 -0
  252. package/plugins/pbr/dist/post-bash-triage.js +96 -0
  253. package/plugins/pbr/dist/post-compact.js +135 -0
  254. package/plugins/pbr/dist/post-hoc.js +237 -0
  255. package/plugins/pbr/dist/post-write-dispatch.js +243 -0
  256. package/plugins/pbr/dist/post-write-quality.js +208 -0
  257. package/plugins/pbr/dist/pre-bash-dispatch.js +212 -0
  258. package/plugins/pbr/dist/pre-skill-dispatch.js +114 -0
  259. package/plugins/pbr/dist/pre-task-dispatch.js +269 -0
  260. package/plugins/pbr/dist/pre-write-dispatch.js +234 -0
  261. package/plugins/pbr/dist/progress-tracker.js +173 -0
  262. package/plugins/pbr/dist/prompt-guard.js +114 -0
  263. package/plugins/pbr/dist/prompt-routing.js +209 -0
  264. package/plugins/pbr/dist/quick-status.js +179 -0
  265. package/plugins/pbr/dist/record-incident.js +37 -0
  266. package/plugins/pbr/dist/run-hook.js +144 -0
  267. package/plugins/pbr/dist/session-cleanup.js +653 -0
  268. package/plugins/pbr/dist/session-tracker.js +124 -0
  269. package/plugins/pbr/dist/status-line.js +849 -0
  270. package/plugins/pbr/dist/suggest-compact.js +307 -0
  271. package/plugins/pbr/dist/sync-context-to-claude.js +100 -0
  272. package/plugins/pbr/dist/task-completed.js +206 -0
  273. package/plugins/pbr/dist/track-context-budget.js +432 -0
  274. package/plugins/pbr/dist/track-user-gates.js +88 -0
  275. package/plugins/pbr/dist/trust-tracker.js +193 -0
  276. package/plugins/pbr/dist/validate-commit.js +215 -0
  277. package/plugins/pbr/dist/validate-skill-args.js +222 -0
  278. package/plugins/pbr/dist/validate-task.js +271 -0
  279. package/plugins/pbr/dist/worktree-create.js +144 -0
  280. package/plugins/pbr/dist/worktree-remove.js +147 -0
  281. package/plugins/pbr/hooks/hooks.json +143 -60
  282. package/plugins/pbr/references/agent-contracts.md +39 -8
  283. package/plugins/pbr/references/agent-teams.md +3 -3
  284. package/plugins/pbr/references/archive/checkpoints.md +189 -0
  285. package/plugins/pbr/references/archive/context-quality-tiers.md +45 -0
  286. package/plugins/pbr/references/archive/hook-ordering.md +89 -0
  287. package/plugins/pbr/references/archive/limitations.md +106 -0
  288. package/plugins/pbr/references/archive/pbr-rules.md +194 -0
  289. package/plugins/pbr/references/archive/pbr-tools-cli.md +415 -0
  290. package/plugins/pbr/references/archive/pretooluse-jsonl-behavior.md +58 -0
  291. package/plugins/pbr/references/archive/signal-files.md +41 -0
  292. package/plugins/pbr/references/archive/tmux-setup.md +288 -0
  293. package/plugins/pbr/references/archive/verification-matrix.md +34 -0
  294. package/plugins/pbr/references/archive/verification-patterns.md +277 -0
  295. package/plugins/pbr/references/archive/worktree-sparse-checkout.md +86 -0
  296. package/plugins/pbr/references/checkpoints.md +723 -104
  297. package/plugins/pbr/references/config-reference.md +376 -10
  298. package/plugins/pbr/references/continuation-format.md +1 -0
  299. package/plugins/pbr/references/decimal-phase-calculation.md +65 -0
  300. package/plugins/pbr/references/deviation-rules.md +12 -0
  301. package/plugins/pbr/references/git-integration.md +110 -27
  302. package/plugins/pbr/references/git-planning-commit.md +35 -0
  303. package/plugins/pbr/references/model-profile-resolution.md +34 -0
  304. package/plugins/pbr/references/model-profiles.md +90 -7
  305. package/plugins/pbr/references/model-selection.md +1 -1
  306. package/plugins/pbr/references/node-repair.md +48 -0
  307. package/plugins/pbr/references/plan-authoring.md +65 -0
  308. package/plugins/pbr/references/plan-format.md +161 -10
  309. package/plugins/pbr/references/questioning.md +138 -49
  310. package/plugins/pbr/references/reading-verification.md +4 -4
  311. package/plugins/pbr/references/tdd.md +263 -0
  312. package/plugins/pbr/references/ui-brand.md +449 -0
  313. package/plugins/pbr/references/verification-overrides.md +39 -0
  314. package/plugins/pbr/references/verification-patterns.md +529 -113
  315. package/plugins/pbr/scripts/architecture-guard.js +59 -0
  316. package/plugins/pbr/scripts/audit-checks/behavioral-compliance.js +2098 -0
  317. package/plugins/pbr/scripts/audit-checks/error-analysis.js +989 -0
  318. package/plugins/pbr/scripts/audit-checks/feature-verification.js +723 -0
  319. package/plugins/pbr/scripts/audit-checks/index.js +433 -0
  320. package/plugins/pbr/scripts/audit-checks/infrastructure.js +816 -0
  321. package/plugins/pbr/scripts/audit-checks/quality-metrics.js +452 -0
  322. package/plugins/pbr/scripts/audit-checks/session-quality.js +980 -0
  323. package/plugins/pbr/scripts/audit-checks/si-agent-hook-config-checks.js +396 -0
  324. package/plugins/pbr/scripts/audit-checks/si-cross-cutting-checks.js +272 -0
  325. package/plugins/pbr/scripts/audit-checks/si-skill-checks.js +424 -0
  326. package/plugins/pbr/scripts/audit-checks/workflow-compliance.js +1175 -0
  327. package/plugins/pbr/scripts/audit-dimensions.js +556 -0
  328. package/plugins/pbr/scripts/auto-continue.js +192 -31
  329. package/plugins/pbr/scripts/block-skill-self-read.js +124 -0
  330. package/plugins/pbr/scripts/check-agent-state-write.js +63 -0
  331. package/plugins/pbr/scripts/check-config-change.js +155 -0
  332. package/plugins/pbr/scripts/check-cross-plugin-sync.js +93 -0
  333. package/plugins/pbr/scripts/check-dangerous-commands.js +18 -5
  334. package/plugins/pbr/scripts/check-direct-state-write.js +37 -0
  335. package/plugins/pbr/scripts/check-phase-boundary.js +3 -8
  336. package/plugins/pbr/scripts/check-plan-format.js +135 -278
  337. package/plugins/pbr/scripts/check-read-first.js +345 -0
  338. package/plugins/pbr/scripts/check-roadmap-sync.js +182 -21
  339. package/plugins/pbr/scripts/check-skill-workflow.js +24 -27
  340. package/plugins/pbr/scripts/check-state-sync.js +339 -215
  341. package/plugins/pbr/scripts/check-subagent-output.js +281 -275
  342. package/plugins/pbr/scripts/check-summary-gate.js +5 -15
  343. package/plugins/pbr/scripts/config-schema.json +1134 -95
  344. package/plugins/pbr/scripts/context-bridge.js +425 -0
  345. package/plugins/pbr/scripts/context-budget-check.js +169 -14
  346. package/plugins/pbr/scripts/context-quality.js +271 -0
  347. package/plugins/pbr/scripts/enforce-context-budget.js +138 -0
  348. package/plugins/pbr/scripts/enforce-pbr-workflow.js +277 -0
  349. package/plugins/pbr/scripts/event-handler.js +127 -87
  350. package/plugins/pbr/scripts/event-logger.js +58 -25
  351. package/plugins/pbr/scripts/feedback-loop.js +155 -0
  352. package/plugins/pbr/scripts/graph-update.js +422 -0
  353. package/plugins/pbr/scripts/hook-logger.js +69 -35
  354. package/plugins/pbr/scripts/hook-server-client.js +361 -0
  355. package/plugins/pbr/scripts/hook-server.js +658 -0
  356. package/plugins/pbr/scripts/hooks-schema.json +13 -5
  357. package/plugins/pbr/scripts/instructions-loaded.js +173 -0
  358. package/plugins/pbr/scripts/intent-router.cjs +147 -0
  359. package/plugins/pbr/scripts/intercept-plan-mode.js +52 -18
  360. package/plugins/pbr/scripts/lib/alternatives.js +203 -0
  361. package/plugins/pbr/scripts/lib/audit.js +65 -0
  362. package/plugins/pbr/scripts/lib/auto-cleanup.js +221 -0
  363. package/plugins/pbr/scripts/lib/auto-verify.js +103 -0
  364. package/plugins/pbr/scripts/lib/build.js +719 -0
  365. package/plugins/pbr/scripts/lib/ci-fix-loop.js +228 -0
  366. package/plugins/pbr/scripts/lib/commands.js +483 -0
  367. package/plugins/pbr/scripts/lib/compound.js +216 -0
  368. package/plugins/pbr/scripts/lib/config.js +1308 -0
  369. package/plugins/pbr/scripts/lib/context.js +254 -0
  370. package/plugins/pbr/scripts/lib/contextual-help.js +183 -0
  371. package/plugins/pbr/scripts/lib/convention-detector.js +413 -0
  372. package/plugins/pbr/scripts/lib/core.js +1569 -0
  373. package/plugins/pbr/scripts/lib/dashboard-launch.js +364 -0
  374. package/plugins/pbr/scripts/lib/data-hygiene.js +179 -0
  375. package/plugins/pbr/scripts/lib/decision-extraction.js +183 -0
  376. package/plugins/pbr/scripts/lib/decisions.js +194 -0
  377. package/plugins/pbr/scripts/lib/dependency-break.js +147 -0
  378. package/plugins/pbr/scripts/lib/format-validators.js +1025 -0
  379. package/plugins/pbr/scripts/lib/frontmatter.js +302 -0
  380. package/plugins/pbr/scripts/lib/gates/advisories.js +129 -0
  381. package/plugins/pbr/scripts/lib/gates/build-dependency.js +115 -0
  382. package/plugins/pbr/scripts/lib/gates/build-executor.js +104 -0
  383. package/plugins/pbr/scripts/lib/gates/doc-existence.js +46 -0
  384. package/plugins/pbr/scripts/lib/gates/helpers.js +93 -0
  385. package/plugins/pbr/scripts/lib/gates/inline-execution.js +185 -0
  386. package/plugins/pbr/scripts/lib/gates/milestone-complete.js +136 -0
  387. package/plugins/pbr/scripts/lib/gates/milestone-summary.js +119 -0
  388. package/plugins/pbr/scripts/lib/gates/multi-phase-loader.js +147 -0
  389. package/plugins/pbr/scripts/lib/gates/plan-executor.js +36 -0
  390. package/plugins/pbr/scripts/lib/gates/plan-validation.js +114 -0
  391. package/plugins/pbr/scripts/lib/gates/quick-executor.js +76 -0
  392. package/plugins/pbr/scripts/lib/gates/review-planner.js +61 -0
  393. package/plugins/pbr/scripts/lib/gates/review-verifier.js +69 -0
  394. package/plugins/pbr/scripts/lib/gates/rich-agent-context.js +137 -0
  395. package/plugins/pbr/scripts/lib/gates/user-confirmation.js +93 -0
  396. package/plugins/pbr/scripts/lib/graph-cli.js +89 -0
  397. package/plugins/pbr/scripts/lib/graph.js +553 -0
  398. package/plugins/pbr/scripts/lib/health-checks.js +107 -0
  399. package/plugins/pbr/scripts/lib/health-phase06.js +120 -0
  400. package/plugins/pbr/scripts/lib/health.js +132 -0
  401. package/plugins/pbr/scripts/lib/help.js +100 -0
  402. package/plugins/pbr/scripts/lib/history.js +150 -0
  403. package/plugins/pbr/scripts/lib/impact-analysis.js +319 -0
  404. package/plugins/pbr/scripts/lib/incidents.js +190 -0
  405. package/plugins/pbr/scripts/lib/init.js +643 -0
  406. package/plugins/pbr/scripts/lib/insights-parser.js +320 -0
  407. package/plugins/pbr/scripts/lib/intel.js +653 -0
  408. package/plugins/pbr/scripts/lib/learnings.js +511 -0
  409. package/plugins/pbr/scripts/lib/migrate.js +298 -0
  410. package/plugins/pbr/scripts/lib/milestone.js +306 -0
  411. package/plugins/pbr/scripts/lib/negative-knowledge.js +194 -0
  412. package/plugins/pbr/scripts/lib/notification-throttle.js +141 -0
  413. package/plugins/pbr/scripts/lib/onboarding-generator.js +288 -0
  414. package/plugins/pbr/scripts/lib/parse-args.js +134 -0
  415. package/plugins/pbr/scripts/lib/pattern-routing.js +55 -0
  416. package/plugins/pbr/scripts/lib/patterns.js +272 -0
  417. package/plugins/pbr/scripts/lib/perf.js +190 -0
  418. package/plugins/pbr/scripts/lib/phase.js +1025 -0
  419. package/plugins/pbr/scripts/lib/pid-lock.js +154 -0
  420. package/plugins/pbr/scripts/lib/post-hoc.js +160 -0
  421. package/plugins/pbr/scripts/lib/pre-commit-checks.js +220 -0
  422. package/plugins/pbr/scripts/lib/pre-research.js +126 -0
  423. package/plugins/pbr/scripts/lib/preview.js +174 -0
  424. package/plugins/pbr/scripts/lib/progress-visualization.js +296 -0
  425. package/plugins/pbr/scripts/lib/quick-init.js +131 -0
  426. package/plugins/pbr/scripts/lib/reference.js +236 -0
  427. package/plugins/pbr/scripts/lib/requirements.js +153 -0
  428. package/plugins/pbr/scripts/lib/resolve-root.js +66 -0
  429. package/plugins/pbr/scripts/lib/reverse-spec.js +259 -0
  430. package/plugins/pbr/scripts/lib/roadmap.js +1089 -0
  431. package/plugins/pbr/scripts/lib/security-scan.js +200 -0
  432. package/plugins/pbr/scripts/lib/session-briefing.js +895 -0
  433. package/plugins/pbr/scripts/lib/skill-section.js +99 -0
  434. package/plugins/pbr/scripts/lib/smart-next-task.js +198 -0
  435. package/plugins/pbr/scripts/lib/snapshot-manager.js +232 -0
  436. package/plugins/pbr/scripts/lib/spec-diff.js +209 -0
  437. package/plugins/pbr/scripts/lib/spec-engine.js +189 -0
  438. package/plugins/pbr/scripts/lib/spot-check.js +539 -0
  439. package/plugins/pbr/scripts/lib/state-queue.js +171 -0
  440. package/plugins/pbr/scripts/lib/state.js +1082 -0
  441. package/plugins/pbr/scripts/lib/status-render.js +511 -0
  442. package/plugins/pbr/scripts/lib/step-verify.js +149 -0
  443. package/plugins/pbr/scripts/lib/subagent-validators.js +1059 -0
  444. package/plugins/pbr/scripts/lib/suggest-next.js +435 -0
  445. package/plugins/pbr/scripts/lib/tech-debt-scanner.js +116 -0
  446. package/plugins/pbr/scripts/lib/templates.js +362 -0
  447. package/plugins/pbr/scripts/lib/test-selection.js +163 -0
  448. package/plugins/pbr/scripts/lib/todo.js +300 -0
  449. package/plugins/pbr/scripts/lib/verify.js +1483 -0
  450. package/plugins/pbr/scripts/log-notification.js +131 -0
  451. package/plugins/pbr/scripts/log-subagent.js +203 -18
  452. package/plugins/pbr/scripts/log-tool-failure.js +60 -8
  453. package/plugins/pbr/scripts/milestone-learnings.js +519 -0
  454. package/plugins/pbr/scripts/package.json +1 -1
  455. package/plugins/pbr/scripts/pbr-tools.js +1754 -1171
  456. package/plugins/pbr/scripts/post-bash-triage.js +96 -0
  457. package/plugins/pbr/scripts/post-compact.js +135 -0
  458. package/plugins/pbr/scripts/post-hoc.js +237 -0
  459. package/plugins/pbr/scripts/post-write-dispatch.js +201 -31
  460. package/plugins/pbr/scripts/post-write-quality.js +4 -3
  461. package/plugins/pbr/scripts/pre-bash-dispatch.js +147 -51
  462. package/plugins/pbr/scripts/pre-skill-dispatch.js +114 -0
  463. package/plugins/pbr/scripts/pre-task-dispatch.js +269 -0
  464. package/plugins/pbr/scripts/pre-write-dispatch.js +170 -73
  465. package/plugins/pbr/scripts/progress-tracker.js +122 -310
  466. package/plugins/pbr/scripts/prompt-guard.js +114 -0
  467. package/plugins/pbr/scripts/prompt-routing.js +209 -0
  468. package/plugins/pbr/scripts/quick-status.js +179 -0
  469. package/plugins/pbr/scripts/record-incident.js +37 -0
  470. package/plugins/pbr/scripts/risk-classifier.cjs +123 -0
  471. package/plugins/pbr/scripts/run-hook.js +62 -10
  472. package/plugins/pbr/scripts/session-cleanup.js +428 -29
  473. package/plugins/pbr/scripts/session-tracker.js +124 -0
  474. package/plugins/pbr/scripts/status-line.js +593 -32
  475. package/plugins/pbr/scripts/suggest-compact.js +201 -13
  476. package/plugins/pbr/scripts/sync-context-to-claude.js +100 -0
  477. package/plugins/pbr/scripts/task-completed.js +165 -4
  478. package/plugins/pbr/scripts/test/config.test.js +126 -0
  479. package/plugins/pbr/scripts/test/cross-platform.test.js +131 -0
  480. package/plugins/pbr/scripts/test/fixtures/config.json +20 -0
  481. package/plugins/pbr/scripts/test/fixtures/plan.md +54 -0
  482. package/plugins/pbr/scripts/test/fixtures/project.md +30 -0
  483. package/plugins/pbr/scripts/test/fixtures/roadmap.md +55 -0
  484. package/plugins/pbr/scripts/test/fixtures/state.md +60 -0
  485. package/plugins/pbr/scripts/test/fixtures/summary.md +35 -0
  486. package/plugins/pbr/scripts/test/fixtures.test.js +184 -0
  487. package/plugins/pbr/scripts/test/phase.test.js +142 -0
  488. package/plugins/pbr/scripts/test/roadmap.test.js +96 -0
  489. package/plugins/pbr/scripts/test/state.test.js +155 -0
  490. package/plugins/pbr/scripts/track-context-budget.js +368 -99
  491. package/plugins/pbr/scripts/track-user-gates.js +88 -0
  492. package/plugins/pbr/scripts/trust-tracker.js +193 -0
  493. package/plugins/pbr/scripts/validate-commit.js +41 -26
  494. package/plugins/pbr/scripts/validate-skill-args.js +87 -15
  495. package/plugins/pbr/scripts/validate-task.js +83 -627
  496. package/plugins/pbr/scripts/worktree-create.js +144 -0
  497. package/plugins/pbr/scripts/worktree-remove.js +147 -0
  498. package/plugins/pbr/skills/audit/SKILL.md +195 -24
  499. package/plugins/pbr/skills/audit-fix/SKILL.md +326 -0
  500. package/plugins/pbr/skills/autonomous/SKILL.md +545 -0
  501. package/plugins/pbr/skills/backlog/SKILL.md +56 -0
  502. package/plugins/pbr/skills/begin/SKILL.md +508 -153
  503. package/plugins/pbr/skills/begin/templates/STATE.md.tmpl +1 -2
  504. package/plugins/pbr/skills/begin/templates/config.json.tmpl +411 -36
  505. package/plugins/pbr/skills/begin/templates/researcher-prompt.md.tmpl +28 -0
  506. package/plugins/pbr/skills/begin/templates/roadmap-prompt.md.tmpl +28 -3
  507. package/plugins/pbr/skills/begin/templates/synthesis-prompt.md.tmpl +33 -5
  508. package/plugins/pbr/skills/build/SKILL.md +1040 -354
  509. package/plugins/pbr/skills/build/templates/continuation-prompt.md.tmpl +26 -0
  510. package/plugins/pbr/skills/build/templates/executor-prompt.md.tmpl +77 -0
  511. package/plugins/pbr/skills/build/templates/inline-verifier-prompt.md.tmpl +33 -0
  512. package/plugins/pbr/skills/config/SKILL.md +112 -9
  513. package/plugins/pbr/skills/continue/SKILL.md +113 -33
  514. package/plugins/pbr/skills/dashboard/SKILL.md +21 -9
  515. package/plugins/pbr/skills/debug/SKILL.md +70 -12
  516. package/plugins/pbr/skills/debug/templates/continuation-prompt.md.tmpl +12 -1
  517. package/plugins/pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +12 -5
  518. package/plugins/pbr/skills/discuss/SKILL.md +206 -25
  519. package/plugins/pbr/skills/discuss/templates/CONTEXT.md.tmpl +21 -1
  520. package/plugins/pbr/skills/do/SKILL.md +119 -24
  521. package/plugins/pbr/skills/explore/SKILL.md +95 -20
  522. package/plugins/pbr/skills/fast/SKILL.md +94 -0
  523. package/plugins/pbr/skills/forensics/SKILL.md +144 -0
  524. package/plugins/pbr/skills/health/SKILL.md +35 -117
  525. package/plugins/pbr/skills/help/SKILL.md +84 -101
  526. package/plugins/pbr/skills/import/SKILL.md +332 -13
  527. package/plugins/pbr/skills/intel/SKILL.md +131 -0
  528. package/plugins/pbr/skills/list-phase-assumptions/SKILL.md +231 -0
  529. package/plugins/pbr/skills/milestone/SKILL.md +421 -263
  530. package/plugins/pbr/skills/milestone/templates/audit-output.md.tmpl +76 -0
  531. package/plugins/pbr/skills/milestone/templates/complete-output.md.tmpl +32 -0
  532. package/plugins/pbr/skills/milestone/templates/edge-cases.md +54 -0
  533. package/plugins/pbr/skills/milestone/templates/gaps-output.md.tmpl +25 -0
  534. package/plugins/pbr/skills/milestone/templates/integration-checker-prompt.md.tmpl +25 -0
  535. package/plugins/pbr/skills/milestone/templates/new-output.md.tmpl +29 -0
  536. package/plugins/pbr/skills/milestone-summary/SKILL.md +86 -0
  537. package/plugins/pbr/skills/note/SKILL.md +20 -4
  538. package/plugins/pbr/skills/pause/SKILL.md +54 -14
  539. package/plugins/pbr/skills/pause/templates/continue-here.md.tmpl +33 -52
  540. package/plugins/pbr/skills/plan/SKILL.md +526 -280
  541. package/plugins/pbr/skills/plan/templates/checker-prompt.md.tmpl +5 -2
  542. package/plugins/pbr/skills/plan/templates/completion-output.md.tmpl +27 -0
  543. package/plugins/pbr/skills/plan/templates/planner-prompt.md.tmpl +27 -1
  544. package/plugins/pbr/skills/plan/templates/revision-prompt.md.tmpl +21 -5
  545. package/plugins/pbr/skills/profile/SKILL.md +185 -0
  546. package/plugins/pbr/skills/profile-user/SKILL.md +227 -0
  547. package/plugins/pbr/skills/quick/SKILL.md +435 -100
  548. package/plugins/pbr/skills/release/SKILL.md +206 -0
  549. package/plugins/pbr/skills/resume/SKILL.md +170 -46
  550. package/plugins/pbr/skills/review/SKILL.md +217 -164
  551. package/plugins/pbr/skills/review/templates/verifier-prompt.md.tmpl +7 -0
  552. package/plugins/pbr/skills/scan/SKILL.md +152 -106
  553. package/plugins/pbr/skills/scan/templates/mapper-prompt.md.tmpl +5 -56
  554. package/plugins/pbr/skills/seed/SKILL.md +87 -0
  555. package/plugins/pbr/skills/session-report/SKILL.md +130 -0
  556. package/plugins/pbr/skills/setup/SKILL.md +150 -202
  557. package/plugins/pbr/skills/shared/agent-context-enrichment.md +21 -0
  558. package/plugins/pbr/skills/shared/agent-type-resolution.md +32 -0
  559. package/plugins/pbr/skills/shared/commit-planning-docs.md +8 -0
  560. package/plugins/pbr/skills/shared/context-budget.md +66 -1
  561. package/plugins/pbr/skills/shared/context-loader-task.md +18 -11
  562. package/plugins/pbr/skills/shared/digest-select.md +2 -2
  563. package/plugins/pbr/skills/shared/domain-probes.md +1 -1
  564. package/plugins/pbr/skills/shared/error-reporting.md +38 -60
  565. package/plugins/pbr/skills/shared/gate-prompts.md +4 -2
  566. package/plugins/pbr/skills/shared/memory-capture.md +48 -0
  567. package/plugins/pbr/skills/shared/phase-argument-parsing.md +4 -4
  568. package/plugins/pbr/skills/shared/revision-loop.md +24 -6
  569. package/plugins/pbr/skills/shared/state-update.md +49 -56
  570. package/plugins/pbr/skills/shared/universal-anti-patterns.md +27 -4
  571. package/plugins/pbr/skills/ship/SKILL.md +155 -0
  572. package/plugins/pbr/skills/stats/SKILL.md +80 -0
  573. package/plugins/pbr/skills/status/SKILL.md +185 -119
  574. package/plugins/pbr/skills/test/SKILL.md +254 -0
  575. package/plugins/pbr/skills/thread/SKILL.md +73 -0
  576. package/plugins/pbr/skills/todo/SKILL.md +28 -72
  577. package/plugins/pbr/skills/ui-phase/SKILL.md +180 -0
  578. package/plugins/pbr/skills/ui-review/SKILL.md +206 -0
  579. package/plugins/pbr/skills/undo/SKILL.md +221 -0
  580. package/plugins/pbr/skills/validate-phase/SKILL.md +362 -0
  581. package/plugins/pbr/templates/CONTEXT.md.tmpl +45 -20
  582. package/plugins/pbr/templates/DISCOVERY.md.tmpl +29 -0
  583. package/plugins/pbr/templates/DISCUSSION-LOG.md.tmpl +49 -0
  584. package/plugins/pbr/templates/HANDOFF.json.tmpl +30 -0
  585. package/plugins/pbr/templates/INTEGRATION-REPORT.md.tmpl +18 -2
  586. package/plugins/pbr/templates/MILESTONE-AUDIT.md.tmpl +44 -0
  587. package/plugins/pbr/templates/PROJECT.md.tmpl +126 -0
  588. package/plugins/pbr/templates/REQUIREMENTS.md.tmpl +96 -0
  589. package/plugins/pbr/templates/RETROSPECTIVE.md.tmpl +43 -0
  590. package/plugins/pbr/templates/ROADMAP.md.tmpl +108 -14
  591. package/plugins/pbr/templates/SUMMARY-complex.md.tmpl +133 -0
  592. package/plugins/pbr/templates/SUMMARY-minimal.md.tmpl +55 -0
  593. package/plugins/pbr/templates/SUMMARY.md.tmpl +21 -0
  594. package/plugins/pbr/templates/UAT.md.tmpl +94 -0
  595. package/plugins/pbr/templates/UI-SPEC.md.tmpl +144 -0
  596. package/plugins/pbr/templates/VALIDATION.md.tmpl +94 -0
  597. package/plugins/pbr/templates/VERIFICATION-DETAIL.md.tmpl +49 -13
  598. package/plugins/pbr/templates/project-CONTEXT.md.tmpl +59 -0
  599. package/plugins/pbr/templates/research-outputs/ARCHITECTURE.md.tmpl +91 -0
  600. package/plugins/pbr/templates/research-outputs/FEATURES.md.tmpl +64 -0
  601. package/plugins/pbr/templates/research-outputs/PITFALLS.md.tmpl +50 -0
  602. package/plugins/pbr/templates/research-outputs/STACK.md.tmpl +63 -0
  603. package/plugins/pbr/templates/research-outputs/SUMMARY.md.tmpl +98 -0
  604. package/scripts/build-hooks.js +61 -0
  605. package/scripts/check-ci.js +100 -0
  606. package/scripts/clean-changelog.js +364 -0
  607. package/scripts/generate-derivatives.js +581 -0
  608. package/scripts/posttest.js +93 -0
  609. package/scripts/release.js +262 -0
  610. package/scripts/run-tests.cjs +29 -0
  611. package/scripts/test-wrapper.js +43 -0
  612. package/dashboard/bin/cli.js +0 -25
  613. package/dashboard/public/css/layout.css +0 -704
  614. package/dashboard/public/css/status-colors.css +0 -98
  615. package/dashboard/public/css/tokens.css +0 -59
  616. package/dashboard/public/js/htmx-title.js +0 -5
  617. package/dashboard/public/js/sidebar-toggle.js +0 -34
  618. package/dashboard/public/js/sse-client.js +0 -100
  619. package/dashboard/public/js/theme-toggle.js +0 -46
  620. package/dashboard/src/app.js +0 -91
  621. package/dashboard/src/middleware/current-phase.js +0 -24
  622. package/dashboard/src/middleware/errorHandler.js +0 -52
  623. package/dashboard/src/middleware/notFoundHandler.js +0 -9
  624. package/dashboard/src/repositories/planning.repository.js +0 -130
  625. package/dashboard/src/routes/events.routes.js +0 -45
  626. package/dashboard/src/routes/index.routes.js +0 -35
  627. package/dashboard/src/routes/pages.routes.js +0 -426
  628. package/dashboard/src/server.js +0 -42
  629. package/dashboard/src/services/analytics.service.js +0 -141
  630. package/dashboard/src/services/dashboard.service.js +0 -309
  631. package/dashboard/src/services/milestone.service.js +0 -222
  632. package/dashboard/src/services/notes.service.js +0 -50
  633. package/dashboard/src/services/phase.service.js +0 -232
  634. package/dashboard/src/services/project.service.js +0 -57
  635. package/dashboard/src/services/roadmap.service.js +0 -258
  636. package/dashboard/src/services/sse.service.js +0 -58
  637. package/dashboard/src/services/todo.service.js +0 -272
  638. package/dashboard/src/services/watcher.service.js +0 -48
  639. package/dashboard/src/utils/cache.js +0 -55
  640. package/dashboard/src/views/analytics.ejs +0 -5
  641. package/dashboard/src/views/coming-soon.ejs +0 -11
  642. package/dashboard/src/views/dependencies.ejs +0 -5
  643. package/dashboard/src/views/error.ejs +0 -20
  644. package/dashboard/src/views/index.ejs +0 -5
  645. package/dashboard/src/views/milestone-detail.ejs +0 -5
  646. package/dashboard/src/views/milestones.ejs +0 -5
  647. package/dashboard/src/views/notes.ejs +0 -5
  648. package/dashboard/src/views/partials/analytics-content.ejs +0 -90
  649. package/dashboard/src/views/partials/breadcrumbs.ejs +0 -14
  650. package/dashboard/src/views/partials/dashboard-content.ejs +0 -84
  651. package/dashboard/src/views/partials/dependencies-content.ejs +0 -48
  652. package/dashboard/src/views/partials/empty-state.ejs +0 -7
  653. package/dashboard/src/views/partials/footer.ejs +0 -3
  654. package/dashboard/src/views/partials/head.ejs +0 -30
  655. package/dashboard/src/views/partials/header.ejs +0 -21
  656. package/dashboard/src/views/partials/layout-bottom.ejs +0 -43
  657. package/dashboard/src/views/partials/layout-top.ejs +0 -16
  658. package/dashboard/src/views/partials/milestone-detail-content.ejs +0 -20
  659. package/dashboard/src/views/partials/milestones-content.ejs +0 -88
  660. package/dashboard/src/views/partials/notes-content.ejs +0 -23
  661. package/dashboard/src/views/partials/phase-content.ejs +0 -193
  662. package/dashboard/src/views/partials/phase-doc-content.ejs +0 -38
  663. package/dashboard/src/views/partials/phases-content.ejs +0 -124
  664. package/dashboard/src/views/partials/roadmap-content.ejs +0 -180
  665. package/dashboard/src/views/partials/sidebar.ejs +0 -99
  666. package/dashboard/src/views/partials/todo-create-content.ejs +0 -54
  667. package/dashboard/src/views/partials/todo-detail-content.ejs +0 -42
  668. package/dashboard/src/views/partials/todos-content.ejs +0 -97
  669. package/dashboard/src/views/phase-detail.ejs +0 -5
  670. package/dashboard/src/views/phase-doc.ejs +0 -5
  671. package/dashboard/src/views/phases.ejs +0 -5
  672. package/dashboard/src/views/roadmap.ejs +0 -5
  673. package/dashboard/src/views/todo-create.ejs +0 -5
  674. package/dashboard/src/views/todo-detail.ejs +0 -5
  675. package/dashboard/src/views/todos.ejs +0 -5
  676. package/plugins/copilot-pbr/CHANGELOG.md +0 -19
  677. package/plugins/copilot-pbr/README.md +0 -139
  678. package/plugins/copilot-pbr/agents/audit.agent.md +0 -113
  679. package/plugins/copilot-pbr/agents/codebase-mapper.agent.md +0 -151
  680. package/plugins/copilot-pbr/agents/debugger.agent.md +0 -182
  681. package/plugins/copilot-pbr/agents/executor.agent.md +0 -267
  682. package/plugins/copilot-pbr/agents/general.agent.md +0 -88
  683. package/plugins/copilot-pbr/agents/integration-checker.agent.md +0 -119
  684. package/plugins/copilot-pbr/agents/plan-checker.agent.md +0 -208
  685. package/plugins/copilot-pbr/agents/planner.agent.md +0 -238
  686. package/plugins/copilot-pbr/agents/researcher.agent.md +0 -186
  687. package/plugins/copilot-pbr/agents/synthesizer.agent.md +0 -126
  688. package/plugins/copilot-pbr/agents/verifier.agent.md +0 -228
  689. package/plugins/copilot-pbr/hooks/hooks.json +0 -156
  690. package/plugins/copilot-pbr/plugin.json +0 -30
  691. package/plugins/copilot-pbr/references/agent-anti-patterns.md +0 -25
  692. package/plugins/copilot-pbr/references/agent-contracts.md +0 -297
  693. package/plugins/copilot-pbr/references/agent-interactions.md +0 -135
  694. package/plugins/copilot-pbr/references/agent-teams.md +0 -55
  695. package/plugins/copilot-pbr/references/checkpoints.md +0 -158
  696. package/plugins/copilot-pbr/references/common-bug-patterns.md +0 -14
  697. package/plugins/copilot-pbr/references/config-reference.md +0 -442
  698. package/plugins/copilot-pbr/references/continuation-format.md +0 -213
  699. package/plugins/copilot-pbr/references/deviation-rules.md +0 -113
  700. package/plugins/copilot-pbr/references/git-integration.md +0 -227
  701. package/plugins/copilot-pbr/references/integration-patterns.md +0 -118
  702. package/plugins/copilot-pbr/references/model-profiles.md +0 -100
  703. package/plugins/copilot-pbr/references/model-selection.md +0 -32
  704. package/plugins/copilot-pbr/references/pbr-rules.md +0 -195
  705. package/plugins/copilot-pbr/references/pbr-tools-cli.md +0 -285
  706. package/plugins/copilot-pbr/references/plan-authoring.md +0 -182
  707. package/plugins/copilot-pbr/references/plan-format.md +0 -288
  708. package/plugins/copilot-pbr/references/planning-config.md +0 -214
  709. package/plugins/copilot-pbr/references/questioning.md +0 -215
  710. package/plugins/copilot-pbr/references/reading-verification.md +0 -128
  711. package/plugins/copilot-pbr/references/stub-patterns.md +0 -161
  712. package/plugins/copilot-pbr/references/subagent-coordination.md +0 -120
  713. package/plugins/copilot-pbr/references/ui-formatting.md +0 -444
  714. package/plugins/copilot-pbr/references/verification-patterns.md +0 -199
  715. package/plugins/copilot-pbr/references/wave-execution.md +0 -96
  716. package/plugins/copilot-pbr/rules/pbr-workflow.mdc +0 -48
  717. package/plugins/copilot-pbr/setup.ps1 +0 -93
  718. package/plugins/copilot-pbr/setup.sh +0 -93
  719. package/plugins/copilot-pbr/skills/audit/SKILL.md +0 -330
  720. package/plugins/copilot-pbr/skills/begin/SKILL.md +0 -589
  721. package/plugins/copilot-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
  722. package/plugins/copilot-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
  723. package/plugins/copilot-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
  724. package/plugins/copilot-pbr/skills/begin/templates/config.json.tmpl +0 -64
  725. package/plugins/copilot-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
  726. package/plugins/copilot-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
  727. package/plugins/copilot-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
  728. package/plugins/copilot-pbr/skills/build/SKILL.md +0 -960
  729. package/plugins/copilot-pbr/skills/config/SKILL.md +0 -250
  730. package/plugins/copilot-pbr/skills/continue/SKILL.md +0 -159
  731. package/plugins/copilot-pbr/skills/dashboard/SKILL.md +0 -43
  732. package/plugins/copilot-pbr/skills/debug/SKILL.md +0 -508
  733. package/plugins/copilot-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
  734. package/plugins/copilot-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
  735. package/plugins/copilot-pbr/skills/discuss/SKILL.md +0 -353
  736. package/plugins/copilot-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
  737. package/plugins/copilot-pbr/skills/discuss/templates/decision-categories.md +0 -10
  738. package/plugins/copilot-pbr/skills/do/SKILL.md +0 -66
  739. package/plugins/copilot-pbr/skills/explore/SKILL.md +0 -373
  740. package/plugins/copilot-pbr/skills/health/SKILL.md +0 -283
  741. package/plugins/copilot-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
  742. package/plugins/copilot-pbr/skills/health/templates/output-format.md.tmpl +0 -64
  743. package/plugins/copilot-pbr/skills/help/SKILL.md +0 -170
  744. package/plugins/copilot-pbr/skills/import/SKILL.md +0 -502
  745. package/plugins/copilot-pbr/skills/milestone/SKILL.md +0 -745
  746. package/plugins/copilot-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
  747. package/plugins/copilot-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
  748. package/plugins/copilot-pbr/skills/note/SKILL.md +0 -213
  749. package/plugins/copilot-pbr/skills/pause/SKILL.md +0 -247
  750. package/plugins/copilot-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
  751. package/plugins/copilot-pbr/skills/plan/SKILL.md +0 -662
  752. package/plugins/copilot-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
  753. package/plugins/copilot-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
  754. package/plugins/copilot-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
  755. package/plugins/copilot-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
  756. package/plugins/copilot-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
  757. package/plugins/copilot-pbr/skills/quick/SKILL.md +0 -376
  758. package/plugins/copilot-pbr/skills/resume/SKILL.md +0 -399
  759. package/plugins/copilot-pbr/skills/review/SKILL.md +0 -653
  760. package/plugins/copilot-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
  761. package/plugins/copilot-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
  762. package/plugins/copilot-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
  763. package/plugins/copilot-pbr/skills/scan/SKILL.md +0 -299
  764. package/plugins/copilot-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
  765. package/plugins/copilot-pbr/skills/setup/SKILL.md +0 -296
  766. package/plugins/copilot-pbr/skills/shared/commit-planning-docs.md +0 -36
  767. package/plugins/copilot-pbr/skills/shared/config-loading.md +0 -103
  768. package/plugins/copilot-pbr/skills/shared/context-budget.md +0 -41
  769. package/plugins/copilot-pbr/skills/shared/context-loader-task.md +0 -87
  770. package/plugins/copilot-pbr/skills/shared/digest-select.md +0 -80
  771. package/plugins/copilot-pbr/skills/shared/domain-probes.md +0 -126
  772. package/plugins/copilot-pbr/skills/shared/error-reporting.md +0 -81
  773. package/plugins/copilot-pbr/skills/shared/gate-prompts.md +0 -389
  774. package/plugins/copilot-pbr/skills/shared/phase-argument-parsing.md +0 -46
  775. package/plugins/copilot-pbr/skills/shared/progress-display.md +0 -53
  776. package/plugins/copilot-pbr/skills/shared/revision-loop.md +0 -82
  777. package/plugins/copilot-pbr/skills/shared/state-loading.md +0 -63
  778. package/plugins/copilot-pbr/skills/shared/state-update.md +0 -162
  779. package/plugins/copilot-pbr/skills/shared/universal-anti-patterns.md +0 -38
  780. package/plugins/copilot-pbr/skills/status/SKILL.md +0 -362
  781. package/plugins/copilot-pbr/skills/statusline/SKILL.md +0 -149
  782. package/plugins/copilot-pbr/skills/todo/SKILL.md +0 -279
  783. package/plugins/copilot-pbr/templates/CONTEXT.md.tmpl +0 -53
  784. package/plugins/copilot-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
  785. package/plugins/copilot-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
  786. package/plugins/copilot-pbr/templates/ROADMAP.md.tmpl +0 -41
  787. package/plugins/copilot-pbr/templates/SUMMARY.md.tmpl +0 -82
  788. package/plugins/copilot-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
  789. package/plugins/copilot-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
  790. package/plugins/copilot-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
  791. package/plugins/copilot-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
  792. package/plugins/copilot-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
  793. package/plugins/copilot-pbr/templates/codebase/STACK.md.tmpl +0 -78
  794. package/plugins/copilot-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
  795. package/plugins/copilot-pbr/templates/codebase/TESTING.md.tmpl +0 -107
  796. package/plugins/copilot-pbr/templates/continue-here.md.tmpl +0 -74
  797. package/plugins/copilot-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
  798. package/plugins/copilot-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  799. package/plugins/copilot-pbr/templates/research/STACK.md.tmpl +0 -71
  800. package/plugins/copilot-pbr/templates/research/SUMMARY.md.tmpl +0 -112
  801. package/plugins/copilot-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  802. package/plugins/copilot-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  803. package/plugins/copilot-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  804. package/plugins/cursor-pbr/.cursor-plugin/plugin.json +0 -32
  805. package/plugins/cursor-pbr/CHANGELOG.md +0 -15
  806. package/plugins/cursor-pbr/README.md +0 -123
  807. package/plugins/cursor-pbr/agents/audit.md +0 -178
  808. package/plugins/cursor-pbr/agents/codebase-mapper.md +0 -150
  809. package/plugins/cursor-pbr/agents/debugger.md +0 -181
  810. package/plugins/cursor-pbr/agents/executor.md +0 -266
  811. package/plugins/cursor-pbr/agents/general.md +0 -87
  812. package/plugins/cursor-pbr/agents/integration-checker.md +0 -118
  813. package/plugins/cursor-pbr/agents/plan-checker.md +0 -207
  814. package/plugins/cursor-pbr/agents/planner.md +0 -237
  815. package/plugins/cursor-pbr/agents/researcher.md +0 -185
  816. package/plugins/cursor-pbr/agents/synthesizer.md +0 -125
  817. package/plugins/cursor-pbr/agents/verifier.md +0 -227
  818. package/plugins/cursor-pbr/assets/.gitkeep +0 -0
  819. package/plugins/cursor-pbr/assets/logo.svg +0 -21
  820. package/plugins/cursor-pbr/hooks/hooks.json +0 -213
  821. package/plugins/cursor-pbr/references/agent-anti-patterns.md +0 -25
  822. package/plugins/cursor-pbr/references/agent-contracts.md +0 -297
  823. package/plugins/cursor-pbr/references/agent-interactions.md +0 -135
  824. package/plugins/cursor-pbr/references/agent-teams.md +0 -55
  825. package/plugins/cursor-pbr/references/checkpoints.md +0 -158
  826. package/plugins/cursor-pbr/references/common-bug-patterns.md +0 -14
  827. package/plugins/cursor-pbr/references/config-reference.md +0 -442
  828. package/plugins/cursor-pbr/references/continuation-format.md +0 -213
  829. package/plugins/cursor-pbr/references/deviation-rules.md +0 -113
  830. package/plugins/cursor-pbr/references/git-integration.md +0 -227
  831. package/plugins/cursor-pbr/references/integration-patterns.md +0 -118
  832. package/plugins/cursor-pbr/references/model-profiles.md +0 -100
  833. package/plugins/cursor-pbr/references/model-selection.md +0 -32
  834. package/plugins/cursor-pbr/references/pbr-rules.md +0 -195
  835. package/plugins/cursor-pbr/references/pbr-tools-cli.md +0 -285
  836. package/plugins/cursor-pbr/references/plan-authoring.md +0 -182
  837. package/plugins/cursor-pbr/references/plan-format.md +0 -288
  838. package/plugins/cursor-pbr/references/planning-config.md +0 -214
  839. package/plugins/cursor-pbr/references/questioning.md +0 -215
  840. package/plugins/cursor-pbr/references/reading-verification.md +0 -128
  841. package/plugins/cursor-pbr/references/stub-patterns.md +0 -161
  842. package/plugins/cursor-pbr/references/subagent-coordination.md +0 -120
  843. package/plugins/cursor-pbr/references/ui-formatting.md +0 -444
  844. package/plugins/cursor-pbr/references/verification-patterns.md +0 -199
  845. package/plugins/cursor-pbr/references/wave-execution.md +0 -96
  846. package/plugins/cursor-pbr/rules/pbr-workflow.mdc +0 -48
  847. package/plugins/cursor-pbr/setup.ps1 +0 -78
  848. package/plugins/cursor-pbr/setup.sh +0 -83
  849. package/plugins/cursor-pbr/skills/audit/SKILL.md +0 -331
  850. package/plugins/cursor-pbr/skills/begin/SKILL.md +0 -589
  851. package/plugins/cursor-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
  852. package/plugins/cursor-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
  853. package/plugins/cursor-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
  854. package/plugins/cursor-pbr/skills/begin/templates/config.json.tmpl +0 -64
  855. package/plugins/cursor-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
  856. package/plugins/cursor-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
  857. package/plugins/cursor-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
  858. package/plugins/cursor-pbr/skills/build/SKILL.md +0 -961
  859. package/plugins/cursor-pbr/skills/config/SKILL.md +0 -252
  860. package/plugins/cursor-pbr/skills/continue/SKILL.md +0 -159
  861. package/plugins/cursor-pbr/skills/dashboard/SKILL.md +0 -44
  862. package/plugins/cursor-pbr/skills/debug/SKILL.md +0 -512
  863. package/plugins/cursor-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
  864. package/plugins/cursor-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
  865. package/plugins/cursor-pbr/skills/discuss/SKILL.md +0 -354
  866. package/plugins/cursor-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
  867. package/plugins/cursor-pbr/skills/discuss/templates/decision-categories.md +0 -10
  868. package/plugins/cursor-pbr/skills/do/SKILL.md +0 -67
  869. package/plugins/cursor-pbr/skills/explore/SKILL.md +0 -376
  870. package/plugins/cursor-pbr/skills/health/SKILL.md +0 -283
  871. package/plugins/cursor-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
  872. package/plugins/cursor-pbr/skills/health/templates/output-format.md.tmpl +0 -64
  873. package/plugins/cursor-pbr/skills/help/SKILL.md +0 -170
  874. package/plugins/cursor-pbr/skills/import/SKILL.md +0 -505
  875. package/plugins/cursor-pbr/skills/milestone/SKILL.md +0 -746
  876. package/plugins/cursor-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
  877. package/plugins/cursor-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
  878. package/plugins/cursor-pbr/skills/note/SKILL.md +0 -214
  879. package/plugins/cursor-pbr/skills/pause/SKILL.md +0 -248
  880. package/plugins/cursor-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
  881. package/plugins/cursor-pbr/skills/plan/SKILL.md +0 -663
  882. package/plugins/cursor-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
  883. package/plugins/cursor-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
  884. package/plugins/cursor-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
  885. package/plugins/cursor-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
  886. package/plugins/cursor-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
  887. package/plugins/cursor-pbr/skills/quick/SKILL.md +0 -376
  888. package/plugins/cursor-pbr/skills/resume/SKILL.md +0 -399
  889. package/plugins/cursor-pbr/skills/review/SKILL.md +0 -654
  890. package/plugins/cursor-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
  891. package/plugins/cursor-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
  892. package/plugins/cursor-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
  893. package/plugins/cursor-pbr/skills/scan/SKILL.md +0 -300
  894. package/plugins/cursor-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
  895. package/plugins/cursor-pbr/skills/setup/SKILL.md +0 -296
  896. package/plugins/cursor-pbr/skills/shared/commit-planning-docs.md +0 -36
  897. package/plugins/cursor-pbr/skills/shared/config-loading.md +0 -103
  898. package/plugins/cursor-pbr/skills/shared/context-budget.md +0 -41
  899. package/plugins/cursor-pbr/skills/shared/context-loader-task.md +0 -87
  900. package/plugins/cursor-pbr/skills/shared/digest-select.md +0 -80
  901. package/plugins/cursor-pbr/skills/shared/domain-probes.md +0 -126
  902. package/plugins/cursor-pbr/skills/shared/error-reporting.md +0 -81
  903. package/plugins/cursor-pbr/skills/shared/gate-prompts.md +0 -389
  904. package/plugins/cursor-pbr/skills/shared/phase-argument-parsing.md +0 -46
  905. package/plugins/cursor-pbr/skills/shared/progress-display.md +0 -53
  906. package/plugins/cursor-pbr/skills/shared/revision-loop.md +0 -82
  907. package/plugins/cursor-pbr/skills/shared/state-loading.md +0 -63
  908. package/plugins/cursor-pbr/skills/shared/state-update.md +0 -162
  909. package/plugins/cursor-pbr/skills/shared/universal-anti-patterns.md +0 -38
  910. package/plugins/cursor-pbr/skills/status/SKILL.md +0 -362
  911. package/plugins/cursor-pbr/skills/statusline/SKILL.md +0 -150
  912. package/plugins/cursor-pbr/skills/todo/SKILL.md +0 -280
  913. package/plugins/cursor-pbr/templates/CONTEXT.md.tmpl +0 -53
  914. package/plugins/cursor-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
  915. package/plugins/cursor-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
  916. package/plugins/cursor-pbr/templates/ROADMAP.md.tmpl +0 -41
  917. package/plugins/cursor-pbr/templates/SUMMARY.md.tmpl +0 -82
  918. package/plugins/cursor-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
  919. package/plugins/cursor-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
  920. package/plugins/cursor-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
  921. package/plugins/cursor-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
  922. package/plugins/cursor-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
  923. package/plugins/cursor-pbr/templates/codebase/STACK.md.tmpl +0 -78
  924. package/plugins/cursor-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
  925. package/plugins/cursor-pbr/templates/codebase/TESTING.md.tmpl +0 -107
  926. package/plugins/cursor-pbr/templates/continue-here.md.tmpl +0 -74
  927. package/plugins/cursor-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
  928. package/plugins/cursor-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  929. package/plugins/cursor-pbr/templates/research/STACK.md.tmpl +0 -71
  930. package/plugins/cursor-pbr/templates/research/SUMMARY.md.tmpl +0 -112
  931. package/plugins/cursor-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  932. package/plugins/cursor-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  933. package/plugins/cursor-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  934. package/plugins/pbr/references/agent-interactions.md +0 -134
  935. package/plugins/pbr/references/pbr-rules.md +0 -194
  936. package/plugins/pbr/references/pbr-tools-cli.md +0 -285
  937. package/plugins/pbr/references/planning-config.md +0 -213
  938. package/plugins/pbr/references/subagent-coordination.md +0 -119
  939. package/plugins/pbr/references/ui-formatting.md +0 -444
  940. package/plugins/pbr/scripts/validate-plugin-structure.js +0 -183
  941. package/plugins/pbr/skills/milestone/templates/audit-report.md.tmpl +0 -48
  942. package/plugins/pbr/skills/shared/progress-display.md +0 -53
  943. package/plugins/pbr/skills/shared/state-loading.md +0 -62
  944. package/plugins/pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -97
  945. package/plugins/pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  946. package/plugins/pbr/templates/research/STACK.md.tmpl +0 -71
  947. package/plugins/pbr/templates/research/SUMMARY.md.tmpl +0 -112
  948. package/plugins/pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  949. package/plugins/pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  950. package/plugins/pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  951. /package/plugins/pbr/references/{agent-anti-patterns.md → archive/agent-anti-patterns.md} +0 -0
@@ -0,0 +1,2098 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // Shared JSONL Helpers
8
+ // ---------------------------------------------------------------------------
9
+
10
+ /**
11
+ * Return the logs directory for a given .planning directory.
12
+ * @param {string} planningDir - Path to .planning/
13
+ * @returns {string}
14
+ */
15
+ function getLogsDir(planningDir) {
16
+ return path.join(planningDir, 'logs');
17
+ }
18
+
19
+ /**
20
+ * Read all JSONL files matching `{prefix}-*.jsonl` in a directory.
21
+ * Parses each line as JSON, skipping malformed lines.
22
+ * Returns array sorted by timestamp (ts field).
23
+ *
24
+ * @param {string} dir - Directory to scan
25
+ * @param {string} prefix - File prefix (e.g., 'events', 'hooks')
26
+ * @returns {Array<Object>} Parsed entries sorted by timestamp
27
+ */
28
+ function readJsonlFiles(dir, prefix) {
29
+ if (!fs.existsSync(dir)) return [];
30
+
31
+ const entries = [];
32
+ const pattern = new RegExp(`^${prefix}-.*\\.jsonl$`);
33
+
34
+ let files;
35
+ try {
36
+ files = fs.readdirSync(dir).filter(f => pattern.test(f)).sort();
37
+ } catch (_e) {
38
+ return [];
39
+ }
40
+
41
+ for (const file of files) {
42
+ try {
43
+ const content = fs.readFileSync(path.join(dir, file), 'utf8');
44
+ const lines = content.split('\n').filter(l => l.trim());
45
+ for (const line of lines) {
46
+ try {
47
+ entries.push(JSON.parse(line));
48
+ } catch (_e) {
49
+ // Skip malformed JSON lines
50
+ }
51
+ }
52
+ } catch (_e) {
53
+ // Skip unreadable files
54
+ }
55
+ }
56
+
57
+ // Sort by timestamp
58
+ entries.sort((a, b) => {
59
+ const ta = a.ts || a.timestamp || '';
60
+ const tb = b.ts || b.timestamp || '';
61
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
62
+ });
63
+
64
+ return entries;
65
+ }
66
+
67
+ /**
68
+ * Read session event logs from .planning/logs/events-*.jsonl.
69
+ * @param {string} planningDir - Path to .planning/
70
+ * @returns {Array<Object>}
71
+ */
72
+ function readSessionEvents(planningDir) {
73
+ return readJsonlFiles(getLogsDir(planningDir), 'events');
74
+ }
75
+
76
+ /**
77
+ * Read hook logs from .planning/logs/hooks-*.jsonl.
78
+ * @param {string} planningDir - Path to .planning/
79
+ * @returns {Array<Object>}
80
+ */
81
+ function readHookLogs(planningDir) {
82
+ return readJsonlFiles(getLogsDir(planningDir), 'hooks');
83
+ }
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // BC-01: Skill Sequence Compliance
87
+ // ---------------------------------------------------------------------------
88
+
89
+ /**
90
+ * Known PBR skill names relevant to workflow ordering.
91
+ */
92
+ const WORKFLOW_SKILLS = ['plan', 'build', 'verify', 'review', 'begin', 'autonomous', 'continue'];
93
+
94
+ /**
95
+ * Valid skill ordering within a phase: plan < build < verify.
96
+ * Lower index = must come first.
97
+ */
98
+ const SKILL_ORDER = { plan: 0, build: 1, verify: 2, review: 3 };
99
+
100
+ /**
101
+ * Extract phase number from an event entry.
102
+ * Looks in various fields for phase references.
103
+ * @param {Object} entry - JSONL entry
104
+ * @returns {string|null} Phase number or null
105
+ */
106
+ function extractPhaseFromEntry(entry) {
107
+ // Check direct phase field
108
+ if (entry.phase) return String(entry.phase);
109
+
110
+ // Check details object
111
+ if (entry.details && entry.details.phase) return String(entry.details.phase);
112
+
113
+ // Check event string for phase references like "phase 3" or "03-"
114
+ const searchStr = JSON.stringify(entry);
115
+ const phaseMatch = searchStr.match(/phase[- _]?(\d+)/i) || searchStr.match(/(\d{2})-\d{2}/);
116
+ if (phaseMatch) return String(parseInt(phaseMatch[1], 10));
117
+
118
+ return null;
119
+ }
120
+
121
+ /**
122
+ * Extract skill name from an event entry.
123
+ * @param {Object} entry - JSONL entry
124
+ * @returns {string|null}
125
+ */
126
+ function extractSkillFromEntry(entry) {
127
+ // Direct event field matching skill names
128
+ if (entry.event && WORKFLOW_SKILLS.includes(entry.event)) return entry.event;
129
+
130
+ // Category-based skill detection
131
+ if (entry.cat === 'skill' && entry.event) return entry.event;
132
+
133
+ // Check for skill in hook entries (check-skill-workflow)
134
+ if (entry.hook === 'check-skill-workflow' && entry.details) {
135
+ const skill = entry.details.skill || entry.details.active_skill;
136
+ if (skill) return skill;
137
+ }
138
+
139
+ // Check for pbr: prefix in event names
140
+ if (entry.event && entry.event.startsWith('pbr:')) {
141
+ const name = entry.event.replace('pbr:', '').split('-')[0];
142
+ if (WORKFLOW_SKILLS.includes(name)) return name;
143
+ }
144
+
145
+ return null;
146
+ }
147
+
148
+ /**
149
+ * BC-01: Check that skills are invoked in valid order within each phase.
150
+ * plan must precede build, build must precede verify.
151
+ *
152
+ * @param {string} planningDir - Path to .planning/
153
+ * @param {Object} [_config] - Config object (unused, for API consistency)
154
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
155
+ */
156
+ function checkSkillSequenceCompliance(planningDir, _config) {
157
+ const events = readSessionEvents(planningDir);
158
+ const hookLogs = readHookLogs(planningDir);
159
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
160
+ const ta = a.ts || a.timestamp || '';
161
+ const tb = b.ts || b.timestamp || '';
162
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
163
+ });
164
+
165
+ if (allEntries.length === 0) {
166
+ return {
167
+ status: 'pass',
168
+ evidence: [],
169
+ message: 'BC-01: No session data available — cannot assess skill sequence'
170
+ };
171
+ }
172
+
173
+ // Build per-phase skill invocation timeline
174
+ // Map<phase, Array<{ skill, ts }>>
175
+ const phaseSkills = new Map();
176
+
177
+ for (const entry of allEntries) {
178
+ const skill = extractSkillFromEntry(entry);
179
+ const phase = extractPhaseFromEntry(entry);
180
+ if (!skill || !phase) continue;
181
+ if (!(skill in SKILL_ORDER)) continue;
182
+
183
+ if (!phaseSkills.has(phase)) phaseSkills.set(phase, []);
184
+ phaseSkills.get(phase).push({
185
+ skill,
186
+ ts: entry.ts || entry.timestamp || ''
187
+ });
188
+ }
189
+
190
+ const evidence = [];
191
+
192
+ for (const [phase, invocations] of phaseSkills) {
193
+ // Check ordering: for each pair, later skills should not appear before earlier ones
194
+ for (let i = 0; i < invocations.length; i++) {
195
+ for (let j = i + 1; j < invocations.length; j++) {
196
+ const earlier = invocations[i];
197
+ const later = invocations[j];
198
+ if (SKILL_ORDER[later.skill] < SKILL_ORDER[earlier.skill]) {
199
+ const time = later.ts ? ` at ${later.ts}` : '';
200
+ evidence.push(
201
+ `Phase ${phase}: ${later.skill} invoked before ${earlier.skill}` +
202
+ ` (${later.skill}${time}, no prior ${earlier.skill} event)`
203
+ );
204
+ }
205
+ }
206
+ }
207
+ }
208
+
209
+ if (evidence.length > 0) {
210
+ return {
211
+ status: 'fail',
212
+ evidence,
213
+ message: `BC-01: Found ${evidence.length} skill ordering violation(s)`
214
+ };
215
+ }
216
+
217
+ return {
218
+ status: 'pass',
219
+ evidence: [],
220
+ message: 'BC-01: All skill invocations follow valid ordering (plan < build < verify)'
221
+ };
222
+ }
223
+
224
+ // ---------------------------------------------------------------------------
225
+ // BC-02: State Machine Transition Validation
226
+ // ---------------------------------------------------------------------------
227
+
228
+ /**
229
+ * Valid state transitions in PBR workflow.
230
+ * Key = from status, Value = set of valid "to" statuses.
231
+ */
232
+ const VALID_TRANSITIONS = {
233
+ not_started: new Set(['discussed', 'ready_to_plan', 'planning']),
234
+ discussed: new Set(['ready_to_plan', 'planning']),
235
+ ready_to_plan: new Set(['planning']),
236
+ planning: new Set(['planned', 'building']), // building = inline execution skips planned
237
+ planned: new Set(['ready_to_execute', 'building']),
238
+ ready_to_execute: new Set(['building']),
239
+ building: new Set(['built', 'partial']),
240
+ built: new Set(['verified', 'needs_fixes']),
241
+ partial: new Set(['building', 'needs_fixes']),
242
+ verified: new Set(['complete']),
243
+ needs_fixes: new Set(['building', 'planning']),
244
+ complete: new Set([]), // terminal
245
+ skipped: new Set([]), // terminal
246
+ };
247
+
248
+ /**
249
+ * Extract status value from a JSONL entry that represents a state write.
250
+ * @param {Object} entry - JSONL entry
251
+ * @returns {string|null} Status value or null
252
+ */
253
+ function extractStatusFromEntry(entry) {
254
+ // Hook log entries from check-state-sync
255
+ if (entry.hook === 'check-state-sync' && entry.details) {
256
+ return entry.details.status || entry.details.new_status || null;
257
+ }
258
+
259
+ // Event logger entries for state writes
260
+ if (entry.event === 'state-update' || entry.event === 'status-change') {
261
+ return (entry.details && entry.details.status) || entry.status || null;
262
+ }
263
+
264
+ // Generic: look for status in tool_input targeting STATE.md
265
+ if (entry.tool_input && typeof entry.tool_input === 'string') {
266
+ if (entry.tool_input.includes('STATE.md')) {
267
+ const statusMatch = entry.tool_input.match(/status:\s*["']?(\w+)/);
268
+ if (statusMatch) return statusMatch[1];
269
+ }
270
+ }
271
+
272
+ // Check details.content for STATE.md writes
273
+ if (entry.details && entry.details.content && typeof entry.details.content === 'string') {
274
+ if (entry.details.file && entry.details.file.includes('STATE.md')) {
275
+ const statusMatch = entry.details.content.match(/status:\s*["']?(\w+)/);
276
+ if (statusMatch) return statusMatch[1];
277
+ }
278
+ }
279
+
280
+ return null;
281
+ }
282
+
283
+ /**
284
+ * BC-02: Check that state transitions follow the valid state machine.
285
+ * Detects invalid transitions like planning->verified (skipping building).
286
+ *
287
+ * @param {string} planningDir - Path to .planning/
288
+ * @param {Object} [_config] - Config object (unused, for API consistency)
289
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
290
+ */
291
+ function checkStateMachineTransitions(planningDir, _config) {
292
+ const events = readSessionEvents(planningDir);
293
+ const hookLogs = readHookLogs(planningDir);
294
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
295
+ const ta = a.ts || a.timestamp || '';
296
+ const tb = b.ts || b.timestamp || '';
297
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
298
+ });
299
+
300
+ if (allEntries.length === 0) {
301
+ return {
302
+ status: 'pass',
303
+ evidence: [],
304
+ message: 'BC-02: No session data available — cannot assess state transitions'
305
+ };
306
+ }
307
+
308
+ // Collect status transitions per phase
309
+ // Map<phase, Array<{ status, ts }>>
310
+ const phaseTransitions = new Map();
311
+
312
+ for (const entry of allEntries) {
313
+ const status = extractStatusFromEntry(entry);
314
+ const phase = extractPhaseFromEntry(entry);
315
+ if (!status || !phase) continue;
316
+
317
+ if (!phaseTransitions.has(phase)) phaseTransitions.set(phase, []);
318
+ const transitions = phaseTransitions.get(phase);
319
+
320
+ // Deduplicate consecutive identical statuses
321
+ if (transitions.length > 0 && transitions[transitions.length - 1].status === status) continue;
322
+
323
+ transitions.push({
324
+ status,
325
+ ts: entry.ts || entry.timestamp || ''
326
+ });
327
+ }
328
+
329
+ const evidence = [];
330
+
331
+ for (const [phase, transitions] of phaseTransitions) {
332
+ for (let i = 0; i < transitions.length - 1; i++) {
333
+ const from = transitions[i].status;
334
+ const to = transitions[i + 1].status;
335
+ const validTargets = VALID_TRANSITIONS[from];
336
+
337
+ if (!validTargets || !validTargets.has(to)) {
338
+ const time = transitions[i + 1].ts ? ` at ${transitions[i + 1].ts}` : '';
339
+ evidence.push(
340
+ `Phase ${phase}: ${from}->${to} (invalid transition${time})`
341
+ );
342
+ }
343
+ }
344
+ }
345
+
346
+ if (evidence.length > 0) {
347
+ return {
348
+ status: 'fail',
349
+ evidence,
350
+ message: `BC-02: Found ${evidence.length} invalid state transition(s)`
351
+ };
352
+ }
353
+
354
+ return {
355
+ status: 'pass',
356
+ evidence: [],
357
+ message: 'BC-02: All state transitions follow valid state machine'
358
+ };
359
+ }
360
+
361
+ // ---------------------------------------------------------------------------
362
+ // BC-03: Pre-Condition Verification
363
+ // ---------------------------------------------------------------------------
364
+
365
+ /**
366
+ * Check if a file write event targets a specific pattern.
367
+ * @param {Object} entry - JSONL entry
368
+ * @param {RegExp} pattern - Pattern to match against file paths
369
+ * @returns {boolean}
370
+ */
371
+ function entryTargetsFile(entry, pattern) {
372
+ // Check tool_input for file path
373
+ if (entry.tool_input) {
374
+ const input = typeof entry.tool_input === 'string' ? entry.tool_input : JSON.stringify(entry.tool_input);
375
+ if (pattern.test(input)) return true;
376
+ }
377
+
378
+ // Check details for file path
379
+ if (entry.details) {
380
+ const details = typeof entry.details === 'string' ? entry.details : JSON.stringify(entry.details);
381
+ if (pattern.test(details)) return true;
382
+ }
383
+
384
+ // Check file/path fields directly
385
+ if (entry.file && pattern.test(entry.file)) return true;
386
+ if (entry.path && pattern.test(entry.path)) return true;
387
+
388
+ return false;
389
+ }
390
+
391
+ /**
392
+ * Check if an entry represents a build-start event.
393
+ * @param {Object} entry - JSONL entry
394
+ * @returns {boolean}
395
+ */
396
+ function isBuildStart(entry) {
397
+ // Skill invocation of build
398
+ if (entry.event === 'build' || entry.event === 'pbr:build') return true;
399
+ if (entry.cat === 'skill' && entry.event === 'build') return true;
400
+
401
+ // Task spawn with executor agent
402
+ if (entry.event === 'task-spawn' && entry.details) {
403
+ if (entry.details.agent === 'executor' || entry.details.subagent_type === 'pbr:executor') return true;
404
+ }
405
+
406
+ // Hook log from validate-task with buildExecutorGate
407
+ if (entry.hook === 'validate-task' && entry.details) {
408
+ if (entry.details.gate === 'buildExecutorGate' || entry.details.check === 'buildExecutorGate') return true;
409
+ }
410
+
411
+ return false;
412
+ }
413
+
414
+ /**
415
+ * Check if an entry represents a verify-start event.
416
+ * @param {Object} entry - JSONL entry
417
+ * @returns {boolean}
418
+ */
419
+ function isVerifyStart(entry) {
420
+ if (entry.event === 'verify' || entry.event === 'pbr:verify') return true;
421
+ if (entry.cat === 'skill' && entry.event === 'verify') return true;
422
+
423
+ // Task spawn with verifier agent
424
+ if (entry.event === 'task-spawn' && entry.details) {
425
+ if (entry.details.agent === 'verifier' || entry.details.subagent_type === 'pbr:verifier') return true;
426
+ }
427
+
428
+ return false;
429
+ }
430
+
431
+ /**
432
+ * BC-03: Check that pre-conditions are met before skill execution.
433
+ * Build requires PLAN-*.md to exist, verify requires SUMMARY*.md to exist.
434
+ *
435
+ * @param {string} planningDir - Path to .planning/
436
+ * @param {Object} [_config] - Config object (unused, for API consistency)
437
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
438
+ */
439
+ function checkPreConditionVerification(planningDir, _config) {
440
+ const events = readSessionEvents(planningDir);
441
+ const hookLogs = readHookLogs(planningDir);
442
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
443
+ const ta = a.ts || a.timestamp || '';
444
+ const tb = b.ts || b.timestamp || '';
445
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
446
+ });
447
+
448
+ if (allEntries.length === 0) {
449
+ return {
450
+ status: 'pass',
451
+ evidence: [],
452
+ message: 'BC-03: No session data available — cannot assess pre-conditions'
453
+ };
454
+ }
455
+
456
+ const evidence = [];
457
+ const planPattern = /PLAN-?\d*\.md/i;
458
+ const summaryPattern = /SUMMARY/i;
459
+
460
+ // Track which file patterns have been written so far
461
+ let planWriteSeen = false;
462
+ let summaryWriteSeen = false;
463
+
464
+ // Also check hook logs for validate-task entries that confirm pre-conditions
465
+ const gateChecks = hookLogs.filter(e =>
466
+ e.hook === 'validate-task' && e.details &&
467
+ (e.details.gate === 'buildExecutorGate' || e.details.check === 'buildExecutorGate')
468
+ );
469
+ const gateBlocks = gateChecks.filter(e =>
470
+ e.details && (e.details.decision === 'block' || e.details.result === 'block')
471
+ );
472
+
473
+ for (const entry of allEntries) {
474
+ // Track file writes
475
+ if (entryTargetsFile(entry, planPattern)) {
476
+ planWriteSeen = true;
477
+ }
478
+ if (entryTargetsFile(entry, summaryPattern)) {
479
+ summaryWriteSeen = true;
480
+ }
481
+
482
+ // Check build pre-conditions
483
+ if (isBuildStart(entry)) {
484
+ const phase = extractPhaseFromEntry(entry);
485
+ if (!planWriteSeen) {
486
+ // Check if validate-task gate caught this
487
+ const wasCaught = gateBlocks.some(g => {
488
+ const gPhase = extractPhaseFromEntry(g);
489
+ return gPhase === phase;
490
+ });
491
+ if (wasCaught) continue; // Gate caught it, not a violation
492
+
493
+ const time = entry.ts || entry.timestamp || '';
494
+ evidence.push(
495
+ `${phase ? `Phase ${phase} ` : ''}build started but no PLAN-*.md write event found in prior session activity` +
496
+ (time ? ` (build at ${time})` : '')
497
+ );
498
+ }
499
+ }
500
+
501
+ // Check verify pre-conditions
502
+ if (isVerifyStart(entry)) {
503
+ const phase = extractPhaseFromEntry(entry);
504
+ if (!summaryWriteSeen) {
505
+ const time = entry.ts || entry.timestamp || '';
506
+ evidence.push(
507
+ `${phase ? `Phase ${phase} ` : ''}verify started but no SUMMARY.md write event found in prior session activity` +
508
+ (time ? ` (verify at ${time})` : '')
509
+ );
510
+ }
511
+ }
512
+ }
513
+
514
+ if (evidence.length > 0) {
515
+ // Use 'warn' for ambiguous cases (session data might be incomplete)
516
+ const hasAmbiguity = evidence.some(e => e.includes('no') && e.includes('write event'));
517
+ return {
518
+ status: hasAmbiguity ? 'warn' : 'fail',
519
+ evidence,
520
+ message: `BC-03: Found ${evidence.length} pre-condition concern(s) — data may be incomplete`
521
+ };
522
+ }
523
+
524
+ return {
525
+ status: 'pass',
526
+ evidence: [],
527
+ message: 'BC-03: All pre-conditions verified (PLAN before build, SUMMARY before verify)'
528
+ };
529
+ }
530
+
531
+ // ---------------------------------------------------------------------------
532
+ // BC-04: Post-Condition Verification
533
+ // ---------------------------------------------------------------------------
534
+
535
+ /**
536
+ * Check if an entry represents a build-complete event.
537
+ * @param {Object} entry - JSONL entry
538
+ * @returns {boolean}
539
+ */
540
+ function isBuildComplete(entry) {
541
+ // Task completion for executor agent
542
+ if (entry.event === 'task-complete' && entry.details) {
543
+ if (entry.details.agent === 'executor' || entry.details.subagent_type === 'pbr:executor') return true;
544
+ }
545
+
546
+ // Subagent stop for executor
547
+ if (entry.event === 'subagent-stop' && entry.details) {
548
+ if (entry.details.agent === 'executor' || entry.details.subagent_type === 'pbr:executor') return true;
549
+ }
550
+
551
+ // Hook log from check-subagent-output indicating build completion
552
+ if (entry.hook === 'check-subagent-output' && entry.details) {
553
+ if (entry.details.skill === 'build') return true;
554
+ }
555
+
556
+ return false;
557
+ }
558
+
559
+ /**
560
+ * Check if an entry represents a verify-complete event.
561
+ * @param {Object} entry - JSONL entry
562
+ * @returns {boolean}
563
+ */
564
+ function isVerifyComplete(entry) {
565
+ if (entry.event === 'task-complete' && entry.details) {
566
+ if (entry.details.agent === 'verifier' || entry.details.subagent_type === 'pbr:verifier') return true;
567
+ }
568
+
569
+ if (entry.event === 'subagent-stop' && entry.details) {
570
+ if (entry.details.agent === 'verifier' || entry.details.subagent_type === 'pbr:verifier') return true;
571
+ }
572
+
573
+ if (entry.hook === 'check-subagent-output' && entry.details) {
574
+ if (entry.details.skill === 'verify') return true;
575
+ }
576
+
577
+ return false;
578
+ }
579
+
580
+ /**
581
+ * BC-04: Check that post-conditions are met after skill execution.
582
+ * Build must produce SUMMARY.md, verify must produce VERIFICATION.md.
583
+ *
584
+ * @param {string} planningDir - Path to .planning/
585
+ * @param {Object} [_config] - Config object (unused, for API consistency)
586
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
587
+ */
588
+ function checkPostConditionVerification(planningDir, _config) {
589
+ const events = readSessionEvents(planningDir);
590
+ const hookLogs = readHookLogs(planningDir);
591
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
592
+ const ta = a.ts || a.timestamp || '';
593
+ const tb = b.ts || b.timestamp || '';
594
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
595
+ });
596
+
597
+ if (allEntries.length === 0) {
598
+ return {
599
+ status: 'pass',
600
+ evidence: [],
601
+ message: 'BC-04: No session data available — cannot assess post-conditions'
602
+ };
603
+ }
604
+
605
+ const evidence = [];
606
+ const summaryPattern = /SUMMARY.*\.md/i;
607
+ const verificationPattern = /VERIFICATION.*\.md/i;
608
+
609
+ // Track build-complete and verify-complete events, then look for subsequent artifact writes
610
+ const buildCompletes = [];
611
+ const verifyCompletes = [];
612
+
613
+ for (const entry of allEntries) {
614
+ if (isBuildComplete(entry)) {
615
+ const phase = extractPhaseFromEntry(entry);
616
+ buildCompletes.push({ phase, ts: entry.ts || entry.timestamp || '' });
617
+ }
618
+ if (isVerifyComplete(entry)) {
619
+ const phase = extractPhaseFromEntry(entry);
620
+ verifyCompletes.push({ phase, ts: entry.ts || entry.timestamp || '' });
621
+ }
622
+ }
623
+
624
+ // For each build-complete, check if SUMMARY.md was written in the session
625
+ for (const bc of buildCompletes) {
626
+ const summaryWritten = allEntries.some(e => entryTargetsFile(e, summaryPattern));
627
+ if (!summaryWritten) {
628
+ const phaseLabel = bc.phase ? `Phase ${bc.phase}` : 'Unknown phase';
629
+ evidence.push(`${phaseLabel}: build completed but no SUMMARY.md written in session`);
630
+ }
631
+ }
632
+
633
+ // For each verify-complete, check if VERIFICATION.md was written in the session
634
+ for (const vc of verifyCompletes) {
635
+ const verificationWritten = allEntries.some(e => entryTargetsFile(e, verificationPattern));
636
+ if (!verificationWritten) {
637
+ const phaseLabel = vc.phase ? `Phase ${vc.phase}` : 'Unknown phase';
638
+ evidence.push(`${phaseLabel}: verify completed but no VERIFICATION.md written in session`);
639
+ }
640
+ }
641
+
642
+ if (evidence.length > 0) {
643
+ return {
644
+ status: 'warn',
645
+ evidence,
646
+ message: `BC-04: Found ${evidence.length} missing post-condition artifact(s)`
647
+ };
648
+ }
649
+
650
+ return {
651
+ status: 'pass',
652
+ evidence: [],
653
+ message: 'BC-04: All post-conditions verified (SUMMARY after build, VERIFICATION after verify)'
654
+ };
655
+ }
656
+
657
+ // ---------------------------------------------------------------------------
658
+ // BC-05: Orchestrator Budget Discipline
659
+ // ---------------------------------------------------------------------------
660
+
661
+ /**
662
+ * Check if an event appears to be from orchestrator context (not inside a Task subagent).
663
+ * Events inside Task() typically have subagent markers or task_id fields.
664
+ * @param {Object} entry - JSONL entry
665
+ * @returns {boolean} true if this looks like an orchestrator-level event
666
+ */
667
+ function isOrchestratorLevel(entry) {
668
+ // Events with subagent or task markers are inside Task() contexts
669
+ if (entry.task_id) return false;
670
+ if (entry.subagent) return false;
671
+ if (entry.details && entry.details.subagent_type) return false;
672
+ if (entry.details && entry.details.task_id) return false;
673
+ if (entry.cat === 'subagent') return false;
674
+
675
+ // Hook logs from within subagents
676
+ if (entry.hook && entry.details && entry.details.inside_task) return false;
677
+
678
+ return true;
679
+ }
680
+
681
+ /**
682
+ * Check if a file path is an executor-level file that the orchestrator should not read.
683
+ * @param {string} filePath - File path to check
684
+ * @returns {boolean}
685
+ */
686
+ function isExecutorLevelFile(filePath) {
687
+ if (!filePath) return false;
688
+ const normalized = filePath.replace(/\\/g, '/');
689
+
690
+ // Source files that should be delegated to executor
691
+ if (/plugins\/pbr\/scripts\//.test(normalized)) return true;
692
+ if (/\/src\//.test(normalized)) return true;
693
+
694
+ // Plan action details (the executor reads these, not the orchestrator)
695
+ if (/PLAN-?\d+\.md$/i.test(normalized)) return true;
696
+
697
+ return false;
698
+ }
699
+
700
+ /**
701
+ * BC-05: Check that the orchestrator stays within its context budget.
702
+ * Flags orchestrator-level reads of executor files and budget overruns.
703
+ *
704
+ * @param {string} planningDir - Path to .planning/
705
+ * @param {Object} [config] - Config object with orchestrator_budget_pct
706
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
707
+ */
708
+ function checkOrchestratorBudgetDiscipline(planningDir, config) {
709
+ const events = readSessionEvents(planningDir);
710
+ const hookLogs = readHookLogs(planningDir);
711
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
712
+ const ta = a.ts || a.timestamp || '';
713
+ const tb = b.ts || b.timestamp || '';
714
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
715
+ });
716
+
717
+ if (allEntries.length === 0) {
718
+ return {
719
+ status: 'pass',
720
+ evidence: [],
721
+ message: 'BC-05: No session data available — cannot assess orchestrator budget'
722
+ };
723
+ }
724
+
725
+ const evidence = [];
726
+ const budgetPct = (config && config.orchestrator_budget_pct) || 35;
727
+
728
+ // Find orchestrator-level Read events targeting executor files
729
+ const orchestratorSourceReads = [];
730
+ for (const entry of allEntries) {
731
+ if (!isOrchestratorLevel(entry)) continue;
732
+
733
+ // Look for Read tool events
734
+ const isRead = entry.tool === 'Read' || entry.event === 'read' ||
735
+ (entry.details && entry.details.tool === 'Read');
736
+ if (!isRead) continue;
737
+
738
+ // Extract file path
739
+ const filePath = (entry.tool_input && entry.tool_input.file_path) ||
740
+ (entry.details && entry.details.file_path) ||
741
+ entry.file || entry.path || '';
742
+
743
+ if (isExecutorLevelFile(filePath)) {
744
+ orchestratorSourceReads.push(filePath);
745
+ }
746
+ }
747
+
748
+ if (orchestratorSourceReads.length > 0) {
749
+ evidence.push(
750
+ `Orchestrator read ${orchestratorSourceReads.length} executor-level file(s) directly: ` +
751
+ orchestratorSourceReads.slice(0, 5).map(f => path.basename(f)).join(', ') +
752
+ (orchestratorSourceReads.length > 5 ? ` (+${orchestratorSourceReads.length - 5} more)` : '')
753
+ );
754
+ }
755
+
756
+ // Check context budget data from .context-budget.json
757
+ const budgetPath = path.join(planningDir, '.context-budget.json');
758
+ try {
759
+ if (fs.existsSync(budgetPath)) {
760
+ const budgetData = JSON.parse(fs.readFileSync(budgetPath, 'utf8'));
761
+ const pctUsed = budgetData.pct_used || budgetData.estimated_percent || 0;
762
+ if (pctUsed > budgetPct) {
763
+ evidence.push(
764
+ `Orchestrator context budget exceeded: ${pctUsed}% used (threshold: ${budgetPct}%)`
765
+ );
766
+ }
767
+ }
768
+ } catch (_e) {
769
+ // Budget file may not exist or be unreadable — not an error
770
+ }
771
+
772
+ if (evidence.length > 0) {
773
+ return {
774
+ status: 'warn',
775
+ evidence,
776
+ message: `BC-05: Orchestrator budget discipline concern(s) found`
777
+ };
778
+ }
779
+
780
+ return {
781
+ status: 'pass',
782
+ evidence: [],
783
+ message: `BC-05: Orchestrator stayed within budget (threshold: ${budgetPct}%)`
784
+ };
785
+ }
786
+
787
+ // ---------------------------------------------------------------------------
788
+ // BC-06: Artifact Creation Order
789
+ // ---------------------------------------------------------------------------
790
+
791
+ /**
792
+ * Artifact types in expected creation order within a phase.
793
+ * Lower index = must be created first.
794
+ */
795
+ const ARTIFACT_ORDER = {
796
+ 'PLAN': 0,
797
+ 'SUMMARY': 1,
798
+ 'VERIFICATION': 2,
799
+ };
800
+
801
+ /**
802
+ * Extract artifact type and phase directory from a file path.
803
+ * @param {string} filePath - File path from event
804
+ * @returns {{ artifact: string, phaseDir: string }|null}
805
+ */
806
+ function extractArtifactInfo(filePath) {
807
+ if (!filePath) return null;
808
+ const normalized = filePath.replace(/\\/g, '/');
809
+
810
+ // Match phase directory patterns like phases/03-something/PLAN-01.md
811
+ const phaseMatch = normalized.match(/phases\/(\d{2}-[^/]+)\//);
812
+ if (!phaseMatch) return null;
813
+
814
+ const phaseDir = phaseMatch[1];
815
+ const basename = path.basename(normalized);
816
+
817
+ if (/^PLAN/i.test(basename)) return { artifact: 'PLAN', phaseDir };
818
+ if (/^SUMMARY/i.test(basename)) return { artifact: 'SUMMARY', phaseDir };
819
+ if (/^VERIFICATION/i.test(basename)) return { artifact: 'VERIFICATION', phaseDir };
820
+
821
+ return null;
822
+ }
823
+
824
+ /**
825
+ * Check if an entry is a git commit event (Bash tool running git commit).
826
+ * @param {Object} entry - JSONL entry
827
+ * @returns {string|null} Phase directory if commit is phase-related, null otherwise
828
+ */
829
+ function extractCommitPhase(entry) {
830
+ // Look for Bash tool events containing "git commit"
831
+ const input = entry.tool_input || (entry.details && entry.details.command) || '';
832
+ const inputStr = typeof input === 'string' ? input : JSON.stringify(input);
833
+
834
+ if (!/git\s+commit/i.test(inputStr)) return null;
835
+
836
+ // Try to extract phase from the commit message or context
837
+ const phaseMatch = inputStr.match(/(\d{2})-(\d{2})/);
838
+ if (phaseMatch) {
839
+ // This gives us a phase-plan reference; return just the phase part
840
+ return phaseMatch[1];
841
+ }
842
+
843
+ return null;
844
+ }
845
+
846
+ /**
847
+ * BC-06: Check that artifacts are created in the expected order within each phase.
848
+ * Expected: PLAN write < commit(s) < SUMMARY write < VERIFICATION write.
849
+ *
850
+ * @param {string} planningDir - Path to .planning/
851
+ * @param {Object} [_config] - Config object (unused, for API consistency)
852
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
853
+ */
854
+ function checkArtifactCreationOrder(planningDir, _config) {
855
+ const events = readSessionEvents(planningDir);
856
+ const hookLogs = readHookLogs(planningDir);
857
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
858
+ const ta = a.ts || a.timestamp || '';
859
+ const tb = b.ts || b.timestamp || '';
860
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
861
+ });
862
+
863
+ if (allEntries.length === 0) {
864
+ return {
865
+ status: 'pass',
866
+ evidence: [],
867
+ message: 'BC-06: No session data available — cannot assess artifact order'
868
+ };
869
+ }
870
+
871
+ // Collect artifact write timestamps per phase directory
872
+ // Map<phaseDir, Map<artifactType, { ts: string, index: number }>>
873
+ const phaseArtifacts = new Map();
874
+
875
+ let eventIndex = 0;
876
+ for (const entry of allEntries) {
877
+ eventIndex++;
878
+ const ts = entry.ts || entry.timestamp || '';
879
+
880
+ // Check for Write/Edit events targeting artifact files
881
+ const isWrite = entry.tool === 'Write' || entry.tool === 'Edit' ||
882
+ (entry.details && (entry.details.tool === 'Write' || entry.details.tool === 'Edit'));
883
+
884
+ if (isWrite) {
885
+ const filePath = (entry.tool_input && (entry.tool_input.file_path || entry.tool_input.filePath)) ||
886
+ (entry.details && (entry.details.file_path || entry.details.file)) ||
887
+ entry.file || entry.path || '';
888
+
889
+ const info = extractArtifactInfo(filePath);
890
+ if (info) {
891
+ if (!phaseArtifacts.has(info.phaseDir)) phaseArtifacts.set(info.phaseDir, new Map());
892
+ const artifacts = phaseArtifacts.get(info.phaseDir);
893
+ // Record first occurrence only (the initial write matters for ordering)
894
+ if (!artifacts.has(info.artifact)) {
895
+ artifacts.set(info.artifact, { ts, index: eventIndex });
896
+ }
897
+ }
898
+ }
899
+
900
+ // Also check entryTargetsFile for artifact patterns
901
+ if (!isWrite) {
902
+ for (const [artifactName, _order] of Object.entries(ARTIFACT_ORDER)) {
903
+ const pattern = new RegExp(`${artifactName}.*\\.md`, 'i');
904
+ if (entryTargetsFile(entry, pattern)) {
905
+ // Try to extract phase from the entry
906
+ const searchStr = JSON.stringify(entry);
907
+ const pdMatch = searchStr.match(/phases\/(\d{2}-[^/"]+)\//);
908
+ if (pdMatch) {
909
+ const phaseDir = pdMatch[1];
910
+ if (!phaseArtifacts.has(phaseDir)) phaseArtifacts.set(phaseDir, new Map());
911
+ const artifacts = phaseArtifacts.get(phaseDir);
912
+ if (!artifacts.has(artifactName)) {
913
+ artifacts.set(artifactName, { ts, index: eventIndex });
914
+ }
915
+ }
916
+ }
917
+ }
918
+ }
919
+ }
920
+
921
+ const evidence = [];
922
+
923
+ // Check ordering within each phase
924
+ for (const [phaseDir, artifacts] of phaseArtifacts) {
925
+ const entries = Array.from(artifacts.entries())
926
+ .map(([name, data]) => ({ name, ...data, order: ARTIFACT_ORDER[name] }))
927
+ .sort((a, b) => a.index - b.index); // Sort by actual occurrence order
928
+
929
+ // Compare each pair: if a later-ordered artifact appears before an earlier one
930
+ for (let i = 0; i < entries.length; i++) {
931
+ for (let j = i + 1; j < entries.length; j++) {
932
+ if (entries[j].order < entries[i].order) {
933
+ const timeA = entries[i].ts ? ` (${entries[i].ts.substring(11, 16)})` : '';
934
+ const timeB = entries[j].ts ? ` (${entries[j].ts.substring(11, 16)})` : '';
935
+ evidence.push(
936
+ `${phaseDir}: ${entries[j].name}.md written${timeB} before ${entries[i].name}.md${timeA} — out of order`
937
+ );
938
+ }
939
+ }
940
+ }
941
+ }
942
+
943
+ if (evidence.length > 0) {
944
+ return {
945
+ status: 'warn',
946
+ evidence,
947
+ message: `BC-06: Found ${evidence.length} out-of-order artifact creation(s)`
948
+ };
949
+ }
950
+
951
+ return {
952
+ status: 'pass',
953
+ evidence: [],
954
+ message: 'BC-06: All artifacts created in expected order (PLAN < SUMMARY < VERIFICATION)'
955
+ };
956
+ }
957
+
958
+ // ---------------------------------------------------------------------------
959
+ // BC-07: CRITICAL Marker Compliance
960
+ // ---------------------------------------------------------------------------
961
+
962
+ /**
963
+ * Known CRITICAL steps keyed by skill name.
964
+ * Each entry maps a skill to the file patterns that MUST be written after invocation.
965
+ */
966
+ const CRITICAL_STEPS = {
967
+ build: [
968
+ { description: 'SUMMARY.md written after build', pattern: /SUMMARY.*\.md/i },
969
+ ],
970
+ quick: [
971
+ { description: 'quick task directory created', pattern: /\.planning\/quick\/\d{3}-[^/]+\//i },
972
+ ],
973
+ begin: [
974
+ { description: '.planning/ directory and STATE.md created', pattern: /STATE\.md/i },
975
+ ],
976
+ };
977
+
978
+ /**
979
+ * Check if an entry represents a skill invocation.
980
+ * @param {Object} entry - JSONL entry
981
+ * @returns {string|null} skill name or null
982
+ */
983
+ function extractSkillInvocation(entry) {
984
+ // Direct skill event
985
+ if (entry.cat === 'skill' && entry.event) return entry.event;
986
+
987
+ // pbr: prefix events
988
+ if (entry.event && entry.event.startsWith('pbr:')) {
989
+ return entry.event.replace('pbr:', '').split('-')[0];
990
+ }
991
+
992
+ // Skill field in details
993
+ if (entry.details && entry.details.skill) return entry.details.skill;
994
+
995
+ return null;
996
+ }
997
+
998
+ /**
999
+ * BC-07: Check that CRITICAL/STOP markers in skills were followed by the LLM.
1000
+ * Detects when expected artifact writes are missing after skill invocations.
1001
+ *
1002
+ * @param {string} planningDir - Path to .planning/
1003
+ * @param {Object} [_config] - Config object (unused, for API consistency)
1004
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
1005
+ */
1006
+ function checkCriticalMarkerCompliance(planningDir, _config) {
1007
+ const events = readSessionEvents(planningDir);
1008
+ const hookLogs = readHookLogs(planningDir);
1009
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
1010
+ const ta = a.ts || a.timestamp || '';
1011
+ const tb = b.ts || b.timestamp || '';
1012
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
1013
+ });
1014
+
1015
+ if (allEntries.length === 0) {
1016
+ return {
1017
+ status: 'pass',
1018
+ evidence: [],
1019
+ message: 'BC-07: No session data available — cannot assess CRITICAL marker compliance'
1020
+ };
1021
+ }
1022
+
1023
+ const evidence = [];
1024
+
1025
+ // Collect skill invocations and all write targets
1026
+ const skillInvocations = []; // { skill, ts, index }
1027
+ const writeTargets = []; // { target: string, index }
1028
+
1029
+ let idx = 0;
1030
+ for (const entry of allEntries) {
1031
+ idx++;
1032
+ const skill = extractSkillInvocation(entry);
1033
+ if (skill && CRITICAL_STEPS[skill]) {
1034
+ skillInvocations.push({ skill, ts: entry.ts || entry.timestamp || '', index: idx });
1035
+ }
1036
+
1037
+ // Collect all write events
1038
+ const isWrite = entry.tool === 'Write' || entry.tool === 'Edit' ||
1039
+ (entry.details && (entry.details.tool === 'Write' || entry.details.tool === 'Edit'));
1040
+
1041
+ if (isWrite) {
1042
+ const filePath = (entry.tool_input && (entry.tool_input.file_path || entry.tool_input.filePath)) ||
1043
+ (entry.details && (entry.details.file_path || entry.details.file)) ||
1044
+ entry.file || entry.path || '';
1045
+ if (filePath) writeTargets.push({ target: filePath, index: idx });
1046
+ }
1047
+
1048
+ // Also check entry details for file references
1049
+ if (entryTargetsFile(entry, /\.(md|json)$/i)) {
1050
+ const filePath = (entry.tool_input && (entry.tool_input.file_path || entry.tool_input.filePath)) ||
1051
+ (entry.details && (entry.details.file_path || entry.details.file)) ||
1052
+ entry.file || entry.path || '';
1053
+ if (filePath) writeTargets.push({ target: filePath, index: idx });
1054
+ }
1055
+ }
1056
+
1057
+ // For each skill invocation, check if CRITICAL writes occurred afterward
1058
+ for (const invocation of skillInvocations) {
1059
+ const steps = CRITICAL_STEPS[invocation.skill];
1060
+ for (const step of steps) {
1061
+ const hasWrite = writeTargets.some(
1062
+ w => w.index > invocation.index && step.pattern.test(w.target)
1063
+ );
1064
+ if (!hasWrite) {
1065
+ const time = invocation.ts ? ` at ${invocation.ts}` : '';
1066
+ evidence.push(
1067
+ `${invocation.skill} skill invoked${time} but ${step.description} — CRITICAL step may have been skipped`
1068
+ );
1069
+ }
1070
+ }
1071
+ }
1072
+
1073
+ if (evidence.length > 0) {
1074
+ return {
1075
+ status: 'warn',
1076
+ evidence,
1077
+ message: `BC-07: Found ${evidence.length} potentially skipped CRITICAL step(s)`
1078
+ };
1079
+ }
1080
+
1081
+ return {
1082
+ status: 'pass',
1083
+ evidence: [],
1084
+ message: 'BC-07: All CRITICAL marker steps appear to have been followed'
1085
+ };
1086
+ }
1087
+
1088
+ // ---------------------------------------------------------------------------
1089
+ // BC-08: Gate Compliance
1090
+ // ---------------------------------------------------------------------------
1091
+
1092
+ /**
1093
+ * BC-08: Check that gate behavior matches config settings.
1094
+ * In autonomous mode with gates disabled, AskUserQuestion should not be used for gate prompts.
1095
+ * In interactive mode with gates enabled, AskUserQuestion should be present.
1096
+ *
1097
+ * @param {string} planningDir - Path to .planning/
1098
+ * @param {Object} [config] - Config object with mode and gates settings
1099
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
1100
+ */
1101
+ function checkGateCompliance(planningDir, config) {
1102
+ const events = readSessionEvents(planningDir);
1103
+ const hookLogs = readHookLogs(planningDir);
1104
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
1105
+ const ta = a.ts || a.timestamp || '';
1106
+ const tb = b.ts || b.timestamp || '';
1107
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
1108
+ });
1109
+
1110
+ if (allEntries.length === 0) {
1111
+ return {
1112
+ status: 'pass',
1113
+ evidence: [],
1114
+ message: 'BC-08: No session data available — cannot assess gate compliance'
1115
+ };
1116
+ }
1117
+
1118
+ // Determine mode and gate settings
1119
+ const mode = (config && config.mode) || 'interactive';
1120
+ const gates = (config && config.gates) || {};
1121
+ const isAutonomous = mode === 'autonomous';
1122
+ const allGatesFalse = Object.keys(gates).length > 0 &&
1123
+ Object.keys(gates).filter(k => k.startsWith('confirm_')).every(k => !gates[k]);
1124
+
1125
+ const evidence = [];
1126
+
1127
+ // Find AskUserQuestion events that look gate-like
1128
+ const gatePatterns = /\b(confirm|proceed|approve|accept|continue\s+with|go\s+ahead)\b/i;
1129
+ const gateAskEvents = [];
1130
+
1131
+ for (const entry of allEntries) {
1132
+ const isAsk = entry.tool === 'AskUserQuestion' ||
1133
+ (entry.details && entry.details.tool === 'AskUserQuestion');
1134
+ if (!isAsk) continue;
1135
+
1136
+ // Extract the question content
1137
+ const question = (entry.tool_input && (entry.tool_input.question || entry.tool_input.message)) ||
1138
+ (entry.details && (entry.details.question || entry.details.message)) || '';
1139
+ const questionStr = typeof question === 'string' ? question : JSON.stringify(question);
1140
+
1141
+ if (gatePatterns.test(questionStr)) {
1142
+ gateAskEvents.push({
1143
+ question: questionStr.substring(0, 100),
1144
+ ts: entry.ts || entry.timestamp || ''
1145
+ });
1146
+ }
1147
+ }
1148
+
1149
+ if (isAutonomous && allGatesFalse && gateAskEvents.length > 0) {
1150
+ for (const ask of gateAskEvents) {
1151
+ const time = ask.ts ? ` at ${ask.ts}` : '';
1152
+ evidence.push(
1153
+ `Autonomous mode with gates disabled but AskUserQuestion asked "${ask.question}"${time}`
1154
+ );
1155
+ }
1156
+ }
1157
+
1158
+ if (!isAutonomous && Object.keys(gates).some(k => k.startsWith('confirm_') && gates[k])) {
1159
+ // Interactive mode with some gates enabled — check if gates were actually used
1160
+ const enabledGates = Object.keys(gates).filter(k => k.startsWith('confirm_') && gates[k]);
1161
+ if (gateAskEvents.length === 0 && enabledGates.length > 0) {
1162
+ evidence.push(
1163
+ `Interactive mode with ${enabledGates.length} gate(s) enabled (${enabledGates.join(', ')}) ` +
1164
+ `but no gate-like AskUserQuestion events detected — gates may have been skipped`
1165
+ );
1166
+ }
1167
+ }
1168
+
1169
+ if (evidence.length > 0) {
1170
+ return {
1171
+ status: 'warn',
1172
+ evidence,
1173
+ message: `BC-08: Gate behavior does not match config (${evidence.length} concern(s))`
1174
+ };
1175
+ }
1176
+
1177
+ return {
1178
+ status: 'pass',
1179
+ evidence: [],
1180
+ message: `BC-08: Gate behavior aligned with config (mode=${mode}, gates=${isAutonomous && allGatesFalse ? 'all disabled' : 'active'})`
1181
+ };
1182
+ }
1183
+
1184
+ // ---------------------------------------------------------------------------
1185
+ // BC-09: Enforce-PBR-Workflow Advisory Tracking
1186
+ // ---------------------------------------------------------------------------
1187
+
1188
+ /**
1189
+ * BC-09: Track enforce-PBR-workflow hook advisories and whether they were addressed.
1190
+ * Reads hook logs for prompt-routing and check-skill-workflow advisory entries,
1191
+ * then checks if the advised PBR skill was subsequently used.
1192
+ *
1193
+ * @param {string} planningDir - Path to .planning/
1194
+ * @param {Object} [config] - Config object with workflow.enforce_pbr_skills setting
1195
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
1196
+ */
1197
+ function checkEnforceWorkflowAdvisory(planningDir, config) {
1198
+ // Check enforce_pbr_skills setting
1199
+ const enforceSetting = (config && config.workflow && config.workflow.enforce_pbr_skills) || 'off';
1200
+
1201
+ if (enforceSetting === 'off') {
1202
+ return {
1203
+ status: 'pass',
1204
+ evidence: [],
1205
+ message: 'BC-09: enforce_pbr_skills is disabled — advisory tracking skipped'
1206
+ };
1207
+ }
1208
+
1209
+ const hookLogs = readHookLogs(planningDir);
1210
+ const events = readSessionEvents(planningDir);
1211
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
1212
+ const ta = a.ts || a.timestamp || '';
1213
+ const tb = b.ts || b.timestamp || '';
1214
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
1215
+ });
1216
+
1217
+ if (allEntries.length === 0) {
1218
+ return {
1219
+ status: 'info',
1220
+ evidence: [],
1221
+ message: 'BC-09: No session data available — cannot assess workflow advisory compliance'
1222
+ };
1223
+ }
1224
+
1225
+ // Collect advisory entries from prompt-routing and check-skill-workflow hooks
1226
+ const advisories = [];
1227
+ const skillUsages = []; // Track actual PBR skill invocations
1228
+
1229
+ for (const entry of allEntries) {
1230
+ // Detect advisory warnings from relevant hooks
1231
+ const isAdvisoryHook = entry.hook === 'prompt-routing' ||
1232
+ entry.hook === 'check-skill-workflow' ||
1233
+ entry.hook === 'enforce-pbr-workflow';
1234
+
1235
+ if (isAdvisoryHook && entry.details) {
1236
+ const isAdvisory = entry.details.level === 'advisory' ||
1237
+ entry.details.decision === 'allow' ||
1238
+ entry.details.type === 'advisory' ||
1239
+ (entry.details.message && /suggest|consider|recommend|use \/pbr:/i.test(entry.details.message));
1240
+
1241
+ if (isAdvisory) {
1242
+ // Extract the suggested command if available
1243
+ const suggestedCommand = entry.details.suggested_command ||
1244
+ entry.details.command || '';
1245
+ const message = entry.details.message || entry.details.reason || '';
1246
+ const cmdMatch = message.match(/\/pbr:(\w+)/);
1247
+ const suggestedSkill = cmdMatch ? cmdMatch[1] : (suggestedCommand.replace('/pbr:', '') || null);
1248
+
1249
+ advisories.push({
1250
+ ts: entry.ts || entry.timestamp || '',
1251
+ suggestedSkill,
1252
+ message: message.substring(0, 120),
1253
+ index: allEntries.indexOf(entry)
1254
+ });
1255
+ }
1256
+ }
1257
+
1258
+ // Track PBR skill usages
1259
+ const skill = extractSkillInvocation(entry);
1260
+ if (skill) {
1261
+ skillUsages.push({
1262
+ skill,
1263
+ ts: entry.ts || entry.timestamp || '',
1264
+ index: allEntries.indexOf(entry)
1265
+ });
1266
+ }
1267
+ }
1268
+
1269
+ if (advisories.length === 0) {
1270
+ return {
1271
+ status: 'info',
1272
+ evidence: [],
1273
+ message: `BC-09: No workflow advisories issued (enforce_pbr_skills=${enforceSetting})`
1274
+ };
1275
+ }
1276
+
1277
+ // For each advisory, check if the suggested skill was used afterward
1278
+ let heeded = 0;
1279
+ let ignored = 0;
1280
+ const ignoredExamples = [];
1281
+
1282
+ for (const advisory of advisories) {
1283
+ if (!advisory.suggestedSkill) {
1284
+ // Cannot determine if heeded without a specific suggestion
1285
+ continue;
1286
+ }
1287
+
1288
+ const wasHeeded = skillUsages.some(
1289
+ s => s.index > advisory.index && s.skill === advisory.suggestedSkill
1290
+ );
1291
+
1292
+ if (wasHeeded) {
1293
+ heeded++;
1294
+ } else {
1295
+ ignored++;
1296
+ if (ignoredExamples.length < 3) {
1297
+ const time = advisory.ts ? ` at ${advisory.ts}` : '';
1298
+ ignoredExamples.push(
1299
+ `Advisory to use /pbr:${advisory.suggestedSkill} was ignored${time}: "${advisory.message}"`
1300
+ );
1301
+ }
1302
+ }
1303
+ }
1304
+
1305
+ const total = heeded + ignored;
1306
+ const complianceRate = total > 0 ? Math.round((heeded / total) * 100) : 100;
1307
+
1308
+ const evidence = [
1309
+ `${advisories.length} workflow advisories issued, ${heeded} heeded, ${ignored} ignored (${complianceRate}% compliance rate)`,
1310
+ ...ignoredExamples
1311
+ ];
1312
+
1313
+ return {
1314
+ status: 'info',
1315
+ evidence,
1316
+ message: `BC-09: ${total} trackable advisories — ${complianceRate}% compliance rate (enforce_pbr_skills=${enforceSetting})`
1317
+ };
1318
+ }
1319
+
1320
+ // ---------------------------------------------------------------------------
1321
+ // BC-10: Unmanaged Commit Detection
1322
+ // ---------------------------------------------------------------------------
1323
+
1324
+ /**
1325
+ * BC-10: Detect git commits made outside PBR skill context.
1326
+ * Cross-references .active-skill state against git commit Bash events.
1327
+ * Commits without an active PBR skill context are "unmanaged".
1328
+ *
1329
+ * @param {string} planningDir - Path to .planning/
1330
+ * @param {Object} [_config] - Config object (unused, for API consistency)
1331
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
1332
+ */
1333
+ function checkUnmanagedCommitDetection(planningDir, _config) {
1334
+ const events = readSessionEvents(planningDir);
1335
+ const hookLogs = readHookLogs(planningDir);
1336
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
1337
+ const ta = a.ts || a.timestamp || '';
1338
+ const tb = b.ts || b.timestamp || '';
1339
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
1340
+ });
1341
+
1342
+ if (allEntries.length === 0) {
1343
+ return {
1344
+ status: 'pass',
1345
+ evidence: [],
1346
+ message: 'BC-10: No session data available — cannot assess commit management'
1347
+ };
1348
+ }
1349
+
1350
+ const evidence = [];
1351
+ const commitFormatPattern = /^(feat|fix|refactor|test|docs|chore|wip|revert)\([a-zA-Z0-9._-]+\):\s+.+/;
1352
+
1353
+ // Track active-skill state over time from hook logs
1354
+ // Hook logs from validate-commit contain the commit validation context
1355
+ const activeSkillEntries = [];
1356
+ for (const entry of allEntries) {
1357
+ // Detect active-skill state changes from hook logs
1358
+ if (entry.hook && entry.details && entry.details.active_skill != null) {
1359
+ activeSkillEntries.push({
1360
+ skill: entry.details.active_skill,
1361
+ ts: entry.ts || entry.timestamp || '',
1362
+ });
1363
+ }
1364
+ // Also detect skill invocations as implicit active-skill markers
1365
+ const skill = extractSkillInvocation(entry);
1366
+ if (skill) {
1367
+ activeSkillEntries.push({ skill, ts: entry.ts || entry.timestamp || '' });
1368
+ }
1369
+ }
1370
+
1371
+ // Find all git commit Bash events
1372
+ for (const entry of allEntries) {
1373
+ const command = (entry.tool_input && entry.tool_input.command) ||
1374
+ (entry.details && entry.details.command) || '';
1375
+ const commandStr = typeof command === 'string' ? command : JSON.stringify(command);
1376
+
1377
+ if (!/\bgit\s+commit\b/i.test(commandStr)) continue;
1378
+
1379
+ const ts = entry.ts || entry.timestamp || '';
1380
+ const time = ts ? ts.substring(11, 16) : 'unknown';
1381
+
1382
+ // Extract commit message for format check
1383
+ const msgMatch = commandStr.match(/-m\s+["']([^"']+)["']/) ||
1384
+ commandStr.match(/<<'?EOF'?\s*\n([\s\S]*?)\nEOF/);
1385
+ const commitMsg = msgMatch ? msgMatch[1].trim().split('\n')[0].trim() : '';
1386
+
1387
+ // Check if there was an active PBR skill at the time of this commit
1388
+ // Look at the most recent active-skill entry before this commit timestamp
1389
+ const priorSkills = activeSkillEntries.filter(s => s.ts <= ts || !ts);
1390
+ const lastSkill = priorSkills.length > 0 ? priorSkills[priorSkills.length - 1] : null;
1391
+
1392
+ // Also check validate-commit hook logs for this commit — if the hook fired,
1393
+ // there may be active-skill info
1394
+ const commitHookEntry = allEntries.find(e =>
1395
+ e.hook === 'validate-commit' &&
1396
+ e.details && e.details.message === commitMsg
1397
+ );
1398
+ const hookActiveSkill = commitHookEntry && commitHookEntry.details &&
1399
+ commitHookEntry.details.active_skill;
1400
+
1401
+ const hasActiveSkill = (lastSkill && lastSkill.skill) || hookActiveSkill;
1402
+
1403
+ if (!hasActiveSkill) {
1404
+ // No active skill context detected
1405
+ const hasGoodFormat = commitFormatPattern.test(commitMsg);
1406
+ const formatNote = hasGoodFormat ? '' : ' (also lacks conventional format)';
1407
+ evidence.push(
1408
+ `Commit '${commitMsg || '(unparseable)'}' at ${time} — no active PBR skill context detected${formatNote}`
1409
+ );
1410
+ }
1411
+ }
1412
+
1413
+ if (evidence.length > 0) {
1414
+ return {
1415
+ status: 'warn',
1416
+ evidence,
1417
+ message: `BC-10: Found ${evidence.length} unmanaged commit(s) outside PBR skill context`
1418
+ };
1419
+ }
1420
+
1421
+ return {
1422
+ status: 'pass',
1423
+ evidence: [],
1424
+ message: 'BC-10: All commits were within PBR skill context'
1425
+ };
1426
+ }
1427
+
1428
+ // ---------------------------------------------------------------------------
1429
+ // BC-11: Context Delegation Threshold
1430
+ // ---------------------------------------------------------------------------
1431
+
1432
+ /**
1433
+ * BC-11: Detect when context exceeded inline_context_cap_pct but no subagent was spawned.
1434
+ * Uses config.workflow.inline_context_cap_pct threshold.
1435
+ *
1436
+ * @param {string} planningDir - Path to .planning/
1437
+ * @param {Object} [config] - Config object with workflow.inline_context_cap_pct
1438
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
1439
+ */
1440
+ function checkContextDelegationThreshold(planningDir, config) {
1441
+ const hookLogs = readHookLogs(planningDir);
1442
+ const events = readSessionEvents(planningDir);
1443
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
1444
+ const ta = a.ts || a.timestamp || '';
1445
+ const tb = b.ts || b.timestamp || '';
1446
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
1447
+ });
1448
+
1449
+ if (allEntries.length === 0) {
1450
+ return {
1451
+ status: 'pass',
1452
+ evidence: [],
1453
+ message: 'BC-11: No session data available — cannot assess context delegation'
1454
+ };
1455
+ }
1456
+
1457
+ // Read inline_context_cap_pct from config (default 50)
1458
+ const capPct = (config && config.workflow && config.workflow.inline_context_cap_pct != null)
1459
+ ? config.workflow.inline_context_cap_pct
1460
+ : 50;
1461
+
1462
+ const evidence = [];
1463
+
1464
+ // Find context usage reports from suggest-compact or track-context-budget hooks
1465
+ for (let i = 0; i < allEntries.length; i++) {
1466
+ const entry = allEntries[i];
1467
+
1468
+ // Look for context percentage reports
1469
+ const isContextHook = entry.hook === 'suggest-compact' ||
1470
+ entry.hook === 'track-context-budget' ||
1471
+ entry.hook === 'context-budget-check';
1472
+
1473
+ if (!isContextHook) continue;
1474
+
1475
+ // Extract context percentage from entry details
1476
+ let pctUsed = null;
1477
+ if (entry.details) {
1478
+ pctUsed = entry.details.pct_used || entry.details.estimated_percent ||
1479
+ entry.details.percent || entry.details.context_pct || null;
1480
+ // Also check tier info — DEGRADING is typically >=50%, POOR >=70%
1481
+ if (pctUsed == null && entry.details.tier) {
1482
+ if (entry.details.tier === 'POOR' || entry.details.tier === 'CRITICAL') pctUsed = 70;
1483
+ else if (entry.details.tier === 'DEGRADING') pctUsed = 55;
1484
+ }
1485
+ }
1486
+
1487
+ if (pctUsed == null || pctUsed <= capPct) continue;
1488
+
1489
+ const ts = entry.ts || entry.timestamp || '';
1490
+ const time = ts ? ts.substring(11, 16) : 'unknown';
1491
+
1492
+ // Check if a Task (subagent) was spawned in the next 5 events
1493
+ const lookAhead = allEntries.slice(i + 1, i + 6);
1494
+ const taskSpawned = lookAhead.some(e =>
1495
+ e.tool === 'Task' ||
1496
+ e.event === 'subagent-start' ||
1497
+ e.event === 'task-start' ||
1498
+ (e.details && e.details.tool === 'Task') ||
1499
+ (e.details && e.details.subagent_type)
1500
+ );
1501
+
1502
+ if (!taskSpawned) {
1503
+ evidence.push(
1504
+ `Context at ${pctUsed}% (cap: ${capPct}%) at ${time} but no subagent spawned in next 5 tool calls`
1505
+ );
1506
+ }
1507
+ }
1508
+
1509
+ if (evidence.length > 0) {
1510
+ return {
1511
+ status: 'warn',
1512
+ evidence,
1513
+ message: `BC-11: Context delegation threshold breached ${evidence.length} time(s) without subagent spawn`
1514
+ };
1515
+ }
1516
+
1517
+ return {
1518
+ status: 'pass',
1519
+ evidence: [],
1520
+ message: `BC-11: Context delegation threshold (${capPct}%) respected — subagents spawned when needed`
1521
+ };
1522
+ }
1523
+
1524
+ // ---------------------------------------------------------------------------
1525
+ // BC-12: Skill Self-Read Prevention
1526
+ // ---------------------------------------------------------------------------
1527
+
1528
+ /**
1529
+ * BC-12: Detect skills that wasted tokens reading their own SKILL.md.
1530
+ * Claude Code auto-loads SKILL.md for the active skill, so reading it again
1531
+ * is a token waste.
1532
+ *
1533
+ * @param {string} planningDir - Path to .planning/
1534
+ * @param {Object} [_config] - Config object (unused, for API consistency)
1535
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
1536
+ */
1537
+ function checkSkillSelfReadPrevention(planningDir, _config) {
1538
+ const events = readSessionEvents(planningDir);
1539
+ const hookLogs = readHookLogs(planningDir);
1540
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
1541
+ const ta = a.ts || a.timestamp || '';
1542
+ const tb = b.ts || b.timestamp || '';
1543
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
1544
+ });
1545
+
1546
+ if (allEntries.length === 0) {
1547
+ return {
1548
+ status: 'pass',
1549
+ evidence: [],
1550
+ message: 'BC-12: No session data available — cannot assess skill self-reads'
1551
+ };
1552
+ }
1553
+
1554
+ const evidence = [];
1555
+
1556
+ // Track active-skill state and detect Read events for SKILL.md files
1557
+ let currentSkill = null;
1558
+
1559
+ for (const entry of allEntries) {
1560
+ // Update current active skill from hook logs
1561
+ if (entry.hook && entry.details && entry.details.active_skill != null) {
1562
+ currentSkill = entry.details.active_skill;
1563
+ }
1564
+ // Also update from skill invocations
1565
+ const invokedSkill = extractSkillInvocation(entry);
1566
+ if (invokedSkill) {
1567
+ currentSkill = invokedSkill;
1568
+ }
1569
+
1570
+ // Skip entries from agent contexts (agents may legitimately read skill files)
1571
+ if (entry.task_id || entry.subagent ||
1572
+ (entry.details && (entry.details.subagent_type || entry.details.task_id || entry.details.inside_task))) {
1573
+ continue;
1574
+ }
1575
+
1576
+ // Detect Read tool events targeting SKILL.md files
1577
+ const isRead = entry.tool === 'Read' ||
1578
+ (entry.details && entry.details.tool === 'Read');
1579
+ if (!isRead) continue;
1580
+
1581
+ const filePath = (entry.tool_input && entry.tool_input.file_path) ||
1582
+ (entry.details && (entry.details.file_path || entry.details.file)) ||
1583
+ entry.file || entry.path || '';
1584
+ const normalized = filePath.replace(/\\/g, '/');
1585
+
1586
+ // Check if this is a SKILL.md file under /skills/
1587
+ if (!/\/skills\//.test(normalized) || !/SKILL\.md$/i.test(normalized)) continue;
1588
+
1589
+ // Extract skill name from path: skills/{skill-name}/SKILL.md
1590
+ const skillMatch = normalized.match(/\/skills\/([^/]+)\/SKILL\.md$/i);
1591
+ if (!skillMatch) continue;
1592
+ const readSkillName = skillMatch[1];
1593
+
1594
+ // Check if the active skill matches the SKILL.md being read
1595
+ if (currentSkill && readSkillName === currentSkill) {
1596
+ const ts = entry.ts || entry.timestamp || '';
1597
+ const time = ts ? ts.substring(11, 16) : 'unknown';
1598
+ evidence.push(
1599
+ `${currentSkill} skill read its own skills/${currentSkill}/SKILL.md at ${time} — wasted tokens`
1600
+ );
1601
+ }
1602
+ }
1603
+
1604
+ if (evidence.length > 0) {
1605
+ return {
1606
+ status: 'info',
1607
+ evidence,
1608
+ message: `BC-12: Found ${evidence.length} skill self-read(s) — token waste detected`
1609
+ };
1610
+ }
1611
+
1612
+ return {
1613
+ status: 'pass',
1614
+ evidence: [],
1615
+ message: 'BC-12: No skill self-reads detected'
1616
+ };
1617
+ }
1618
+
1619
+ // ---------------------------------------------------------------------------
1620
+ // BC-13: Hook Output Effectiveness
1621
+ // ---------------------------------------------------------------------------
1622
+
1623
+ /**
1624
+ * BC-13: Check whether hook advisory outputs were actually followed by the LLM.
1625
+ * Reads hook logs for additionalContext/warning outputs, then checks subsequent
1626
+ * session events to see if the LLM's next action aligned with the advisory.
1627
+ *
1628
+ * @param {string} planningDir - Path to .planning/
1629
+ * @param {Object} [_config] - Config object (unused, for API consistency)
1630
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
1631
+ */
1632
+ function checkHookOutputEffectiveness(planningDir, _config) {
1633
+ const hookLogs = readHookLogs(planningDir);
1634
+ const events = readSessionEvents(planningDir);
1635
+ const allEntries = [...events, ...hookLogs].sort((a, b) => {
1636
+ const ta = a.ts || a.timestamp || '';
1637
+ const tb = b.ts || b.timestamp || '';
1638
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
1639
+ });
1640
+
1641
+ if (allEntries.length === 0) {
1642
+ return {
1643
+ status: 'pass',
1644
+ evidence: [],
1645
+ message: 'BC-13: No session data available — cannot assess hook output effectiveness'
1646
+ };
1647
+ }
1648
+
1649
+ // Find hook entries that produced advisory output (additionalContext, warnings)
1650
+ const advisoryHookEntries = [];
1651
+ for (let i = 0; i < allEntries.length; i++) {
1652
+ const entry = allEntries[i];
1653
+ if (!entry.hook) continue;
1654
+
1655
+ const hasAdvisory =
1656
+ (entry.details && entry.details.additionalContext) ||
1657
+ (entry.details && entry.details.warning) ||
1658
+ (entry.details && entry.details.level === 'advisory') ||
1659
+ (entry.output && typeof entry.output === 'string' && entry.output.includes('additionalContext'));
1660
+
1661
+ if (hasAdvisory) {
1662
+ // Extract advisory content
1663
+ const advisory = (entry.details && entry.details.additionalContext) ||
1664
+ (entry.details && entry.details.warning) ||
1665
+ (entry.details && entry.details.message) || '';
1666
+ advisoryHookEntries.push({ index: i, hook: entry.hook, advisory, ts: entry.ts || entry.timestamp || '' });
1667
+ }
1668
+ }
1669
+
1670
+ if (advisoryHookEntries.length === 0) {
1671
+ return {
1672
+ status: 'info',
1673
+ evidence: ['No hook advisory outputs found in logs'],
1674
+ message: 'BC-13: No hook advisories detected — nothing to measure effectiveness against'
1675
+ };
1676
+ }
1677
+
1678
+ // For each advisory, check if the next LLM action appears to comply
1679
+ // Heuristic: if the advisory mentions "block" or "stop" and the next tool call
1680
+ // is in the same file/area, the advisory was ignored
1681
+ let followed = 0;
1682
+ let ignored = 0;
1683
+ const evidence = [];
1684
+
1685
+ for (const adv of advisoryHookEntries) {
1686
+ // Look at next 3 entries after the advisory
1687
+ const lookAhead = allEntries.slice(adv.index + 1, adv.index + 4);
1688
+
1689
+ // Check if advisory suggested stopping/blocking and LLM continued same action
1690
+ const advisoryStr = typeof adv.advisory === 'string' ? adv.advisory : JSON.stringify(adv.advisory);
1691
+ const suggestsStop = /\b(stop|block|avoid|do not|don't|warning|caution)\b/i.test(advisoryStr);
1692
+
1693
+ if (suggestsStop) {
1694
+ // If the LLM's next action is a Write/Edit to the same area, advisory was ignored
1695
+ const nextWrite = lookAhead.find(e =>
1696
+ e.tool === 'Write' || e.tool === 'Edit' ||
1697
+ (e.details && (e.details.tool === 'Write' || e.details.tool === 'Edit'))
1698
+ );
1699
+ if (nextWrite) {
1700
+ ignored++;
1701
+ if (evidence.length < 3) {
1702
+ const time = adv.ts ? ` at ${adv.ts.substring(11, 16)}` : '';
1703
+ evidence.push(`${adv.hook} advisory${time} suggested caution but LLM proceeded with write`);
1704
+ }
1705
+ } else {
1706
+ followed++;
1707
+ }
1708
+ } else {
1709
+ // Non-blocking advisory — assume followed unless we see contradictory evidence
1710
+ followed++;
1711
+ }
1712
+ }
1713
+
1714
+ const total = followed + ignored;
1715
+ const complianceRate = total > 0 ? Math.round((followed / total) * 100) : 100;
1716
+
1717
+ evidence.unshift(`Hook advisories: ${total} total, ${followed} followed, ${ignored} ignored (${complianceRate}% compliance)`);
1718
+
1719
+ let status;
1720
+ if (complianceRate >= 70) {
1721
+ status = 'pass';
1722
+ } else {
1723
+ status = 'warn';
1724
+ }
1725
+
1726
+ return {
1727
+ status,
1728
+ evidence,
1729
+ message: `BC-13: Hook output effectiveness: ${complianceRate}% of advisories followed (${followed}/${total})`
1730
+ };
1731
+ }
1732
+
1733
+ // ---------------------------------------------------------------------------
1734
+ // BC-14: Agent Scope Compliance
1735
+ // ---------------------------------------------------------------------------
1736
+
1737
+ /**
1738
+ * BC-14: Check that agents only modified files listed in their PLAN.md files_modified.
1739
+ * Compares SUMMARY.md key_files against PLAN.md files_modified for each phase.
1740
+ *
1741
+ * @param {string} planningDir - Path to .planning/
1742
+ * @param {Object} [_config] - Config object (unused, for API consistency)
1743
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
1744
+ */
1745
+ function checkAgentScopeCompliance(planningDir, _config) {
1746
+ const phasesDir = path.join(planningDir, 'phases');
1747
+ if (!fs.existsSync(phasesDir)) {
1748
+ return {
1749
+ status: 'info',
1750
+ evidence: ['No phases directory found'],
1751
+ message: 'BC-14: No phases directory — cannot assess agent scope compliance'
1752
+ };
1753
+ }
1754
+
1755
+ let phaseDirs;
1756
+ try {
1757
+ phaseDirs = fs.readdirSync(phasesDir, { withFileTypes: true })
1758
+ .filter(d => d.isDirectory())
1759
+ .map(d => d.name);
1760
+ } catch (_e) {
1761
+ return {
1762
+ status: 'info',
1763
+ evidence: ['Could not read phases directory'],
1764
+ message: 'BC-14: Could not read phases directory'
1765
+ };
1766
+ }
1767
+
1768
+ const evidence = [];
1769
+ let totalPlans = 0;
1770
+ let outOfScope = 0;
1771
+
1772
+ for (const phaseDir of phaseDirs) {
1773
+ const phaseFullPath = path.join(phasesDir, phaseDir);
1774
+
1775
+ // Find PLAN files
1776
+ let files;
1777
+ try {
1778
+ files = fs.readdirSync(phaseFullPath).filter(f => /^PLAN.*\.md$/i.test(f));
1779
+ } catch (_e) {
1780
+ continue;
1781
+ }
1782
+
1783
+ for (const planFile of files) {
1784
+ // Extract plan ID from filename (e.g., PLAN-01.md -> 01)
1785
+ const planIdMatch = planFile.match(/PLAN-?(\d+)/i);
1786
+ if (!planIdMatch) continue;
1787
+ const planId = planIdMatch[1];
1788
+
1789
+ // Read PLAN file for files_modified
1790
+ let planContent;
1791
+ try {
1792
+ planContent = fs.readFileSync(path.join(phaseFullPath, planFile), 'utf8');
1793
+ } catch (_e) {
1794
+ continue;
1795
+ }
1796
+
1797
+ // Extract files_modified from YAML frontmatter
1798
+ const fmMatch = planContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
1799
+ if (!fmMatch) continue;
1800
+ const fm = fmMatch[1];
1801
+
1802
+ const plannedFiles = [];
1803
+ const fmLines = fm.split(/\r?\n/);
1804
+ let inFilesModified = false;
1805
+ for (const line of fmLines) {
1806
+ if (/^\s*files_modified\s*:/.test(line)) {
1807
+ inFilesModified = true;
1808
+ continue;
1809
+ }
1810
+ if (inFilesModified) {
1811
+ if (/^\s*-\s+"?(.+?)"?\s*$/.test(line)) {
1812
+ const fileMatch = line.match(/^\s*-\s+"?(.+?)"?\s*$/);
1813
+ if (fileMatch) plannedFiles.push(fileMatch[1].trim());
1814
+ } else if (/^\s*\w/.test(line)) {
1815
+ inFilesModified = false;
1816
+ }
1817
+ }
1818
+ }
1819
+
1820
+ if (plannedFiles.length === 0) continue;
1821
+
1822
+ // Find matching SUMMARY file
1823
+ const summaryPattern = new RegExp(`SUMMARY.*${planId}.*\\.md$`, 'i');
1824
+ let summaryFiles;
1825
+ try {
1826
+ summaryFiles = fs.readdirSync(phaseFullPath).filter(f => summaryPattern.test(f));
1827
+ } catch (_e) {
1828
+ continue;
1829
+ }
1830
+
1831
+ for (const summaryFile of summaryFiles) {
1832
+ let summaryContent;
1833
+ try {
1834
+ summaryContent = fs.readFileSync(path.join(phaseFullPath, summaryFile), 'utf8');
1835
+ } catch (_e) {
1836
+ continue;
1837
+ }
1838
+
1839
+ // Extract key_files from SUMMARY frontmatter
1840
+ const sfmMatch = summaryContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
1841
+ if (!sfmMatch) continue;
1842
+ const sfm = sfmMatch[1];
1843
+
1844
+ const actualFiles = [];
1845
+ const sfmLines = sfm.split(/\r?\n/);
1846
+ let inKeyFiles = false;
1847
+ for (const line of sfmLines) {
1848
+ if (/^\s*key_files\s*:/.test(line)) {
1849
+ inKeyFiles = true;
1850
+ continue;
1851
+ }
1852
+ if (inKeyFiles) {
1853
+ if (/^\s*-\s+"?(.+?)"?\s*$/.test(line)) {
1854
+ const fileMatch = line.match(/^\s*-\s+"?(.+?)"?\s*$/);
1855
+ if (fileMatch) {
1856
+ // key_files may have ": description" suffix, strip it
1857
+ const filePath = fileMatch[1].split(':')[0].trim();
1858
+ actualFiles.push(filePath);
1859
+ }
1860
+ } else if (/^\s*\w/.test(line)) {
1861
+ inKeyFiles = false;
1862
+ }
1863
+ }
1864
+ }
1865
+
1866
+ totalPlans++;
1867
+
1868
+ // Compare: any actualFiles not in plannedFiles?
1869
+ const outOfScopeFiles = actualFiles.filter(af =>
1870
+ !plannedFiles.some(pf => af.includes(pf) || pf.includes(af))
1871
+ );
1872
+
1873
+ if (outOfScopeFiles.length > 0) {
1874
+ outOfScope++;
1875
+ evidence.push(
1876
+ `${phaseDir}/${planFile}: ${outOfScopeFiles.length} file(s) modified outside plan scope: ${outOfScopeFiles.slice(0, 3).join(', ')}`
1877
+ );
1878
+ }
1879
+ }
1880
+ }
1881
+ }
1882
+
1883
+ if (totalPlans === 0) {
1884
+ return {
1885
+ status: 'info',
1886
+ evidence: ['No PLAN/SUMMARY pairs found to compare'],
1887
+ message: 'BC-14: No plan/summary pairs found — cannot assess scope compliance'
1888
+ };
1889
+ }
1890
+
1891
+ evidence.unshift(`Checked ${totalPlans} plan(s): ${outOfScope} had out-of-scope modifications`);
1892
+
1893
+ if (outOfScope > 0) {
1894
+ return {
1895
+ status: 'warn',
1896
+ evidence,
1897
+ message: `BC-14: ${outOfScope}/${totalPlans} plan(s) had files modified outside declared scope`
1898
+ };
1899
+ }
1900
+
1901
+ return {
1902
+ status: 'pass',
1903
+ evidence,
1904
+ message: `BC-14: All ${totalPlans} plan(s) stayed within declared file scope`
1905
+ };
1906
+ }
1907
+
1908
+ // ---------------------------------------------------------------------------
1909
+ // BC-15: Agent Plan Adherence
1910
+ // ---------------------------------------------------------------------------
1911
+
1912
+ /**
1913
+ * BC-15: Check that executor task completion counts match planned task counts.
1914
+ * Compares SUMMARY.md tasks_completed against PLAN.md <task> block count.
1915
+ *
1916
+ * @param {string} planningDir - Path to .planning/
1917
+ * @param {Object} [_config] - Config object (unused, for API consistency)
1918
+ * @returns {{ status: string, evidence: Array<string>, message: string }}
1919
+ */
1920
+ function checkAgentPlanAdherence(planningDir, _config) {
1921
+ const phasesDir = path.join(planningDir, 'phases');
1922
+ if (!fs.existsSync(phasesDir)) {
1923
+ return {
1924
+ status: 'info',
1925
+ evidence: ['No phases directory found'],
1926
+ message: 'BC-15: No phases directory — cannot assess plan adherence'
1927
+ };
1928
+ }
1929
+
1930
+ let phaseDirs;
1931
+ try {
1932
+ phaseDirs = fs.readdirSync(phasesDir, { withFileTypes: true })
1933
+ .filter(d => d.isDirectory())
1934
+ .map(d => d.name);
1935
+ } catch (_e) {
1936
+ return {
1937
+ status: 'info',
1938
+ evidence: ['Could not read phases directory'],
1939
+ message: 'BC-15: Could not read phases directory'
1940
+ };
1941
+ }
1942
+
1943
+ const evidence = [];
1944
+ let totalPlans = 0;
1945
+ let mismatches = 0;
1946
+
1947
+ for (const phaseDir of phaseDirs) {
1948
+ const phaseFullPath = path.join(phasesDir, phaseDir);
1949
+
1950
+ let files;
1951
+ try {
1952
+ files = fs.readdirSync(phaseFullPath).filter(f => /^PLAN.*\.md$/i.test(f));
1953
+ } catch (_e) {
1954
+ continue;
1955
+ }
1956
+
1957
+ for (const planFile of files) {
1958
+ const planIdMatch = planFile.match(/PLAN-?(\d+)/i);
1959
+ if (!planIdMatch) continue;
1960
+ const planId = planIdMatch[1];
1961
+
1962
+ // Count <task> blocks in PLAN
1963
+ let planContent;
1964
+ try {
1965
+ planContent = fs.readFileSync(path.join(phaseFullPath, planFile), 'utf8');
1966
+ } catch (_e) {
1967
+ continue;
1968
+ }
1969
+
1970
+ const taskMatches = planContent.match(/<task\s/g);
1971
+ const plannedTasks = taskMatches ? taskMatches.length : 0;
1972
+ if (plannedTasks === 0) continue;
1973
+
1974
+ // Find matching SUMMARY
1975
+ const summaryPattern = new RegExp(`SUMMARY.*${planId}.*\\.md$`, 'i');
1976
+ let summaryFiles;
1977
+ try {
1978
+ summaryFiles = fs.readdirSync(phaseFullPath).filter(f => summaryPattern.test(f));
1979
+ } catch (_e) {
1980
+ continue;
1981
+ }
1982
+
1983
+ for (const summaryFile of summaryFiles) {
1984
+ let summaryContent;
1985
+ try {
1986
+ summaryContent = fs.readFileSync(path.join(phaseFullPath, summaryFile), 'utf8');
1987
+ } catch (_e) {
1988
+ continue;
1989
+ }
1990
+
1991
+ totalPlans++;
1992
+
1993
+ // Extract tasks_completed from SUMMARY frontmatter
1994
+ const fmMatch = summaryContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
1995
+ if (!fmMatch) {
1996
+ mismatches++;
1997
+ evidence.push(`${phaseDir}/${summaryFile}: no frontmatter — cannot determine tasks_completed`);
1998
+ continue;
1999
+ }
2000
+
2001
+ const fm = fmMatch[1];
2002
+ const completedMatch = fm.match(/tasks_completed\s*:\s*(\d+)/);
2003
+ const totalMatch = fm.match(/tasks_total\s*:\s*(\d+)/);
2004
+
2005
+ const completedTasks = completedMatch ? parseInt(completedMatch[1], 10) : null;
2006
+ const reportedTotal = totalMatch ? parseInt(totalMatch[1], 10) : null;
2007
+
2008
+ if (completedTasks === null) {
2009
+ // Check status field as fallback
2010
+ const statusMatch = fm.match(/status\s*:\s*["']?(\w+)/);
2011
+ const status = statusMatch ? statusMatch[1] : 'unknown';
2012
+ if (status === 'complete') {
2013
+ // Assume all tasks completed if status is complete
2014
+ continue;
2015
+ }
2016
+ mismatches++;
2017
+ evidence.push(`${phaseDir}/${summaryFile}: no tasks_completed field (status: ${status})`);
2018
+ continue;
2019
+ }
2020
+
2021
+ // Check: planned == completed?
2022
+ if (completedTasks !== plannedTasks) {
2023
+ mismatches++;
2024
+ evidence.push(
2025
+ `${phaseDir}/${planFile}: planned ${plannedTasks} tasks, completed ${completedTasks}` +
2026
+ (reportedTotal ? ` (reported total: ${reportedTotal})` : '')
2027
+ );
2028
+ }
2029
+ }
2030
+ }
2031
+ }
2032
+
2033
+ if (totalPlans === 0) {
2034
+ return {
2035
+ status: 'info',
2036
+ evidence: ['No PLAN/SUMMARY pairs found to compare'],
2037
+ message: 'BC-15: No plan/summary pairs found — cannot assess plan adherence'
2038
+ };
2039
+ }
2040
+
2041
+ evidence.unshift(`Checked ${totalPlans} plan(s): ${mismatches} had task count mismatches`);
2042
+
2043
+ if (mismatches > 0) {
2044
+ return {
2045
+ status: 'warn',
2046
+ evidence,
2047
+ message: `BC-15: ${mismatches}/${totalPlans} plan(s) had task completion mismatches`
2048
+ };
2049
+ }
2050
+
2051
+ return {
2052
+ status: 'pass',
2053
+ evidence,
2054
+ message: `BC-15: All ${totalPlans} plan(s) completed their planned task counts`
2055
+ };
2056
+ }
2057
+
2058
+ // ---------------------------------------------------------------------------
2059
+ // Exports
2060
+ // ---------------------------------------------------------------------------
2061
+
2062
+ module.exports = {
2063
+ // Shared helpers
2064
+ getLogsDir,
2065
+ readJsonlFiles,
2066
+ readSessionEvents,
2067
+ readHookLogs,
2068
+ // BC-01
2069
+ checkSkillSequenceCompliance,
2070
+ // BC-02
2071
+ checkStateMachineTransitions,
2072
+ // BC-03
2073
+ checkPreConditionVerification,
2074
+ // BC-04
2075
+ checkPostConditionVerification,
2076
+ // BC-05
2077
+ checkOrchestratorBudgetDiscipline,
2078
+ // BC-06
2079
+ checkArtifactCreationOrder,
2080
+ // BC-07
2081
+ checkCriticalMarkerCompliance,
2082
+ // BC-08
2083
+ checkGateCompliance,
2084
+ // BC-09
2085
+ checkEnforceWorkflowAdvisory,
2086
+ // BC-10
2087
+ checkUnmanagedCommitDetection,
2088
+ // BC-11
2089
+ checkContextDelegationThreshold,
2090
+ // BC-12
2091
+ checkSkillSelfReadPrevention,
2092
+ // BC-13
2093
+ checkHookOutputEffectiveness,
2094
+ // BC-14
2095
+ checkAgentScopeCompliance,
2096
+ // BC-15
2097
+ checkAgentPlanAdherence,
2098
+ };