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,244 @@
1
+ /**
2
+ * emitter.mjs — Reconciliation-engine activation-metadata emitter (Epic #693 FA2, issue #695).
3
+ *
4
+ * Converts a single ALREADY-ELIGIBLE learning (one that passed
5
+ * {@link module:reconcile/eligibility.classifyLearning} — i.e. carries a
6
+ * non-empty `file_paths[]`) into the activation-metadata object a renderer
7
+ * (the next wave) turns into `.claude/rules/<slug>.md` frontmatter.
8
+ *
9
+ * Pure function — no file I/O. Node 20+ stdlib only (`node:path` for dirname,
10
+ * the sibling learnings/schema.mjs for the TTL-derived expiry).
11
+ *
12
+ * ── The never-always-on invariant (the "brandmauer") ─────────────────────────
13
+ * docs/rule-authoring.md § "The Never-Always-On Invariant". Any rule with
14
+ * `auto-generated: true` MUST carry ≥1 activation axis (`globs` OR `mode` OR
15
+ * `host-class`), a `learning-key`, and an `expires-at`. This emitter ALWAYS
16
+ * stamps `autoGenerated: true` / `alwaysApply: false`, always emits a
17
+ * `learningKey` and an `expiresAt`, and THROWS when it cannot produce at least
18
+ * one activation axis (empty globs AND no hostClass). It can never emit an
19
+ * always-on auto-generated rule.
20
+ *
21
+ * `mode` is intentionally NOT emitted: a learning's `scope`
22
+ * (local/private/public) is a privacy tier, NOT a session mode — conflating the
23
+ * two would mis-gate the rule. The activation axes used here are `globs`
24
+ * (always, derived from file_paths) and optionally `host-class`.
25
+ *
26
+ * The output key names match the camelCase RuleEntry fields the rule-loader
27
+ * surfaces (`globs`, `description`, `learningKey`, `confidence`, `expiresAt`,
28
+ * `autoGenerated`, `alwaysApply`, `hostClass`).
29
+ */
30
+
31
+ import { dirname } from 'node:path';
32
+
33
+ import { deriveExpiresAt } from '../learnings/schema.mjs';
34
+
35
+ const DAY_MS = 86400 * 1000;
36
+ const DESCRIPTION_MAX = 120;
37
+
38
+ // The "born-dead expiry" floor (issue #741.1a). A rule whose NATURAL expiry
39
+ // (created_at + TTL) already sits in the past — or within this many days of
40
+ // `now` — at proposal time would be excluded on first read: rule-loader.mjs
41
+ // drops any rule whose `expires-at` satisfies `Date.parse(expiry) < Date.now()`
42
+ // (strict `<`). 7 days sits below every per-type TTL in LEARNING_TTL_DAYS
43
+ // (min 30d), so the floor only ever RAISES a near-dead/past expiry — it never
44
+ // shortens a healthy future one.
45
+ const MIN_RULE_DAYS_DEFAULT = 7;
46
+
47
+ // Control chars: C0 range (U+0000–U+001F, includes \n \r \t) plus DEL (U+007F).
48
+ // A newline in frontmatter would corrupt the YAML parse — stripping these is
49
+ // critical. Written with \u escapes so the source stays pure ASCII.
50
+ // eslint-disable-next-line no-control-regex
51
+ const CONTROL_CHARS_RE = /[\u0000-\u001F\u007F]/g;
52
+
53
+ /**
54
+ * Slugify a string into a stable kebab-case token.
55
+ * Lowercases, collapses every run of non-`[a-z0-9]` chars into a single `-`,
56
+ * and trims leading/trailing `-`. UUID subjects produce ugly-but-stable keys.
57
+ *
58
+ * @param {string} s
59
+ * @returns {string}
60
+ */
61
+ function kebab(s) {
62
+ return String(s)
63
+ .toLowerCase()
64
+ .replace(/[^a-z0-9]+/g, '-')
65
+ .replace(/^-+|-+$/g, '');
66
+ }
67
+
68
+ /**
69
+ * Derive non-empty directory globs from a learning's `file_paths`.
70
+ *
71
+ * For each path: take its directory (`path.dirname`) and emit `<dir>/**`. When
72
+ * the file sits at the repo top level (`dirname` === '.'), emit the bare
73
+ * basename pattern instead of `./**` (e.g. `"foo.mjs"`). Results are deduped,
74
+ * order-preserving on first occurrence.
75
+ *
76
+ * @param {string[]} filePaths
77
+ * @returns {string[]}
78
+ */
79
+ function globsFromFilePaths(filePaths) {
80
+ const out = [];
81
+ const seen = new Set();
82
+ for (const raw of filePaths) {
83
+ if (typeof raw !== 'string' || raw === '') continue;
84
+ const normalized = raw.replace(/\\/g, '/');
85
+ const dir = dirname(normalized);
86
+ const pattern = dir === '.' ? normalized : `${dir}/**`;
87
+ if (!seen.has(pattern)) {
88
+ seen.add(pattern);
89
+ out.push(pattern);
90
+ }
91
+ }
92
+ return out;
93
+ }
94
+
95
+ /**
96
+ * Build a single-line, frontmatter-safe description from a learning.
97
+ *
98
+ * Source precedence: `insight` → `title` → `subject`. Takes the first sentence
99
+ * (split on `. `), strips ALL newlines and control chars, collapses internal
100
+ * whitespace, and hard-truncates to {@link DESCRIPTION_MAX} chars (appending
101
+ * `…` when it had to cut).
102
+ *
103
+ * @param {object} learning
104
+ * @returns {string}
105
+ */
106
+ function buildDescription(learning) {
107
+ const source =
108
+ typeof learning.insight === 'string' && learning.insight !== ''
109
+ ? learning.insight
110
+ : learning.title || learning.subject || '';
111
+
112
+ // First sentence — split on '. ' so the headline stays terse.
113
+ const firstSentence = String(source).split('. ')[0];
114
+
115
+ const cleaned = firstSentence
116
+ .replace(CONTROL_CHARS_RE, ' ')
117
+ .replace(/\s+/g, ' ')
118
+ .trim()
119
+ // A trailing period from the first sentence reads oddly once truncated.
120
+ .replace(/\.$/, '');
121
+
122
+ if (cleaned.length <= DESCRIPTION_MAX) return cleaned;
123
+ // Reserve one char for the ellipsis.
124
+ return `${cleaned.slice(0, DESCRIPTION_MAX - 1).trimEnd()}…`;
125
+ }
126
+
127
+ /**
128
+ * Compute the `expiresAt` YYYY-MM-DD date string for the emitted rule.
129
+ *
130
+ * When `ruleExpiryDays` is a finite number, expiry = `created_at` + N days.
131
+ * Otherwise delegate to {@link deriveExpiresAt} (per-type TTL, default 60d).
132
+ * When `created_at` is missing/unparseable, fall back to the injectable `now`
133
+ * (epoch ms or Date) so the result stays deterministic under test.
134
+ *
135
+ * The result is then FLOORED at `now + minRuleDays` (default
136
+ * {@link MIN_RULE_DAYS_DEFAULT}) so an older learning whose natural expiry has
137
+ * already elapsed — or sits within the floor window — never produces a
138
+ * born-dead rule (issue #741.1a). The floor only ever raises the expiry; a
139
+ * healthy future expiry passes through unchanged.
140
+ *
141
+ * @param {object} learning
142
+ * @param {number|undefined} ruleExpiryDays
143
+ * @param {number|Date|undefined} now
144
+ * @param {number|undefined} minRuleDays - floor window in days; defaults to
145
+ * {@link MIN_RULE_DAYS_DEFAULT} when not a finite positive number.
146
+ * @returns {string} YYYY-MM-DD
147
+ */
148
+ function computeExpiresAt(learning, ruleExpiryDays, now, minRuleDays) {
149
+ const toYmd = (iso) => String(iso).slice(0, 10);
150
+ const nowMs = () =>
151
+ now instanceof Date ? now.getTime() : typeof now === 'number' ? now : Date.now();
152
+
153
+ let derivedMs;
154
+ if (typeof ruleExpiryDays === 'number' && Number.isFinite(ruleExpiryDays)) {
155
+ let baseMs = Date.parse(learning.created_at);
156
+ if (!Number.isFinite(baseMs)) baseMs = nowMs();
157
+ derivedMs = baseMs + ruleExpiryDays * DAY_MS;
158
+ } else {
159
+ // No explicit override → per-type TTL. deriveExpiresAt falls back to
160
+ // `Date.now()` internally when created_at is missing; to keep the injectable
161
+ // `now` honoured for tests, substitute its ISO form as created_at in that case.
162
+ let createdAt = learning.created_at;
163
+ if (typeof createdAt !== 'string' || !Number.isFinite(Date.parse(createdAt))) {
164
+ createdAt = new Date(nowMs()).toISOString();
165
+ }
166
+ derivedMs = Date.parse(deriveExpiresAt(createdAt, learning.type));
167
+ }
168
+
169
+ const days =
170
+ Number.isFinite(minRuleDays) && minRuleDays > 0 ? minRuleDays : MIN_RULE_DAYS_DEFAULT;
171
+ const floorMs = nowMs() + days * DAY_MS;
172
+ return toYmd(new Date(Math.max(derivedMs, floorMs)).toISOString());
173
+ }
174
+
175
+ /**
176
+ * Convert one eligible learning into renderer-ready activation metadata.
177
+ *
178
+ * @param {object} learning - an eligible learning (non-empty `file_paths[]`),
179
+ * carrying `type`, `subject`/`title`, `insight`, `confidence`, `created_at`,
180
+ * and optionally `host_class`.
181
+ * @param {object} [opts]
182
+ * @param {number} [opts.ruleExpiryDays] - explicit expiry window in days; when a
183
+ * finite number, overrides the per-type TTL.
184
+ * @param {number|Date} [opts.now] - injectable clock used ONLY as a fallback when
185
+ * `created_at` is missing/unparseable (keeps expiry deterministic in tests).
186
+ * @param {number} [opts.minRuleDays] - floor window (days) applied to the
187
+ * emitted expiry so it never falls in the past — see
188
+ * {@link computeExpiresAt} and {@link MIN_RULE_DAYS_DEFAULT}. Defaults
189
+ * internally when omitted; config plumbing for this is a later wave.
190
+ * @returns {{
191
+ * globs: string[],
192
+ * description: string,
193
+ * learningKey: string,
194
+ * confidence: number,
195
+ * expiresAt: string,
196
+ * autoGenerated: true,
197
+ * alwaysApply: false,
198
+ * hostClass?: string,
199
+ * }}
200
+ * @throws {Error} when no activation axis can be produced (empty globs AND no
201
+ * hostClass) — the never-always-on invariant.
202
+ */
203
+ export function toActivationMetadata(learning, { ruleExpiryDays, now, minRuleDays } = {}) {
204
+ if (learning === null || typeof learning !== 'object' || Array.isArray(learning)) {
205
+ throw new Error('emitter: learning must be a non-null object');
206
+ }
207
+
208
+ const filePaths = Array.isArray(learning.file_paths) ? learning.file_paths : [];
209
+ const globs = globsFromFilePaths(filePaths);
210
+
211
+ const hostClass =
212
+ typeof learning.host_class === 'string' && learning.host_class !== ''
213
+ ? learning.host_class
214
+ : undefined;
215
+
216
+ // The brandmauer: an auto-generated rule must carry ≥1 activation axis. If we
217
+ // could derive neither a glob nor a host-class, refuse — never emit always-on.
218
+ if (globs.length === 0 && hostClass === undefined) {
219
+ throw new Error(
220
+ 'emitter: no activation axis (globs/host-class) — refusing to emit always-on auto-generated rule (never-always-on invariant)',
221
+ );
222
+ }
223
+
224
+ const subjectOrTitle = learning.title || learning.subject || '';
225
+ const learningKey = `${learning.type}/${kebab(subjectOrTitle)}`;
226
+
227
+ const confidence = typeof learning.confidence === 'number' ? learning.confidence : 0;
228
+
229
+ const metadata = {
230
+ globs,
231
+ description: buildDescription(learning),
232
+ learningKey,
233
+ confidence,
234
+ expiresAt: computeExpiresAt(learning, ruleExpiryDays, now, minRuleDays),
235
+ autoGenerated: true,
236
+ alwaysApply: false,
237
+ };
238
+
239
+ if (hostClass !== undefined) {
240
+ metadata.hostClass = hostClass;
241
+ }
242
+
243
+ return metadata;
244
+ }
@@ -0,0 +1,412 @@
1
+ /**
2
+ * engine.mjs — Reconciliation-engine ORCHESTRATOR (Epic #693 FA2, issue #695).
3
+ *
4
+ * This is the keystone of #695: it composes the four already-verified sibling
5
+ * leaf modules into a single never-throwing pipeline that turns the
6
+ * `learnings.jsonl` corpus into RULE PROPOSALS, records each proposal/rejection
7
+ * in the idempotency sidecar, and returns the rendered rule content for the
8
+ * operator to approve. It is the reconcile-side analogue of
9
+ * `scripts/lib/skill-evolution/engine.mjs` (the C2 repair orchestrator) and
10
+ * mirrors its defensive posture: DI seams for every disk touch, a never-throws
11
+ * public boundary, and an empty short-circuit that touches no disk.
12
+ *
13
+ * COMPOSITION (the four siblings):
14
+ * - eligibility.mjs → filterEligible (pure partition — INVERTED allow-list)
15
+ * - emitter.mjs → toActivationMetadata (eligible learning → metadata; THROWS on no axis)
16
+ * - renderer.mjs → renderRule (metadata → `.claude/rules/<slug>.md` string)
17
+ * - idempotency.mjs → mergeCandidates / makeCandidateId (store I/O + logical dedupe)
18
+ *
19
+ * PIPELINE (load → short-circuit → filter → per-item → record → summary):
20
+ * loadLearnings()
21
+ * → empty? return zeroed result, touch no disk
22
+ * → filterEligible(learnings)
23
+ * → per eligible: toActivationMetadata → renderRule → makeCandidateId → proposal
24
+ * → per rejected: record rejection with audit reason
25
+ * → mergeCandidates({ candidates, repoRoot }) (SKIPPED on dryRun)
26
+ * → return { proposals, rejected, summary }
27
+ *
28
+ * ── CRITICAL scope constraint (the FA2/FA3 brandmauer) ───────────────────────
29
+ * The engine COMPUTES proposals and records them in the idempotency sidecar.
30
+ * It MUST NOT write any file into `.claude/rules/` — that write happens only in
31
+ * FA3 (#696) AFTER operator approval. The rendered content lives inside each
32
+ * proposal object; the engine never persists a rule file. The ONLY disk write
33
+ * the engine performs is into the reconcile-candidates sidecar (via
34
+ * idempotency.mjs), and even that is skipped under `dryRun`.
35
+ *
36
+ * ── never-throws contract ────────────────────────────────────────────────────
37
+ * `runReconcile` NEVER throws to its caller. A per-learning emit/render failure
38
+ * degrades to a recorded rejection (never a crash); any unexpected top-level
39
+ * error returns a zeroed result with an `error` field.
40
+ *
41
+ * Plain Node ESM, no external deps — Node 20+ stdlib + the four siblings only.
42
+ *
43
+ * Part of Epic #693 → issue #695 (FA2 Reconciliation Engine).
44
+ *
45
+ * @typedef {Object} ReconcileProposal
46
+ * @property {string} learningKey - the logical learning key (from the emitter metadata).
47
+ * @property {string} slug - the `.claude/rules/<slug>.md` slug.
48
+ * @property {string} path - the intended repo-relative rule path (NOT written).
49
+ * @property {string} content - the rendered rule markdown (NOT written).
50
+ * @property {number} confidence - the learning's confidence.
51
+ * @property {string} candidateId - the deterministic sidecar candidate id.
52
+ * @property {'proposed'} status
53
+ *
54
+ * @typedef {Object} ReconcileRejection
55
+ * @property {string|null} learningKey - best-effort logical key, or null when underivable.
56
+ * @property {string} type - the learning's `type` (or 'unknown').
57
+ * @property {string} reason - the audit reason for rejection.
58
+ * @property {'rejected'} status
59
+ *
60
+ * @typedef {Object} ReconcileSummary
61
+ * @property {number} totalLearnings
62
+ * @property {number} eligible
63
+ * @property {number} proposed
64
+ * @property {number} rejected
65
+ * @property {boolean} written
66
+ *
67
+ * @typedef {Object} ReconcileResult
68
+ * @property {ReconcileProposal[]} proposals
69
+ * @property {ReconcileRejection[]} rejected
70
+ * @property {ReconcileSummary} summary
71
+ * @property {string} [error] - present only when the never-throws top-level guard fired.
72
+ */
73
+
74
+ import { readFileSync } from 'node:fs';
75
+ import { isAbsolute, join } from 'node:path';
76
+
77
+ import { migrateLegacyLearning, normalizeLearning } from '../learnings/schema.mjs';
78
+ import { filterEligible } from './eligibility.mjs';
79
+ import { toActivationMetadata } from './emitter.mjs';
80
+ import { renderRule } from './renderer.mjs';
81
+ import { makeCandidateId, mergeCandidates as realMergeCandidates } from './idempotency.mjs';
82
+
83
+ /** Default repo-relative location of the learnings corpus. */
84
+ const DEFAULT_LEARNINGS_PATH = '.orchestrator/metrics/learnings.jsonl';
85
+
86
+ /**
87
+ * Build a fully-zeroed result (the empty / error shape). Touches no disk.
88
+ * @param {string} [error]
89
+ * @returns {ReconcileResult}
90
+ */
91
+ function zeroedResult(error) {
92
+ /** @type {ReconcileResult} */
93
+ const result = {
94
+ proposals: [],
95
+ rejected: [],
96
+ summary: {
97
+ totalLearnings: 0,
98
+ eligible: 0,
99
+ proposed: 0,
100
+ rejected: 0,
101
+ written: false,
102
+ },
103
+ };
104
+ if (typeof error === 'string') result.error = error;
105
+ return result;
106
+ }
107
+
108
+ /**
109
+ * Default learnings loader — read + parse `<repoRoot>/.orchestrator/metrics/learnings.jsonl`
110
+ * line-by-line, migrate/normalize records through the learnings schema SSOT,
111
+ * and skip blank/malformed lines. A missing file yields `[]`. Never throws (a
112
+ * read error degrades to `[]`).
113
+ *
114
+ * @param {string|undefined} repoRoot
115
+ * @returns {Array<Record<string, unknown>>}
116
+ */
117
+ function defaultLoadLearnings(repoRoot) {
118
+ const root = typeof repoRoot === 'string' && repoRoot.length > 0 ? repoRoot : process.cwd();
119
+ const absPath = isAbsolute(DEFAULT_LEARNINGS_PATH)
120
+ ? DEFAULT_LEARNINGS_PATH
121
+ : join(root, DEFAULT_LEARNINGS_PATH);
122
+
123
+ let raw;
124
+ try {
125
+ raw = readFileSync(absPath, 'utf8');
126
+ } catch {
127
+ return []; // ENOENT or any read error → empty corpus.
128
+ }
129
+
130
+ /** @type {Array<Record<string, unknown>>} */
131
+ const records = [];
132
+ for (const line of raw.split('\n')) {
133
+ const trimmed = line.trim();
134
+ if (trimmed.length === 0) continue;
135
+ let parsed;
136
+ try {
137
+ parsed = JSON.parse(trimmed);
138
+ } catch {
139
+ continue; // skip malformed line
140
+ }
141
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
142
+ records.push(
143
+ /** @type {Record<string, unknown>} */ (normalizeLearning(migrateLegacyLearning(parsed))),
144
+ );
145
+ }
146
+ }
147
+ return records;
148
+ }
149
+
150
+ /**
151
+ * Best-effort logical key for a REJECTED learning (rejections never run the
152
+ * emitter, so there is no metadata.learningKey). Mirrors the emitter's key
153
+ * shape `${type}/${kebab(subject||title)}` when both halves are present; falls
154
+ * back to `null` when the type or subject/title is unusable. Never throws.
155
+ *
156
+ * @param {unknown} learning
157
+ * @returns {string|null}
158
+ */
159
+ function rejectedLearningKey(learning) {
160
+ if (learning === null || typeof learning !== 'object' || Array.isArray(learning)) return null;
161
+ const rec = /** @type {Record<string, unknown>} */ (learning);
162
+ const type = typeof rec.type === 'string' && rec.type !== '' ? rec.type : '';
163
+ const subjectOrTitle =
164
+ (typeof rec.title === 'string' && rec.title !== '' ? rec.title : '') ||
165
+ (typeof rec.subject === 'string' && rec.subject !== '' ? rec.subject : '');
166
+ if (type === '' || subjectOrTitle === '') return null;
167
+ const kebab = subjectOrTitle
168
+ .toLowerCase()
169
+ .replace(/[^a-z0-9]+/g, '-')
170
+ .replace(/^-+|-+$/g, '');
171
+ return `${type}/${kebab}`;
172
+ }
173
+
174
+ /**
175
+ * Resolve the `type` string of a (possibly malformed) learning for the rejected
176
+ * record. Never throws.
177
+ * @param {unknown} learning
178
+ * @returns {string}
179
+ */
180
+ function learningType(learning) {
181
+ if (learning && typeof learning === 'object' && !Array.isArray(learning)) {
182
+ const t = /** @type {Record<string, unknown>} */ (learning).type;
183
+ if (typeof t === 'string' && t.length > 0) return t;
184
+ }
185
+ return 'unknown';
186
+ }
187
+
188
+ /**
189
+ * Build a sidecar ReconcileCandidate line-record (idempotency.mjs schema) for a
190
+ * proposed or rejected learning. `created_at` is stamped from the injectable
191
+ * clock so output stays deterministic under test.
192
+ *
193
+ * @param {Object} params
194
+ * @param {string} params.id
195
+ * @param {string|null} params.learningKey
196
+ * @param {string} params.slug
197
+ * @param {'proposed'|'rejected'} params.status
198
+ * @param {string} params.reason
199
+ * @param {number} params.confidence
200
+ * @param {string} params.createdAt - ISO timestamp.
201
+ * @returns {import('./idempotency.mjs').ReconcileCandidate}
202
+ */
203
+ function buildCandidate({ id, learningKey, slug, status, reason, confidence, createdAt }) {
204
+ return {
205
+ id,
206
+ schema_version: 1,
207
+ learning_key: typeof learningKey === 'string' ? learningKey : '',
208
+ slug,
209
+ status,
210
+ reason,
211
+ confidence,
212
+ created_at: createdAt,
213
+ processed_at: null,
214
+ superseded_by: null,
215
+ };
216
+ }
217
+
218
+ /**
219
+ * Run the reconciliation engine.
220
+ *
221
+ * Composes the four leaf modules into the full proposal pipeline. NEVER throws —
222
+ * per-learning failures degrade to recorded rejections; any unexpected
223
+ * top-level error returns a zeroed result with an `error` field.
224
+ *
225
+ * The engine COMPUTES and RECORDS proposals; it NEVER writes `.claude/rules/`
226
+ * (that is FA3 / #696, post-approval). Its only disk write is into the
227
+ * reconcile-candidates sidecar (via the `merge` seam), skipped when `dryRun`.
228
+ *
229
+ * @param {Object} [params]
230
+ * @param {string} [params.repoRoot] - repo root; defaults to `process.cwd()` for the default loader/merge.
231
+ * @param {number} [params.ruleExpiryDays]- explicit rule expiry window (passed to the emitter).
232
+ * @param {number} [params.minRuleDays] - floor window (days) applied to the emitted `expires-at`
233
+ * so it never falls in the past (forwarded to the emitter — see `emitter.mjs`
234
+ * `computeExpiresAt`). Defaults internally (7d) when omitted.
235
+ * @param {number} [params.minInsightChars]- opt-in minimum insight length gating the
236
+ * eligibility placeholder-insight check (forwarded to `filterEligible`). Inert
237
+ * (no additional rejections) when omitted.
238
+ * @param {number|Date} [params.now] - injectable clock (emitter fallback + candidate `created_at`).
239
+ * @param {boolean} [params.dryRun] - when true, compute proposals but SKIP the merge entirely
240
+ * (also accepted as `opts.dryRun`; either location sets it).
241
+ * @param {Object} [opts] - DI seams (all default to real behaviour).
242
+ * @param {(repoRoot?: string) => Array<Record<string, unknown>>} [opts.loadLearnings]
243
+ * override the file read (REQUIRED for tests to avoid disk).
244
+ * @param {Array<Record<string, unknown>>} [opts.learnings]
245
+ * direct learnings injection (takes precedence over `loadLearnings`).
246
+ * @param {typeof realMergeCandidates} [opts.merge]
247
+ * override the sidecar merge (so tests never touch real `.orchestrator/runtime/`).
248
+ * @param {boolean} [opts.dryRun] - when true, compute proposals but SKIP the merge entirely.
249
+ * @returns {Promise<ReconcileResult>}
250
+ */
251
+ export async function runReconcile(
252
+ { repoRoot, ruleExpiryDays, minRuleDays, minInsightChars, now, dryRun: dryRunParam } = {},
253
+ opts = {},
254
+ ) {
255
+ try {
256
+ // --- Resolve DI seams (real behaviour as defaults) ---------------------
257
+ // dryRun may arrive in the first arg (acceptance criterion 1) OR in opts
258
+ // (the documented DI seam) — either location flips it on.
259
+ const dryRun = dryRunParam === true || opts.dryRun === true;
260
+ const merge = typeof opts.merge === 'function' ? opts.merge : realMergeCandidates;
261
+
262
+ // --- Pipeline step 1 — load learnings ----------------------------------
263
+ /** @type {Array<Record<string, unknown>>} */
264
+ let learnings;
265
+ if (Array.isArray(opts.learnings)) {
266
+ learnings = opts.learnings;
267
+ } else if (typeof opts.loadLearnings === 'function') {
268
+ learnings = opts.loadLearnings(repoRoot);
269
+ } else {
270
+ learnings = defaultLoadLearnings(repoRoot);
271
+ }
272
+ if (!Array.isArray(learnings)) learnings = [];
273
+
274
+ const totalLearnings = learnings.length;
275
+
276
+ // --- Pipeline step 2 — empty short-circuit (touches no disk) ------------
277
+ if (totalLearnings === 0) {
278
+ return zeroedResult();
279
+ }
280
+
281
+ // Deterministic created_at for sidecar records (honours injectable clock).
282
+ const nowMs =
283
+ now instanceof Date ? now.getTime() : typeof now === 'number' ? now : Date.now();
284
+ const createdAt = new Date(nowMs).toISOString();
285
+
286
+ // --- Pipeline step 3 — partition ---------------------------------------
287
+ const { eligible, rejected: rejectedLearnings } = filterEligible(learnings, {
288
+ now: nowMs,
289
+ minInsightChars,
290
+ });
291
+
292
+ /** @type {ReconcileProposal[]} */
293
+ const proposals = [];
294
+ /** @type {ReconcileRejection[]} */
295
+ const rejected = [];
296
+ /** @type {import('./idempotency.mjs').ReconcileCandidate[]} */
297
+ const candidates = [];
298
+
299
+ // --- Pipeline step 4 — per eligible learning (wrapped per-item) ---------
300
+ for (const learning of eligible) {
301
+ try {
302
+ const metadata = toActivationMetadata(learning, { ruleExpiryDays, now, minRuleDays });
303
+ const { slug, path, content } = renderRule(learning, metadata);
304
+ const candidateId = makeCandidateId(metadata.learningKey, slug);
305
+
306
+ proposals.push({
307
+ learningKey: metadata.learningKey,
308
+ slug,
309
+ path,
310
+ content,
311
+ confidence: metadata.confidence,
312
+ candidateId,
313
+ status: 'proposed',
314
+ });
315
+
316
+ candidates.push(
317
+ buildCandidate({
318
+ id: candidateId,
319
+ learningKey: metadata.learningKey,
320
+ slug,
321
+ status: 'proposed',
322
+ reason: 'reconciliation engine proposed a conditional rule',
323
+ confidence: metadata.confidence,
324
+ createdAt,
325
+ }),
326
+ );
327
+ } catch (err) {
328
+ // A single bad learning must not crash the run — degrade to a rejection.
329
+ const msg = err && err.message ? err.message : String(err);
330
+ const learningKey = rejectedLearningKey(learning);
331
+ const reason = `emit/render error: ${msg}`;
332
+ rejected.push({
333
+ learningKey,
334
+ type: learningType(learning),
335
+ reason,
336
+ status: 'rejected',
337
+ });
338
+ candidates.push(
339
+ buildCandidate({
340
+ id: makeCandidateId(learningKey ?? '', `rejected-${learningType(learning)}`),
341
+ learningKey,
342
+ slug: '',
343
+ status: 'rejected',
344
+ reason,
345
+ confidence:
346
+ learning && typeof learning === 'object' && typeof learning.confidence === 'number'
347
+ ? learning.confidence
348
+ : 0,
349
+ createdAt,
350
+ }),
351
+ );
352
+ }
353
+ }
354
+
355
+ // --- Pipeline step 5 — per rejected learning (eligibility rejects) ------
356
+ for (const { learning, reason } of rejectedLearnings) {
357
+ const learningKey = rejectedLearningKey(learning);
358
+ const type = learningType(learning);
359
+ rejected.push({
360
+ learningKey,
361
+ type,
362
+ reason,
363
+ status: 'rejected',
364
+ });
365
+ candidates.push(
366
+ buildCandidate({
367
+ id: makeCandidateId(learningKey ?? '', `rejected-${type}`),
368
+ learningKey,
369
+ slug: '',
370
+ status: 'rejected',
371
+ reason,
372
+ confidence:
373
+ learning && typeof learning === 'object' && typeof learning.confidence === 'number'
374
+ ? learning.confidence
375
+ : 0,
376
+ createdAt,
377
+ }),
378
+ );
379
+ }
380
+
381
+ // --- Pipeline step 6 — record into the idempotency sidecar --------------
382
+ // The engine's ONLY disk write — and it is skipped entirely under dryRun.
383
+ // It never writes `.claude/rules/` (FA3 / #696 owns that, post-approval).
384
+ let written = false;
385
+ if (!dryRun) {
386
+ try {
387
+ const mergeResult = merge({ candidates, repoRoot });
388
+ written = !!(mergeResult && mergeResult.written === true);
389
+ } catch {
390
+ // Merge failure is non-fatal; proposals still returned, written stays false.
391
+ written = false;
392
+ }
393
+ }
394
+
395
+ // --- Pipeline step 7 — summary -----------------------------------------
396
+ return {
397
+ proposals,
398
+ rejected,
399
+ summary: {
400
+ totalLearnings,
401
+ eligible: eligible.length,
402
+ proposed: proposals.length,
403
+ rejected: rejected.length,
404
+ written,
405
+ },
406
+ };
407
+ } catch (err) {
408
+ // never-throws top-level guard.
409
+ const msg = err && err.message ? err.message : String(err);
410
+ return zeroedResult(msg);
411
+ }
412
+ }