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,289 @@
1
+ /**
2
+ * markdown-writer.mjs — Render and write the GitLab Portfolio dashboard file.
3
+ *
4
+ * Exports:
5
+ * GENERATOR_MARKER — frontmatter sentinel
6
+ * renderPortfolio — pure render: Map<repo, summary> → markdown string
7
+ * writePortfolio — idempotent write with skip-handwritten / skip-noop / dry-run
8
+ *
9
+ * Skip-on-manual-edit: if the existing file's _generator is absent or differs,
10
+ * return { action: 'skipped-handwritten' } without writing.
11
+ *
12
+ * Skip-noop: if the rendered content (modulo the `updated:` timestamp) matches
13
+ * the existing content byte-for-byte, return { action: 'skipped-noop' }.
14
+ *
15
+ * Dry-run: when dryRun: true, never write; return { action: 'dry-run' }.
16
+ *
17
+ * Created-preservation: if the existing file has a `created:` frontmatter field,
18
+ * propagate it into the new render.
19
+ */
20
+
21
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
22
+ import { dirname } from 'node:path';
23
+ import { parseFrontmatter } from '../vault-mirror/utils.mjs';
24
+
25
+ /** Frontmatter sentinel that identifies generator-owned files. */
26
+ export const GENERATOR_MARKER = 'session-orchestrator-gitlab-portfolio@1';
27
+
28
+ /** Placeholder used for noop comparison (replaces the live `updated:` value). */
29
+ const UPDATED_PLACEHOLDER = '__UPDATED_PLACEHOLDER__';
30
+
31
+ // ── Formatting helpers ─────────────────────────────────────────────────────────
32
+
33
+ /**
34
+ * Format a Date as "YYYY-MM-DD HH:MM UTC".
35
+ *
36
+ * @param {Date} date
37
+ * @returns {string}
38
+ */
39
+ function formatDateUTC(date) {
40
+ const pad = (n) => String(n).padStart(2, '0');
41
+ return (
42
+ `${date.getUTCFullYear()}-` +
43
+ `${pad(date.getUTCMonth() + 1)}-` +
44
+ `${pad(date.getUTCDate())} ` +
45
+ `${pad(date.getUTCHours())}:` +
46
+ `${pad(date.getUTCMinutes())} UTC`
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Format a Date as ISO 8601 (for frontmatter fields).
52
+ *
53
+ * @param {Date} date
54
+ * @returns {string}
55
+ */
56
+ function toIso(date) {
57
+ return date.toISOString();
58
+ }
59
+
60
+ /**
61
+ * Format a last-activity ISO string for display. Returns '—' if absent.
62
+ *
63
+ * @param {string|null|undefined} iso
64
+ * @returns {string}
65
+ */
66
+ function fmtActivity(iso) {
67
+ if (!iso) return '—';
68
+ // Display as YYYY-MM-DD HH:MM UTC
69
+ try {
70
+ const d = new Date(iso);
71
+ return formatDateUTC(d);
72
+ } catch {
73
+ return iso;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Format a milestone for display.
79
+ *
80
+ * @param {{ title: string, due_date: string } | null} ms
81
+ * @returns {string}
82
+ */
83
+ function fmtMilestone(ms) {
84
+ if (!ms) return '—';
85
+ return ms.due_date ? `${ms.title} (${ms.due_date})` : ms.title;
86
+ }
87
+
88
+ // ── Render ─────────────────────────────────────────────────────────────────────
89
+
90
+ /**
91
+ * Render the portfolio markdown body from per-repo summaries.
92
+ *
93
+ * Repos are sorted alphabetically by name for stable output.
94
+ * topThree within each repo is already sorted by updated_at desc (from summarizeRepo).
95
+ *
96
+ * @param {Map<string, {
97
+ * openCount: number,
98
+ * criticalCount: number,
99
+ * staleCount: number,
100
+ * nextMilestone: { title: string, due_date: string } | null,
101
+ * lastActivity: string | null,
102
+ * topThree: Array<{ iid: number|string, title: string, labels: string[], url: string }>,
103
+ * }>} summaries
104
+ * @param {{
105
+ * now: Date,
106
+ * createdIso?: string,
107
+ * staleDays?: number,
108
+ * updatedPlaceholder?: string,
109
+ * }} opts
110
+ * @returns {string} full markdown (frontmatter + body)
111
+ */
112
+ export function renderPortfolio(summaries, opts) {
113
+ const {
114
+ now,
115
+ createdIso,
116
+ staleDays = 30,
117
+ updatedPlaceholder,
118
+ } = opts;
119
+
120
+ const nowIso = toIso(now);
121
+ const updatedValue = updatedPlaceholder ?? nowIso;
122
+ const createdValue = createdIso ?? nowIso;
123
+
124
+ // Aggregate totals
125
+ let totalOpen = 0;
126
+ let totalCritical = 0;
127
+ let totalStale = 0;
128
+ const sortedRepos = [...summaries.keys()].sort((a, b) => a.localeCompare(b));
129
+
130
+ for (const repo of sortedRepos) {
131
+ const s = summaries.get(repo);
132
+ totalOpen += s.openCount;
133
+ totalCritical += s.criticalCount;
134
+ totalStale += s.staleCount;
135
+ }
136
+
137
+ const lines = [];
138
+
139
+ // Frontmatter
140
+ lines.push('---');
141
+ lines.push(`_generator: ${GENERATOR_MARKER}`);
142
+ lines.push(`type: dashboard`);
143
+ lines.push(`created: ${createdValue}`);
144
+ lines.push(`updated: ${updatedValue}`);
145
+ lines.push('---');
146
+ lines.push('');
147
+
148
+ // Title + preamble
149
+ lines.push('# GitLab Portfolio');
150
+ lines.push('');
151
+ lines.push(`> Generated ${formatDateUTC(now)}. Re-run /portfolio to refresh.`);
152
+ lines.push('');
153
+
154
+ // Summary table
155
+ lines.push('## Summary');
156
+ lines.push('');
157
+ lines.push('| Metric | Count |');
158
+ lines.push('|---|---|');
159
+ lines.push(`| Total repos | ${sortedRepos.length} |`);
160
+ lines.push(`| Total open issues | ${totalOpen} |`);
161
+ lines.push(`| Critical | ${totalCritical} |`);
162
+ lines.push(`| Stale (>${staleDays} days) | ${totalStale} |`);
163
+ lines.push('');
164
+
165
+ // Repos overview table
166
+ lines.push('## Repos');
167
+ lines.push('');
168
+ lines.push('| Repo | Open | Critical | Stale | Last activity | Next milestone |');
169
+ lines.push('|---|---|---|---|---|---|');
170
+
171
+ for (const repo of sortedRepos) {
172
+ const s = summaries.get(repo);
173
+ lines.push(
174
+ `| ${repo} | ${s.openCount} | ${s.criticalCount} | ${s.staleCount} | ${fmtActivity(s.lastActivity)} | ${fmtMilestone(s.nextMilestone)} |`,
175
+ );
176
+ }
177
+ lines.push('');
178
+
179
+ // Per-repo detail
180
+ lines.push('## Per-Repo Detail');
181
+ lines.push('');
182
+
183
+ for (const repo of sortedRepos) {
184
+ const s = summaries.get(repo);
185
+ lines.push(`### ${repo}`);
186
+ lines.push('');
187
+ lines.push(`- Last activity: ${fmtActivity(s.lastActivity)}`);
188
+ lines.push(`- Next milestone: ${fmtMilestone(s.nextMilestone)}`);
189
+
190
+ if (s.topThree.length > 0) {
191
+ lines.push('- Top 3 open issues:');
192
+ for (const issue of s.topThree) {
193
+ const labelStr = issue.labels.length > 0 ? `labels: ${issue.labels.join(', ')}` : 'no labels';
194
+ lines.push(` - #${issue.iid} ${issue.title} (${labelStr}) — ${issue.url}`);
195
+ }
196
+ } else {
197
+ lines.push('- No open issues.');
198
+ }
199
+ lines.push('');
200
+ }
201
+
202
+ return lines.join('\n');
203
+ }
204
+
205
+ // ── Write (idempotent) ─────────────────────────────────────────────────────────
206
+
207
+ /**
208
+ * Normalize a markdown string by replacing the `updated:` frontmatter line
209
+ * with a stable placeholder, enabling byte-for-byte noop comparison.
210
+ *
211
+ * @param {string} content
212
+ * @returns {string}
213
+ */
214
+ function normalizeUpdated(content) {
215
+ // Replace `updated: <anything>` in the frontmatter block with the placeholder.
216
+ // Only replace within the first frontmatter section (between the first --- pair).
217
+ return content.replace(
218
+ /^(updated:\s*)(.+)$/m,
219
+ `$1${UPDATED_PLACEHOLDER}`,
220
+ );
221
+ }
222
+
223
+ /**
224
+ * Write the portfolio file with idempotency guards.
225
+ *
226
+ * @param {{
227
+ * outputPath: string,
228
+ * content: string,
229
+ * now: Date,
230
+ * dryRun?: boolean,
231
+ * fs?: { readFileSync?: Function, writeFileSync?: Function, mkdirSync?: Function, existsSync?: Function },
232
+ * }} opts
233
+ * @returns {{ action: 'written'|'skipped-handwritten'|'skipped-noop'|'dry-run', path: string }}
234
+ */
235
+ export function writePortfolio(opts) {
236
+ const {
237
+ outputPath,
238
+ content,
239
+ dryRun = false,
240
+ fs: injectedFs,
241
+ } = opts;
242
+
243
+ // Resolve fs functions (injectable for tests)
244
+ const fsReadFile = injectedFs?.readFileSync ?? readFileSync;
245
+ const fsWriteFile = injectedFs?.writeFileSync ?? writeFileSync;
246
+ const fsMkdir = injectedFs?.mkdirSync ?? mkdirSync;
247
+ const fsExists = injectedFs?.existsSync ?? existsSync;
248
+
249
+ // Dry-run: never write
250
+ if (dryRun) {
251
+ return { action: 'dry-run', path: outputPath };
252
+ }
253
+
254
+ if (fsExists(outputPath)) {
255
+ let existingContent;
256
+ try {
257
+ existingContent = fsReadFile(outputPath, 'utf8');
258
+ } catch {
259
+ // Cannot read → treat as non-existent, write fresh
260
+ existingContent = null;
261
+ }
262
+
263
+ if (existingContent !== null) {
264
+ const fm = parseFrontmatter(existingContent);
265
+
266
+ // Skip-on-manual-edit: no _generator or different generator
267
+ if (!fm || !fm['_generator']) {
268
+ return { action: 'skipped-handwritten', path: outputPath };
269
+ }
270
+ if (fm['_generator'] !== GENERATOR_MARKER) {
271
+ return { action: 'skipped-handwritten', path: outputPath };
272
+ }
273
+
274
+ // Skip-noop: compare content modulo `updated:` timestamp
275
+ const existingNormalized = normalizeUpdated(existingContent);
276
+ const newNormalized = normalizeUpdated(content);
277
+ if (existingNormalized === newNormalized) {
278
+ return { action: 'skipped-noop', path: outputPath };
279
+ }
280
+ }
281
+ }
282
+
283
+ // Write
284
+ const targetDir = dirname(outputPath);
285
+ fsMkdir(targetDir, { recursive: true });
286
+ fsWriteFile(outputPath, content, 'utf8');
287
+
288
+ return { action: 'written', path: outputPath };
289
+ }
@@ -0,0 +1,182 @@
1
+ /**
2
+ * vcs-detect.mjs — VCS auto-detection helpers for the gitlab-portfolio skill.
3
+ *
4
+ * Exports:
5
+ * discoverVaultRepos — scan vault 01-projects/ for registered repos
6
+ * detectVcsForRepo — resolve 'gitlab' | 'github' | null for a single spec
7
+ *
8
+ * Used by cli.mjs as the discovery step before aggregation.
9
+ */
10
+
11
+ import os from 'node:os';
12
+ import fsP from 'node:fs/promises';
13
+ import path from 'node:path';
14
+
15
+ import { parseFrontmatter as _parseFrontmatter } from '../vault-mirror/utils.mjs';
16
+
17
+ // ── detectVcsForRepo ──────────────────────────────────────────────────────────
18
+
19
+ /**
20
+ * Resolve the VCS platform for a single repo spec.
21
+ *
22
+ * Priority:
23
+ * 1. Explicit `frontmatter.gitlab` field present → 'gitlab'
24
+ * 2. Explicit `frontmatter.github` field present → 'github'
25
+ * 3. `repo` matches /(?:^|[@/])gitlab\./ or starts with 'gitlab/' → 'gitlab'
26
+ * 4. `repo` contains 'github.com' or starts with 'github/' → 'github'
27
+ * 5. `repo` matches host-less <org>/<repo> shorthand /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/ → 'github' (last resort)
28
+ * 6. Otherwise → null (caller decides)
29
+ *
30
+ * @param {{ frontmatter?: Record<string, string>, repo?: string }} input
31
+ * @returns {'gitlab'|'github'|null}
32
+ */
33
+ export function detectVcsForRepo(input) {
34
+ const { frontmatter, repo } = input ?? {};
35
+
36
+ // 1. Frontmatter: explicit gitlab field
37
+ if (frontmatter && frontmatter['gitlab']) {
38
+ return 'gitlab';
39
+ }
40
+
41
+ // 2. Frontmatter: explicit github field
42
+ if (frontmatter && frontmatter['github']) {
43
+ return 'github';
44
+ }
45
+
46
+ if (repo && typeof repo === 'string') {
47
+ // 3. Host-pattern: any GitLab instance (hostname starts with 'gitlab.') or group prefix
48
+ if (
49
+ /(?:^|[@/])gitlab\./.test(repo) ||
50
+ repo.startsWith('gitlab/')
51
+ ) {
52
+ return 'gitlab';
53
+ }
54
+
55
+ // 4. Host-pattern: GitHub
56
+ if (repo.includes('github.com') || repo.startsWith('github/')) {
57
+ return 'github';
58
+ }
59
+
60
+ // Shorthand <org>/<repo> with no host marker: assume GitHub (common convention).
61
+ // We only do this as a last resort — explicit frontmatter fields are preferred.
62
+ if (/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(repo)) {
63
+ return 'github';
64
+ }
65
+ }
66
+
67
+ return null;
68
+ }
69
+
70
+ // ── discoverVaultRepos ────────────────────────────────────────────────────────
71
+
72
+ /**
73
+ * Discover registered repos from a vault directory.
74
+ *
75
+ * Scans `<vaultDir>/01-projects/<slug>/_overview.md` frontmatter for `gitlab:`
76
+ * and/or `github:` keys. If BOTH are present, `gitlab` wins (it is the primary
77
+ * VCS; `github` is typically a mirror).
78
+ *
79
+ * Projects without either field are silently skipped. Missing `_overview.md`
80
+ * files are silently skipped.
81
+ *
82
+ * @param {{
83
+ * vaultDir: string,
84
+ * fs?: { readdir: Function, readFile: Function, stat: Function },
85
+ * parseFrontmatter?: (content: string) => Record<string, string> | null,
86
+ * }} opts
87
+ * @returns {Promise<Array<{
88
+ * slug: string,
89
+ * repo: string,
90
+ * vcs: 'gitlab'|'github',
91
+ * overviewPath: string,
92
+ * }>>}
93
+ */
94
+ export async function discoverVaultRepos(opts) {
95
+ const {
96
+ vaultDir: rawVaultDir,
97
+ fs: injectedFs,
98
+ parseFrontmatter = _parseFrontmatter,
99
+ } = opts;
100
+
101
+ if (!rawVaultDir || typeof rawVaultDir !== 'string') {
102
+ return [];
103
+ }
104
+
105
+ // Expand ~ if present
106
+ const vaultDir = rawVaultDir.startsWith('~')
107
+ ? path.join(os.homedir(), rawVaultDir.slice(1))
108
+ : rawVaultDir;
109
+
110
+ // Resolve fs functions (injectable for tests)
111
+ const fsReaddir = injectedFs?.readdir ?? fsP.readdir;
112
+ const fsReadFile = injectedFs?.readFile ?? fsP.readFile;
113
+ const fsStat = injectedFs?.stat ?? fsP.stat;
114
+
115
+ const projectsDir = path.join(vaultDir, '01-projects');
116
+
117
+ // Read the 01-projects directory
118
+ let entries;
119
+ try {
120
+ entries = await fsReaddir(projectsDir);
121
+ } catch {
122
+ // 01-projects dir does not exist — return empty
123
+ return [];
124
+ }
125
+
126
+ const results = [];
127
+
128
+ for (const entry of entries) {
129
+ // Skip dotfiles
130
+ if (entry.startsWith('.')) continue;
131
+
132
+ const entryPath = path.join(projectsDir, entry);
133
+
134
+ // Skip non-directories
135
+ let stat;
136
+ try {
137
+ stat = await fsStat(entryPath);
138
+ } catch {
139
+ continue;
140
+ }
141
+ if (!stat.isDirectory()) continue;
142
+
143
+ const slug = entry;
144
+ const overviewPath = path.join(entryPath, '_overview.md');
145
+
146
+ // Read _overview.md — skip if absent
147
+ let content;
148
+ try {
149
+ content = await fsReadFile(overviewPath, 'utf8');
150
+ } catch {
151
+ continue;
152
+ }
153
+
154
+ // Parse frontmatter
155
+ const fm = parseFrontmatter(content);
156
+ if (!fm) continue;
157
+
158
+ const gitlabField = fm['gitlab'];
159
+ const githubField = fm['github'];
160
+
161
+ // Determine VCS and repo identifier.
162
+ // Priority: gitlab > github (gitlab is primary; github is typically a mirror).
163
+ if (gitlabField && typeof gitlabField === 'string' && gitlabField.trim()) {
164
+ results.push({
165
+ slug,
166
+ repo: gitlabField.trim(),
167
+ vcs: 'gitlab',
168
+ overviewPath,
169
+ });
170
+ } else if (githubField && typeof githubField === 'string' && githubField.trim()) {
171
+ results.push({
172
+ slug,
173
+ repo: githubField.trim(),
174
+ vcs: 'github',
175
+ overviewPath,
176
+ });
177
+ }
178
+ // Neither field present → skip silently
179
+ }
180
+
181
+ return results;
182
+ }
@@ -0,0 +1,222 @@
1
+ /**
2
+ * handover-gate.mjs — deterministic routing helper for the /close Handover
3
+ * Alignment Gate (session-end Phase 1.65).
4
+ *
5
+ * PRD: docs/prd/2026-07-07-close-handover-alignment-gate.md
6
+ * Issue: #769 (Epic #724 — Session-Lifecycle & Close-Friction)
7
+ *
8
+ * ## What this module does
9
+ *
10
+ * The gate is skill-prose-first with a minimal mechanical core (same pattern as
11
+ * memory-proposals / phase-skip.mjs): the LLM coordinator runs the AUQ
12
+ * interaction, and this small pure `.mjs` lib makes the deterministic candidate
13
+ * classification trivially testable. There is NO I/O here — no disk, no clock,
14
+ * no randomness — so every function is a pure, deterministic mapping of input
15
+ * records to a routed shape.
16
+ *
17
+ * ## The routing contract (PRD FA2 + §3.A EARS)
18
+ *
19
+ * Every carryover candidate lands in EXACTLY ONE of `autoCarry` or `ask` — no
20
+ * candidate is ever dropped by classification (the Ubiquitous EARS rule). The
21
+ * coordinator later decides the OB (whether an issue is filed) from these two
22
+ * buckets: `autoCarry` items are listed in the gate summary but are NOT
23
+ * user-deselectable; `ask` items become the Middle-Band multiSelect (preselected
24
+ * = carry).
25
+ *
26
+ * A candidate is routed to `autoCarry` when ANY of:
27
+ * - `priority === 'critical'` or `priority === 'high'`, OR
28
+ * - `bucket === 'spiral-failed'` (the SPIRAL/FAILED safety-net, Phase 1.6), OR
29
+ * - `originIssue === null` (Grill decision: dropping a candidate that has no
30
+ * origin issue would be real forgetting — SKILL.md:853 Critical Rule stays
31
+ * intact).
32
+ *
33
+ * A candidate is routed to `ask` when it has an origin issue AND its priority is
34
+ * `medium` / `low` / `null` AND its bucket is `not-started` / `emergent` /
35
+ * `partially-done` (i.e. not high-prio, not spiral-failed).
36
+ *
37
+ * `malformed` records (missing/empty task text) are routed to `ask` with a
38
+ * `malformed: true` flag — the helper NEVER throws on them, and the malformed
39
+ * branch takes precedence over every autoCarry condition (a record with no task
40
+ * text cannot be meaningfully auto-filed, so the operator decides).
41
+ *
42
+ * Empty / non-array input yields `{ autoCarry: [], ask: [] }` — never throws.
43
+ *
44
+ * @typedef {Object} NormalizedCandidate
45
+ * @property {string} task - task text (required; if absent/empty → malformed:true).
46
+ * @property {string} sourcePhase - '1.2' | '1.3' | '1.4' | '1.6' (best-effort string).
47
+ * @property {number|null} originIssue - issue IID, or null when there is no origin issue.
48
+ * @property {'critical'|'high'|'medium'|'low'|null} priority
49
+ * @property {'partially-done'|'not-started'|'emergent'|'spiral-failed'} bucket
50
+ * @property {boolean} [malformed] - true when task text is missing/empty.
51
+ *
52
+ * @typedef {Object} RoutingResult
53
+ * @property {NormalizedCandidate[]} autoCarry - not user-deselectable; gate-summary only.
54
+ * @property {NormalizedCandidate[]} ask - Middle-Band multiSelect (preselected = carry).
55
+ */
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Coercion tables
59
+ // ---------------------------------------------------------------------------
60
+
61
+ /** Valid carryover buckets (PRD §3.A). */
62
+ const VALID_BUCKETS = new Set(['partially-done', 'not-started', 'emergent', 'spiral-failed']);
63
+
64
+ /** Valid priorities (GitLab priority labels, lowercased, label-prefix stripped). */
65
+ const VALID_PRIORITIES = new Set(['critical', 'high', 'medium', 'low']);
66
+
67
+ /**
68
+ * Source-phase → bucket inference used when a candidate omits an explicit bucket
69
+ * (PRD §2 In-Scope: 1.2 Partially Done, 1.3 Not Started, 1.4 unfinished Emergent,
70
+ * 1.6 SPIRAL/FAILED walk).
71
+ */
72
+ const PHASE_BUCKET = {
73
+ '1.2': 'partially-done',
74
+ '1.3': 'not-started',
75
+ '1.4': 'emergent',
76
+ '1.6': 'spiral-failed',
77
+ };
78
+
79
+ /** Neutral fallback bucket when neither an explicit bucket nor a known phase maps. */
80
+ const DEFAULT_BUCKET = 'not-started';
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Field coercers (each pure, each defensive)
84
+ // ---------------------------------------------------------------------------
85
+
86
+ /**
87
+ * Coerce an origin-issue field. Numeric string ("769") → number; an integer
88
+ * number passes through; everything else (missing, null, fractional
89
+ * number/string, non-numeric) → null. Issue IIDs are always integers, so a
90
+ * fractional number is coerced identically to a fractional numeric string —
91
+ * both are malformed input and fall through to `null` (routing the candidate
92
+ * to autoCarry via the no-origin rule, the safe direction).
93
+ *
94
+ * @param {unknown} v
95
+ * @returns {number|null}
96
+ */
97
+ function coerceOriginIssue(v) {
98
+ if (typeof v === 'number') return Number.isInteger(v) ? v : null;
99
+ if (typeof v === 'string') {
100
+ const t = v.trim();
101
+ return /^-?\d+$/.test(t) ? Number(t) : null;
102
+ }
103
+ return null;
104
+ }
105
+
106
+ /**
107
+ * Coerce a priority field: lowercase, trim, strip a leading `priority:` label,
108
+ * validate against the closed enum. Anything invalid → null.
109
+ *
110
+ * @param {unknown} v
111
+ * @returns {'critical'|'high'|'medium'|'low'|null}
112
+ */
113
+ function coercePriority(v) {
114
+ if (v === null || v === undefined) return null;
115
+ let s = String(v).trim().toLowerCase();
116
+ if (s.startsWith('priority:')) s = s.slice('priority:'.length).trim();
117
+ return VALID_PRIORITIES.has(s) ? /** @type {any} */ (s) : null;
118
+ }
119
+
120
+ /**
121
+ * Coerce the source-phase field to a best-effort trimmed string ('' when absent).
122
+ *
123
+ * @param {unknown} v
124
+ * @returns {string}
125
+ */
126
+ function coerceSourcePhase(v) {
127
+ if (v === null || v === undefined) return '';
128
+ return String(v).trim();
129
+ }
130
+
131
+ /**
132
+ * Resolve the bucket: an explicit valid bucket wins; otherwise infer from the
133
+ * source phase; otherwise fall back to the neutral default.
134
+ *
135
+ * @param {unknown} rawBucket
136
+ * @param {string} sourcePhase
137
+ * @returns {'partially-done'|'not-started'|'emergent'|'spiral-failed'}
138
+ */
139
+ function coerceBucket(rawBucket, sourcePhase) {
140
+ if (rawBucket !== null && rawBucket !== undefined) {
141
+ const b = String(rawBucket).trim().toLowerCase();
142
+ if (VALID_BUCKETS.has(b)) return /** @type {any} */ (b);
143
+ }
144
+ return /** @type {any} */ (PHASE_BUCKET[sourcePhase] ?? DEFAULT_BUCKET);
145
+ }
146
+
147
+ // ---------------------------------------------------------------------------
148
+ // Public API
149
+ // ---------------------------------------------------------------------------
150
+
151
+ /**
152
+ * Normalize a raw carryover-candidate record into a {@link NormalizedCandidate}.
153
+ *
154
+ * Accepts any subset of `{ task, sourcePhase, originIssue, priority, bucket }`
155
+ * (strings or numbers) and coerces each field. Missing/empty `task` yields
156
+ * `task: ''` plus `malformed: true` (never throws). Pure and deterministic —
157
+ * no `Date.now()` / `Math.random()`.
158
+ *
159
+ * @param {unknown} raw
160
+ * @returns {NormalizedCandidate}
161
+ */
162
+ export function normalizeCandidate(raw) {
163
+ const rec = raw && typeof raw === 'object' ? /** @type {Record<string, unknown>} */ (raw) : {};
164
+
165
+ const sourcePhase = coerceSourcePhase(rec.sourcePhase);
166
+ const originIssue = coerceOriginIssue(rec.originIssue);
167
+ const priority = coercePriority(rec.priority);
168
+ const bucket = coerceBucket(rec.bucket, sourcePhase);
169
+
170
+ const hasTask = typeof rec.task === 'string' && rec.task.trim().length > 0;
171
+
172
+ /** @type {NormalizedCandidate} */
173
+ const normalized = {
174
+ task: hasTask ? /** @type {string} */ (rec.task) : '',
175
+ sourcePhase,
176
+ originIssue,
177
+ priority,
178
+ bucket,
179
+ };
180
+ if (!hasTask) normalized.malformed = true;
181
+
182
+ return normalized;
183
+ }
184
+
185
+ /**
186
+ * Route a list of raw carryover candidates into `{ autoCarry, ask }`.
187
+ *
188
+ * Each candidate is first normalized via {@link normalizeCandidate}, then
189
+ * classified per the module routing contract. Every candidate lands in exactly
190
+ * one bucket; nothing is dropped. Empty or non-array input → `{ autoCarry: [],
191
+ * ask: [] }`. Never throws.
192
+ *
193
+ * @param {unknown} candidates
194
+ * @returns {RoutingResult}
195
+ */
196
+ export function routeCandidates(candidates) {
197
+ /** @type {RoutingResult} */
198
+ const result = { autoCarry: [], ask: [] };
199
+ if (!Array.isArray(candidates) || candidates.length === 0) return result;
200
+
201
+ for (const raw of candidates) {
202
+ const c = normalizeCandidate(raw);
203
+
204
+ // malformed takes precedence over every autoCarry condition — a record with
205
+ // no task text cannot be meaningfully auto-filed, so the operator decides.
206
+ if (c.malformed) {
207
+ result.ask.push(c);
208
+ continue;
209
+ }
210
+
211
+ const isAutoCarry =
212
+ c.priority === 'critical' ||
213
+ c.priority === 'high' ||
214
+ c.bucket === 'spiral-failed' ||
215
+ c.originIssue === null;
216
+
217
+ if (isAutoCarry) result.autoCarry.push(c);
218
+ else result.ask.push(c);
219
+ }
220
+
221
+ return result;
222
+ }