session-orchestrator 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (762) hide show
  1. package/.claude-plugin/marketplace.json +29 -0
  2. package/.claude-plugin/plugin.json +18 -0
  3. package/.codex-plugin/agents/explorer.toml +14 -0
  4. package/.codex-plugin/agents/session-reviewer.toml +23 -0
  5. package/.codex-plugin/agents/wave-worker.toml +15 -0
  6. package/.codex-plugin/config.toml +20 -0
  7. package/.codex-plugin/plugin.json +37 -0
  8. package/.cursor/rules/000-session-orchestrator.mdc +73 -0
  9. package/.cursor/rules/010-session-workflow.mdc +170 -0
  10. package/.cursor/rules/020-quality-gates.mdc +128 -0
  11. package/.cursor/rules/030-wave-execution.mdc +216 -0
  12. package/.cursor/rules/040-discovery.mdc +242 -0
  13. package/.cursor/rules/050-plan.mdc +235 -0
  14. package/.cursor/rules/060-evolve.mdc +232 -0
  15. package/.cursor/rules/070-gitlab-ops.mdc +246 -0
  16. package/.cursor/rules/080-ecosystem-health.mdc +145 -0
  17. package/.mcp.json +8 -0
  18. package/CHANGELOG.md +1544 -0
  19. package/LICENSE +21 -0
  20. package/NOTICE +64 -0
  21. package/README.md +242 -0
  22. package/SECURITY.md +90 -0
  23. package/agents/AGENTS.md +136 -0
  24. package/agents/analyst.md +99 -0
  25. package/agents/architect-reviewer.md +93 -0
  26. package/agents/code-implementer.md +106 -0
  27. package/agents/db-specialist.md +104 -0
  28. package/agents/dialectic-deriver.md +139 -0
  29. package/agents/docs-writer.md +113 -0
  30. package/agents/eval-judge.md +146 -0
  31. package/agents/memory-proposal-collector.md +297 -0
  32. package/agents/qa-strategist.md +102 -0
  33. package/agents/schemas/analyst.schema.json +46 -0
  34. package/agents/schemas/architect-reviewer.schema.json +50 -0
  35. package/agents/schemas/code-implementer.schema.json +61 -0
  36. package/agents/schemas/db-specialist.schema.json +80 -0
  37. package/agents/schemas/docs-writer.schema.json +56 -0
  38. package/agents/schemas/persona-panel-sidecar.schema.json +245 -0
  39. package/agents/schemas/qa-strategist.schema.json +46 -0
  40. package/agents/schemas/security-reviewer.schema.json +86 -0
  41. package/agents/schemas/session-reviewer.schema.json +69 -0
  42. package/agents/schemas/test-writer.schema.json +69 -0
  43. package/agents/schemas/ui-developer.schema.json +90 -0
  44. package/agents/schemas/ux-evaluator.schema.json +51 -0
  45. package/agents/security-reviewer.md +236 -0
  46. package/agents/session-reviewer.md +201 -0
  47. package/agents/skill-applied-judge.md +122 -0
  48. package/agents/test-writer.md +123 -0
  49. package/agents/ui-developer.md +109 -0
  50. package/agents/ux-evaluator.md +161 -0
  51. package/assets/icon.svg +11 -0
  52. package/assets/og-card.png +0 -0
  53. package/assets/og-card.svg +47 -0
  54. package/commands/autopilot-multi.md +74 -0
  55. package/commands/autopilot.md +80 -0
  56. package/commands/bootstrap.md +56 -0
  57. package/commands/brainstorm.md +48 -0
  58. package/commands/close.md +24 -0
  59. package/commands/debug.md +36 -0
  60. package/commands/discovery.md +32 -0
  61. package/commands/dispatcher.md +59 -0
  62. package/commands/eval.md +28 -0
  63. package/commands/evolve.md +10 -0
  64. package/commands/go.md +41 -0
  65. package/commands/grill.md +45 -0
  66. package/commands/harness-audit.md +26 -0
  67. package/commands/memory-cleanup.md +25 -0
  68. package/commands/persona-panel.md +121 -0
  69. package/commands/plan.md +15 -0
  70. package/commands/portfolio.md +97 -0
  71. package/commands/reconcile.md +23 -0
  72. package/commands/repo-audit.md +24 -0
  73. package/commands/session.md +30 -0
  74. package/commands/spinout.md +15 -0
  75. package/commands/sunset-review.md +27 -0
  76. package/commands/templates-ack.md +96 -0
  77. package/commands/test.md +97 -0
  78. package/docs/README.md +105 -0
  79. package/docs/USER-GUIDE.md +1403 -0
  80. package/docs/ci-setup.md +81 -0
  81. package/docs/codex-setup.md +142 -0
  82. package/docs/components.md +74 -0
  83. package/docs/cursor-setup.md +104 -0
  84. package/docs/events-schema.md +81 -0
  85. package/docs/migration-v3.md +148 -0
  86. package/docs/owner-config-schema.md +154 -0
  87. package/docs/persona-panel.md +433 -0
  88. package/docs/pi-setup.md +115 -0
  89. package/docs/plugin-architecture-v3.md +296 -0
  90. package/docs/pm-skills-marketplace.md +114 -0
  91. package/docs/policy-cache-validation-2026-04-28.md +118 -0
  92. package/docs/rule-authoring.md +316 -0
  93. package/docs/session-config-reference.md +1439 -0
  94. package/docs/session-config-template.md +961 -0
  95. package/docs/vault-docs-architecture.md +297 -0
  96. package/hooks/_lib/lock-bootstrap.mjs +272 -0
  97. package/hooks/_lib/lock-reconcile.mjs +93 -0
  98. package/hooks/_lib/profile-gate.mjs +95 -0
  99. package/hooks/_lib/transcript-history.mjs +211 -0
  100. package/hooks/agent-teams-h3-test.sh +362 -0
  101. package/hooks/config-protection.mjs +0 -0
  102. package/hooks/cwd-change-restore.mjs +131 -0
  103. package/hooks/enforce-commands.mjs +179 -0
  104. package/hooks/enforce-scope.mjs +273 -0
  105. package/hooks/hooks-codex.json +60 -0
  106. package/hooks/hooks-cursor.json +15 -0
  107. package/hooks/hooks-pi.json +115 -0
  108. package/hooks/hooks.json +215 -0
  109. package/hooks/loop-guard.mjs +260 -0
  110. package/hooks/on-session-end.mjs +217 -0
  111. package/hooks/on-session-start.mjs +660 -0
  112. package/hooks/on-stop.mjs +294 -0
  113. package/hooks/operator-steer.mjs +64 -0
  114. package/hooks/post-edit-validate.mjs +225 -0
  115. package/hooks/post-subagent-discovery-validator.mjs +398 -0
  116. package/hooks/post-tool-batch-wave-signal.mjs +328 -0
  117. package/hooks/post-tool-failure-corrective-context.mjs +248 -0
  118. package/hooks/post-tooluse-frontend-slop.mjs +184 -0
  119. package/hooks/pre-bash-destructive-guard.mjs +515 -0
  120. package/hooks/pre-bash-memory-propose-audit.mjs +206 -0
  121. package/hooks/pre-bash-staging-fence.mjs +223 -0
  122. package/hooks/pre-bash-templates-first.mjs +404 -0
  123. package/hooks/run-node.sh +72 -0
  124. package/hooks/skill-invocation-telemetry.mjs +99 -0
  125. package/hooks/subagent-telemetry.mjs +249 -0
  126. package/hooks/wave-scope-commit-guard.mjs +191 -0
  127. package/monitors/monitors.json +14 -0
  128. package/output-styles/finding-report.md +48 -0
  129. package/output-styles/session-report.md +53 -0
  130. package/output-styles/wave-summary.md +38 -0
  131. package/package.json +94 -0
  132. package/pi/extensions/session-orchestrator.ts +25 -0
  133. package/pi/prompts/autopilot-multi.md +12 -0
  134. package/pi/prompts/autopilot.md +12 -0
  135. package/pi/prompts/bootstrap.md +12 -0
  136. package/pi/prompts/brainstorm.md +12 -0
  137. package/pi/prompts/close.md +11 -0
  138. package/pi/prompts/debug.md +12 -0
  139. package/pi/prompts/discovery.md +12 -0
  140. package/pi/prompts/dispatcher.md +12 -0
  141. package/pi/prompts/eval.md +12 -0
  142. package/pi/prompts/evolve.md +12 -0
  143. package/pi/prompts/go.md +12 -0
  144. package/pi/prompts/grill.md +12 -0
  145. package/pi/prompts/harness-audit.md +12 -0
  146. package/pi/prompts/memory-cleanup.md +12 -0
  147. package/pi/prompts/persona-panel.md +12 -0
  148. package/pi/prompts/plan.md +12 -0
  149. package/pi/prompts/portfolio.md +12 -0
  150. package/pi/prompts/reconcile.md +12 -0
  151. package/pi/prompts/repo-audit.md +12 -0
  152. package/pi/prompts/session.md +12 -0
  153. package/pi/prompts/spinout.md +12 -0
  154. package/pi/prompts/sunset-review.md +12 -0
  155. package/pi/prompts/templates-ack.md +12 -0
  156. package/pi/prompts/test.md +12 -0
  157. package/rules/_index.md +51 -0
  158. package/rules/always-on/commit-discipline.md +26 -0
  159. package/rules/always-on/npm-quality-gates.md +26 -0
  160. package/rules/always-on/parallel-sessions.md +43 -0
  161. package/rules/opt-in-domain/prompt-caching.md +270 -0
  162. package/rules/opt-in-stack/backend-data.md +188 -0
  163. package/rules/opt-in-stack/backend.md +390 -0
  164. package/rules/opt-in-stack/frontend.md +98 -0
  165. package/rules/opt-in-stack/security-web.md +194 -0
  166. package/rules/opt-in-stack/swift.md +65 -0
  167. package/scripts/archive-closed-prds.mjs +416 -0
  168. package/scripts/autopilot-multi.mjs +802 -0
  169. package/scripts/autopilot.mjs +383 -0
  170. package/scripts/backfill-abandoned-sessions.mjs +265 -0
  171. package/scripts/backfill-learnings-expires.mjs +196 -0
  172. package/scripts/backfill-learnings.mjs +203 -0
  173. package/scripts/backfill-sessions.mjs +282 -0
  174. package/scripts/check-doc-consistency.sh +279 -0
  175. package/scripts/check-package-manager.mjs +445 -0
  176. package/scripts/ci/assert-vitest-green.mjs +267 -0
  177. package/scripts/codex-install.mjs +435 -0
  178. package/scripts/compute-grounding-injection.sh +186 -0
  179. package/scripts/cursor-install.mjs +113 -0
  180. package/scripts/dialectic-deriver.mjs +573 -0
  181. package/scripts/emit-event.mjs +160 -0
  182. package/scripts/emit-session.mjs +212 -0
  183. package/scripts/eval-session.mjs +262 -0
  184. package/scripts/export-hw-learnings.mjs +437 -0
  185. package/scripts/gc-stale-worktrees.mjs +666 -0
  186. package/scripts/generate-pi-prompts.mjs +127 -0
  187. package/scripts/harness-audit.mjs +287 -0
  188. package/scripts/lib/agent-frontmatter.mjs +266 -0
  189. package/scripts/lib/agent-output-schema.mjs +166 -0
  190. package/scripts/lib/agent-status.mjs +303 -0
  191. package/scripts/lib/ajv-loader.mjs +34 -0
  192. package/scripts/lib/auto-dialectic.mjs +382 -0
  193. package/scripts/lib/auto-dream.mjs +471 -0
  194. package/scripts/lib/autonomy/suitability.mjs +212 -0
  195. package/scripts/lib/autopilot/dep-graph.mjs +417 -0
  196. package/scripts/lib/autopilot/durable-telemetry.mjs +121 -0
  197. package/scripts/lib/autopilot/flags.mjs +104 -0
  198. package/scripts/lib/autopilot/kill-switches.mjs +174 -0
  199. package/scripts/lib/autopilot/loop.mjs +320 -0
  200. package/scripts/lib/autopilot/mr-draft.mjs +520 -0
  201. package/scripts/lib/autopilot/multi-killswitch.mjs +184 -0
  202. package/scripts/lib/autopilot/recent-runs.mjs +106 -0
  203. package/scripts/lib/autopilot/stall-sampler.mjs +97 -0
  204. package/scripts/lib/autopilot/telemetry.mjs +224 -0
  205. package/scripts/lib/autopilot/worktree-pipeline.mjs +605 -0
  206. package/scripts/lib/autopilot-telemetry.mjs +11 -0
  207. package/scripts/lib/autopilot.mjs +39 -0
  208. package/scripts/lib/backlog-scan.mjs +179 -0
  209. package/scripts/lib/bootstrap-lock-freshness.mjs +260 -0
  210. package/scripts/lib/bootstrap-lock-refresh.mjs +186 -0
  211. package/scripts/lib/build-live-signals.mjs +150 -0
  212. package/scripts/lib/ci-status-banner.mjs +425 -0
  213. package/scripts/lib/claude-md-budget-lint.mjs +246 -0
  214. package/scripts/lib/cli-flags.mjs +158 -0
  215. package/scripts/lib/codex/plugin-contract.mjs +610 -0
  216. package/scripts/lib/cold-start-detector.mjs +240 -0
  217. package/scripts/lib/command-blocker.mjs +458 -0
  218. package/scripts/lib/common.mjs +333 -0
  219. package/scripts/lib/config/auto-dream.mjs +77 -0
  220. package/scripts/lib/config/block-header.mjs +94 -0
  221. package/scripts/lib/config/broken-window.mjs +114 -0
  222. package/scripts/lib/config/coercers.mjs +248 -0
  223. package/scripts/lib/config/cold-start.mjs +92 -0
  224. package/scripts/lib/config/config-protection.mjs +120 -0
  225. package/scripts/lib/config/cross-repo.mjs +104 -0
  226. package/scripts/lib/config/custom-phases.mjs +213 -0
  227. package/scripts/lib/config/dialectic.mjs +92 -0
  228. package/scripts/lib/config/discovery-validator.mjs +75 -0
  229. package/scripts/lib/config/dispatcher-autonomy-capture.mjs +240 -0
  230. package/scripts/lib/config/dispatcher-autonomy.mjs +152 -0
  231. package/scripts/lib/config/docs-orchestrator.mjs +90 -0
  232. package/scripts/lib/config/docs-staleness.mjs +96 -0
  233. package/scripts/lib/config/drift-check.mjs +155 -0
  234. package/scripts/lib/config/eval.mjs +130 -0
  235. package/scripts/lib/config/events-rotation.mjs +74 -0
  236. package/scripts/lib/config/evolve.mjs +308 -0
  237. package/scripts/lib/config/frontend-slop-hook.mjs +104 -0
  238. package/scripts/lib/config/gitlab-portfolio.mjs +150 -0
  239. package/scripts/lib/config/handover-gate.mjs +106 -0
  240. package/scripts/lib/config/host-paths.mjs +76 -0
  241. package/scripts/lib/config/io.mjs +54 -0
  242. package/scripts/lib/config/loop-guard.mjs +117 -0
  243. package/scripts/lib/config/memory.mjs +150 -0
  244. package/scripts/lib/config/persona-gate-wave.mjs +258 -0
  245. package/scripts/lib/config/reconcile.mjs +205 -0
  246. package/scripts/lib/config/section-extractor.mjs +100 -0
  247. package/scripts/lib/config/skill-evolution.mjs +112 -0
  248. package/scripts/lib/config/slopcheck.mjs +99 -0
  249. package/scripts/lib/config/state-md-lock.mjs +83 -0
  250. package/scripts/lib/config/templates-first.mjs +94 -0
  251. package/scripts/lib/config/test.mjs +113 -0
  252. package/scripts/lib/config/vault-integration.mjs +201 -0
  253. package/scripts/lib/config/vault-mirror-quality.mjs +99 -0
  254. package/scripts/lib/config/vault-staleness.mjs +84 -0
  255. package/scripts/lib/config/vault-sync.mjs +96 -0
  256. package/scripts/lib/config/verification-auto-fix.mjs +84 -0
  257. package/scripts/lib/config/wave-reviewers.mjs +133 -0
  258. package/scripts/lib/config-schema.mjs +345 -0
  259. package/scripts/lib/config.mjs +474 -0
  260. package/scripts/lib/convergence-monitor.mjs +389 -0
  261. package/scripts/lib/coordinator-snapshot.mjs +371 -0
  262. package/scripts/lib/crypto-digest-utils.mjs +91 -0
  263. package/scripts/lib/discovery/helpers.mjs +127 -0
  264. package/scripts/lib/discovery/triage-state.mjs +279 -0
  265. package/scripts/lib/dispatcher/cli.mjs +257 -0
  266. package/scripts/lib/dispatcher/enumerate.mjs +243 -0
  267. package/scripts/lib/dispatcher/rank.mjs +363 -0
  268. package/scripts/lib/ecosystem-health.mjs +224 -0
  269. package/scripts/lib/ecosystem-wizard/ci-detector.mjs +18 -0
  270. package/scripts/lib/ecosystem-wizard/config-parser.mjs +54 -0
  271. package/scripts/lib/ecosystem-wizard/config-writer.mjs +287 -0
  272. package/scripts/lib/ecosystem-wizard/package-manager-detector.mjs +42 -0
  273. package/scripts/lib/ecosystem-wizard/wizard-prompt.mjs +246 -0
  274. package/scripts/lib/ecosystem-wizard.mjs +48 -0
  275. package/scripts/lib/env-check.mjs +89 -0
  276. package/scripts/lib/eval/engine.mjs +605 -0
  277. package/scripts/lib/eval/judge.mjs +433 -0
  278. package/scripts/lib/eval/report.mjs +367 -0
  279. package/scripts/lib/eval/schema.mjs +618 -0
  280. package/scripts/lib/eval/session-resolve.mjs +137 -0
  281. package/scripts/lib/eval/sink.mjs +77 -0
  282. package/scripts/lib/events-rotation.mjs +86 -0
  283. package/scripts/lib/events-schema.mjs +81 -0
  284. package/scripts/lib/events.mjs +80 -0
  285. package/scripts/lib/evolve/autonomy-verdict.mjs +461 -0
  286. package/scripts/lib/evolve/autopilot-effectiveness.mjs +293 -0
  287. package/scripts/lib/exclusivity-matrix.mjs +68 -0
  288. package/scripts/lib/fetch-baseline.mjs +311 -0
  289. package/scripts/lib/file-lock.mjs +512 -0
  290. package/scripts/lib/frontend-detect/detect.mjs +138 -0
  291. package/scripts/lib/frontend-detect/rules.mjs +295 -0
  292. package/scripts/lib/frontmatter-guard.mjs +241 -0
  293. package/scripts/lib/gates/echo-stub-detect.mjs +39 -0
  294. package/scripts/lib/gates/gate-baseline.mjs +42 -0
  295. package/scripts/lib/gates/gate-full.mjs +85 -0
  296. package/scripts/lib/gates/gate-helpers.mjs +231 -0
  297. package/scripts/lib/gates/gate-incremental.mjs +76 -0
  298. package/scripts/lib/gates/gate-per-file.mjs +55 -0
  299. package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +447 -0
  300. package/scripts/lib/gitlab-portfolio/aggregator.mjs +383 -0
  301. package/scripts/lib/gitlab-portfolio/cli.mjs +428 -0
  302. package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +289 -0
  303. package/scripts/lib/gitlab-portfolio/vcs-detect.mjs +182 -0
  304. package/scripts/lib/handover-gate.mjs +222 -0
  305. package/scripts/lib/hardening.mjs +43 -0
  306. package/scripts/lib/hardware-pattern-detector.mjs +238 -0
  307. package/scripts/lib/harness-audit/categories/category1.mjs +123 -0
  308. package/scripts/lib/harness-audit/categories/category2.mjs +145 -0
  309. package/scripts/lib/harness-audit/categories/category3.mjs +143 -0
  310. package/scripts/lib/harness-audit/categories/category4.mjs +202 -0
  311. package/scripts/lib/harness-audit/categories/category5.mjs +152 -0
  312. package/scripts/lib/harness-audit/categories/category6.mjs +211 -0
  313. package/scripts/lib/harness-audit/categories/category7.mjs +125 -0
  314. package/scripts/lib/harness-audit/categories/category8.mjs +328 -0
  315. package/scripts/lib/harness-audit/categories/category9.mjs +294 -0
  316. package/scripts/lib/harness-audit/categories/helpers.mjs +165 -0
  317. package/scripts/lib/harness-audit/categories.mjs +19 -0
  318. package/scripts/lib/historical-guard.mjs +15 -0
  319. package/scripts/lib/host-identity.mjs +262 -0
  320. package/scripts/lib/instruction-budget-guard.mjs +332 -0
  321. package/scripts/lib/io.mjs +304 -0
  322. package/scripts/lib/issue-close-strip-labels.mjs +161 -0
  323. package/scripts/lib/language-mappers/README.md +57 -0
  324. package/scripts/lib/language-mappers/index.mjs +165 -0
  325. package/scripts/lib/language-mappers/markdown.mjs +149 -0
  326. package/scripts/lib/language-mappers/python.mjs +249 -0
  327. package/scripts/lib/language-mappers/swift.mjs +201 -0
  328. package/scripts/lib/language-mappers/typescript.mjs +433 -0
  329. package/scripts/lib/learnings/expiry-sweep.mjs +164 -0
  330. package/scripts/lib/learnings/filters.mjs +43 -0
  331. package/scripts/lib/learnings/io.mjs +255 -0
  332. package/scripts/lib/learnings/schema.mjs +518 -0
  333. package/scripts/lib/learnings/surface.mjs +207 -0
  334. package/scripts/lib/learnings.mjs +42 -0
  335. package/scripts/lib/lock-reaper.mjs +648 -0
  336. package/scripts/lib/locks/index.mjs +31 -0
  337. package/scripts/lib/locks/lock-body.mjs +62 -0
  338. package/scripts/lib/locks/staging-fence-lock.mjs +267 -0
  339. package/scripts/lib/locks/state-md-lock.mjs +351 -0
  340. package/scripts/lib/loop-readiness-banner.mjs +144 -0
  341. package/scripts/lib/memory-banner.mjs +478 -0
  342. package/scripts/lib/memory-cleanup/worktree-sweep.mjs +108 -0
  343. package/scripts/lib/memory-cleanup-stamp.mjs +56 -0
  344. package/scripts/lib/memory-paths.mjs +31 -0
  345. package/scripts/lib/memory-proposals/collector.mjs +334 -0
  346. package/scripts/lib/memory-proposals/schema.mjs +289 -0
  347. package/scripts/lib/memory-proposals/sink.mjs +507 -0
  348. package/scripts/lib/memory-proposals/store.mjs +441 -0
  349. package/scripts/lib/mission-status-schema.mjs +114 -0
  350. package/scripts/lib/mode-selector/alternatives.mjs +64 -0
  351. package/scripts/lib/mode-selector/constants.mjs +29 -0
  352. package/scripts/lib/mode-selector/context-pressure.mjs +157 -0
  353. package/scripts/lib/mode-selector/rationale.mjs +55 -0
  354. package/scripts/lib/mode-selector/scoring.mjs +221 -0
  355. package/scripts/lib/mode-selector-accuracy.mjs +121 -0
  356. package/scripts/lib/mode-selector.mjs +160 -0
  357. package/scripts/lib/multi-provider-build/providers.mjs +64 -0
  358. package/scripts/lib/multi-provider-build/templating.mjs +130 -0
  359. package/scripts/lib/named-baseline-resolver.mjs +233 -0
  360. package/scripts/lib/named-vault-resolver.mjs +433 -0
  361. package/scripts/lib/owner-config/coerce.mjs +29 -0
  362. package/scripts/lib/owner-config/constants.mjs +21 -0
  363. package/scripts/lib/owner-config/defaults.mjs +50 -0
  364. package/scripts/lib/owner-config/error.mjs +19 -0
  365. package/scripts/lib/owner-config/index.mjs +13 -0
  366. package/scripts/lib/owner-config/merge.mjs +52 -0
  367. package/scripts/lib/owner-config/validate.mjs +259 -0
  368. package/scripts/lib/owner-config-banner.mjs +126 -0
  369. package/scripts/lib/owner-config-loader.mjs +159 -0
  370. package/scripts/lib/owner-config.example.yaml +72 -0
  371. package/scripts/lib/owner-config.mjs +28 -0
  372. package/scripts/lib/owner-interview.mjs +243 -0
  373. package/scripts/lib/owner-yaml.mjs +571 -0
  374. package/scripts/lib/package-manager.mjs +160 -0
  375. package/scripts/lib/path-utils.mjs +217 -0
  376. package/scripts/lib/peer-cards/merger.mjs +310 -0
  377. package/scripts/lib/peer-cards/reader.mjs +125 -0
  378. package/scripts/lib/peer-cards/schema.mjs +230 -0
  379. package/scripts/lib/peer-cards/staleness-banner.mjs +86 -0
  380. package/scripts/lib/peer-cards/writer.mjs +138 -0
  381. package/scripts/lib/peer-discovery.mjs +200 -0
  382. package/scripts/lib/persona-panel/catalog-loader.mjs +577 -0
  383. package/scripts/lib/persona-panel/consolidator.mjs +370 -0
  384. package/scripts/lib/persona-panel/persona-runner.mjs +375 -0
  385. package/scripts/lib/persona-panel/threshold.mjs +130 -0
  386. package/scripts/lib/pi-hook-bridge.mjs +328 -0
  387. package/scripts/lib/platform.mjs +266 -0
  388. package/scripts/lib/playwright-driver/runner.mjs +297 -0
  389. package/scripts/lib/plugin-root.mjs +210 -0
  390. package/scripts/lib/pre-dispatch-check.mjs +126 -0
  391. package/scripts/lib/product-repo-detect.mjs +121 -0
  392. package/scripts/lib/profiles/registry.mjs +176 -0
  393. package/scripts/lib/profiles/schema.mjs +209 -0
  394. package/scripts/lib/qg-command-drift-banner.mjs +88 -0
  395. package/scripts/lib/quality-gate/diagnostics.mjs +92 -0
  396. package/scripts/lib/quality-gate.mjs +536 -0
  397. package/scripts/lib/quality-gates-cache.mjs +228 -0
  398. package/scripts/lib/quality-gates-policy.mjs +95 -0
  399. package/scripts/lib/recommendations-v0.mjs +156 -0
  400. package/scripts/lib/reconcile/eligibility.mjs +203 -0
  401. package/scripts/lib/reconcile/emitter.mjs +244 -0
  402. package/scripts/lib/reconcile/engine.mjs +412 -0
  403. package/scripts/lib/reconcile/idempotency.mjs +239 -0
  404. package/scripts/lib/reconcile/renderer.mjs +211 -0
  405. package/scripts/lib/reconcile/writer.mjs +293 -0
  406. package/scripts/lib/reconcile-nudge-banner.mjs +284 -0
  407. package/scripts/lib/resource-probe/evaluate.mjs +190 -0
  408. package/scripts/lib/resource-probe/parsers.mjs +181 -0
  409. package/scripts/lib/resource-probe/probe-platform.mjs +300 -0
  410. package/scripts/lib/resource-probe.mjs +95 -0
  411. package/scripts/lib/rule-loader.mjs +552 -0
  412. package/scripts/lib/rules-sync.mjs +439 -0
  413. package/scripts/lib/scope-gate.mjs +496 -0
  414. package/scripts/lib/session-close-backfill.mjs +539 -0
  415. package/scripts/lib/session-discovery.mjs +256 -0
  416. package/scripts/lib/session-end/phase-skip.mjs +357 -0
  417. package/scripts/lib/session-end/worktree-cleanup.mjs +112 -0
  418. package/scripts/lib/session-id.mjs +362 -0
  419. package/scripts/lib/session-lock.mjs +703 -0
  420. package/scripts/lib/session-registry.mjs +355 -0
  421. package/scripts/lib/session-schema/aliases.mjs +71 -0
  422. package/scripts/lib/session-schema/constants.mjs +115 -0
  423. package/scripts/lib/session-schema/normalizer.mjs +66 -0
  424. package/scripts/lib/session-schema/timestamps.mjs +64 -0
  425. package/scripts/lib/session-schema/validator.mjs +453 -0
  426. package/scripts/lib/session-schema.mjs +71 -0
  427. package/scripts/lib/session-token-rollup.mjs +137 -0
  428. package/scripts/lib/sessions-staleness-banner.mjs +247 -0
  429. package/scripts/lib/skill-evolution/blast-radius-classifier.mjs +114 -0
  430. package/scripts/lib/skill-evolution/candidate-intake.mjs +270 -0
  431. package/scripts/lib/skill-evolution/config-validation-gate.mjs +279 -0
  432. package/scripts/lib/skill-evolution/engine.mjs +719 -0
  433. package/scripts/lib/skill-evolution/idempotency.mjs +279 -0
  434. package/scripts/lib/skill-evolution/mr-opener.mjs +507 -0
  435. package/scripts/lib/skill-health/join.mjs +181 -0
  436. package/scripts/lib/skill-health/score.mjs +123 -0
  437. package/scripts/lib/skill-invocations-schema.mjs +214 -0
  438. package/scripts/lib/skill-judge.mjs +348 -0
  439. package/scripts/lib/skill-judgments-schema.mjs +264 -0
  440. package/scripts/lib/slopcheck.mjs +501 -0
  441. package/scripts/lib/soul-resolve.mjs +118 -0
  442. package/scripts/lib/spiral-carryover.mjs +495 -0
  443. package/scripts/lib/state-md/body-sections.mjs +851 -0
  444. package/scripts/lib/state-md/frontmatter-mutators.mjs +453 -0
  445. package/scripts/lib/state-md/mission-status.mjs +247 -0
  446. package/scripts/lib/state-md/recommendations.mjs +57 -0
  447. package/scripts/lib/state-md/yaml-parser.mjs +234 -0
  448. package/scripts/lib/state-md-peer-guard.mjs +232 -0
  449. package/scripts/lib/state-md.mjs +53 -0
  450. package/scripts/lib/subagents-schema.mjs +309 -0
  451. package/scripts/lib/sunset/walker.mjs +1192 -0
  452. package/scripts/lib/test-runner/artifact-paths.mjs +94 -0
  453. package/scripts/lib/test-runner/fingerprint.mjs +33 -0
  454. package/scripts/lib/test-runner/issue-reconcile.mjs +770 -0
  455. package/scripts/lib/tmux-layout/layouts.mjs +224 -0
  456. package/scripts/lib/tmux-layout/telemetry-stats.mjs +100 -0
  457. package/scripts/lib/tmux-layout/telemetry.mjs +88 -0
  458. package/scripts/lib/tmux-layout/tmux-shell.mjs +82 -0
  459. package/scripts/lib/tmux-layout/vcs-detector.mjs +88 -0
  460. package/scripts/lib/validate/check-agents.mjs +457 -0
  461. package/scripts/lib/validate/check-codex-plugin.mjs +37 -0
  462. package/scripts/lib/validate/check-commands.mjs +148 -0
  463. package/scripts/lib/validate/check-component-paths.mjs +112 -0
  464. package/scripts/lib/validate/check-dead-bridge.mjs +180 -0
  465. package/scripts/lib/validate/check-hooks-symmetry.mjs +258 -0
  466. package/scripts/lib/validate/check-json-files.mjs +116 -0
  467. package/scripts/lib/validate/check-owner-leakage.mjs +1011 -0
  468. package/scripts/lib/validate/check-path-utils-canary.mjs +175 -0
  469. package/scripts/lib/validate/check-peekaboo-driver-canary.mjs +201 -0
  470. package/scripts/lib/validate/check-pi-package.mjs +110 -0
  471. package/scripts/lib/validate/check-pi-prompts.mjs +43 -0
  472. package/scripts/lib/validate/check-playwright-mcp-canary.mjs +154 -0
  473. package/scripts/lib/validate/check-plugin-json.mjs +96 -0
  474. package/scripts/lib/validate/check-plugin-monitors.mjs +206 -0
  475. package/scripts/lib/validate/check-plugin-schema.mjs +137 -0
  476. package/scripts/lib/validate/check-rules.mjs +143 -0
  477. package/scripts/lib/validate/check-session-plan-routing.mjs +154 -0
  478. package/scripts/lib/validate/check-test-fixture-shapes.mjs +280 -0
  479. package/scripts/lib/validate/check-unicode-safety.mjs +533 -0
  480. package/scripts/lib/validate/confidential-names.mjs +169 -0
  481. package/scripts/lib/validate/dead-bridge-corpus.mjs +141 -0
  482. package/scripts/lib/validate/dead-bridge-detectors.mjs +568 -0
  483. package/scripts/lib/validate/tier-inference.mjs +100 -0
  484. package/scripts/lib/validate-vendored-rules.mjs +519 -0
  485. package/scripts/lib/vault-archive.mjs +404 -0
  486. package/scripts/lib/vault-backfill/glab.mjs +164 -0
  487. package/scripts/lib/vault-backfill/manifest.mjs +75 -0
  488. package/scripts/lib/vault-backfill/template.mjs +130 -0
  489. package/scripts/lib/vault-consolidate-fs.mjs +331 -0
  490. package/scripts/lib/vault-migration-rules.mjs +155 -0
  491. package/scripts/lib/vault-mirror/auto-commit.mjs +203 -0
  492. package/scripts/lib/vault-mirror/namespace.mjs +152 -0
  493. package/scripts/lib/vault-mirror/process.mjs +567 -0
  494. package/scripts/lib/vault-mirror/pseudonym-map.mjs +164 -0
  495. package/scripts/lib/vault-mirror/render-learnings.mjs +201 -0
  496. package/scripts/lib/vault-mirror/render-sessions.mjs +367 -0
  497. package/scripts/lib/vault-mirror/render.mjs +8 -0
  498. package/scripts/lib/vault-mirror/utils.mjs +217 -0
  499. package/scripts/lib/vault-relocation-rules.mjs +555 -0
  500. package/scripts/lib/vault-repo-backfill.mjs +235 -0
  501. package/scripts/lib/vault-staleness-banner.mjs +142 -0
  502. package/scripts/lib/vault-status/board-writer.mjs +769 -0
  503. package/scripts/lib/vault-status/narrative-mirror.mjs +544 -0
  504. package/scripts/lib/vault-sync-baseline.mjs +152 -0
  505. package/scripts/lib/wave-context.mjs +29 -0
  506. package/scripts/lib/wave-executor/pool.mjs +248 -0
  507. package/scripts/lib/wave-resource-gate.mjs +204 -0
  508. package/scripts/lib/wave-sizing.mjs +75 -0
  509. package/scripts/lib/webhook-url.mjs +105 -0
  510. package/scripts/lib/workspace.mjs +198 -0
  511. package/scripts/lib/worktree/constants.mjs +35 -0
  512. package/scripts/lib/worktree/index.mjs +17 -0
  513. package/scripts/lib/worktree/lifecycle.mjs +287 -0
  514. package/scripts/lib/worktree/listing.mjs +118 -0
  515. package/scripts/lib/worktree/meta.mjs +64 -0
  516. package/scripts/lib/worktree-freshness.mjs +313 -0
  517. package/scripts/lib/worktree.mjs +15 -0
  518. package/scripts/lifecycle-sim-v6.mjs +347 -0
  519. package/scripts/lock-reaper.mjs +185 -0
  520. package/scripts/mcp-server.sh +241 -0
  521. package/scripts/measure-policy-cache-effectiveness.mjs +427 -0
  522. package/scripts/memory-propose.mjs +464 -0
  523. package/scripts/migrate-cold-start-seed.mjs +404 -0
  524. package/scripts/migrate-learnings-jsonl.mjs +189 -0
  525. package/scripts/migrate-legacy-learnings.sh +61 -0
  526. package/scripts/migrate-sessions-jsonl.mjs +448 -0
  527. package/scripts/migrate-subagents-jsonl.mjs +196 -0
  528. package/scripts/migrate-vault-paths.mjs +796 -0
  529. package/scripts/parse-config.mjs +149 -0
  530. package/scripts/pi-install.mjs +117 -0
  531. package/scripts/print-applicable-rules.mjs +247 -0
  532. package/scripts/promote-vault-strict.mjs +496 -0
  533. package/scripts/relocate-vault-corpus.mjs +1178 -0
  534. package/scripts/run-migrate-v2-cross-repo.mjs +385 -0
  535. package/scripts/run-quality-gate.mjs +216 -0
  536. package/scripts/spikes/h3-agent-teams/preflight.sh +53 -0
  537. package/scripts/spikes/h3-agent-teams/run-h3.sh +112 -0
  538. package/scripts/spikes/h3-agent-teams/setup.sh +137 -0
  539. package/scripts/spikes/h3-agent-teams/toggle.sh +38 -0
  540. package/scripts/sweep-expired-learnings.mjs +135 -0
  541. package/scripts/sync-vault-schema.mjs +376 -0
  542. package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +8 -0
  543. package/scripts/tmux-layout.mjs +245 -0
  544. package/scripts/token-audit.sh +191 -0
  545. package/scripts/typecheck.mjs +42 -0
  546. package/scripts/upload-social-preview.mjs +316 -0
  547. package/scripts/validate-config.mjs +46 -0
  548. package/scripts/validate-plugin-manifests.mjs +163 -0
  549. package/scripts/validate-plugin.mjs +264 -0
  550. package/scripts/validate-wave-scope.mjs +289 -0
  551. package/scripts/vault-backfill.mjs +404 -0
  552. package/scripts/vault-consolidate.mjs +596 -0
  553. package/scripts/vault-integration-watcher.mjs +394 -0
  554. package/scripts/vault-mirror.mjs +430 -0
  555. package/skills/_shared/bootstrap-gate.md +111 -0
  556. package/skills/_shared/config-reading.md +226 -0
  557. package/skills/_shared/instruction-file-resolution.md +79 -0
  558. package/skills/_shared/model-selection.md +64 -0
  559. package/skills/_shared/monitor-patterns.md +300 -0
  560. package/skills/_shared/parallel-aware-auq.md +121 -0
  561. package/skills/_shared/parallel-aware-preamble.md +185 -0
  562. package/skills/_shared/platform-tools.md +96 -0
  563. package/skills/_shared/state-ownership.md +221 -0
  564. package/skills/architecture/DEEPENING.md +37 -0
  565. package/skills/architecture/INTERFACE-DESIGN.md +44 -0
  566. package/skills/architecture/LANGUAGE.md +53 -0
  567. package/skills/architecture/SKILL.md +92 -0
  568. package/skills/autopilot/SKILL.md +419 -0
  569. package/skills/bootstrap/SKILL.md +592 -0
  570. package/skills/bootstrap/STATE.md.template +24 -0
  571. package/skills/bootstrap/_shared-template.md +243 -0
  572. package/skills/bootstrap/deep-template.md +659 -0
  573. package/skills/bootstrap/fast-template.md +251 -0
  574. package/skills/bootstrap/intensity-heuristic.md +80 -0
  575. package/skills/bootstrap/public-fallback.md +342 -0
  576. package/skills/bootstrap/standard-template.md +736 -0
  577. package/skills/bootstrap/templates/agents/project-code-review.md +18 -0
  578. package/skills/bootstrap/templates/agents/project-discovery.md +18 -0
  579. package/skills/bootstrap/templates/agents/project-quality-gate.md +18 -0
  580. package/skills/brainstorm/SKILL.md +268 -0
  581. package/skills/brainstorm/soul.md +49 -0
  582. package/skills/claude-md-drift-check/SKILL.md +186 -0
  583. package/skills/claude-md-drift-check/checker.mjs +1380 -0
  584. package/skills/claude-md-drift-check/checker.sh +37 -0
  585. package/skills/claude-md-drift-check/package.json +16 -0
  586. package/skills/convergence-monitoring/README.md +39 -0
  587. package/skills/convergence-monitoring/SIGNALS.md +246 -0
  588. package/skills/convergence-monitoring/SKILL.md +285 -0
  589. package/skills/daily/SKILL.md +222 -0
  590. package/skills/daily/generate.sh +92 -0
  591. package/skills/daily/templates/daily.md.tpl +36 -0
  592. package/skills/debug/SKILL.md +188 -0
  593. package/skills/debug/soul.md +35 -0
  594. package/skills/discovery/SKILL.md +567 -0
  595. package/skills/discovery/issue-templates.md +237 -0
  596. package/skills/discovery/probes/docs-staleness.mjs +195 -0
  597. package/skills/discovery/probes/frontend-slop.mjs +186 -0
  598. package/skills/discovery/probes/ssot-code-diff.mjs +310 -0
  599. package/skills/discovery/probes/supply-chain-slopcheck.mjs +440 -0
  600. package/skills/discovery/probes/vault-narrative-staleness.mjs +355 -0
  601. package/skills/discovery/probes/vault-staleness.mjs +272 -0
  602. package/skills/discovery/probes-arch.md +252 -0
  603. package/skills/discovery/probes-audit.md +95 -0
  604. package/skills/discovery/probes-code.md +329 -0
  605. package/skills/discovery/probes-docs.md +76 -0
  606. package/skills/discovery/probes-feature.md +150 -0
  607. package/skills/discovery/probes-infra.md +138 -0
  608. package/skills/discovery/probes-intro.md +25 -0
  609. package/skills/discovery/probes-session.md +495 -0
  610. package/skills/discovery/probes-supply-chain.md +94 -0
  611. package/skills/discovery/probes-ui.md +147 -0
  612. package/skills/discovery/probes-vault.md +64 -0
  613. package/skills/discovery/slop-patterns.md +115 -0
  614. package/skills/dispatcher/SKILL.md +173 -0
  615. package/skills/docs-orchestrator/SKILL.md +362 -0
  616. package/skills/docs-orchestrator/audience-mapping.md +140 -0
  617. package/skills/domain-model/ADR-FORMAT.md +47 -0
  618. package/skills/domain-model/CONTEXT-FORMAT.md +77 -0
  619. package/skills/domain-model/SKILL.md +85 -0
  620. package/skills/ecosystem-health/SKILL.md +119 -0
  621. package/skills/ecosystem-health/wizard.md +193 -0
  622. package/skills/eval/SKILL.md +293 -0
  623. package/skills/eval/rubric-v1.md +218 -0
  624. package/skills/evolve/SKILL.md +546 -0
  625. package/skills/frontmatter-guard/SKILL.md +126 -0
  626. package/skills/gitlab-ops/SKILL.md +368 -0
  627. package/skills/gitlab-portfolio/SKILL.md +196 -0
  628. package/skills/grill/SKILL.md +185 -0
  629. package/skills/grill/soul.md +55 -0
  630. package/skills/hook-development/SKILL.md +413 -0
  631. package/skills/mcp-builder/SKILL.md +260 -0
  632. package/skills/memory-cleanup/SKILL.md +310 -0
  633. package/skills/mode-selector/SKILL.md +226 -0
  634. package/skills/peekaboo-driver/SKILL.md +237 -0
  635. package/skills/peekaboo-driver/soul.md +32 -0
  636. package/skills/persona-panel/SKILL.md +365 -0
  637. package/skills/persona-panel/persona-format.md +205 -0
  638. package/skills/persona-panel/presets/designer-lens.md +87 -0
  639. package/skills/persona-panel/presets/engineer-lens.md +88 -0
  640. package/skills/persona-panel/presets/pm-lens.md +86 -0
  641. package/skills/plan/SKILL.md +496 -0
  642. package/skills/plan/mode-feature.md +141 -0
  643. package/skills/plan/mode-new.md +297 -0
  644. package/skills/plan/mode-retro.md +271 -0
  645. package/skills/plan/prd-feature-template.md +132 -0
  646. package/skills/plan/prd-full-template.md +151 -0
  647. package/skills/plan/prd-reviewer-prompt.md +103 -0
  648. package/skills/plan/retro-template.md +75 -0
  649. package/skills/plan/soul.md +62 -0
  650. package/skills/playwright-driver/SKILL.md +226 -0
  651. package/skills/playwright-driver/soul.md +30 -0
  652. package/skills/quality-gates/SKILL.md +212 -0
  653. package/skills/reconcile/SKILL.md +324 -0
  654. package/skills/repo-audit/SKILL.md +272 -0
  655. package/skills/session-end/SKILL.md +1044 -0
  656. package/skills/session-end/discovery-scan.md +37 -0
  657. package/skills/session-end/drift-operations.md +97 -0
  658. package/skills/session-end/learning-patterns.md +78 -0
  659. package/skills/session-end/metrics-collection.md +175 -0
  660. package/skills/session-end/phase-3-2-docs-verification.md +148 -0
  661. package/skills/session-end/phase-3-6-tail.md +344 -0
  662. package/skills/session-end/phase-3-7a-recommendations.md +86 -0
  663. package/skills/session-end/plan-verification.md +288 -0
  664. package/skills/session-end/session-metrics-write.md +223 -0
  665. package/skills/session-end/vault-operations.md +50 -0
  666. package/skills/session-end/verification-checklist.md +20 -0
  667. package/skills/session-plan/SKILL.md +554 -0
  668. package/skills/session-plan/wave-template.md +37 -0
  669. package/skills/session-start/SKILL.md +1043 -0
  670. package/skills/session-start/phase-2-5-docs-planning.md +119 -0
  671. package/skills/session-start/phase-4-5-resource-health.md +49 -0
  672. package/skills/session-start/phase-7-1-premise-check.md +47 -0
  673. package/skills/session-start/phase-7-5-mode-selector.md +237 -0
  674. package/skills/session-start/phase-8-5-express-path.md +61 -0
  675. package/skills/session-start/presentation-format.md +81 -0
  676. package/skills/session-start/soul.md +57 -0
  677. package/skills/skill-creator/SKILL.md +168 -0
  678. package/skills/spinout/SKILL.md +76 -0
  679. package/skills/sunset-review/SKILL.md +96 -0
  680. package/skills/test-runner/SKILL.md +362 -0
  681. package/skills/test-runner/rubric-v1.md +388 -0
  682. package/skills/test-runner/soul.md +46 -0
  683. package/skills/tmux-layout/SKILL.md +104 -0
  684. package/skills/ubiquitous-language/SKILL.md +97 -0
  685. package/skills/using-orchestrator/SKILL.md +144 -0
  686. package/skills/vault-mirror/SKILL.md +234 -0
  687. package/skills/vault-sync/SKILL.md +319 -0
  688. package/skills/vault-sync/package-lock.json +40 -0
  689. package/skills/vault-sync/package.json +11 -0
  690. package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +8 -0
  691. package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
  692. package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +8 -0
  693. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
  694. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +8 -0
  695. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +8 -0
  696. package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +8 -0
  697. package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +10 -0
  698. package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +8 -0
  699. package/skills/vault-sync/tests/fixtures/clean-vault/README.md +3 -0
  700. package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +11 -0
  701. package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
  702. package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +9 -0
  703. package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +8 -0
  704. package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
  705. package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
  706. package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +7 -0
  707. package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +9 -0
  708. package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
  709. package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +11 -0
  710. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +3 -0
  711. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +3 -0
  712. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
  713. package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +11 -0
  714. package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
  715. package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +11 -0
  716. package/skills/vault-sync/tests/schema-drift.test.mjs +133 -0
  717. package/skills/vault-sync/validator.mjs +658 -0
  718. package/skills/vault-sync/validator.sh +55 -0
  719. package/skills/wave-executor/SKILL.md +496 -0
  720. package/skills/wave-executor/circuit-breaker.md +169 -0
  721. package/skills/wave-executor/wave-loop.md +1043 -0
  722. package/skills/write-executable-plan/SKILL.md +237 -0
  723. package/skills/write-executable-plan/plan-template.md +154 -0
  724. package/templates/_minimal/CLAUDE.md.tmpl +41 -0
  725. package/templates/_minimal/README.md.tmpl +15 -0
  726. package/templates/_minimal/gitignore.tmpl +47 -0
  727. package/templates/_shared/harte-regeln.md +16 -0
  728. package/templates/_shared/loop.md +90 -0
  729. package/templates/_shared/rules/parallel-sessions.md +77 -0
  730. package/templates/nextjs-minimal/README.md +30 -0
  731. package/templates/nextjs-minimal/app/layout.tsx +18 -0
  732. package/templates/nextjs-minimal/app/page.tsx +7 -0
  733. package/templates/nextjs-minimal/eslint.config.mjs +16 -0
  734. package/templates/nextjs-minimal/next.config.mjs +4 -0
  735. package/templates/nextjs-minimal/package.json +27 -0
  736. package/templates/nextjs-minimal/tsconfig.json +23 -0
  737. package/templates/node-minimal/README.md +33 -0
  738. package/templates/node-minimal/eslint.config.mjs +10 -0
  739. package/templates/node-minimal/package.json +21 -0
  740. package/templates/node-minimal/src/index.ts +1 -0
  741. package/templates/node-minimal/tests/sanity.test.ts +5 -0
  742. package/templates/node-minimal/tsconfig.json +17 -0
  743. package/templates/personas/README.md +150 -0
  744. package/templates/personas/accounting-compliance.v1.md +120 -0
  745. package/templates/personas/accounting-tax-advisor.v1.md +116 -0
  746. package/templates/personas/buyer-p1-cto.v1.md +125 -0
  747. package/templates/personas/buyer-p2-kanzlei.v1.md +134 -0
  748. package/templates/personas/buyer-p3-build.v1.md +130 -0
  749. package/templates/personas/buyer-p4-tech-veto.v1.md +130 -0
  750. package/templates/personas/buyer-p5-solo.v1.md +132 -0
  751. package/templates/personas/buyer-p6-ld.v1.md +130 -0
  752. package/templates/personas/klima-ai-expert.v1.md +114 -0
  753. package/templates/personas/klima-physicist.v1.md +117 -0
  754. package/templates/python-uv/README.md +28 -0
  755. package/templates/python-uv/pyproject.toml +32 -0
  756. package/templates/python-uv/src/__PROJECT_NAME__/__init__.py +0 -0
  757. package/templates/python-uv/src/__PROJECT_NAME__/main.py +6 -0
  758. package/templates/python-uv/tests/test_sanity.py +2 -0
  759. package/templates/static-html/README.md +19 -0
  760. package/templates/static-html/index.html +15 -0
  761. package/templates/static-html/script.js +1 -0
  762. package/templates/static-html/styles.css +26 -0
