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,471 @@
1
+ /**
2
+ * auto-dream.mjs — Auto-Dream Post-Session Hook helper (issue #502, PRD F2.2).
3
+ *
4
+ * Provides the decision + I/O surface used by session-end Phase 3.6.5 and by
5
+ * /memory-cleanup --dry-run / --apply-pending. Decides whether the post-session
6
+ * dream should fire, writes the proposed diff to `.orchestrator/pending-dream.md`
7
+ * atomically, and applies it in a follow-up session.
8
+ *
9
+ * Decision inputs (PRD F2.2 acceptance criteria):
10
+ * - memory-cleanup-threshold (default 5) — sessions since last cleanup
11
+ * - memory-cleanup-soft-limit (default 180) — MEMORY.md line ceiling
12
+ * - kill-switch: threshold === 0 → never trigger
13
+ *
14
+ * Proposal-emit min-confidence filter (issue #566): the `auto-dream.min-confidence`
15
+ * key (default 0.5) is PARSED in `scripts/lib/config/auto-dream.mjs`, but its FILTER
16
+ * BEHAVIOR lives in `collectProposals()` (`scripts/lib/memory-proposals/collector.mjs`
17
+ * ~L319). Accepted trade-off (#589 MED-2): filter co-located with the queue it filters;
18
+ * renaming to a `memory.proposals.*` key would break #566's naming. Phase 3.6.3.
19
+ *
20
+ * No external deps — Node 20+ stdlib only.
21
+ */
22
+
23
+ import { readFile, writeFile, rename, unlink, mkdir } from 'node:fs/promises';
24
+ import { existsSync, statSync } from 'node:fs';
25
+ import { randomUUID } from 'node:crypto';
26
+ import path from 'node:path';
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Signal reader — MEMORY.md size + sessions-since-last-cleanup
30
+ // ---------------------------------------------------------------------------
31
+
32
+ /**
33
+ * Read decision signals used by `shouldDispatchAutoDream`.
34
+ *
35
+ * Returns:
36
+ * - memoryLines: line count of MEMORY.md (0 if absent).
37
+ * - lastCleanupAt: max `memory_cleanup_at` ISO timestamp across sessions.jsonl
38
+ * (null if no entry carries the field).
39
+ * - sessionsSinceCleanup: count of sessions.jsonl entries with `started_at >
40
+ * lastCleanupAt` (or total entries when lastCleanupAt is null).
41
+ * - sessionsFilePath: resolved path to sessions.jsonl (for diagnostics).
42
+ *
43
+ * Schema-additive: `memory_cleanup_at` is read but never assumed present.
44
+ *
45
+ * @param {object} args
46
+ * @param {string} args.repoRoot Absolute path to the repo root.
47
+ * @param {string} args.memoryDir Absolute path to the memory dir (use resolveMemoryDir() from './memory-paths.mjs').
48
+ * @returns {Promise<{memoryLines:number, lastCleanupAt:string|null, sessionsSinceCleanup:number, sessionsFilePath:string}>}
49
+ */
50
+ export async function readDreamSignals({ repoRoot, memoryDir }) {
51
+ const memoryPath = path.join(memoryDir, 'MEMORY.md');
52
+ const sessionsFilePath = path.join(repoRoot, '.orchestrator', 'metrics', 'sessions.jsonl');
53
+
54
+ // 1. Line count of MEMORY.md (missing → 0)
55
+ let memoryLines = 0;
56
+ if (existsSync(memoryPath)) {
57
+ const raw = await readFile(memoryPath, 'utf8');
58
+ memoryLines = raw.length === 0 ? 0 : raw.split('\n').length;
59
+ }
60
+
61
+ // 2. Read sessions.jsonl entries to compute lastCleanupAt + sessionsSinceCleanup
62
+ let lastCleanupAt = null;
63
+ let sessionsSinceCleanup = 0;
64
+
65
+ if (existsSync(sessionsFilePath)) {
66
+ const raw = await readFile(sessionsFilePath, 'utf8');
67
+ const lines = raw.split('\n').filter((l) => l.length > 0);
68
+ const entries = [];
69
+ for (const line of lines) {
70
+ try {
71
+ entries.push(JSON.parse(line));
72
+ } catch {
73
+ // Malformed line — skip silently, this is a best-effort signal reader.
74
+ }
75
+ }
76
+
77
+ // Find the most recent memory_cleanup_at timestamp across all entries.
78
+ for (const entry of entries) {
79
+ const ts = entry.memory_cleanup_at;
80
+ if (typeof ts === 'string' && ts.length > 0) {
81
+ if (lastCleanupAt === null || ts > lastCleanupAt) {
82
+ lastCleanupAt = ts;
83
+ }
84
+ }
85
+ }
86
+
87
+ // Count entries newer than the last cleanup (or total when no cleanup ever ran).
88
+ if (lastCleanupAt === null) {
89
+ sessionsSinceCleanup = entries.length;
90
+ } else {
91
+ for (const entry of entries) {
92
+ const startedAt = entry.started_at;
93
+ if (typeof startedAt === 'string' && startedAt > lastCleanupAt) {
94
+ sessionsSinceCleanup += 1;
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ return { memoryLines, lastCleanupAt, sessionsSinceCleanup, sessionsFilePath };
101
+ }
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // Decision function
105
+ // ---------------------------------------------------------------------------
106
+
107
+ /**
108
+ * Decide whether session-end Phase 3.6.5 should dispatch /memory-cleanup --dry-run.
109
+ *
110
+ * Rules (PRD F2.2):
111
+ * - threshold === 0 → never trigger (kill-switch).
112
+ * - memoryLines > softLimit → trigger (size-based).
113
+ * - sessionsSinceLastCleanup >= threshold → trigger (cadence-based).
114
+ *
115
+ * The function is read-only — it never writes any file. Callers may pass an
116
+ * explicit `signals` object for testing; when omitted, readDreamSignals() is
117
+ * invoked.
118
+ *
119
+ * @param {object} args
120
+ * @param {string} args.repoRoot
121
+ * @param {string} args.memoryDir
122
+ * @param {number} [args.threshold=5] memory-cleanup-threshold from config.
123
+ * @param {number} [args.softLimit=180] memory-cleanup-soft-limit from config.
124
+ * @param {object} [args.signals] Pre-computed signals (skips disk reads).
125
+ * @returns {Promise<{trigger:boolean, reason:string, signals:object}>}
126
+ */
127
+ export async function shouldDispatchAutoDream({
128
+ repoRoot,
129
+ memoryDir,
130
+ threshold = 5,
131
+ softLimit = 180,
132
+ signals,
133
+ } = {}) {
134
+ // Kill-switch first — bail before any I/O.
135
+ if (threshold === 0) {
136
+ return {
137
+ trigger: false,
138
+ reason: 'kill-switch (memory-cleanup-threshold=0)',
139
+ signals: signals ?? { memoryLines: 0, sessionsSinceCleanup: 0, lastCleanupAt: null },
140
+ };
141
+ }
142
+
143
+ const resolved = signals ?? (await readDreamSignals({ repoRoot, memoryDir }));
144
+
145
+ // Size-based trigger first — overrides cadence.
146
+ if (resolved.memoryLines > softLimit) {
147
+ return {
148
+ trigger: true,
149
+ reason: `memory-soft-limit-exceeded (lines=${resolved.memoryLines} > softLimit=${softLimit})`,
150
+ signals: resolved,
151
+ };
152
+ }
153
+
154
+ // Cadence-based trigger.
155
+ if (resolved.sessionsSinceCleanup >= threshold) {
156
+ return {
157
+ trigger: true,
158
+ reason: `cadence-threshold-met (sessions-since-cleanup=${resolved.sessionsSinceCleanup} >= threshold=${threshold})`,
159
+ signals: resolved,
160
+ };
161
+ }
162
+
163
+ return {
164
+ trigger: false,
165
+ reason: `under-thresholds (memory=${resolved.memoryLines}/${softLimit}, sessions=${resolved.sessionsSinceCleanup}/${threshold})`,
166
+ signals: resolved,
167
+ };
168
+ }
169
+
170
+ // ---------------------------------------------------------------------------
171
+ // pending-dream.md — atomic write / read / apply
172
+ // ---------------------------------------------------------------------------
173
+
174
+ /**
175
+ * Resolve the canonical pending-dream sidecar path. The file lives under
176
+ * `.orchestrator/pending-dream.md` relative to the repo root so it survives
177
+ * across sessions while staying out of the vault-mirror scope.
178
+ *
179
+ * @param {string} repoRoot
180
+ * @returns {string}
181
+ */
182
+ function pendingDreamPath(repoRoot) {
183
+ return path.join(repoRoot, '.orchestrator', 'pending-dream.md');
184
+ }
185
+
186
+ /**
187
+ * Write the proposed dream diff to `.orchestrator/pending-dream.md` atomically.
188
+ *
189
+ * Caller supplies the body (a Markdown document containing the diff and any
190
+ * narrative). This helper prepends a minimal YAML frontmatter block carrying
191
+ * the metadata session-end's Final Report and the next session's --apply-pending
192
+ * step both rely on.
193
+ *
194
+ * Atomicity: write to `<path>.<rand>.tmp`, then rename(). Same-fs rename is
195
+ * atomic on POSIX — observers see either the previous file or the new one,
196
+ * never a half-written intermediate.
197
+ *
198
+ * @param {object} args
199
+ * @param {string} args.repoRoot
200
+ * @param {string} args.diff Complete-replacement MEMORY.md body, in exactly one
201
+ * fenced ```markdown block (never git-style unified-diff hunks) — see the
202
+ * Serialisation contract in `skills/memory-cleanup/SKILL.md`.
203
+ * @param {string} [args.sourceSession] Session id that produced the proposal.
204
+ * @param {number} [args.memoryLinesBefore] MEMORY.md line count before.
205
+ * @param {number} [args.proposedLinesAfter] MEMORY.md line count after the proposed diff.
206
+ * @returns {Promise<{path:string, bytes:number}>}
207
+ */
208
+ export async function writePendingDream({
209
+ repoRoot,
210
+ diff,
211
+ sourceSession = null,
212
+ memoryLinesBefore = null,
213
+ proposedLinesAfter = null,
214
+ }) {
215
+ if (typeof diff !== 'string' || diff.trim().length === 0) {
216
+ throw new TypeError('writePendingDream: diff must be a non-empty string');
217
+ }
218
+
219
+ const target = pendingDreamPath(repoRoot);
220
+ await mkdir(path.dirname(target), { recursive: true });
221
+
222
+ const generatedAt = new Date().toISOString();
223
+ const frontmatter = [
224
+ '---',
225
+ `generated_at: ${generatedAt}`,
226
+ `source_session: ${JSON.stringify(sourceSession ?? 'unknown')}`,
227
+ `memory_lines_before: ${JSON.stringify(memoryLinesBefore ?? null)}`,
228
+ `proposed_lines_after: ${JSON.stringify(proposedLinesAfter ?? null)}`,
229
+ '---',
230
+ '',
231
+ ].join('\n');
232
+
233
+ const content = `${frontmatter}${diff}${diff.endsWith('\n') ? '' : '\n'}`;
234
+ const tmp = `${target}.${randomUUID().slice(0, 8)}.tmp`;
235
+ await writeFile(tmp, content, 'utf8');
236
+ await rename(tmp, target);
237
+
238
+ return { path: target, bytes: Buffer.byteLength(content, 'utf8') };
239
+ }
240
+
241
+ /**
242
+ * Read `.orchestrator/pending-dream.md` if present. Returns the raw file body
243
+ * (including frontmatter) so callers can decide how to parse it. Returns null
244
+ * when the file is absent.
245
+ *
246
+ * @param {object} args
247
+ * @param {string} args.repoRoot
248
+ * @returns {Promise<string|null>}
249
+ */
250
+ export async function readPendingDream({ repoRoot }) {
251
+ const target = pendingDreamPath(repoRoot);
252
+ if (!existsSync(target)) return null;
253
+ return readFile(target, 'utf8');
254
+ }
255
+
256
+ /**
257
+ * Internal: parse the YAML-style frontmatter block at the top of a
258
+ * pending-dream.md file. Returns the parsed fields and the body that follows.
259
+ *
260
+ * The frontmatter is intentionally hand-rolled (no yaml dep) — it only carries
261
+ * the three flat keys writePendingDream() emits.
262
+ *
263
+ * @param {string} content
264
+ * @returns {{frontmatter: Record<string,string>, body: string}}
265
+ */
266
+ function parsePendingDream(content) {
267
+ const lines = content.split('\n');
268
+ if (lines[0] !== '---') {
269
+ return { frontmatter: {}, body: content };
270
+ }
271
+ const frontmatter = {};
272
+ let i = 1;
273
+ while (i < lines.length && lines[i] !== '---') {
274
+ const line = lines[i];
275
+ const idx = line.indexOf(':');
276
+ if (idx > 0) {
277
+ const key = line.slice(0, idx).trim();
278
+ const val = line.slice(idx + 1).trim();
279
+ frontmatter[key] = val;
280
+ }
281
+ i += 1;
282
+ }
283
+ // Skip the closing '---' and an optional blank separator line.
284
+ let bodyStart = i + 1;
285
+ if (lines[bodyStart] === '') bodyStart += 1;
286
+ return { frontmatter, body: lines.slice(bodyStart).join('\n') };
287
+ }
288
+
289
+ /**
290
+ * Internal: extract a fenced code block tagged as `diff` or `markdown` from
291
+ * the body, returning its inner contents. When no fenced diff is present,
292
+ * returns the full body verbatim — letting --dry-run drop a freeform proposal.
293
+ *
294
+ * @param {string} body
295
+ * @returns {string}
296
+ */
297
+ function extractDiffBlock(body) {
298
+ const fenceMatch = body.match(/```(?:diff|markdown)?\n([\s\S]*?)```/);
299
+ if (fenceMatch) return fenceMatch[1];
300
+ return body;
301
+ }
302
+
303
+ /**
304
+ * Internal: detect whether an extracted block is a git-style unified diff
305
+ * (hunks meant to be applied against an existing file) rather than a
306
+ * complete-replacement Markdown body (issue #717).
307
+ *
308
+ * Detection is keyed on git-diff-SPECIFIC line signatures — deliberately NOT
309
+ * a bare `^[+-]` / `^- ` matcher, because MEMORY.md legitimately contains
310
+ * markdown bullets (`- [Title](file.md) — hook`) that such a naive matcher
311
+ * would false-positive on.
312
+ *
313
+ * @param {string} block
314
+ * @returns {boolean}
315
+ */
316
+ function isGitStyleDiff(block) {
317
+ return /^--- /m.test(block) || /^\+\+\+ /m.test(block) || /^@@ .* @@/m.test(block);
318
+ }
319
+
320
+ /**
321
+ * Internal: count fenced ```diff / ```markdown (or untagged ```) code blocks
322
+ * in a raw sidecar body. More than one such fence is the multi-file /
323
+ * multi-hunk sidecar signature (issue #717) — `extractDiffBlock()` only ever
324
+ * consumes the FIRST fence, so a multi-fence body silently drops every
325
+ * subsequent hunk/section if applied naively.
326
+ *
327
+ * @param {string} body
328
+ * @returns {number}
329
+ */
330
+ function countFencedBlocks(body) {
331
+ const matches = body.match(/```(?:diff|markdown)?\n[\s\S]*?```/g);
332
+ return matches ? matches.length : 0;
333
+ }
334
+
335
+ /**
336
+ * Internal: detect a fenced code block tagged with a language extractDiffBlock()
337
+ * does not recognize (issue #720) — e.g. ```js, ```python. extractDiffBlock()'s
338
+ * regex only matches `diff` / `markdown` / untagged (bare ``` immediately
339
+ * followed by a newline) fences; any OTHER tag word sits between the opening
340
+ * ``` and the required newline, so the match fails outright and
341
+ * extractDiffBlock() falls back to returning the RAW body verbatim — literal
342
+ * fence markers included — which would otherwise be written straight into
343
+ * MEMORY.md unrefused.
344
+ *
345
+ * Detection is keyed on `extractedBlock === body` (extraction found nothing to
346
+ * strip) AND the body containing an actual fence-opening line. The first half
347
+ * of that conjunction alone is not sufficient: a genuinely fence-free freeform
348
+ * body (issue #502's accepted verbatim-fallback contract) also has
349
+ * `extractedBlock === body`, and must NOT be refused.
350
+ *
351
+ * @param {string} body
352
+ * @param {string} extractedBlock
353
+ * @returns {boolean}
354
+ */
355
+ function hasUnrecognizedFence(body, extractedBlock) {
356
+ return extractedBlock === body && /^```/m.test(body);
357
+ }
358
+
359
+ /**
360
+ * Apply the pending dream by overwriting MEMORY.md with the body the
361
+ * proposal carries, then deleting the sidecar.
362
+ *
363
+ * Behaviour (PRD F2.2):
364
+ * - Returns `{ applied: false, reason: 'missing' }` when no sidecar exists.
365
+ * - Returns `{ applied: false, reason: 'stale' }` when the sidecar is older
366
+ * than 14 days (caller should re-run --dry-run).
367
+ * - Returns `{ applied: false, reason: 'stale-index', driftMs }` (#788) when
368
+ * MEMORY.md's mtime is newer than the sidecar's `generated_at` — MEMORY.md
369
+ * was updated BETWEEN the producing --dry-run and this apply, so a
370
+ * complete-replacement write would clobber those interim edits. MEMORY.md
371
+ * is left untouched and the sidecar is PRESERVED so the caller can re-run
372
+ * --dry-run against the current MEMORY.md. A missing MEMORY.md never
373
+ * triggers this (nothing to clobber). Strict `>`: equal mtimes are clock
374
+ * resolution noise, not a drift signal.
375
+ * - Returns `{ applied: false, reason: 'unsupported-format' }` (#717) when
376
+ * the extracted block is a git-style diff (`isGitStyleDiff()`), the raw
377
+ * sidecar body contains more than one fenced block (`countFencedBlocks()`
378
+ * > 1), or the body contains a fence tagged with an unrecognized language
379
+ * (`hasUnrecognizedFence()`, issue #720 — e.g. ```js, ```python; only
380
+ * `diff` / `markdown` / untagged fences are recognized). MEMORY.md is left
381
+ * untouched and the sidecar is PRESERVED (not unlinked) so the proposal
382
+ * can be regenerated in the correct format.
383
+ * - On success: deletes the sidecar, returns line-count deltas.
384
+ *
385
+ * The proposal is expected to embed the *complete* replacement body of
386
+ * MEMORY.md inside a single fenced ```` ```diff ```` or ```` ```markdown ````
387
+ * block. Free-form bodies are accepted but treated as the full replacement
388
+ * verbatim — the dry-run subagent owns the format; this helper is the
389
+ * consumer, and it refuses formats it cannot safely apply (see above).
390
+ *
391
+ * @param {object} args
392
+ * @param {string} args.repoRoot
393
+ * @param {string} args.memoryDir
394
+ * @param {number} [args.maxAgeDays=14]
395
+ * @returns {Promise<{applied:boolean, reason?:string, ageDays?:number, driftMs?:number, linesBefore?:number, linesAfter?:number, path?:string}>}
396
+ */
397
+ export async function applyPendingDream({ repoRoot, memoryDir, maxAgeDays = 14 }) {
398
+ const target = pendingDreamPath(repoRoot);
399
+ if (!existsSync(target)) {
400
+ return { applied: false, reason: 'missing' };
401
+ }
402
+
403
+ const content = await readFile(target, 'utf8');
404
+ const { frontmatter, body } = parsePendingDream(content);
405
+
406
+ const memoryPath = path.join(memoryDir, 'MEMORY.md');
407
+
408
+ // Staleness check — refuse if generated_at is older than maxAgeDays.
409
+ const generatedAt = frontmatter.generated_at;
410
+ if (typeof generatedAt === 'string' && generatedAt.length > 0) {
411
+ const generatedMs = Date.parse(generatedAt);
412
+ if (!Number.isNaN(generatedMs)) {
413
+ const ageDays = (Date.now() - generatedMs) / (1000 * 60 * 60 * 24);
414
+ if (ageDays > maxAgeDays) {
415
+ return { applied: false, reason: 'stale', ageDays: Math.round(ageDays) };
416
+ }
417
+
418
+ // Drift check (#788) — refuse if MEMORY.md changed AFTER the producing
419
+ // --dry-run. applyPendingDream is a complete-replacement write frozen on
420
+ // the dry-run snapshot; an interim MEMORY.md edit would be silently
421
+ // clobbered. A missing MEMORY.md has nothing to clobber.
422
+ //
423
+ // Resolution note: `generated_at` is `new Date().toISOString()`, truncated
424
+ // to whole milliseconds, whereas statSync().mtimeMs carries sub-ms
425
+ // (nanosecond) precision on APFS/ext4. Comparing them raw makes a
426
+ // MEMORY.md written in the SAME millisecond as the sidecar (mtimeMs
427
+ // fractionally > the floored generatedMs) look like drift. Floor mtimeMs
428
+ // to the resolution `generated_at` actually has, then apply strict `>`
429
+ // (mirrors `ageDays > maxAgeDays`): same-millisecond = clock-resolution
430
+ // noise, not a drift signal; a later write (≥1ms) is a real signal.
431
+ if (existsSync(memoryPath)) {
432
+ const memoryMtimeMs = statSync(memoryPath).mtimeMs;
433
+ if (Math.floor(memoryMtimeMs) > generatedMs) {
434
+ return { applied: false, reason: 'stale-index', driftMs: Math.round(memoryMtimeMs - generatedMs) };
435
+ }
436
+ }
437
+ }
438
+ }
439
+
440
+ let linesBefore = 0;
441
+ if (existsSync(memoryPath)) {
442
+ const raw = await readFile(memoryPath, 'utf8');
443
+ linesBefore = raw.length === 0 ? 0 : raw.split('\n').length;
444
+ }
445
+
446
+ const extractedBlock = extractDiffBlock(body);
447
+
448
+ // Guard (#717, extended #720): refuse formats this applier cannot safely
449
+ // consume. A git-style diff (hunks), a multi-fence body, or a fence tagged
450
+ // with a language extractDiffBlock() doesn't recognize (e.g. ```js) written
451
+ // verbatim (fence markers included) as the new MEMORY.md would corrupt the
452
+ // index / silently drop subsequent sections / pollute MEMORY.md with a
453
+ // literal fence marker. Preserve the sidecar — do NOT write, do NOT unlink
454
+ // — so the operator can regenerate a complete-body proposal via --dry-run.
455
+ if (isGitStyleDiff(extractedBlock) || countFencedBlocks(body) > 1 || hasUnrecognizedFence(body, extractedBlock)) {
456
+ return { applied: false, reason: 'unsupported-format' };
457
+ }
458
+
459
+ const newBody = extractedBlock.trimEnd() + '\n';
460
+ await mkdir(path.dirname(memoryPath), { recursive: true });
461
+ const tmp = `${memoryPath}.${randomUUID().slice(0, 8)}.tmp`;
462
+ await writeFile(tmp, newBody, 'utf8');
463
+ await rename(tmp, memoryPath);
464
+
465
+ const linesAfter = newBody.length === 0 ? 0 : newBody.split('\n').length;
466
+
467
+ // Consume the sidecar — success leaves no pending file behind.
468
+ await unlink(target);
469
+
470
+ return { applied: true, linesBefore, linesAfter, path: memoryPath };
471
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * autonomy/suitability.mjs — pure four-gate suitability verdict for autonomous
3
+ * dispatch (GitLab issue #680, Epic #673 cross-repo dispatcher).
4
+ *
5
+ * `computeSuitabilityVerdict(deps)` answers ONE question: "is this repo/run a
6
+ * SAFE candidate for autonomous action right now?" It is a pure decision function
7
+ * over four AND-composed gates — confidence, kill-switch rate, CI status, and
8
+ * resource verdict. Every signal is INJECTED by the caller (the dispatcher); the
9
+ * engine reads no files and calls nothing external (no checkCiStatus / evaluate /
10
+ * selectMode). The dispatcher supplies the snapshots; this module only judges them.
11
+ *
12
+ * THE FOUR-GATE CONTRACT (suitable = G1 && G2 && G3 && G4):
13
+ * G1 confidenceOk = Number.isFinite(confidence) && confidence >= confidenceFloor
14
+ * (fail-closed: null/undefined/NaN ⇒ false)
15
+ * G2 killSwitchOk = recentRuns.length < 5 ? PASS (omitted, warned)
16
+ * : (fired / N) < 0.2
17
+ * (non-array recentRuns ⇒ [] ⇒ omitted+warned; malformed
18
+ * run records / non-string kill_switch ⇒ NOT-fired)
19
+ * G3 ciNotRed = ci.status !== 'red' (null, 'unknown', AND malformed
20
+ * ci — non-object / missing status / non-string status —
21
+ * are "no signal" ⇒ pass + warn)
22
+ * G4 resourceNotCritical = String(resourceVerdict).toLowerCase().trim() !== 'critical'
23
+ * (green/warn/degraded/null pass; 'CRITICAL' IS caught;
24
+ * non-string ⇒ "no signal" ⇒ pass + warn)
25
+ *
26
+ * PRD override "CI red OR resource critical ⇒ non-suitable REGARDLESS of
27
+ * confidence" needs no special branch — it is satisfied by the pure AND. The
28
+ * `forcedFail` flag is computed ONLY to word the rationale.
29
+ *
30
+ * Mirrors engine.mjs: the `Number.isFinite && >=` fail-closed gate, the
31
+ * [0,1]-clamped floor coercion (default 0.5, NEVER 0), the `detailParts.join('; ')`
32
+ * rationale style (engine.mjs L481-485), and the never-throws contract.
33
+ *
34
+ * Pure + DI, NEVER throws, NO I/O. NOT wired into dispatcher launch this session
35
+ * (that is #682). No imports beyond node builtins (none needed).
36
+ */
37
+
38
+ const DEFAULT_CONFIDENCE_FLOOR = 0.5;
39
+ const KILL_SWITCH_MIN_RUNS = 5; // below this the kill-switch gate is OMITTED (treated as PASS)
40
+ const KILL_SWITCH_MAX_RATE = 0.2; // fired/N must be strictly below this
41
+
42
+ /**
43
+ * @typedef {Object} SuitabilityDeps
44
+ * @property {'off'|'advisory'|'autonomous-gated'} [autonomy]
45
+ * Autonomy dial. Forward-compat ONLY — does NOT gate `suitable`. When
46
+ * 'off' an advisory warning is pushed, but the verdict is unchanged.
47
+ * @property {number} [confidenceFloor=0.5]
48
+ * Minimum confidence. Coerced like engine.mjs: finite && in [0,1] else 0.5.
49
+ * @property {number} confidence
50
+ * mode-selector 0..1 float (injected). null/undefined/NaN ⇒ G1 FAILS.
51
+ * @property {null | { status?: ('green'|'red'|'unknown'), ok?: boolean }} [ci]
52
+ * checkCiStatus result. null = no signal (passes G3, warns). Malformed
53
+ * (non-object / missing status / non-string status) is treated as
54
+ * absent (passes G3 + 'CI signal malformed' warning) — never throws.
55
+ * @property {'green'|'warn'|'degraded'|'critical'|null} [resourceVerdict]
56
+ * Resource verdict. null = no signal (passes G4, warns). A string is
57
+ * lowercase-trim-normalized before the !== 'critical' check (so
58
+ * 'CRITICAL' fails). A non-string non-null value is "no signal"
59
+ * (passes G4 + 'resource signal malformed' warning) — never throws.
60
+ * @property {Array<{ kill_switch?: string|null }>} [recentRuns]
61
+ * Repo-scoped most-recent-N autopilot run records (caller supplies; the
62
+ * engine never reads autopilot.jsonl). A non-array value is coerced to
63
+ * [] (G2 omitted + warning). Null/non-object records and non-string
64
+ * kill_switch fields count as NOT-fired — never throws.
65
+ */
66
+
67
+ /**
68
+ * @typedef {Object} SuitabilityVerdict
69
+ * @property {boolean} suitable The four-gate AND.
70
+ * @property {number} confidence The validated injected confidence (NaN/missing ⇒ 0 echo).
71
+ * @property {string} rationale One line, segments joined with '; '.
72
+ * @property {string[]} warnings Omission / absent-signal / dial advisory notes.
73
+ */
74
+
75
+ /**
76
+ * Coerce the confidence floor exactly as engine.mjs does: a finite number in the
77
+ * closed interval [0,1] is honoured; everything else (null/undefined/NaN/string/
78
+ * out-of-range) falls back to the safe default 0.5 — NEVER 0.
79
+ * @param {unknown} rawFloor
80
+ * @returns {number}
81
+ */
82
+ function coerceFloor(rawFloor) {
83
+ const coerced =
84
+ typeof rawFloor === 'number'
85
+ ? rawFloor
86
+ : typeof rawFloor === 'string' && rawFloor.trim() !== ''
87
+ ? Number(rawFloor)
88
+ : NaN;
89
+ return Number.isFinite(coerced) && coerced >= 0 && coerced <= 1 ? coerced : DEFAULT_CONFIDENCE_FLOOR;
90
+ }
91
+
92
+ /**
93
+ * Compute the autonomous-dispatch suitability verdict over four injected signal
94
+ * gates. PURE, NEVER throws, NO I/O. The caller (dispatcher) is responsible for
95
+ * gathering ci / resourceVerdict / confidence / recentRuns and acting on the
96
+ * verdict (#682 wires this into launch).
97
+ *
98
+ * @param {SuitabilityDeps} [deps]
99
+ * @returns {SuitabilityVerdict}
100
+ */
101
+ export function computeSuitabilityVerdict(deps = {}) {
102
+ const {
103
+ autonomy,
104
+ confidenceFloor: rawFloor,
105
+ confidence: rawConfidence,
106
+ ci = null,
107
+ resourceVerdict = null,
108
+ recentRuns = [],
109
+ } = deps && typeof deps === 'object' ? deps : {};
110
+
111
+ /** @type {string[]} */
112
+ const warnings = [];
113
+
114
+ const floor = coerceFloor(rawFloor);
115
+
116
+ // --- G1 — confidence gate (fail-closed: null/undefined/NaN ⇒ false) --------
117
+ const confidenceOk = Number.isFinite(rawConfidence) && rawConfidence >= floor;
118
+ // Echo the validated confidence; NaN/missing ⇒ 0 (G1 already failed regardless).
119
+ const confidenceEcho = Number.isFinite(rawConfidence) ? rawConfidence : 0;
120
+
121
+ // --- G2 — kill-switch rate gate (OMITTED below the run-count floor) --------
122
+ // Defensive: a non-array recentRuns (undefined/null/string/object — e.g. a
123
+ // malformed autopilot.jsonl read) is coerced to [] and warned, never thrown.
124
+ const recentRunsIsArray = Array.isArray(recentRuns);
125
+ const runs = recentRunsIsArray ? recentRuns : [];
126
+ if (!recentRunsIsArray && recentRuns !== undefined && recentRuns !== null) {
127
+ warnings.push('recentRuns not an array — kill-switch gate omitted');
128
+ }
129
+ const n = runs.length;
130
+ let killSwitchOk;
131
+ let killSwitchSegment;
132
+ if (n < KILL_SWITCH_MIN_RUNS) {
133
+ killSwitchOk = true; // OMITTED ⇒ treat as PASS (insufficient signal)
134
+ warnings.push(`kill-switch signal omitted: only ${n}<${KILL_SWITCH_MIN_RUNS} runs`);
135
+ killSwitchSegment = `omitted (${n}<${KILL_SWITCH_MIN_RUNS} runs)`;
136
+ } else {
137
+ let fired = 0;
138
+ for (const run of runs) {
139
+ // Defensive: a null / non-object run, a record MISSING kill_switch, or a
140
+ // kill_switch of a non-string type (number/object/boolean) all count as
141
+ // NOT-fired. ONLY a non-empty string counts as fired. Never throws.
142
+ const ks = run && typeof run === 'object' ? run.kill_switch : undefined;
143
+ if (typeof ks === 'string' && ks.length > 0) fired += 1;
144
+ }
145
+ const rate = fired / n;
146
+ killSwitchOk = rate < KILL_SWITCH_MAX_RATE;
147
+ const ratePct = Math.round(rate * 100) / 100;
148
+ killSwitchSegment = `${fired}/${n} fired rate=${ratePct} ${killSwitchOk ? 'ok' : 'FAIL'}`;
149
+ }
150
+
151
+ // --- G3 — CI gate (null & 'unknown' pass; only 'red' fails) ----------------
152
+ // Defensive: `ci` present but not an object, an object without `status`, or a
153
+ // non-string `status` ⇒ "no signal" (gate PASSES + warning), NEVER a crash.
154
+ // ONLY a literal `status === 'red'` fails the gate.
155
+ const ciAbsent = ci === null || ci === undefined;
156
+ const ciStatus = !ciAbsent && typeof ci === 'object' && typeof ci.status === 'string' ? ci.status : null;
157
+ const ciMalformed = !ciAbsent && ciStatus === null; // present but unusable
158
+ const ciNotRed = ciStatus !== 'red'; // null status (absent/malformed) passes
159
+ if (ciAbsent) warnings.push('CI signal absent');
160
+ else if (ciMalformed) warnings.push('CI signal malformed — treated as absent');
161
+ const ciSegment = ciAbsent ? 'absent' : ciMalformed ? 'malformed' : `${ciStatus}`;
162
+
163
+ // --- G4 — resource gate (green/warn/degraded/null pass; only 'critical' fails)
164
+ // Defensive: normalize a STRING verdict via toLowerCase().trim() so 'CRITICAL'
165
+ // / ' critical ' are correctly caught (only exact lowercase 'critical' fails).
166
+ // A non-string, non-null verdict (number/object/bool) is "no signal" ⇒ PASS +
167
+ // warning, never a crash. null/undefined = absent (passes, warns).
168
+ const resourceAbsent = resourceVerdict === null || resourceVerdict === undefined;
169
+ const resourceIsString = typeof resourceVerdict === 'string';
170
+ const resourceMalformed = !resourceAbsent && !resourceIsString; // present but unusable
171
+ const resourceNorm = resourceIsString ? resourceVerdict.toLowerCase().trim() : null;
172
+ const resourceNotCritical = resourceNorm !== 'critical'; // non-string/absent ⇒ passes
173
+ if (resourceAbsent) warnings.push('resource signal absent');
174
+ else if (resourceMalformed) warnings.push('resource signal malformed — treated as absent');
175
+ const resourceSegment = resourceAbsent
176
+ ? 'absent'
177
+ : resourceMalformed
178
+ ? 'malformed'
179
+ : `${resourceNorm}`;
180
+
181
+ // --- Autonomy dial (forward-compat advisory ONLY — never flips `suitable`) -
182
+ if (autonomy === 'off') {
183
+ warnings.push('autonomy off — verdict advisory only; caller will confirm');
184
+ }
185
+
186
+ // --- The four-gate AND -----------------------------------------------------
187
+ const suitable = confidenceOk && killSwitchOk && ciNotRed && resourceNotCritical;
188
+
189
+ // `forcedFail` exists ONLY to word the rationale — the PRD override is already
190
+ // satisfied by the pure AND above (CI red / resource critical fail their gate).
191
+ const forcedFail = !ciNotRed || !resourceNotCritical;
192
+
193
+ // --- Rationale (segments joined with '; ', mirroring engine.mjs L481-485) --
194
+ const detailParts = [`suitable=${suitable}`];
195
+ if (forcedFail) {
196
+ const reasons = [];
197
+ if (!ciNotRed) reasons.push('CI red');
198
+ if (!resourceNotCritical) reasons.push('resource critical');
199
+ detailParts.push(`FORCED: ${reasons.join(' + ')}`);
200
+ }
201
+ detailParts.push(`confidence ${confidenceEcho}>=${floor} ${confidenceOk ? 'ok' : 'FAIL'}`);
202
+ detailParts.push(`kill-switch ${killSwitchSegment}`);
203
+ detailParts.push(`CI ${ciSegment} ${ciNotRed ? 'ok' : 'FAIL'}`);
204
+ detailParts.push(`resource ${resourceSegment} ${resourceNotCritical ? 'ok' : 'FAIL'}`);
205
+
206
+ return {
207
+ suitable,
208
+ confidence: confidenceEcho,
209
+ rationale: detailParts.join('; '),
210
+ warnings,
211
+ };
212
+ }