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,62 @@
1
+ /**
2
+ * locks/lock-body.mjs — shared leaf helpers for the short-lived lock protocols
3
+ * (STATE.md write-lock + staging-fence commit-mutex) split out of
4
+ * session-lock.mjs in #630 (A1 barrel-preserving split).
5
+ *
6
+ * This is a PURE leaf: it imports NOTHING (Node stdlib only used inline). Both
7
+ * scripts/lib/locks/state-md-lock.mjs and staging-fence-lock.mjs import these
8
+ * three helpers so the previously-shared `nowIso` / `delay` / `parseLockBody`
9
+ * live in exactly one place instead of being duplicated across the two modules.
10
+ *
11
+ * It does NOT import session-lock.mjs (which re-exports the two protocol
12
+ * modules) — the dependency edge points locks/* → lock-body, never the reverse,
13
+ * so there is no import cycle.
14
+ */
15
+
16
+ /**
17
+ * Return the current time as an ISO-8601 string.
18
+ * @returns {string}
19
+ */
20
+ export function nowIso() {
21
+ return new Date().toISOString();
22
+ }
23
+
24
+ /**
25
+ * Sleep helper for the acquire poll-loop. Promise-returning, so the loop is
26
+ * async without blocking the event loop.
27
+ * @param {number} ms
28
+ * @returns {Promise<void>}
29
+ */
30
+ export function delay(ms) {
31
+ return new Promise((resolve) => setTimeout(resolve, ms));
32
+ }
33
+
34
+ /**
35
+ * Parse a lock-file body shared by the state-lock and staging-fence-lock.
36
+ * Both locks use identical { pid, host, acquiredAt, holder } shape so a single
37
+ * parser serves both. Returns null on any malformed input.
38
+ *
39
+ * Moved verbatim from session-lock.mjs in #630 — behaviour preserved exactly
40
+ * (renamed from parseStateLock in #558 M4 because it serves both locks).
41
+ *
42
+ * @param {string} raw
43
+ * @returns {{ pid: number, host: string, acquiredAt: string, holder: string }|null}
44
+ */
45
+ export function parseLockBody(raw) {
46
+ try {
47
+ const obj = JSON.parse(raw);
48
+ if (
49
+ typeof obj === 'object' &&
50
+ obj !== null &&
51
+ typeof obj.pid === 'number' &&
52
+ typeof obj.host === 'string' &&
53
+ typeof obj.acquiredAt === 'string' &&
54
+ typeof obj.holder === 'string'
55
+ ) {
56
+ return obj;
57
+ }
58
+ return null;
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
@@ -0,0 +1,267 @@
1
+ /**
2
+ * locks/staging-fence-lock.mjs — staging-fence commit-mutex protocol
3
+ * (PSA-004 sub-mode C, issue #552).
4
+ *
5
+ * Split out of session-lock.mjs in #630 (A1 barrel-preserving split). The
6
+ * symbols below are re-exported UNCHANGED from session-lock.mjs so the original
7
+ * import surface is preserved for all 17 importers (incl.
8
+ * hooks/wave-scope-commit-guard.mjs).
9
+ *
10
+ * Held only around the wave-scope-commit-guard cross-fence check. Two sibling
11
+ * wave-agents that both pass through the per-agent guard race to acquire this
12
+ * lock; the winner inspects ALL fence files, the loser polls until the winner
13
+ * releases. Without the mutex the check is TOCTOU-vulnerable: agent A reads
14
+ * agent B's fence file BEFORE B writes agent B's last `git add` intent, and
15
+ * both proceed to `git commit` with overlapping staged paths.
16
+ *
17
+ * Implementation reuses the same tmp+linkSync cross-process pattern as the
18
+ * STATE.md lock (delegated to the shared file-lock primitive). The two lockfiles
19
+ * are distinct so STATE.md writes never contend with commit-guard checks.
20
+ *
21
+ * Dependency edges point locks/staging-fence-lock → { file-lock, locks/lock-body },
22
+ * NEVER → session-lock.mjs, so there is no import cycle.
23
+ */
24
+
25
+ import fs from 'node:fs';
26
+ import os from 'node:os';
27
+ import path from 'node:path';
28
+ import crypto from 'node:crypto';
29
+
30
+ import { tryAcquireFileLock } from '../file-lock.mjs';
31
+ import { nowIso, delay, parseLockBody } from './lock-body.mjs';
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // Constants
35
+ // ---------------------------------------------------------------------------
36
+
37
+ // Staging-fence commit-mutex (PSA-004 sub-mode C, issue #552). Held only for
38
+ // the duration of the wave-scope-commit-guard's cross-agent fence check.
39
+ // - state.lock = "STATE.md is being written right now"
40
+ // - staging-fence.lock = "the cross-fence commit check is running right now"
41
+ // Distinct lockfile so the two locks never contend with each other.
42
+ export const STAGING_FENCE_LOCK_PATH = '.orchestrator/staging-fence/.commit.lock';
43
+ export const DEFAULT_STAGING_FENCE_LOCK_TIMEOUT_MS = 10000;
44
+ export const STAGING_FENCE_LOCK_POLL_MS = 100;
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Internal helpers
48
+ // ---------------------------------------------------------------------------
49
+
50
+ /**
51
+ * Resolve the absolute path to the staging-fence commit lock file.
52
+ * @param {string|undefined} repoRoot
53
+ * @returns {string}
54
+ */
55
+ function stagingFenceLockPathFor(repoRoot) {
56
+ return path.join(repoRoot ?? process.cwd(), STAGING_FENCE_LOCK_PATH);
57
+ }
58
+
59
+ /**
60
+ * Build a fresh staging-fence lock body. Same shape as buildStateLockBody so
61
+ * the parseLockBody parser works identically — there is no need for a
62
+ * second parser.
63
+ *
64
+ * @param {{ holder?: string }} args
65
+ * @returns {{ pid: number, host: string, acquiredAt: string, holder: string }}
66
+ */
67
+ function buildStagingFenceLockBody({ holder }) {
68
+ return {
69
+ pid: process.pid,
70
+ host: os.hostname(),
71
+ acquiredAt: nowIso(),
72
+ holder: typeof holder === 'string' && holder.length > 0 ? holder : `pid-${process.pid}`,
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Single-pass acquire attempt for the staging-fence lock. Mirrors
78
+ * tryAcquireStateLock — only the lockfile path + tmp prefix differ.
79
+ *
80
+ * Returns:
81
+ * { ok: true, lock } — acquired
82
+ * { ok: false, reason: 'held', existingLock } — live holder; caller polls
83
+ * { ok: false, reason: 'fs-error', error } — filesystem failure
84
+ */
85
+ function tryAcquireStagingFenceLock(lockFile, body) {
86
+ // Delegates to the shared file-lock primitive (issue #630). Structurally
87
+ // identical to tryAcquireStateLock — only the tmp prefix + WARN messages
88
+ // differ. Behavior preserved EXACTLY: pretty body (indent 2), PID staleCheck,
89
+ // console.warn channel, override prefix `.staging-fence.lock.tmp`,
90
+ // ENOENT-on-read collapsed into `held`/existingLock:null.
91
+ const attempt = tryAcquireFileLock(lockFile, {
92
+ staleCheck: 'pid',
93
+ holder: body.holder,
94
+ indent: 2,
95
+ tmpPrefix: '.staging-fence.lock.tmp',
96
+ warnMessage: (reason, _lp, existing) =>
97
+ existing === null
98
+ ? 'stale staging-fence.lock (unparseable contents) overridden'
99
+ : `stale staging-fence.lock from PID ${existing.pid} overridden`,
100
+ });
101
+
102
+ if (attempt.acquired) return { ok: true, lock: attempt.body };
103
+ if (attempt.reason === 'fs-error') return { ok: false, reason: 'fs-error', error: attempt.error };
104
+ return { ok: false, reason: 'held', existingLock: attempt.existing ?? null };
105
+ }
106
+
107
+ // ---------------------------------------------------------------------------
108
+ // Exported API
109
+ // ---------------------------------------------------------------------------
110
+
111
+ /**
112
+ * Acquire the staging-fence commit-lock. Polls every STAGING_FENCE_LOCK_POLL_MS
113
+ * until the lock is acquired or the timeout expires. Same semantics as
114
+ * acquireStateLock.
115
+ *
116
+ * @param {object} [opts]
117
+ * @param {number} [opts.timeoutMs=10000]
118
+ * @param {string} [opts.repoRoot]
119
+ * @param {string} [opts.holder]
120
+ * @param {number} [opts.pollMs]
121
+ */
122
+ export async function acquireStagingFenceLock({
123
+ timeoutMs = DEFAULT_STAGING_FENCE_LOCK_TIMEOUT_MS,
124
+ repoRoot,
125
+ holder,
126
+ pollMs = STAGING_FENCE_LOCK_POLL_MS,
127
+ } = {}) {
128
+ const lockFile = stagingFenceLockPathFor(repoRoot);
129
+ const body = buildStagingFenceLockBody({ holder });
130
+ const deadline = Date.now() + (typeof timeoutMs === 'number' && timeoutMs >= 0
131
+ ? timeoutMs
132
+ : DEFAULT_STAGING_FENCE_LOCK_TIMEOUT_MS);
133
+ const effectivePollMs = typeof pollMs === 'number' && pollMs > 0
134
+ ? pollMs
135
+ : STAGING_FENCE_LOCK_POLL_MS;
136
+
137
+ for (;;) {
138
+ const attempt = tryAcquireStagingFenceLock(lockFile, body);
139
+ if (attempt.ok) return attempt;
140
+ if (attempt.reason === 'fs-error') return attempt;
141
+
142
+ if (Date.now() >= deadline) {
143
+ return {
144
+ ok: false,
145
+ reason: 'timeout',
146
+ existingLock: attempt.existingLock ?? null,
147
+ };
148
+ }
149
+ await delay(effectivePollMs);
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Release the staging-fence commit-lock IFF the holder matches.
155
+ *
156
+ * @param {object} [opts]
157
+ * @param {string} [opts.repoRoot]
158
+ * @param {string} [opts.holder]
159
+ * @returns {{ ok: true } | { ok: false, reason: 'not-found'|'not-owner'|'fs-error', error?: string }}
160
+ */
161
+ export function releaseStagingFenceLock({ repoRoot, holder } = {}) {
162
+ const lockFile = stagingFenceLockPathFor(repoRoot);
163
+
164
+ let raw;
165
+ try {
166
+ raw = fs.readFileSync(lockFile, 'utf8');
167
+ } catch (err) {
168
+ if (err.code === 'ENOENT') {
169
+ return { ok: false, reason: 'not-found' };
170
+ }
171
+ return { ok: false, reason: 'fs-error', error: err.message };
172
+ }
173
+
174
+ const lock = parseLockBody(raw);
175
+ if (lock === null) {
176
+ return { ok: false, reason: 'not-owner' };
177
+ }
178
+
179
+ const ownerMatch = typeof holder === 'string' && holder.length > 0
180
+ ? lock.holder === holder
181
+ : lock.pid === process.pid && lock.host === os.hostname();
182
+
183
+ if (!ownerMatch) {
184
+ return { ok: false, reason: 'not-owner' };
185
+ }
186
+
187
+ try {
188
+ fs.unlinkSync(lockFile);
189
+ return { ok: true };
190
+ } catch (err) {
191
+ if (err.code === 'ENOENT') {
192
+ return { ok: false, reason: 'not-found' };
193
+ }
194
+ return { ok: false, reason: 'fs-error', error: err.message };
195
+ }
196
+ }
197
+
198
+ /**
199
+ * High-level wrapper: acquire the staging-fence commit-lock, run `fn`,
200
+ * release on completion or throw. Always releases — even if `fn` throws —
201
+ * before re-raising.
202
+ *
203
+ * No Session Config short-circuit is provided. Unlike the STATE.md lock
204
+ * (which is bypassed when `state-md-lock.enabled: false`), this lock guards
205
+ * a single small read-modify-write on a hidden runtime directory and has
206
+ * no performance cost worth opting out of. If the lock genuinely needs to
207
+ * be disabled, callers can skip the wrapper entirely.
208
+ *
209
+ * Throws when:
210
+ * - acquireStagingFenceLock fails (timeout or fs-error) → labelled Error.
211
+ * - `fn` throws → the original error is re-thrown after release.
212
+ *
213
+ * @param {string|undefined} repoRoot
214
+ * @param {() => (T | Promise<T>)} fn
215
+ * @param {object} [opts]
216
+ * @param {number} [opts.timeoutMs]
217
+ * @param {string} [opts.holder]
218
+ * @param {number} [opts.pollMs]
219
+ * @returns {Promise<T>}
220
+ * @template T
221
+ */
222
+ export async function withStagingFenceLock(repoRoot, fn, opts = {}) {
223
+ if (typeof fn !== 'function') {
224
+ throw new TypeError('withStagingFenceLock: fn must be a function');
225
+ }
226
+
227
+ const holder = typeof opts.holder === 'string' && opts.holder.length > 0
228
+ ? opts.holder
229
+ : `pid-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
230
+
231
+ const acquireResult = await acquireStagingFenceLock({
232
+ repoRoot,
233
+ timeoutMs: opts.timeoutMs,
234
+ holder,
235
+ pollMs: opts.pollMs,
236
+ });
237
+
238
+ if (!acquireResult.ok) {
239
+ const reason = acquireResult.reason;
240
+ const extra = reason === 'timeout' && acquireResult.existingLock
241
+ ? ` (held by ${acquireResult.existingLock.holder}, pid=${acquireResult.existingLock.pid})`
242
+ : reason === 'fs-error' && acquireResult.error
243
+ ? `: ${acquireResult.error}`
244
+ : '';
245
+ const err = new Error(`withStagingFenceLock: acquire failed (${reason})${extra}`);
246
+ err.code = `STAGING_FENCE_LOCK_${reason.toUpperCase().replace(/-/g, '_')}`;
247
+ throw err;
248
+ }
249
+
250
+ let result;
251
+ let caughtError = null;
252
+ try {
253
+ result = await fn();
254
+ } catch (err) {
255
+ caughtError = err;
256
+ } finally {
257
+ const releaseResult = releaseStagingFenceLock({ repoRoot, holder });
258
+ if (!releaseResult.ok && releaseResult.reason === 'fs-error') {
259
+ console.warn(
260
+ `withStagingFenceLock: release failed (fs-error: ${releaseResult.error ?? 'unknown'})`,
261
+ );
262
+ }
263
+ }
264
+
265
+ if (caughtError !== null) throw caughtError;
266
+ return result;
267
+ }
@@ -0,0 +1,351 @@
1
+ /**
2
+ * locks/state-md-lock.mjs — STATE.md write-lock protocol
3
+ * (PRD 2026-05-22 § 4 — Pattern 1, issue #518).
4
+ *
5
+ * Split out of session-lock.mjs in #630 (A1 barrel-preserving split). The
6
+ * symbols below are re-exported UNCHANGED from session-lock.mjs so the original
7
+ * import surface is preserved for all 17 importers.
8
+ *
9
+ * Mechanical enforcement of PSA-004 for STATE.md writes. Whereas the
10
+ * session-lock (session-lock.mjs) guards "this working-copy is held by one
11
+ * session", the state-lock guards "STATE.md is being written right now" — a
12
+ * short-lived lock acquired around every read-modify-write cycle.
13
+ *
14
+ * Design:
15
+ * - Atomic create via tmp + linkSync, delegated to the shared file-lock
16
+ * primitive (tryAcquireFileLock).
17
+ * - Body: { pid, host, acquiredAt, holder } — host is included so cross-host
18
+ * callers (rare but possible via shared filesystems) avoid spurious PID
19
+ * liveness checks against unrelated PIDs.
20
+ * - Stale detection: process.kill(pid, 0). When the holder is on the same
21
+ * host and the PID is dead (ESRCH), the lock is overridden atomically and
22
+ * a WARN is written to stderr. Cross-host stale locks are NOT auto-cleared
23
+ * — they fall through to the timeout path.
24
+ * - Poll cadence: 100 ms by default. Configurable via STATE_LOCK_POLL_MS but
25
+ * no public override — tests inject via the optional `pollMs` parameter.
26
+ *
27
+ * Returns structured results, never throws (acquireStateLock / releaseStateLock).
28
+ * withStateMdLock re-throws caller errors after releasing.
29
+ *
30
+ * Dependency edges point locks/state-md-lock → { file-lock, config/state-md-lock,
31
+ * locks/lock-body }, NEVER → session-lock.mjs, so there is no import cycle.
32
+ *
33
+ * NAMING NOTE: scripts/lib/config/state-md-lock.mjs is a DIFFERENT file — it
34
+ * parses the `state-md-lock:` Session Config key. This module is the lock IMPL.
35
+ */
36
+
37
+ import fs from 'node:fs';
38
+ import os from 'node:os';
39
+ import path from 'node:path';
40
+ import crypto from 'node:crypto';
41
+
42
+ import { _parseStateMdLock } from '../config/state-md-lock.mjs';
43
+ import { tryAcquireFileLock } from '../file-lock.mjs';
44
+ import { nowIso, delay, parseLockBody } from './lock-body.mjs';
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Constants
48
+ // ---------------------------------------------------------------------------
49
+
50
+ // STATE.md write-lock (PRD 2026-05-22 § 4 — Pattern 1, issue #518).
51
+ // Orthogonal to the session-lock:
52
+ // - session.lock = "this repo working-copy is held by an active session"
53
+ // - state.lock = "STATE.md is being written right now"
54
+ // Two distinct lock files so a session can hold its session-lock for hours
55
+ // while still allowing fast acquire/release cycles around individual writes.
56
+ export const STATE_LOCK_PATH = '.orchestrator/state.lock';
57
+ export const DEFAULT_STATE_LOCK_TIMEOUT_MS = 10000;
58
+ export const STATE_LOCK_POLL_MS = 100;
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Internal helpers
62
+ // ---------------------------------------------------------------------------
63
+
64
+ /**
65
+ * Resolve the absolute path to the state-lock file.
66
+ * @param {string|undefined} repoRoot
67
+ * @returns {string}
68
+ */
69
+ function stateLockPathFor(repoRoot) {
70
+ return path.join(repoRoot ?? process.cwd(), STATE_LOCK_PATH);
71
+ }
72
+
73
+ /**
74
+ * Build a fresh state-lock body.
75
+ * @param {{ holder?: string }} args
76
+ * @returns {{ pid: number, host: string, acquiredAt: string, holder: string }}
77
+ */
78
+ function buildStateLockBody({ holder }) {
79
+ return {
80
+ pid: process.pid,
81
+ host: os.hostname(),
82
+ acquiredAt: nowIso(),
83
+ holder: typeof holder === 'string' && holder.length > 0 ? holder : `pid-${process.pid}`,
84
+ };
85
+ }
86
+
87
+ /**
88
+ * Attempt one acquisition pass. Returns:
89
+ * { ok: true, lock } — lock written
90
+ * { ok: false, reason: 'held', existingLock } — held by a live holder
91
+ * { ok: false, reason: 'fs-error', error } — filesystem failure
92
+ *
93
+ * Strategy:
94
+ * 1. Try O_EXCL create (cross-process mutex). Success → return immediately.
95
+ * 2. On EEXIST → read the existing lock, check PID liveness on same host.
96
+ * - Live PID → `held` (caller polls).
97
+ * - Dead PID (or unparseable contents) → stale, atomic override + WARN.
98
+ *
99
+ * Side-effect: stale-lock override writes a WARN to stderr. Cross-host locks
100
+ * are never auto-overridden (can't signal a process on another machine).
101
+ */
102
+ function tryAcquireStateLock(lockFile, body) {
103
+ // Delegates to the shared file-lock primitive (issue #630). Behavior is
104
+ // preserved EXACTLY: pretty-printed body `{pid, host, acquiredAt, holder}`
105
+ // (indent 2), PID staleCheck, console.warn override channel with the original
106
+ // messages, override tmp prefix `.state.lock.tmp`, and the ENOENT-on-read race
107
+ // collapsed into `held`/existingLock:null (signalVanished:false).
108
+ //
109
+ // The primitive builds its own body (fresh acquiredAt per attempt) from the
110
+ // pid/host of THIS process plus the holder carried on `body`. acquiredAt is
111
+ // only meaningful on the written (winning) attempt, so reusing vs regenerating
112
+ // it across poll passes is observationally identical.
113
+ const attempt = tryAcquireFileLock(lockFile, {
114
+ staleCheck: 'pid',
115
+ holder: body.holder,
116
+ indent: 2,
117
+ tmpPrefix: '.state.lock.tmp',
118
+ warnMessage: (reason, _lp, existing) =>
119
+ existing === null
120
+ ? 'stale state.lock (unparseable contents) overridden'
121
+ : `stale state.lock from PID ${existing.pid} overridden`,
122
+ });
123
+
124
+ if (attempt.acquired) return { ok: true, lock: attempt.body };
125
+ if (attempt.reason === 'fs-error') return { ok: false, reason: 'fs-error', error: attempt.error };
126
+ // reason === 'held' (live holder, cross-host, or vanished-collapsed-to-held).
127
+ return { ok: false, reason: 'held', existingLock: attempt.existing ?? null };
128
+ }
129
+
130
+ // ---------------------------------------------------------------------------
131
+ // Exported API
132
+ // ---------------------------------------------------------------------------
133
+
134
+ /**
135
+ * Acquire the STATE.md write-lock. Polls every STATE_LOCK_POLL_MS until the
136
+ * lock is acquired or the timeout expires.
137
+ *
138
+ * Returns:
139
+ * { ok: true, lock } — lock acquired (possibly after waiting)
140
+ * { ok: false, reason: 'timeout', existingLock? } — timed out waiting for live holder
141
+ * { ok: false, reason: 'fs-error', error: string } — filesystem failure
142
+ *
143
+ * Stale-lock side-effects: when the existing lock points to a dead PID on
144
+ * the same host, the helper overrides it atomically and writes a WARN to
145
+ * stderr. The next poll iteration will then succeed.
146
+ *
147
+ * Never throws.
148
+ *
149
+ * @param {object} [opts]
150
+ * @param {number} [opts.timeoutMs=10000] — max wait in milliseconds.
151
+ * @param {string} [opts.repoRoot] — defaults to process.cwd().
152
+ * @param {string} [opts.holder] — human-readable holder string (default `pid-<pid>`).
153
+ * @param {number} [opts.pollMs] — test-only override of poll cadence.
154
+ */
155
+ export async function acquireStateLock({
156
+ timeoutMs = DEFAULT_STATE_LOCK_TIMEOUT_MS,
157
+ repoRoot,
158
+ holder,
159
+ pollMs = STATE_LOCK_POLL_MS,
160
+ } = {}) {
161
+ const lockFile = stateLockPathFor(repoRoot);
162
+ const body = buildStateLockBody({ holder });
163
+ const deadline = Date.now() + (typeof timeoutMs === 'number' && timeoutMs >= 0 ? timeoutMs : DEFAULT_STATE_LOCK_TIMEOUT_MS);
164
+ const effectivePollMs = typeof pollMs === 'number' && pollMs > 0 ? pollMs : STATE_LOCK_POLL_MS;
165
+
166
+ // Loop until acquired or deadline reached. The first iteration runs
167
+ // unconditionally so a timeoutMs of 0 still attempts one acquisition.
168
+ for (;;) {
169
+ const attempt = tryAcquireStateLock(lockFile, body);
170
+ if (attempt.ok) return attempt;
171
+ if (attempt.reason === 'fs-error') return attempt;
172
+
173
+ if (Date.now() >= deadline) {
174
+ return {
175
+ ok: false,
176
+ reason: 'timeout',
177
+ existingLock: attempt.existingLock ?? null,
178
+ };
179
+ }
180
+ await delay(effectivePollMs);
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Release the STATE.md write-lock IFF the holder matches.
186
+ *
187
+ * Caller must pass the same identifier they used in acquireStateLock — either
188
+ * `holder` (free-form string) OR `sessionId` (matched against the holder
189
+ * field when holder follows the `<sessionId>` convention). If neither is
190
+ * provided, the helper falls back to PID equality.
191
+ *
192
+ * Returns (per PRD § 4):
193
+ * { ok: true } — lock unlinked
194
+ * { ok: false, reason: 'not-found' } — no lock file exists
195
+ * { ok: false, reason: 'not-owner' } — lock held by different holder/PID
196
+ * { ok: false, reason: 'fs-error', error } — filesystem failure
197
+ *
198
+ * Never throws.
199
+ *
200
+ * @param {object} [opts]
201
+ * @param {string} [opts.repoRoot]
202
+ * @param {string} [opts.sessionId] — matched against the `holder` field.
203
+ * @param {string} [opts.holder] — matched against the `holder` field (overrides sessionId).
204
+ */
205
+ export function releaseStateLock({ repoRoot, sessionId, holder } = {}) {
206
+ const lockFile = stateLockPathFor(repoRoot);
207
+
208
+ let raw;
209
+ try {
210
+ raw = fs.readFileSync(lockFile, 'utf8');
211
+ } catch (err) {
212
+ if (err.code === 'ENOENT') {
213
+ return { ok: false, reason: 'not-found' };
214
+ }
215
+ return { ok: false, reason: 'fs-error', error: err.message };
216
+ }
217
+
218
+ const lock = parseLockBody(raw);
219
+ if (lock === null) {
220
+ // Unparseable — refuse to delete; some other process may be writing now.
221
+ return { ok: false, reason: 'not-owner' };
222
+ }
223
+
224
+ const expectedHolder = holder ?? sessionId ?? null;
225
+ const ownerMatch = expectedHolder !== null
226
+ ? lock.holder === expectedHolder
227
+ : lock.pid === process.pid && lock.host === os.hostname();
228
+
229
+ if (!ownerMatch) {
230
+ return { ok: false, reason: 'not-owner' };
231
+ }
232
+
233
+ try {
234
+ fs.unlinkSync(lockFile);
235
+ return { ok: true };
236
+ } catch (err) {
237
+ if (err.code === 'ENOENT') {
238
+ return { ok: false, reason: 'not-found' };
239
+ }
240
+ return { ok: false, reason: 'fs-error', error: err.message };
241
+ }
242
+ }
243
+
244
+ /**
245
+ * High-level wrapper: acquire the STATE.md write-lock, run `fn`, release on
246
+ * completion or throw. Always releases the lock — even if `fn` throws —
247
+ * before re-raising the original error.
248
+ *
249
+ * Short-circuit: when `state-md-lock.enabled: false` is set in CLAUDE.md
250
+ * (or AGENTS.md on Codex CLI — the two are aliases per
251
+ * `skills/_shared/instruction-file-resolution.md`) Session Config, the lock
252
+ * is bypassed entirely and `fn` is called directly. A stderr WARN line is
253
+ * emitted so operators can detect the bypass. This honours the config knob
254
+ * documented in `.claude/rules/parallel-sessions.md` PSA-005 without
255
+ * removing the lock infrastructure.
256
+ *
257
+ * Per-call override via `opts._stateMdLockEnabled` (boolean): when provided,
258
+ * takes precedence over the config value. Useful for tests that need to
259
+ * exercise the short-circuit without touching CLAUDE.md on disk.
260
+ * The leading underscore marks this as a test-only seam — production callers
261
+ * MUST omit this option.
262
+ *
263
+ * Fail-safe: if CLAUDE.md cannot be read or the config block is malformed,
264
+ * `enabled` defaults to `true` — lock is always acquired on errors.
265
+ *
266
+ * Throws when:
267
+ * - acquireStateLock fails (timeout or fs-error) → throws a labelled Error
268
+ * so callers see the failure as an exception rather than a silent
269
+ * {ok:false} return. This is the contract that lets call sites use plain
270
+ * `await withStateMdLock(repoRoot, async () => …)` without branching.
271
+ * - `fn` throws → the original error is re-thrown after release.
272
+ *
273
+ * @param {string|undefined} repoRoot
274
+ * @param {() => (T | Promise<T>)} fn
275
+ * @param {object} [opts]
276
+ * @param {number} [opts.timeoutMs]
277
+ * @param {string} [opts.holder]
278
+ * @param {number} [opts.pollMs]
279
+ * @param {boolean} [opts._stateMdLockEnabled] — test-only per-call override;
280
+ * takes precedence over the Session Config value when set. Production
281
+ * callers MUST omit this option.
282
+ * @returns {Promise<T>}
283
+ * @template T
284
+ */
285
+ export async function withStateMdLock(repoRoot, fn, opts = {}) {
286
+ if (typeof fn !== 'function') {
287
+ throw new TypeError('withStateMdLock: fn must be a function');
288
+ }
289
+
290
+ // Short-circuit: respect state-md-lock.enabled: false from Session Config.
291
+ // opts._stateMdLockEnabled (test-only per-call override) takes precedence when set.
292
+ let enabled = opts._stateMdLockEnabled;
293
+ if (enabled === undefined) {
294
+ try {
295
+ const claudeMdPath = path.join(repoRoot ?? process.cwd(), 'CLAUDE.md');
296
+ const claudeMdContents = fs.readFileSync(claudeMdPath, 'utf8');
297
+ const cfg = _parseStateMdLock(claudeMdContents);
298
+ enabled = cfg.enabled;
299
+ } catch {
300
+ // Fail-safe: if CLAUDE.md is absent or unreadable, default to locked.
301
+ enabled = true;
302
+ }
303
+ }
304
+
305
+ if (enabled === false) {
306
+ process.stderr.write('⚠ withStateMdLock: short-circuit (state-md-lock.enabled: false) — running fn without lock\n');
307
+ return await fn();
308
+ }
309
+
310
+ const holder = typeof opts.holder === 'string' && opts.holder.length > 0
311
+ ? opts.holder
312
+ : `pid-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
313
+
314
+ const acquireResult = await acquireStateLock({
315
+ repoRoot,
316
+ timeoutMs: opts.timeoutMs,
317
+ holder,
318
+ pollMs: opts.pollMs,
319
+ });
320
+
321
+ if (!acquireResult.ok) {
322
+ const reason = acquireResult.reason;
323
+ const extra = reason === 'timeout' && acquireResult.existingLock
324
+ ? ` (held by ${acquireResult.existingLock.holder}, pid=${acquireResult.existingLock.pid})`
325
+ : reason === 'fs-error' && acquireResult.error
326
+ ? `: ${acquireResult.error}`
327
+ : '';
328
+ const err = new Error(`withStateMdLock: acquire failed (${reason})${extra}`);
329
+ err.code = `STATE_LOCK_${reason.toUpperCase().replace(/-/g, '_')}`;
330
+ throw err;
331
+ }
332
+
333
+ let result;
334
+ let caughtError = null;
335
+ try {
336
+ result = await fn();
337
+ } catch (err) {
338
+ caughtError = err;
339
+ } finally {
340
+ // Always release — even on fn() throw — so the lock does not leak.
341
+ // Only WARN on fs-error: 'not-found' and 'not-owner' are recoverable race
342
+ // conditions (someone else cleaned up our lock — already safe to proceed).
343
+ const releaseResult = releaseStateLock({ repoRoot, holder });
344
+ if (!releaseResult.ok && releaseResult.reason === 'fs-error') {
345
+ console.warn(`withStateMdLock: release failed (fs-error: ${releaseResult.error ?? 'unknown'})`);
346
+ }
347
+ }
348
+
349
+ if (caughtError !== null) throw caughtError;
350
+ return result;
351
+ }