@@ -0,0 +1,851 @@
1
+ /**
2
+ * Body-section helpers for STATE.md.
3
+ *
4
+ * Pure functions — no file I/O.
5
+ *
6
+ * Plus on-disk wrappers (`appendDeviationOnDisk`, `markExpressPathCompleteOnDisk`,
7
+ * `recordAutoCommitOnDisk`) added for PRD 2026-05-22 § 4 Pattern 1 (issue #518).
8
+ * The on-disk wrappers delegate to the pure helpers above and route the
9
+ * read+write cycle through `writeStateMd` from frontmatter-mutators.mjs, which
10
+ * acquires `.orchestrator/state.lock` for mechanical serialization (PSA-004).
11
+ */
12
+
13
+ import { parseStateMd, serializeStateMd } from './yaml-parser.mjs';
14
+ import {
15
+ updateFrontmatterFields,
16
+ touchUpdatedField,
17
+ writeStateMd,
18
+ } from './frontmatter-mutators.mjs';
19
+
20
+ /**
21
+ * Extracts the current-wave banner info from the `## Current Wave` body
22
+ * section: first non-blank line after the heading.
23
+ *
24
+ * @param {string} contents
25
+ * @returns {{waveNumber: number|null, description: string}|null}
26
+ */
27
+ export function readCurrentTask(contents) {
28
+ const parsed = parseStateMd(contents);
29
+ if (parsed === null) return null;
30
+ const lines = parsed.body.split(/\r?\n/);
31
+ let i = 0;
32
+ while (i < lines.length && !/^##\s+Current Wave\b/.test(lines[i])) i++;
33
+ if (i === lines.length) return null;
34
+ i++;
35
+ while (i < lines.length && lines[i].trim() === '') i++;
36
+ if (i === lines.length) return null;
37
+ const description = lines[i].trim();
38
+ const waveMatch = /^Wave\s+(\d+)\b/.exec(description);
39
+ const waveNumber = waveMatch ? parseInt(waveMatch[1], 10) : null;
40
+ return { waveNumber, description };
41
+ }
42
+
43
+ /**
44
+ * Appends a timestamped bullet to the `## Deviations` section in the STATE.md body.
45
+ * Creates the section if missing. Replaces a `(none yet)` placeholder if present.
46
+ *
47
+ * Defensive guard (issue #560): when `isoTimestamp` is omitted, `undefined`, `null`,
48
+ * or a non-string value, defaults to `new Date().toISOString()`. This prevents the
49
+ * literal text `undefined` from rendering in the deviations log when a caller
50
+ * forgets to compute the timestamp (the deep-2115 inter-wave 2→3 incident).
51
+ *
52
+ * @param {string} contents
53
+ * @param {string} [isoTimestamp] - ISO 8601 UTC, e.g. '2026-05-01T17:50:00Z'.
54
+ * When omitted or not a non-empty string, defaults to `new Date().toISOString()`.
55
+ * @param {string} message - free text; no leading dash, no surrounding brackets
56
+ * @returns {string}
57
+ */
58
+ export function appendDeviation(contents, isoTimestamp, message) {
59
+ const parsed = parseStateMd(contents);
60
+ if (parsed === null) return contents;
61
+ const ts = (typeof isoTimestamp === 'string' && isoTimestamp.length > 0)
62
+ ? isoTimestamp
63
+ : new Date().toISOString();
64
+ const bullet = `- [${ts}] ${message}`;
65
+ const lines = parsed.body.split('\n');
66
+ let headingIdx = -1;
67
+ for (let i = 0; i < lines.length; i++) {
68
+ if (/^##\s+Deviations\b/.test(lines[i])) {
69
+ headingIdx = i;
70
+ break;
71
+ }
72
+ }
73
+ if (headingIdx === -1) {
74
+ // Append section at end of body. Ensure trailing newline before adding section.
75
+ let bodyOut = parsed.body;
76
+ if (!bodyOut.endsWith('\n')) bodyOut += '\n';
77
+ bodyOut += `\n## Deviations\n\n${bullet}\n`;
78
+ return serializeStateMd({ frontmatter: parsed.frontmatter, body: bodyOut });
79
+ }
80
+ // Find end-of-section: next `## ` heading or end of lines.
81
+ let sectionEnd = lines.length;
82
+ for (let i = headingIdx + 1; i < lines.length; i++) {
83
+ if (/^##\s+/.test(lines[i])) {
84
+ sectionEnd = i;
85
+ break;
86
+ }
87
+ }
88
+ // Within section body (headingIdx+1 .. sectionEnd-1), look for placeholder
89
+ // or last bullet.
90
+ let placeholderIdx = -1;
91
+ let lastBulletIdx = -1;
92
+ for (let i = headingIdx + 1; i < sectionEnd; i++) {
93
+ const t = lines[i].trim();
94
+ if (t === '(none yet)' || t === '_(none yet)_' || t === '*(none yet)*') {
95
+ placeholderIdx = i;
96
+ }
97
+ if (/^-\s+/.test(t)) {
98
+ lastBulletIdx = i;
99
+ }
100
+ }
101
+ if (placeholderIdx !== -1) {
102
+ lines[placeholderIdx] = bullet;
103
+ } else if (lastBulletIdx !== -1) {
104
+ lines.splice(lastBulletIdx + 1, 0, bullet);
105
+ } else {
106
+ // Insert after heading, with one blank line between heading and bullet.
107
+ // Trim any leading blank lines in section, then insert.
108
+ let insertAt = headingIdx + 1;
109
+ // Skip existing blank lines after heading
110
+ while (insertAt < sectionEnd && lines[insertAt].trim() === '') insertAt++;
111
+ // We want exactly: heading, blank, bullet, [existing trailing content].
112
+ const before = lines.slice(0, headingIdx + 1);
113
+ const after = lines.slice(insertAt);
114
+ const rebuilt = [...before, '', bullet, ...after];
115
+ return serializeStateMd({
116
+ frontmatter: parsed.frontmatter,
117
+ body: rebuilt.join('\n'),
118
+ });
119
+ }
120
+ return serializeStateMd({
121
+ frontmatter: parsed.frontmatter,
122
+ body: lines.join('\n'),
123
+ });
124
+ }
125
+
126
+ /**
127
+ * Maximum number of top-level `## What Not To Retry` entries retained.
128
+ *
129
+ * "Pruned on success" (issue #623) is implemented as a simple last-N FIFO:
130
+ * after each append, the section is trimmed to the most-recent
131
+ * `MAX_WHAT_NOT_TO_RETRY` entries (oldest dropped first). This is NOT a
132
+ * per-entry success-clear — entries are not individually retired when an
133
+ * approach later succeeds; the cap is the only pruning mechanism. The slot is
134
+ * a cross-session continuity record that SURVIVES the completed-branch Idle
135
+ * Reset (unlike per-session `## Deviations`).
136
+ */
137
+ export const MAX_WHAT_NOT_TO_RETRY = 10;
138
+
139
+ /**
140
+ * Collapse every run of CR/LF into a single space (#623).
141
+ *
142
+ * `## What Not To Retry` entries are single-line bullets and `readWhatNotToRetry`
143
+ * reads ONE line per field, so an embedded newline in `approach` or `why_failed`
144
+ * would lose everything after line 1 on round-trip. Collapsing here keeps the
145
+ * entry single-line and lossless. Leading/trailing whitespace introduced by the
146
+ * collapse is trimmed.
147
+ *
148
+ * @param {string} value
149
+ * @returns {string}
150
+ */
151
+ function collapseNewlines(value) {
152
+ return value.replace(/\r?\n+/g, ' ').trim();
153
+ }
154
+
155
+ /**
156
+ * Reads the `## What Not To Retry` section into an array of entries.
157
+ *
158
+ * Each top-level `- **<approach>** (<session_id>, <date>)` bullet (optionally
159
+ * followed by an indented `- why: <why_failed>` sub-bullet) becomes one entry.
160
+ * Returns `[]` when the section is absent, empty, or holds only the
161
+ * `(none yet)` placeholder. Returns `[]` on unparseable input.
162
+ *
163
+ * @param {string} contents
164
+ * @returns {Array<{approach: string, why_failed: string, session_id: string, date: string}>}
165
+ */
166
+ export function readWhatNotToRetry(contents) {
167
+ const parsed = parseStateMd(contents);
168
+ if (parsed === null) return [];
169
+ const lines = parsed.body.split(/\r?\n/);
170
+ let i = 0;
171
+ while (i < lines.length && !/^##\s+What Not To Retry\b/.test(lines[i])) i++;
172
+ if (i === lines.length) return [];
173
+ i++;
174
+ const entries = [];
175
+ for (; i < lines.length; i++) {
176
+ if (/^##\s+/.test(lines[i])) break;
177
+ // Top-level entry: `- **<approach>** (<session_id>, <date>)`
178
+ const head = /^-\s+\*\*(.+?)\*\*\s+\((.+),\s*(.+)\)\s*$/.exec(lines[i]);
179
+ if (!head) continue;
180
+ const approach = head[1];
181
+ const session_id = head[2].trim();
182
+ const date = head[3].trim();
183
+ let why_failed = '';
184
+ // Look ahead for the `- why: <...>` sub-bullet within this entry.
185
+ for (let j = i + 1; j < lines.length; j++) {
186
+ if (/^##\s+/.test(lines[j])) break;
187
+ if (/^-\s+\*\*/.test(lines[j].trim())) break; // next top-level entry
188
+ const why = /^-\s+why:\s*(.*)$/.exec(lines[j].trim());
189
+ if (why) {
190
+ why_failed = why[1];
191
+ break;
192
+ }
193
+ }
194
+ entries.push({ approach, why_failed, session_id, date });
195
+ }
196
+ return entries;
197
+ }
198
+
199
+ /**
200
+ * Appends an entry to the `## What Not To Retry` section in the STATE.md body.
201
+ *
202
+ * Creates the section if missing. Replaces a `(none yet)` placeholder if
203
+ * present. After appending, prunes the section to the most-recent
204
+ * `MAX_WHAT_NOT_TO_RETRY` top-level entries (FIFO — oldest dropped first).
205
+ *
206
+ * Mirrors `appendDeviation` in structure. No-op (returns input unchanged) on
207
+ * unparseable input.
208
+ *
209
+ * @param {string} contents
210
+ * @param {object} entry
211
+ * @param {string} [entry.approach] — the approach that failed; defaults to '(unspecified approach)'
212
+ * @param {string} [entry.why_failed] — why it failed; defaults to '(no reason recorded)'
213
+ * @param {string} [entry.session_id] — originating session id; defaults to 'unknown-session'
214
+ * @param {string} [entry.date] — YYYY-MM-DD; defaults to today (UTC date slice)
215
+ * @returns {string}
216
+ */
217
+ export function appendWhatNotToRetry(contents, entry) {
218
+ const parsed = parseStateMd(contents);
219
+ if (parsed === null) return contents;
220
+ const e = entry || {};
221
+ const approach = (typeof e.approach === 'string' && e.approach.length > 0)
222
+ ? e.approach
223
+ : '(unspecified approach)';
224
+ const why = (typeof e.why_failed === 'string' && e.why_failed.length > 0)
225
+ ? e.why_failed
226
+ : '(no reason recorded)';
227
+ const sid = (typeof e.session_id === 'string' && e.session_id.length > 0)
228
+ ? e.session_id
229
+ : 'unknown-session';
230
+ const date = (typeof e.date === 'string' && e.date.length > 0)
231
+ ? e.date
232
+ : new Date().toISOString().slice(0, 10);
233
+
234
+ // Collapse embedded newlines (#623): the entry renders as single-line bullets
235
+ // (`- **<approach>**` + ` - why: <why>`), and readWhatNotToRetry reads ONE
236
+ // line per field. A multi-line `approach`/`why_failed` would silently truncate
237
+ // everything after line 1 on round-trip. Collapse `\r?\n+` runs to a single
238
+ // space so the entry stays single-line and round-trips losslessly.
239
+ const headBullet = `- **${collapseNewlines(approach)}** (${sid}, ${date})`;
240
+ const whyBullet = ` - why: ${collapseNewlines(why)}`;
241
+
242
+ const lines = parsed.body.split('\n');
243
+ let headingIdx = -1;
244
+ for (let i = 0; i < lines.length; i++) {
245
+ if (/^##\s+What Not To Retry\b/.test(lines[i])) {
246
+ headingIdx = i;
247
+ break;
248
+ }
249
+ }
250
+
251
+ let rebuiltBody;
252
+ if (headingIdx === -1) {
253
+ // Append section at end of body. Ensure trailing newline before adding.
254
+ let bodyOut = parsed.body;
255
+ if (!bodyOut.endsWith('\n')) bodyOut += '\n';
256
+ bodyOut += `\n## What Not To Retry\n\n${headBullet}\n${whyBullet}\n`;
257
+ rebuiltBody = bodyOut;
258
+ } else {
259
+ // Find end-of-section: next `## ` heading or end of lines.
260
+ let sectionEnd = lines.length;
261
+ for (let i = headingIdx + 1; i < lines.length; i++) {
262
+ if (/^##\s+/.test(lines[i])) {
263
+ sectionEnd = i;
264
+ break;
265
+ }
266
+ }
267
+ // Look for placeholder or last top-level entry bullet within the section.
268
+ let placeholderIdx = -1;
269
+ let lastEntryIdx = -1;
270
+ for (let i = headingIdx + 1; i < sectionEnd; i++) {
271
+ const t = lines[i].trim();
272
+ if (t === '(none yet)' || t === '_(none yet)_' || t === '*(none yet)*') {
273
+ placeholderIdx = i;
274
+ }
275
+ if (/^-\s+\*\*/.test(t)) {
276
+ lastEntryIdx = i;
277
+ }
278
+ }
279
+ if (placeholderIdx !== -1) {
280
+ // Replace the placeholder line with the head bullet, then insert why.
281
+ lines.splice(placeholderIdx, 1, headBullet, whyBullet);
282
+ } else if (lastEntryIdx !== -1) {
283
+ // Insert after the last entry's why sub-bullet (or the entry itself):
284
+ // scan forward over any indented sub-bullets belonging to lastEntryIdx.
285
+ let insertAt = lastEntryIdx + 1;
286
+ while (
287
+ insertAt < sectionEnd &&
288
+ lines[insertAt].trim() !== '' &&
289
+ !/^-\s+\*\*/.test(lines[insertAt].trim()) &&
290
+ /^\s+-\s+/.test(lines[insertAt])
291
+ ) {
292
+ insertAt++;
293
+ }
294
+ lines.splice(insertAt, 0, headBullet, whyBullet);
295
+ } else {
296
+ // Empty section (heading only): insert heading, blank, entry, why.
297
+ let insertAt = headingIdx + 1;
298
+ while (insertAt < sectionEnd && lines[insertAt].trim() === '') insertAt++;
299
+ const before = lines.slice(0, headingIdx + 1);
300
+ const after = lines.slice(insertAt);
301
+ const rebuilt = [...before, '', headBullet, whyBullet, ...after];
302
+ rebuiltBody = rebuilt.join('\n');
303
+ }
304
+ if (rebuiltBody === undefined) {
305
+ rebuiltBody = lines.join('\n');
306
+ }
307
+ }
308
+
309
+ // Prune to the most-recent MAX_WHAT_NOT_TO_RETRY top-level entries (FIFO).
310
+ rebuiltBody = pruneWhatNotToRetry(rebuiltBody);
311
+
312
+ return serializeStateMd({ frontmatter: parsed.frontmatter, body: rebuiltBody });
313
+ }
314
+
315
+ /**
316
+ * Trims the `## What Not To Retry` section to the most-recent
317
+ * `MAX_WHAT_NOT_TO_RETRY` top-level entries, dropping the oldest. Each entry is
318
+ * its top-level `- **...**` bullet plus any immediately-following indented
319
+ * sub-bullets (e.g. ` - why: ...`). No-op when the section is absent or the
320
+ * entry count is at/under the cap.
321
+ *
322
+ * @param {string} body — STATE.md body (no frontmatter)
323
+ * @returns {string}
324
+ */
325
+ function pruneWhatNotToRetry(body) {
326
+ const lines = body.split('\n');
327
+ let headingIdx = -1;
328
+ for (let i = 0; i < lines.length; i++) {
329
+ if (/^##\s+What Not To Retry\b/.test(lines[i])) {
330
+ headingIdx = i;
331
+ break;
332
+ }
333
+ }
334
+ if (headingIdx === -1) return body;
335
+ let sectionEnd = lines.length;
336
+ for (let i = headingIdx + 1; i < lines.length; i++) {
337
+ if (/^##\s+/.test(lines[i])) {
338
+ sectionEnd = i;
339
+ break;
340
+ }
341
+ }
342
+ // Collect the start index of each top-level entry within the section.
343
+ const entryStarts = [];
344
+ for (let i = headingIdx + 1; i < sectionEnd; i++) {
345
+ if (/^-\s+\*\*/.test(lines[i].trim())) entryStarts.push(i);
346
+ }
347
+ if (entryStarts.length <= MAX_WHAT_NOT_TO_RETRY) return body;
348
+ // Keep the last MAX_WHAT_NOT_TO_RETRY entries; drop everything from the first
349
+ // entry up to (but not including) the first kept entry.
350
+ const dropCount = entryStarts.length - MAX_WHAT_NOT_TO_RETRY;
351
+ const firstKeptIdx = entryStarts[dropCount];
352
+ const firstDropIdx = entryStarts[0];
353
+ lines.splice(firstDropIdx, firstKeptIdx - firstDropIdx);
354
+ return lines.join('\n');
355
+ }
356
+
357
+ // ---------------------------------------------------------------------------
358
+ // Open Questions (Close Handover-Alignment-Gate, PRD 2026-07-07)
359
+ // ---------------------------------------------------------------------------
360
+ //
361
+ // Unresolved user-facing questions surfaced by wave agents during a session,
362
+ // collected at inter-wave checkpoints, and (optionally) marked answered by
363
+ // session-end. This is a cross-session-visible channel — unlike per-session
364
+ // `## Deviations`, unanswered questions are meant to surface to the NEXT
365
+ // session's operator via the Handover-Alignment-Gate.
366
+ //
367
+ // SSOT format note: the bullet template lives in ONE place
368
+ // (`formatOpenQuestionBullet`) and `OPEN_QUESTION_RE` is the ONE regex that
369
+ // parses it back. A reader regex that drifts from the writer's template
370
+ // silently returns `[]` on any format change — the narrative-mirror.mjs
371
+ // lockstep gotcha. Keep both derived from this single definition.
372
+
373
+ /**
374
+ * Maximum number of `## Open Questions` entries retained on disk.
375
+ *
376
+ * This is a storage cap (FIFO, oldest dropped first) — distinct from the
377
+ * Handover-Alignment-Gate's own `max-open-questions` config, which caps how
378
+ * many questions are surfaced/asked in a single gate run, not how many are
379
+ * stored in STATE.md.
380
+ */
381
+ export const MAX_OPEN_QUESTIONS_STORED = 20;
382
+
383
+ /**
384
+ * Builds a single `## Open Questions` bullet line from an entry. The single
385
+ * source of truth for the on-disk format — `OPEN_QUESTION_RE` below MUST stay
386
+ * in lockstep with this template.
387
+ *
388
+ * @param {object} entry
389
+ * @param {string} entry.question
390
+ * @param {string} entry.source
391
+ * @param {'high'|'medium'|'low'} entry.priority
392
+ * @param {boolean} [entry.answered=false]
393
+ * @param {string} [entry.answer]
394
+ * @returns {string}
395
+ */
396
+ function formatOpenQuestionBullet({ question, source, priority, answered = false, answer }) {
397
+ const base = `- [${answered ? 'x' : ' '}] ${question} (source: ${source}, prio: ${priority})`;
398
+ if (answered) {
399
+ return `${base} → Antwort: ${typeof answer === 'string' ? answer : ''}`;
400
+ }
401
+ return base;
402
+ }
403
+
404
+ /**
405
+ * Parses one `## Open Questions` bullet line. Derived in lockstep with
406
+ * `formatOpenQuestionBullet` — see the SSOT note above.
407
+ *
408
+ * Capture groups: 1=checkbox state (' '|'x'), 2=question, 3=source,
409
+ * 4=priority, 5=answer (only present when checkbox is 'x' and an
410
+ * `→ Antwort: ...` suffix follows).
411
+ *
412
+ * The `source` group intentionally uses `(.+?)` rather than a comma-excluding
413
+ * `([^,]+?)`: the writer (`formatOpenQuestionBullet`) permits free-text
414
+ * `source` values that may themselves contain a comma (e.g.
415
+ * `'W2/agent-desc, extra'`). A comma-intolerant capture would silently fail to
416
+ * match such a writer-valid bullet — the question would vanish from the
417
+ * roundtrip with no error. `(.+?)` stays unambiguously anchored because the
418
+ * lazy quantifier backtracks until it finds the trailing
419
+ * `,\s*prio:(high|medium|low)\)` literal, which cannot itself appear inside a
420
+ * legitimate `source` value.
421
+ */
422
+ const OPEN_QUESTION_RE =
423
+ /^-\s+\[([ x])\]\s+(.+?)\s+\(source:\s*(.+?),\s*prio:\s*(high|medium|low)\)(?:\s*→\s*Antwort:\s*(.*))?$/;
424
+
425
+ /**
426
+ * Reads the `## Open Questions` section into an array of entries.
427
+ *
428
+ * Returns `[]` when the section is absent, empty, holds only the
429
+ * `(none yet)` placeholder, or on unparseable input. Lines that do not match
430
+ * `OPEN_QUESTION_RE` (malformed bullets) are silently skipped rather than
431
+ * throwing — tolerant of hand-edited STATE.md content.
432
+ *
433
+ * @param {string} contents
434
+ * @returns {Array<{question: string, source: string, priority: 'high'|'medium'|'low', answered: boolean, answer?: string}>}
435
+ */
436
+ export function readOpenQuestions(contents) {
437
+ const parsed = parseStateMd(contents);
438
+ if (parsed === null) return [];
439
+ const lines = parsed.body.split(/\r?\n/);
440
+ let i = 0;
441
+ while (i < lines.length && !/^##\s+Open Questions\b/.test(lines[i])) i++;
442
+ if (i === lines.length) return [];
443
+ i++;
444
+ const entries = [];
445
+ for (; i < lines.length; i++) {
446
+ if (/^##\s+/.test(lines[i])) break;
447
+ const m = OPEN_QUESTION_RE.exec(lines[i].trim());
448
+ if (!m) continue;
449
+ const answered = m[1] === 'x';
450
+ const entry = {
451
+ question: m[2].trim(),
452
+ source: m[3].trim(),
453
+ priority: m[4],
454
+ answered,
455
+ };
456
+ if (answered && typeof m[5] === 'string') {
457
+ entry.answer = m[5].trim();
458
+ }
459
+ entries.push(entry);
460
+ }
461
+ return entries;
462
+ }
463
+
464
+ /**
465
+ * Appends an entry to the `## Open Questions` section in the STATE.md body.
466
+ *
467
+ * Creates the section if missing. Replaces a `(none yet)` placeholder if
468
+ * present. Deduplicates by question text — keyed replace-else-append, like
469
+ * `setMissionStatus` (mission-status.mjs): if an existing bullet's question
470
+ * text matches (after newline-collapse + trim), it is replaced in place
471
+ * (reset to unanswered) rather than duplicated. After appending, prunes the
472
+ * section to the most-recent `MAX_OPEN_QUESTIONS_STORED` entries (FIFO —
473
+ * oldest dropped first).
474
+ *
475
+ * No-op (returns input unchanged) on unparseable input.
476
+ *
477
+ * @param {string} contents
478
+ * @param {object} entry
479
+ * @param {string} [entry.question] — defaults to '(unspecified question)'
480
+ * @param {string} [entry.source] — defaults to 'unknown-source'
481
+ * @param {'high'|'medium'|'low'} [entry.priority] — defaults to 'medium'
482
+ * @returns {string}
483
+ */
484
+ export function appendOpenQuestion(contents, entry) {
485
+ const parsed = parseStateMd(contents);
486
+ if (parsed === null) return contents;
487
+ const e = entry || {};
488
+ const question = (typeof e.question === 'string' && e.question.length > 0)
489
+ ? collapseNewlines(e.question)
490
+ : '(unspecified question)';
491
+ const source = (typeof e.source === 'string' && e.source.length > 0)
492
+ ? collapseNewlines(e.source)
493
+ : 'unknown-source';
494
+ const priority = ['high', 'medium', 'low'].includes(e.priority) ? e.priority : 'medium';
495
+
496
+ const bullet = formatOpenQuestionBullet({ question, source, priority, answered: false });
497
+
498
+ const lines = parsed.body.split('\n');
499
+ let headingIdx = -1;
500
+ for (let i = 0; i < lines.length; i++) {
501
+ if (/^##\s+Open Questions\b/.test(lines[i])) {
502
+ headingIdx = i;
503
+ break;
504
+ }
505
+ }
506
+
507
+ let rebuiltBody;
508
+ if (headingIdx === -1) {
509
+ // Append section at end of body. Ensure trailing newline before adding.
510
+ let bodyOut = parsed.body;
511
+ if (!bodyOut.endsWith('\n')) bodyOut += '\n';
512
+ bodyOut += `\n## Open Questions\n\n${bullet}\n`;
513
+ rebuiltBody = bodyOut;
514
+ } else {
515
+ // Find end-of-section: next `## ` heading or end of lines.
516
+ let sectionEnd = lines.length;
517
+ for (let i = headingIdx + 1; i < lines.length; i++) {
518
+ if (/^##\s+/.test(lines[i])) {
519
+ sectionEnd = i;
520
+ break;
521
+ }
522
+ }
523
+ // Look for a dedup match, a placeholder, or the last bullet within the section.
524
+ let existingIdx = -1;
525
+ let placeholderIdx = -1;
526
+ let lastBulletIdx = -1;
527
+ for (let i = headingIdx + 1; i < sectionEnd; i++) {
528
+ const t = lines[i].trim();
529
+ if (t === '(none yet)' || t === '_(none yet)_' || t === '*(none yet)*') {
530
+ placeholderIdx = i;
531
+ }
532
+ const m = OPEN_QUESTION_RE.exec(t);
533
+ if (m) {
534
+ lastBulletIdx = i;
535
+ if (m[2].trim() === question) existingIdx = i;
536
+ }
537
+ }
538
+ if (existingIdx !== -1) {
539
+ lines[existingIdx] = bullet;
540
+ } else if (placeholderIdx !== -1) {
541
+ lines[placeholderIdx] = bullet;
542
+ } else if (lastBulletIdx !== -1) {
543
+ lines.splice(lastBulletIdx + 1, 0, bullet);
544
+ } else {
545
+ // Empty section (heading only): insert heading, blank, bullet.
546
+ let insertAt = headingIdx + 1;
547
+ while (insertAt < sectionEnd && lines[insertAt].trim() === '') insertAt++;
548
+ const before = lines.slice(0, headingIdx + 1);
549
+ const after = lines.slice(insertAt);
550
+ const rebuilt = [...before, '', bullet, ...after];
551
+ rebuiltBody = rebuilt.join('\n');
552
+ }
553
+ if (rebuiltBody === undefined) {
554
+ rebuiltBody = lines.join('\n');
555
+ }
556
+ }
557
+
558
+ rebuiltBody = pruneOpenQuestions(rebuiltBody);
559
+
560
+ return serializeStateMd({ frontmatter: parsed.frontmatter, body: rebuiltBody });
561
+ }
562
+
563
+ /**
564
+ * Trims the `## Open Questions` section to the most-recent
565
+ * `MAX_OPEN_QUESTIONS_STORED` entries, dropping the oldest. Unlike
566
+ * `## What Not To Retry`, each entry is exactly one line (no sub-bullets).
567
+ * No-op when the section is absent or the entry count is at/under the cap.
568
+ *
569
+ * @param {string} body — STATE.md body (no frontmatter)
570
+ * @returns {string}
571
+ */
572
+ function pruneOpenQuestions(body) {
573
+ const lines = body.split('\n');
574
+ let headingIdx = -1;
575
+ for (let i = 0; i < lines.length; i++) {
576
+ if (/^##\s+Open Questions\b/.test(lines[i])) {
577
+ headingIdx = i;
578
+ break;
579
+ }
580
+ }
581
+ if (headingIdx === -1) return body;
582
+ let sectionEnd = lines.length;
583
+ for (let i = headingIdx + 1; i < lines.length; i++) {
584
+ if (/^##\s+/.test(lines[i])) {
585
+ sectionEnd = i;
586
+ break;
587
+ }
588
+ }
589
+ const entryIdxs = [];
590
+ for (let i = headingIdx + 1; i < sectionEnd; i++) {
591
+ if (OPEN_QUESTION_RE.test(lines[i].trim())) entryIdxs.push(i);
592
+ }
593
+ if (entryIdxs.length <= MAX_OPEN_QUESTIONS_STORED) return body;
594
+ const dropCount = entryIdxs.length - MAX_OPEN_QUESTIONS_STORED;
595
+ const firstKeptIdx = entryIdxs[dropCount];
596
+ const firstDropIdx = entryIdxs[0];
597
+ lines.splice(firstDropIdx, firstKeptIdx - firstDropIdx);
598
+ return lines.join('\n');
599
+ }
600
+
601
+ /**
602
+ * Flips an unanswered `## Open Questions` bullet to answered:
603
+ * `- [ ] <q> (...)` → `- [x] <q> (...) → Antwort: <answer>`.
604
+ *
605
+ * No-op (returns input unchanged) when: the section is absent, the question
606
+ * text is not found (matched by trimmed, newline-collapsed equality against
607
+ * each bullet's question field), or the matched bullet is already answered.
608
+ *
609
+ * @param {string} contents
610
+ * @param {string} question — must match an existing unanswered bullet's question text
611
+ * @param {string} answer
612
+ * @returns {string}
613
+ */
614
+ export function markOpenQuestionAnswered(contents, question, answer) {
615
+ const parsed = parseStateMd(contents);
616
+ if (parsed === null) return contents;
617
+ if (typeof question !== 'string' || question.length === 0) return contents;
618
+ const needle = collapseNewlines(question);
619
+
620
+ const lines = parsed.body.split('\n');
621
+ let headingIdx = -1;
622
+ for (let i = 0; i < lines.length; i++) {
623
+ if (/^##\s+Open Questions\b/.test(lines[i])) {
624
+ headingIdx = i;
625
+ break;
626
+ }
627
+ }
628
+ if (headingIdx === -1) return contents;
629
+ let sectionEnd = lines.length;
630
+ for (let i = headingIdx + 1; i < lines.length; i++) {
631
+ if (/^##\s+/.test(lines[i])) {
632
+ sectionEnd = i;
633
+ break;
634
+ }
635
+ }
636
+
637
+ for (let i = headingIdx + 1; i < sectionEnd; i++) {
638
+ const m = OPEN_QUESTION_RE.exec(lines[i].trim());
639
+ if (!m) continue;
640
+ if (m[2].trim() !== needle) continue;
641
+ if (m[1] === 'x') return contents; // already answered — no-op
642
+ lines[i] = formatOpenQuestionBullet({
643
+ question: m[2].trim(),
644
+ source: m[3].trim(),
645
+ priority: m[4],
646
+ answered: true,
647
+ answer: typeof answer === 'string' ? collapseNewlines(answer) : '',
648
+ });
649
+ return serializeStateMd({ frontmatter: parsed.frontmatter, body: lines.join('\n') });
650
+ }
651
+ return contents; // question not found
652
+ }
653
+
654
+ /**
655
+ * Records an auto-commit checkpoint in the `## Deviations` section of STATE.md.
656
+ *
657
+ * Phase-1 stub — appends a human-readable deviation entry only. Does NOT perform
658
+ * any git operations. The procedural commit body (`scripts/lib/auto-commit.mjs`)
659
+ * is deferred to V3.6 (GitLab #214). Until then, callers (wave-executor coordinator)
660
+ * invoke this function AFTER the git commit succeeds to create the audit trail.
661
+ *
662
+ * @param {string} contents - current STATE.md text
663
+ * @param {object} options
664
+ * @param {string} options.sha - short git SHA of the auto-commit (e.g. 'a1b2c3d')
665
+ * @param {number} options.waveN - wave number that triggered the checkpoint
666
+ * @param {string} options.waveResultSummary - one-line summary, e.g. 'Impl-Core, Quality-Lite PASS, 12 files'
667
+ * @param {string} [options.timestamp] - ISO 8601 UTC; defaults to current time
668
+ * @returns {string} updated STATE.md text
669
+ */
670
+ export function recordAutoCommit(contents, options) {
671
+ const opts = options || {};
672
+ const { sha, waveN, waveResultSummary } = opts;
673
+ const timestamp = opts.timestamp ?? new Date().toISOString();
674
+ const message = `Wave ${waveN} auto-commit: ${sha} (${waveResultSummary})`;
675
+ return appendDeviation(contents, timestamp, message);
676
+ }
677
+
678
+ /**
679
+ * Finalizes a STATE.md after express-path coord-direct execution.
680
+ * Sets `status: completed`, appends an `Express path:` deviation, refreshes `updated`.
681
+ * No-op (returns input unchanged) if `contents` is unparseable.
682
+ *
683
+ * @param {string} contents
684
+ * @param {object} options
685
+ * @param {number} options.taskCount - number of tasks executed coord-direct (required)
686
+ * @param {string} [options.sessionType='housekeeping']
687
+ * @param {boolean} [options.expressPathEnabled=true]
688
+ * @param {string} [options.timestamp] - ISO 8601, defaults to current time
689
+ * @returns {string}
690
+ */
691
+ export function markExpressPathComplete(contents, options) {
692
+ const parsed = parseStateMd(contents);
693
+ if (parsed === null) return contents;
694
+ const opts = options || {};
695
+ const taskCount = opts.taskCount;
696
+ const sessionType = opts.sessionType ?? 'housekeeping';
697
+ const expressPathEnabled = opts.expressPathEnabled ?? true;
698
+ const timestamp = opts.timestamp ?? new Date().toISOString();
699
+ const message = `Express path: ${taskCount} tasks executed coord-direct (express-path.enabled: ${expressPathEnabled}, session-type: ${sessionType}, scope: ${taskCount} issues)`;
700
+ let next = updateFrontmatterFields(contents, { status: 'completed' });
701
+ next = appendDeviation(next, timestamp, message);
702
+ next = touchUpdatedField(next, timestamp);
703
+ return next;
704
+ }
705
+
706
+ // ---------------------------------------------------------------------------
707
+ // On-disk wrappers (PRD 2026-05-22 § 4 — Pattern 1, issue #518)
708
+ // ---------------------------------------------------------------------------
709
+
710
+ /**
711
+ * Guard: throws a clear Error when repoRoot is undefined, null, or empty.
712
+ * Parallel-session CWD drift (PSA rules) makes `process.cwd()` fallbacks a
713
+ * footgun — callers MUST be explicit about which repo root they target.
714
+ *
715
+ * @param {unknown} repoRoot
716
+ * @param {string} fnName — name of the calling function, for error messages
717
+ */
718
+ function requireRepoRoot(repoRoot, fnName) {
719
+ if (!repoRoot) {
720
+ throw new Error(
721
+ `${fnName}: repoRoot is required (got ${typeof repoRoot}). Pass an explicit repo root, e.g. via execSync('git rev-parse --show-toplevel').`
722
+ );
723
+ }
724
+ }
725
+
726
+ /**
727
+ * Lock-guarded append to the `## Deviations` section.
728
+ *
729
+ * Delegates to the pure `appendDeviation` helper; the read+write cycle is
730
+ * serialized via `withStateMdLock` (PSA-004 mechanical enforcement).
731
+ *
732
+ * When `isoTimestamp` is omitted, `undefined`, `null`, or a non-string value,
733
+ * the underlying `appendDeviation` defaults to `new Date().toISOString()` (issue #560).
734
+ *
735
+ * @param {string} repoRoot — absolute path to the repository root (required)
736
+ * @param {string} [isoTimestamp] — defaults to current time if omitted
737
+ * @param {string} message
738
+ * @param {object} [opts]
739
+ * @returns {Promise<{ written: boolean, path: string, contents: string|null }>}
740
+ * @throws {Error} when repoRoot is undefined, null, or empty
741
+ */
742
+ export async function appendDeviationOnDisk(repoRoot, isoTimestamp, message, opts = {}) {
743
+ requireRepoRoot(repoRoot, 'appendDeviationOnDisk');
744
+ return writeStateMd(
745
+ repoRoot,
746
+ (contents) => appendDeviation(contents, isoTimestamp, message),
747
+ opts
748
+ );
749
+ }
750
+
751
+ /**
752
+ * Lock-guarded append to the `## What Not To Retry` section (#623).
753
+ *
754
+ * Delegates to the pure `appendWhatNotToRetry` helper; the read+write cycle is
755
+ * serialized via `withStateMdLock` (PSA-005 mechanical enforcement).
756
+ *
757
+ * @param {string} repoRoot — absolute path to the repository root (required)
758
+ * @param {object} entry See appendWhatNotToRetry signature above.
759
+ * @param {object} [opts]
760
+ * @returns {Promise<{ written: boolean, path: string, contents: string|null }>}
761
+ * @throws {Error} when repoRoot is undefined, null, or empty
762
+ */
763
+ export async function appendWhatNotToRetryOnDisk(repoRoot, entry, opts = {}) {
764
+ requireRepoRoot(repoRoot, 'appendWhatNotToRetryOnDisk');
765
+ return writeStateMd(
766
+ repoRoot,
767
+ (contents) => appendWhatNotToRetry(contents, entry),
768
+ opts
769
+ );
770
+ }
771
+
772
+ /**
773
+ * Lock-guarded `recordAutoCommit` for express-path / wave-checkpoint flows.
774
+ *
775
+ * @param {string} repoRoot — absolute path to the repository root (required)
776
+ * @param {{ sha: string, waveN: number, waveResultSummary: string, timestamp?: string }} options
777
+ * @param {object} [opts]
778
+ * @returns {Promise<{ written: boolean, path: string, contents: string|null }>}
779
+ * @throws {Error} when repoRoot is undefined, null, or empty
780
+ */
781
+ export async function recordAutoCommitOnDisk(repoRoot, options, opts = {}) {
782
+ requireRepoRoot(repoRoot, 'recordAutoCommitOnDisk');
783
+ return writeStateMd(
784
+ repoRoot,
785
+ (contents) => recordAutoCommit(contents, options),
786
+ opts
787
+ );
788
+ }
789
+
790
+ /**
791
+ * Lock-guarded `markExpressPathComplete` — used by session-end and the
792
+ * express-path skill flow.
793
+ *
794
+ * @param {string} repoRoot — absolute path to the repository root (required)
795
+ * @param {object} options See markExpressPathComplete signature above.
796
+ * @param {object} [opts]
797
+ * @returns {Promise<{ written: boolean, path: string, contents: string|null }>}
798
+ * @throws {Error} when repoRoot is undefined, null, or empty
799
+ */
800
+ export async function markExpressPathCompleteOnDisk(repoRoot, options, opts = {}) {
801
+ requireRepoRoot(repoRoot, 'markExpressPathCompleteOnDisk');
802
+ return writeStateMd(
803
+ repoRoot,
804
+ (contents) => markExpressPathComplete(contents, options),
805
+ opts
806
+ );
807
+ }
808
+
809
+ /**
810
+ * Lock-guarded append to the `## Open Questions` section (Close
811
+ * Handover-Alignment-Gate, PRD 2026-07-07).
812
+ *
813
+ * Delegates to the pure `appendOpenQuestion` helper; the read+write cycle is
814
+ * serialized via `withStateMdLock` (PSA-005 mechanical enforcement — the
815
+ * lock is acquired exclusively by `writeStateMd`, never by this wrapper
816
+ * directly, to avoid a non-reentrant-mutex deadlock).
817
+ *
818
+ * @param {string} repoRoot — absolute path to the repository root (required)
819
+ * @param {object} entry See appendOpenQuestion signature above.
820
+ * @param {object} [opts]
821
+ * @returns {Promise<{ written: boolean, path: string, contents: string|null }>}
822
+ * @throws {Error} when repoRoot is undefined, null, or empty
823
+ */
824
+ export async function appendOpenQuestionOnDisk(repoRoot, entry, opts = {}) {
825
+ requireRepoRoot(repoRoot, 'appendOpenQuestionOnDisk');
826
+ return writeStateMd(
827
+ repoRoot,
828
+ (contents) => appendOpenQuestion(contents, entry),
829
+ opts
830
+ );
831
+ }
832
+
833
+ /**
834
+ * Lock-guarded `markOpenQuestionAnswered` — used by session-end to record an
835
+ * answer against a previously-surfaced open question.
836
+ *
837
+ * @param {string} repoRoot — absolute path to the repository root (required)
838
+ * @param {string} question
839
+ * @param {string} answer
840
+ * @param {object} [opts]
841
+ * @returns {Promise<{ written: boolean, path: string, contents: string|null }>}
842
+ * @throws {Error} when repoRoot is undefined, null, or empty
843
+ */
844
+ export async function markOpenQuestionAnsweredOnDisk(repoRoot, question, answer, opts = {}) {
845
+ requireRepoRoot(repoRoot, 'markOpenQuestionAnsweredOnDisk');
846
+ return writeStateMd(
847
+ repoRoot,
848
+ (contents) => markOpenQuestionAnswered(contents, question, answer),
849
+ opts
850
+ );
851
+ }