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,228 @@
1
+ /**
2
+ * quality-gates-cache.mjs — Baseline quality-gate result cache (#258).
3
+ *
4
+ * Purpose:
5
+ * Wave-executor Incremental quality checks run once per session but were
6
+ * previously re-executed after every implementation wave, even when the
7
+ * session's dependency tree and session-start ref hadn't changed. This
8
+ * module persists the session-start Baseline result and lets wave-executor
9
+ * skip Incremental when the cache is still valid and the working-tree
10
+ * diff remains narrow.
11
+ *
12
+ * INVARIANT: Full Gate at session-end AND at the Quality wave is NEVER skipped
13
+ * regardless of cache state. shouldSkipIncremental({ waveRole: 'Quality' })
14
+ * hard-returns skip=false (reason 'quality-wave-full-gate-mandate') BEFORE any
15
+ * cache logic runs; the module otherwise only short-circuits Incremental in the
16
+ * Impl waves of wave-executor.
17
+ *
18
+ * Storage:
19
+ * .orchestrator/metrics/baseline-results.jsonl (append-only JSONL, one
20
+ * record per session baseline run — consistent with sessions.jsonl,
21
+ * events.jsonl, learnings.jsonl, audit.jsonl precedent).
22
+ *
23
+ * Record schema (version 1):
24
+ * {
25
+ * version: 1,
26
+ * session_id: string,
27
+ * session_start_ref: string, // git sha captured at session-start
28
+ * captured_at: string, // ISO 8601 UTC
29
+ * dependency_hash: string, // sha256 of package.json + lockfile
30
+ * results: {
31
+ * typecheck: { status: 'pass'|'fail', error_count?: number },
32
+ * test: { status: 'pass'|'fail' },
33
+ * lint: { status: 'pass'|'fail' }
34
+ * }
35
+ * }
36
+ *
37
+ * Validity rules (see isCacheValid):
38
+ * - session_start_ref matches current session
39
+ * - dependency_hash matches current package.json + lockfile
40
+ * - captured_at within TTL (default 7 days)
41
+ * - all results.* statuses === 'pass'
42
+ *
43
+ * Skip rule (see shouldSkipIncremental):
44
+ * cache valid AND changed-file-count < 50
45
+ *
46
+ * Fail-safe: every public function returns a structured result; none throw.
47
+ * On error, shouldSkipIncremental returns skip=false (Incremental runs),
48
+ * loadLatestBaselineResult returns null.
49
+ */
50
+
51
+ import fs from 'node:fs';
52
+ import path from 'node:path';
53
+ import { spawnSync } from 'node:child_process';
54
+ import { digestMultiBufferSha256 } from './crypto-digest-utils.mjs';
55
+ import { isIgnoredRootFile } from './package-manager.mjs';
56
+
57
+ const CACHE_RELATIVE_PATH = '.orchestrator/metrics/baseline-results.jsonl';
58
+ const DEFAULT_TTL_DAYS = 7;
59
+ const DEFAULT_SCOPE_THRESHOLD = 50;
60
+ const RECORD_VERSION = 1;
61
+
62
+ /**
63
+ * Compute a sha256 hash over package.json plus the active lockfile.
64
+ * Missing lockfile → hash only package.json.
65
+ * Missing package.json → returns the sha256 of the empty string (stable null).
66
+ *
67
+ * @param {string} repoRoot Absolute path to repo root.
68
+ * @returns {string} Hex sha256 digest.
69
+ */
70
+ export function computeDependencyHash(repoRoot) {
71
+ const buffers = [];
72
+ const pkgPath = path.join(repoRoot, 'package.json');
73
+ if (fs.existsSync(pkgPath)) {
74
+ buffers.push(fs.readFileSync(pkgPath));
75
+ }
76
+ // Prefer pnpm-lock.yaml (project standard), then package-lock.json, then yarn.lock.
77
+ // A gitignored root lockfile is skipped — a stray/local-only lockfile must
78
+ // not dominate the dependency hash (issue #715 bug class).
79
+ const lockCandidates = ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock'];
80
+ for (const candidate of lockCandidates) {
81
+ const candidatePath = path.join(repoRoot, candidate);
82
+ if (fs.existsSync(candidatePath) && !isIgnoredRootFile(repoRoot, candidate)) {
83
+ buffers.push(fs.readFileSync(candidatePath));
84
+ break;
85
+ }
86
+ }
87
+ return digestMultiBufferSha256(buffers);
88
+ }
89
+
90
+ /**
91
+ * Append one baseline-result record to the JSONL cache.
92
+ * Creates the metrics directory if missing. Atomic append via appendFileSync.
93
+ *
94
+ * @param {object} params
95
+ * @param {string} params.repoRoot
96
+ * @param {string} params.sessionId
97
+ * @param {string} params.sessionStartRef
98
+ * @param {object} params.results { typecheck, test, lint } — see module header.
99
+ */
100
+ export function saveBaselineResult({ repoRoot, sessionId, sessionStartRef, results }) {
101
+ const cachePath = path.join(repoRoot, CACHE_RELATIVE_PATH);
102
+ const cacheDir = path.dirname(cachePath);
103
+ fs.mkdirSync(cacheDir, { recursive: true });
104
+ const record = {
105
+ version: RECORD_VERSION,
106
+ session_id: sessionId,
107
+ session_start_ref: sessionStartRef,
108
+ captured_at: new Date().toISOString(),
109
+ dependency_hash: computeDependencyHash(repoRoot),
110
+ results,
111
+ };
112
+ fs.appendFileSync(cachePath, JSON.stringify(record) + '\n');
113
+ }
114
+
115
+ /**
116
+ * Return the most recent record from the JSONL cache, or null if the file
117
+ * is missing, empty, or the last line is unparseable.
118
+ *
119
+ * Never throws.
120
+ *
121
+ * @param {object} params
122
+ * @param {string} params.repoRoot
123
+ * @returns {object|null}
124
+ */
125
+ export function loadLatestBaselineResult({ repoRoot }) {
126
+ try {
127
+ const cachePath = path.join(repoRoot, CACHE_RELATIVE_PATH);
128
+ if (!fs.existsSync(cachePath)) return null;
129
+ const raw = fs.readFileSync(cachePath, 'utf8');
130
+ if (!raw.trim()) return null;
131
+ const lines = raw.split('\n').filter((line) => line.trim().length > 0);
132
+ if (lines.length === 0) return null;
133
+ const last = lines[lines.length - 1];
134
+ return JSON.parse(last);
135
+ } catch {
136
+ return null;
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Validate a cache record against current session context.
142
+ *
143
+ * @param {object} params
144
+ * @param {string} params.repoRoot
145
+ * @param {object|null} params.latestRecord
146
+ * @param {string} params.currentSessionStartRef
147
+ * @param {number} [params.ttlDays=7]
148
+ * @returns {{ valid: boolean, reason: string }}
149
+ */
150
+ export function isCacheValid({ repoRoot, latestRecord, currentSessionStartRef, ttlDays = DEFAULT_TTL_DAYS }) {
151
+ if (!latestRecord) {
152
+ return { valid: false, reason: 'no-record' };
153
+ }
154
+ if (latestRecord.session_start_ref !== currentSessionStartRef) {
155
+ return { valid: false, reason: 'session-ref-mismatch' };
156
+ }
157
+ const currentDepHash = computeDependencyHash(repoRoot);
158
+ if (latestRecord.dependency_hash !== currentDepHash) {
159
+ return { valid: false, reason: 'dependency-changed' };
160
+ }
161
+ const capturedAt = Date.parse(latestRecord.captured_at);
162
+ if (Number.isNaN(capturedAt)) {
163
+ return { valid: false, reason: 'ttl-expired' };
164
+ }
165
+ const ageMs = Date.now() - capturedAt;
166
+ const ttlMs = ttlDays * 24 * 60 * 60 * 1000;
167
+ if (ageMs > ttlMs) {
168
+ return { valid: false, reason: 'ttl-expired' };
169
+ }
170
+ const r = latestRecord.results || {};
171
+ const statuses = [r.typecheck?.status, r.test?.status, r.lint?.status];
172
+ if (!statuses.every((s) => s === 'pass')) {
173
+ return { valid: false, reason: 'baseline-had-failures' };
174
+ }
175
+ return { valid: true, reason: 'cache-hit' };
176
+ }
177
+
178
+ /**
179
+ * High-level convenience used by wave-executor.
180
+ *
181
+ * Returns { skip, reason, changedFileCount }. Never throws — on any error
182
+ * (git failure, unreadable cache, etc.) returns skip=false so Incremental
183
+ * runs. Fail safe = run Incremental.
184
+ *
185
+ * @param {object} params
186
+ * @param {string} params.repoRoot
187
+ * @param {string} params.sessionStartRef
188
+ * @param {number} [params.scopeThreshold=50]
189
+ * @param {string|null} [params.waveRole=null] Role of the wave being gated.
190
+ * When 'Quality' (case-insensitive, whitespace-trimmed — e.g. 'quality',
191
+ * ' Quality ' all match), the function hard-returns skip=false BEFORE any
192
+ * cache logic so the Quality-wave Full Gate is mechanically un-skippable
193
+ * (#724 C6). All other values (null/undefined/Impl-*) preserve the cache
194
+ * short-circuit path.
195
+ * @returns {{ skip: boolean, reason: string, changedFileCount: number }}
196
+ */
197
+ export function shouldSkipIncremental({ repoRoot, sessionStartRef, scopeThreshold = DEFAULT_SCOPE_THRESHOLD, waveRole = null }) {
198
+ // C6 (#724): the Quality wave's Full Gate is NEVER skipped, regardless of a
199
+ // valid cache or a narrow diff. This mirrors the session-end close-safety
200
+ // invariant and makes the "After Quality: Full Gate" mandate mechanical
201
+ // rather than prose-only. Hard-return BEFORE any cache/git logic.
202
+ // Case-insensitive + trimmed match (#F-B) — a coordinator prose typo like
203
+ // 'quality' or 'Quality ' must not silently disable this invariant.
204
+ if (String(waveRole ?? '').trim().toLowerCase() === 'quality') {
205
+ return { skip: false, reason: 'quality-wave-full-gate-mandate', changedFileCount: -1 };
206
+ }
207
+ try {
208
+ const latestRecord = loadLatestBaselineResult({ repoRoot });
209
+ const validity = isCacheValid({ repoRoot, latestRecord, currentSessionStartRef: sessionStartRef });
210
+ if (!validity.valid) {
211
+ return { skip: false, reason: validity.reason, changedFileCount: -1 };
212
+ }
213
+ const diff = spawnSync('git', ['diff', '--name-only', `${sessionStartRef}..HEAD`], {
214
+ cwd: repoRoot,
215
+ encoding: 'utf8',
216
+ });
217
+ if (diff.status !== 0) {
218
+ return { skip: false, reason: 'git-diff-failed', changedFileCount: -1 };
219
+ }
220
+ const changedFileCount = diff.stdout.split('\n').filter((line) => line.trim().length > 0).length;
221
+ if (changedFileCount >= scopeThreshold) {
222
+ return { skip: false, reason: 'scope-too-large', changedFileCount };
223
+ }
224
+ return { skip: true, reason: 'cache-hit', changedFileCount };
225
+ } catch (err) {
226
+ return { skip: false, reason: `error:${err?.message ?? 'unknown'}`, changedFileCount: -1 };
227
+ }
228
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Quality-gates policy loader.
3
+ *
4
+ * Reads .orchestrator/policy/quality-gates.json (issue #183).
5
+ * Consumed by scripts/run-quality-gate.mjs and the quality-gates skill.
6
+ *
7
+ * Policy-file-first: when a valid policy exists, its commands take precedence
8
+ * over the project-instruction file's Session Config (CLAUDE.md, or AGENTS.md
9
+ * on Codex CLI — see skills/_shared/instruction-file-resolution.md). Falls
10
+ * back to caller-provided defaults when the file is missing or malformed.
11
+ *
12
+ * Never throws.
13
+ */
14
+
15
+ import { existsSync, readFileSync } from 'node:fs';
16
+ import { join } from 'node:path';
17
+
18
+ const REQUIRED_KEYS = ['test', 'typecheck', 'lint'];
19
+
20
+ /**
21
+ * Loads and shape-validates the quality-gates policy file.
22
+ *
23
+ * @param {string} repoRoot - repository root (where .orchestrator/ lives)
24
+ * @returns {object|null} parsed policy, or null if missing/invalid
25
+ */
26
+ export function loadQualityGatesPolicy(repoRoot) {
27
+ const path = join(repoRoot, '.orchestrator/policy/quality-gates.json');
28
+ if (!existsSync(path)) return null;
29
+
30
+ let parsed;
31
+ try {
32
+ parsed = JSON.parse(readFileSync(path, 'utf8'));
33
+ } catch (err) {
34
+ process.stderr.write(`⚠ quality-gates policy: malformed JSON at ${path}: ${err.message}\n`);
35
+ return null;
36
+ }
37
+
38
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
39
+ process.stderr.write(`⚠ quality-gates policy: root must be an object (${path})\n`);
40
+ return null;
41
+ }
42
+ if (parsed['version'] !== 1) {
43
+ process.stderr.write(`⚠ quality-gates policy: unsupported version ${JSON.stringify(parsed['version'])} (${path})\n`);
44
+ return null;
45
+ }
46
+ const commands = parsed['commands'];
47
+ if (commands === null || typeof commands !== 'object' || Array.isArray(commands)) {
48
+ process.stderr.write(`⚠ quality-gates policy: commands must be an object (${path})\n`);
49
+ return null;
50
+ }
51
+ for (const key of REQUIRED_KEYS) {
52
+ const entry = commands[key];
53
+ if (
54
+ entry === null ||
55
+ typeof entry !== 'object' ||
56
+ Array.isArray(entry) ||
57
+ typeof entry['command'] !== 'string' ||
58
+ entry['command'].trim() === ''
59
+ ) {
60
+ process.stderr.write(`⚠ quality-gates policy: commands.${key}.command must be a non-empty string (${path})\n`);
61
+ return null;
62
+ }
63
+ }
64
+ return parsed;
65
+ }
66
+
67
+ /**
68
+ * Resolves a command from policy or falls back to the caller's default.
69
+ *
70
+ * @param {object|null} policy - result of loadQualityGatesPolicy
71
+ * @param {"test"|"typecheck"|"lint"} key
72
+ * @param {string} fallback - fallback command when policy lacks the key
73
+ * @returns {string}
74
+ */
75
+ export function resolveCommand(policy, key, fallback) {
76
+ if (policy && policy.commands && policy.commands[key] && typeof policy.commands[key].command === 'string') {
77
+ return policy.commands[key].command;
78
+ }
79
+ return fallback;
80
+ }
81
+
82
+ /**
83
+ * Maps a Session Config key (e.g. "test-command") from the project-instruction
84
+ * file (CLAUDE.md or AGENTS.md alias) to the policy-file key ("test"). Used by
85
+ * run-quality-gate.mjs.
86
+ *
87
+ * @param {string} configKey
88
+ * @returns {"test"|"typecheck"|"lint"|null}
89
+ */
90
+ export function configKeyToPolicyKey(configKey) {
91
+ if (configKey === 'test-command') return 'test';
92
+ if (configKey === 'typecheck-command') return 'typecheck';
93
+ if (configKey === 'lint-command') return 'lint';
94
+ return null;
95
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * v0 recommendation heuristic (issue #272, Epic #271 Phase A).
3
+ *
4
+ * Deterministic three-branch rule. Consumed by session-end Phase 3.7a Writer
5
+ * and (via STATE.md handoff) by session-start Phase 1.5 Reader.
6
+ */
7
+
8
+ const VALID_MODES = new Set([
9
+ 'housekeeping',
10
+ 'feature',
11
+ 'deep',
12
+ 'discovery',
13
+ 'evolve',
14
+ 'plan-retro',
15
+ ]);
16
+
17
+ /**
18
+ * Compute the v0 recommendation for the next session.
19
+ *
20
+ * Rules (evaluated in order, first match wins):
21
+ * 1. completion_rate < 0.50 → plan-retro
22
+ * 2. carryover_ratio ≥ 0.30 → deep
23
+ * 3. otherwise → feature
24
+ *
25
+ * @param {object} input
26
+ * @param {number} input.completionRate — 0.00..1.00; share of planned issues closed
27
+ * @param {number} input.carryoverRatio — 0.00..1.00; carryover / planned (0 when planned=0)
28
+ * @param {Array<number|string>} [input.carryoverIssues] — IDs/IIDs of carried-over issues,
29
+ * pre-sorted (priority:critical/high first, FIFO tiebreak). Sliced to 0-5 entries for
30
+ * the `priorities` field.
31
+ * @returns {{mode: string, priorities: number[], rationale: string}}
32
+ */
33
+ export function computeV0Recommendation({
34
+ completionRate,
35
+ carryoverRatio,
36
+ carryoverIssues = [],
37
+ }) {
38
+ if (typeof completionRate !== 'number' || Number.isNaN(completionRate)) {
39
+ throw new TypeError('completionRate must be a number');
40
+ }
41
+ if (typeof carryoverRatio !== 'number' || Number.isNaN(carryoverRatio)) {
42
+ throw new TypeError('carryoverRatio must be a number');
43
+ }
44
+
45
+ const priorities = Array.isArray(carryoverIssues)
46
+ ? carryoverIssues.slice(0, 5).map((x) => (typeof x === 'number' ? x : parseInt(String(x), 10)))
47
+ : [];
48
+
49
+ let mode;
50
+ let rationale;
51
+ if (completionRate < 0.5) {
52
+ mode = 'plan-retro';
53
+ rationale = 'v0: completion <50% → retro';
54
+ } else if (carryoverRatio >= 0.3) {
55
+ mode = 'deep';
56
+ rationale = 'v0: carryover ≥30% → deep';
57
+ } else {
58
+ mode = 'feature';
59
+ rationale = 'v0: default clean completion';
60
+ }
61
+
62
+ return { mode, priorities, rationale };
63
+ }
64
+
65
+ /**
66
+ * Validate that a recommendation's mode is one of the allowed values.
67
+ * Defensive for Reader-side parsing — unknown modes are rejected rather than
68
+ * silently propagated into Phase B's mode-selector.
69
+ */
70
+ export function isValidMode(mode) {
71
+ return typeof mode === 'string' && VALID_MODES.has(mode);
72
+ }
73
+
74
+ // ---------------------------------------------------------------------------
75
+ // Dispatcher precursor SUGGESTIONS (#678, PRD §2 P2.4 / line 56, §4 line 244)
76
+ // ---------------------------------------------------------------------------
77
+
78
+ /**
79
+ * Read-only precursor modes the dispatcher MENU may route to (`/discovery`,
80
+ * `/plan`). These are SUGGESTIONS only — never execution-wave modes. The
81
+ * non-execution guardrail lives in mode-selector.mjs (global-max swap excludes
82
+ * these from primary-mode promotion); this set is the source of truth for what
83
+ * the dispatcher is allowed to offer as a precursor.
84
+ * @type {ReadonlyArray<string>}
85
+ */
86
+ export const PRECURSOR_MODES = Object.freeze(['discovery', 'plan-retro']);
87
+
88
+ /**
89
+ * True when `mode` is a read-only precursor (discovery / plan-retro). Mirrors
90
+ * `isNonExecutionMode` in mode-selector/scoring.mjs but kept local so the
91
+ * dispatcher can import suggestion logic without pulling in the scoring graph.
92
+ * @param {unknown} mode
93
+ * @returns {boolean}
94
+ */
95
+ export function isPrecursorMode(mode) {
96
+ return typeof mode === 'string' && PRECURSOR_MODES.includes(mode);
97
+ }
98
+
99
+ /**
100
+ * Suggest read-only precursor session-types for the dispatcher menu (#678).
101
+ *
102
+ * Pure, deterministic, additive. NEVER returns an execution mode — only the
103
+ * read-only precursors (`discovery` → /discovery, `plan-retro` → /plan). The
104
+ * dispatcher presents these as optional routes in its owner-confirmation AUQ;
105
+ * they are suggestions, not the executed primary `mode`.
106
+ *
107
+ * Heuristic (both independent — a session may justify both):
108
+ * - `plan-retro` when there is real carry-forward to plan/retrospect:
109
+ * completion_rate < 0.5 OR carryover_ratio ≥ 0.3.
110
+ * - `discovery` when context/understanding is thin and exploration helps:
111
+ * no recent sessions (cold start) OR an explicit `discoveryHint`.
112
+ *
113
+ * @param {object|null|undefined} signals
114
+ * @param {number} [signals.completionRate]
115
+ * @param {number} [signals.carryoverRatio]
116
+ * @param {Array<unknown>} [signals.recentSessions]
117
+ * @param {boolean} [signals.discoveryHint] — explicit "explore first" hint.
118
+ * @returns {Array<{mode: string, route: string, rationale: string}>}
119
+ * 0–2 entries; deterministic order (plan-retro before discovery when both).
120
+ */
121
+ export function suggestPrecursors(signals) {
122
+ if (signals === null || typeof signals !== 'object') {
123
+ return [];
124
+ }
125
+
126
+ const completionRate =
127
+ typeof signals.completionRate === 'number' && !Number.isNaN(signals.completionRate)
128
+ ? signals.completionRate
129
+ : null;
130
+ const carryoverRatio =
131
+ typeof signals.carryoverRatio === 'number' && !Number.isNaN(signals.carryoverRatio)
132
+ ? signals.carryoverRatio
133
+ : null;
134
+ const recentSessions = Array.isArray(signals.recentSessions) ? signals.recentSessions : [];
135
+
136
+ /** @type {Array<{mode: string, route: string, rationale: string}>} */
137
+ const suggestions = [];
138
+
139
+ if ((completionRate !== null && completionRate < 0.5) || (carryoverRatio !== null && carryoverRatio >= 0.3)) {
140
+ suggestions.push({
141
+ mode: 'plan-retro',
142
+ route: '/plan',
143
+ rationale: 'low completion / carryover → plan-retro precursor',
144
+ });
145
+ }
146
+
147
+ if (recentSessions.length === 0 || signals.discoveryHint === true) {
148
+ suggestions.push({
149
+ mode: 'discovery',
150
+ route: '/discovery',
151
+ rationale: 'cold start / explore-first hint → discovery precursor',
152
+ });
153
+ }
154
+
155
+ return suggestions;
156
+ }
@@ -0,0 +1,203 @@
1
+ /**
2
+ * eligibility.mjs — Reconciliation-engine eligibility filter (Epic #693 FA2, issue #695).
3
+ *
4
+ * Decides which `learnings.jsonl` entries are eligible to be converted into a
5
+ * conditional `.claude/rules/*.md` rule. A learning is a plain in-memory JSON
6
+ * object; this module classifies and partitions such objects without any I/O.
7
+ *
8
+ * ── Safe posture (INVERTED allow-list) ───────────────────────────────────────
9
+ * The engine default-rejects. Only a learning whose `type` is in
10
+ * {@link CONVERT_TYPES} AND that carries a non-empty `file_paths[]` (the scope a
11
+ * conditional rule needs) is eligible. Everything else is rejected with a
12
+ * specific audit reason. This is the safe default the issue demands: a corpus
13
+ * of unknown types never silently produces a rule.
14
+ *
15
+ * ── The file-carrier key ─────────────────────────────────────────────────────
16
+ * The scope key we read is `file_paths` (an array of repo-relative paths).
17
+ * Provenance (corrected 2026-07-02, Epic #723 B2): the live corpus historically
18
+ * carried the scope under `files`, NOT `file_paths` — so this reader matched
19
+ * nothing (0/108 records had `file_paths`). Two fixes now converge on
20
+ * `file_paths` and keep this reader correct:
21
+ * 1. `scripts/backfill-learnings.mjs` normalized the on-disk corpus
22
+ * `files` → `file_paths` (one-shot, 2026-07-02).
23
+ * 2. The schema SSOT (`learnings/schema.mjs` normalizeDialects) renames
24
+ * `files` → `file_paths` on every read + migration going forward.
25
+ * We continue to read `file_paths` — it is now the canonical scope key. The
26
+ * engine's default JSONL loader also runs records through migrateLegacyLearning
27
+ * + normalizeLearning, so direct `runReconcile({ repoRoot })` calls see the same
28
+ * legacy alias + `files`→`file_paths` mapping as the backfill/read funnels.
29
+ *
30
+ * ── Type-name mapping (issue → real) ─────────────────────────────────────────
31
+ * issue 'fragile-file' -> real 'fragile-file' (verbatim; 1 instance)
32
+ * issue 'recurring-issue' -> real 'recurring-issue' (verbatim; 1 instance)
33
+ * issue 'anti-pattern' -> real 'anti-pattern' (verbatim; 22 instances)
34
+ * Forward-compat spec names with 0 instances today are also included so a future
35
+ * renamed corpus still converts (they convert nothing now — harmless). See the
36
+ * CONVERT_TYPES doc for the full 2026-07-02 instance census.
37
+ *
38
+ * Pure functions — depends only on the pure `learnings/schema` type registry
39
+ * (a frozen constant; no I/O). Deterministic, no file I/O.
40
+ */
41
+
42
+ import { LEARNING_TYPE_REGISTRY, LEARNING_TTL_DAYS, deriveExpiresAt } from '../learnings/schema.mjs';
43
+
44
+ /**
45
+ * Recovery-stub / legacy-backfill placeholder insight signature (issue #741.2).
46
+ * Legacy-recovery records occasionally carry a stub insight instead of a real
47
+ * one (e.g. `"(legacy record — insight backfilled during 2026-07-02 recovery)"`).
48
+ * A placeholder insight would produce an EMPTY rule body downstream (the
49
+ * renderer has nothing meaningful to render) — reject it honestly at the
50
+ * eligibility gate instead of letting a hollow rule reach the proposal stage.
51
+ * @type {RegExp}
52
+ */
53
+ const PLACEHOLDER_RE = /insight backfilled during .* recovery|^\(?legacy record\b/i;
54
+
55
+ /**
56
+ * The learning `type` values that may become a conditional rule. DERIVED
57
+ * (Epic #723 I1, issue #733 Teil b) from `scripts/lib/learnings/schema.mjs`'s
58
+ * `LEARNING_TYPE_REGISTRY`: every type whose `ruleConvertible` flag is `true`.
59
+ *
60
+ * Instance census (corrected 2026-07-02, Epic #723 B2 — the earlier comment
61
+ * was a stale mis-count that inverted `fragile-file` and `fragile-pattern`),
62
+ * kept here as a historical record (the registry itself carries no counts):
63
+ * - 'anti-pattern' — 22 instances
64
+ * - 'architecture-pattern' — 2 instances
65
+ * - 'convention' — 1 instance
66
+ * - 'design-pattern' — 1 instance
67
+ * - 'fragile-file' — 1 instance
68
+ * - 'recurring-issue' — 1 instance
69
+ * - 'fragile-pattern' — 0 instances (kept for forward-compat)
70
+ * - 'stagnation-class-frequency' — 0 instances (kept for forward-compat)
71
+ *
72
+ * The membership is UNCHANGED from the prior hand-maintained literal — every
73
+ * listed type stays in the allow-list, so a future renamed/backfilled corpus
74
+ * still converts and 0-instance types convert nothing today.
75
+ *
76
+ * @type {Set<string>}
77
+ */
78
+ export const CONVERT_TYPES = new Set(
79
+ Object.entries(LEARNING_TYPE_REGISTRY)
80
+ .filter(([, meta]) => meta.ruleConvertible)
81
+ .map(([type]) => type)
82
+ );
83
+
84
+ /**
85
+ * Classify a single learning record for rule-conversion eligibility.
86
+ *
87
+ * INVERTED allow-list: eligible only when the type is in CONVERT_TYPES AND a
88
+ * non-empty `file_paths[]` is present. The type gate is evaluated before the
89
+ * file gate, so an out-of-allow-list type is rejected as such even when it
90
+ * carries file paths.
91
+ *
92
+ * Two further gates run AFTER the type + file gates succeed (issue #741):
93
+ * - placeholder-insight (#741.2, always-on): an empty or recovery-stub
94
+ * insight would produce an empty rule body — rejected honestly rather
95
+ * than proposed with hollow content. `minInsightChars` additionally
96
+ * rejects an insight that is non-empty but too short to be useful; it is
97
+ * opt-in (inert when omitted).
98
+ * - already-expired-at-proposal (#741.1c, opt-in via `now`): a learning
99
+ * whose natural TTL (`created_at` + per-type TTL) already elapsed before
100
+ * proposal time is rejected honestly instead of silently floored back to
101
+ * life. Inert when `now` is omitted, so existing single-arg callers are
102
+ * unaffected.
103
+ *
104
+ * Defensive: a null / non-object record, or one missing `type`, is rejected
105
+ * (never throws).
106
+ *
107
+ * @param {unknown} learning - an in-memory learning object.
108
+ * @param {{ now?: number, minInsightChars?: number }} [opts] - now: injectable
109
+ * clock (ms epoch) gating the expiry check; minInsightChars: opt-in
110
+ * minimum insight length gating the placeholder check.
111
+ * @returns {{ eligible: boolean, reason: string }}
112
+ */
113
+ export function classifyLearning(learning, { now, minInsightChars } = {}) {
114
+ if (
115
+ learning === null ||
116
+ typeof learning !== 'object' ||
117
+ Array.isArray(learning) ||
118
+ typeof learning.type !== 'string' ||
119
+ learning.type.length === 0
120
+ ) {
121
+ return { eligible: false, reason: 'invalid learning record (missing type)' };
122
+ }
123
+
124
+ const { type } = learning;
125
+
126
+ // Type gate beats file gate (safe posture: default-reject unknown types).
127
+ if (!CONVERT_TYPES.has(type)) {
128
+ return {
129
+ eligible: false,
130
+ reason: `type '${type}' not in convert allow-list — default-reject`,
131
+ };
132
+ }
133
+
134
+ const filePaths = learning.file_paths;
135
+ if (!Array.isArray(filePaths) || filePaths.length === 0) {
136
+ return {
137
+ eligible: false,
138
+ reason: `eligible type '${type}' but empty file_paths[] — cannot scope a conditional rule`,
139
+ };
140
+ }
141
+
142
+ // Placeholder-insight gate (#741.2, always-on) — empty or recovery-stub
143
+ // insight would produce an empty rule body downstream.
144
+ const insight = typeof learning.insight === 'string' ? learning.insight.trim() : '';
145
+ if (insight === '' || PLACEHOLDER_RE.test(insight)) {
146
+ return {
147
+ eligible: false,
148
+ reason: `placeholder-insight — insight is empty or a recovery placeholder ("${insight.slice(0, 40)}")`,
149
+ };
150
+ }
151
+ if (typeof minInsightChars === 'number' && minInsightChars > 0 && insight.length < minInsightChars) {
152
+ return {
153
+ eligible: false,
154
+ reason: `placeholder-insight — insight ${insight.length} chars < min-insight-chars ${minInsightChars}`,
155
+ };
156
+ }
157
+
158
+ // Already-expired-at-proposal gate (#741.1c, opt-in via `now`) — a learning
159
+ // whose natural TTL already elapsed before proposal time is rejected
160
+ // honestly rather than silently floored back to life at emit time.
161
+ if (typeof now === 'number' && Number.isFinite(now)) {
162
+ const naturalExpiryIso = deriveExpiresAt(learning.created_at, type);
163
+ const naturalExpiryMs = Date.parse(naturalExpiryIso);
164
+ if (Number.isFinite(naturalExpiryMs) && naturalExpiryMs < now) {
165
+ const ttlDays = LEARNING_TTL_DAYS[type] ?? LEARNING_TTL_DAYS.default;
166
+ return {
167
+ eligible: false,
168
+ reason: `already-expired-at-proposal — natural TTL (created_at + ${ttlDays}d) elapsed at ${naturalExpiryIso.slice(0, 10)} before proposal`,
169
+ };
170
+ }
171
+ }
172
+
173
+ return {
174
+ eligible: true,
175
+ reason: `eligible: ${type} with ${filePaths.length} file path(s)`,
176
+ };
177
+ }
178
+
179
+ /**
180
+ * Partition a list of learnings into eligible records and rejected records
181
+ * (each rejection carrying the specific audit reason).
182
+ *
183
+ * @param {unknown[]} learnings - list of in-memory learning objects.
184
+ * @param {{ now?: number, minInsightChars?: number }} [opts] - forwarded verbatim
185
+ * to {@link classifyLearning} for every record.
186
+ * @returns {{ eligible: object[], rejected: Array<{ learning: object, reason: string }> }}
187
+ */
188
+ export function filterEligible(learnings, opts = {}) {
189
+ const eligible = [];
190
+ const rejected = [];
191
+
192
+ const list = Array.isArray(learnings) ? learnings : [];
193
+ for (const learning of list) {
194
+ const { eligible: isEligible, reason } = classifyLearning(learning, opts);
195
+ if (isEligible) {
196
+ eligible.push(learning);
197
+ } else {
198
+ rejected.push({ learning, reason });
199
+ }
200
+ }
201
+
202
+ return { eligible, rejected };
203
+ }