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,441 @@
1
+ /**
2
+ * memory-proposals/store.mjs — atomic JSONL append + per-wave quota counter
3
+ * + per-wave summary writer for memory proposal records.
4
+ *
5
+ * Design:
6
+ * - Lock file: `.orchestrator/metrics/proposals-write.lock`
7
+ * - Lock idiom: tmp-file + linkSync(tmp, lock) — mirrors session-lock.mjs
8
+ * `createStateLockExclusive` pattern (POSIX-atomic create-or-fail).
9
+ * - Proposals JSONL: `.orchestrator/metrics/proposals.jsonl` (O_APPEND)
10
+ * - Per-wave summary: `.orchestrator/metrics/proposals-summary-<wave-id>.json`
11
+ * - Confidence floor check: BEFORE lock acquisition (no I/O, no lock held).
12
+ * - Quota check: INSIDE lock (count + decide + write serialized).
13
+ *
14
+ * No external dependencies — Node 20+ stdlib only.
15
+ */
16
+
17
+ import fs from 'node:fs';
18
+ import path from 'node:path';
19
+ import crypto from 'node:crypto';
20
+ import { serializeProposal } from './schema.mjs';
21
+ import { validatePathInsideProject } from '../path-utils.mjs';
22
+ import { tryAcquireFileLock, releaseFileLock } from '../file-lock.mjs';
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Constants
26
+ // ---------------------------------------------------------------------------
27
+
28
+ const PROPOSALS_JSONL = '.orchestrator/metrics/proposals.jsonl';
29
+ const PROPOSALS_LOCK = '.orchestrator/metrics/proposals-write.lock';
30
+ const PROPOSALS_SUMMARY_PREFIX = '.orchestrator/metrics/proposals-summary-';
31
+
32
+ const DEFAULT_QUOTA_PER_WAVE = 5;
33
+ const DEFAULT_CONFIDENCE_FLOOR = 0.5;
34
+ const DEFAULT_LOCK_TIMEOUT_MS = 1000;
35
+ const LOCK_POLL_MS = 50;
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Internal helpers
39
+ // ---------------------------------------------------------------------------
40
+
41
+ /**
42
+ * Resolve a metrics-relative path against repoRoot, with path-traversal guard.
43
+ * Throws a TypeError on traversal attempts — callers have already validated
44
+ * repoRoot is a trusted absolute path.
45
+ *
46
+ * Delegates to validatePathInsideProject (#544 M3 — canonical two-phase guard)
47
+ * with canonicalizeRoot:true so macOS tmpdir paths (where /var resolves to
48
+ * /private/var) are handled consistently in tests and production.
49
+ *
50
+ * Trust boundary (#554 A4, low severity): On block, the TypeError surfaces
51
+ * the resolved absolute path (`${resolved}`). This path-info disclosure is
52
+ * acceptable per the project's local-trust model — only reachable via an
53
+ * attacker-controlled `relPath`, surfaced only to local stderr and the
54
+ * memory-propose audit log (operator-readable). NOT for environments where
55
+ * filesystem layout is sensitive.
56
+ *
57
+ * @param {string} repoRoot — absolute path to the repo working directory
58
+ * @param {string} relPath — path relative to repoRoot (must not escape it)
59
+ * @returns {string} resolved absolute path (realPath when the target exists, else lexicalPath)
60
+ */
61
+ function safePath(repoRoot, relPath) {
62
+ const result = validatePathInsideProject(relPath, repoRoot, { canonicalizeRoot: true });
63
+ if (!result.ok) {
64
+ const resolved = path.resolve(repoRoot, relPath);
65
+ // #548 A5 — surface result.reason ('lexical' | 'symlink' | 'input') in the
66
+ // error so operators can distinguish path-traversal classes at a glance:
67
+ // - 'lexical' → relPath escaped repoRoot via ../../ before symlink resolution
68
+ // - 'symlink' → relPath resolved through a symlink that points outside repoRoot
69
+ // - 'input' → relPath was malformed (empty, non-string, null byte)
70
+ throw new TypeError(
71
+ `store.mjs: path traversal blocked: ${resolved} is outside ${repoRoot} (reason: ${result.reason})`
72
+ );
73
+ }
74
+ return result.realPath ?? result.lexicalPath;
75
+ }
76
+
77
+ /**
78
+ * Ensure the parent directory of `filePath` exists.
79
+ * @param {string} filePath
80
+ */
81
+ function ensureDir(filePath) {
82
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
83
+ }
84
+
85
+ /**
86
+ * Build the absolute path to the lock file.
87
+ * @param {string} repoRoot
88
+ * @returns {string}
89
+ */
90
+ function lockPathFor(repoRoot) {
91
+ return safePath(repoRoot, PROPOSALS_LOCK);
92
+ }
93
+
94
+ /**
95
+ * Build the absolute path to the JSONL proposals file.
96
+ * @param {string} repoRoot
97
+ * @returns {string}
98
+ */
99
+ function jsonlPathFor(repoRoot) {
100
+ return safePath(repoRoot, PROPOSALS_JSONL);
101
+ }
102
+
103
+ /**
104
+ * Build the absolute path to the per-wave summary JSON file.
105
+ * wave_id values are restricted to alphanumerics + hyphens/underscores to
106
+ * prevent filename injection — any other character causes a throw.
107
+ *
108
+ * @param {string} repoRoot
109
+ * @param {string} waveId
110
+ * @returns {string}
111
+ */
112
+ function summaryPathFor(repoRoot, waveId) {
113
+ if (!/^[A-Za-z0-9_-]+$/.test(waveId)) {
114
+ throw new TypeError(`store.mjs: waveId contains invalid characters: ${waveId}`);
115
+ }
116
+ return safePath(repoRoot, `${PROPOSALS_SUMMARY_PREFIX}${waveId}.json`);
117
+ }
118
+
119
+ /**
120
+ * Acquire the proposals write-lock via spin-poll.
121
+ * Returns when the lock is acquired, or after timeoutMs.
122
+ *
123
+ * Delegates to the shared file-lock primitive (issue #630). Behavior is
124
+ * preserved EXACTLY:
125
+ *
126
+ * - Compact body `{pid, host, acquiredAt}` (indent:null on create).
127
+ * - 3-state distinction via `signalVanished:true` — the ENOENT-on-read race
128
+ * surfaces as `{ acquired: false, reason: 'vanished' }`, and this loop
129
+ * defers WITHOUT any override side-effect. CRITICAL: never override on
130
+ * `vanished` — a concurrent acquirer may have already taken the freshly
131
+ * vacant lock; overriding would create a double-holder race (regression of
132
+ * the C9 8-worker test). This was the reason the memory-proposals copy
133
+ * surfaced `vanished` as a distinct third reason rather than collapsing it
134
+ * into `held` (#548 A1).
135
+ * - PID staleCheck: same-host + dead PID → atomic override + WARN to stderr.
136
+ * - WARN channel: `process.stderr.write` with the original messages.
137
+ * - Override tmp prefix `.proposals-write.lock.tmp`, compact override body
138
+ * (overrideIndent:null) — preserves the original `replaceLockAtomic` format.
139
+ *
140
+ * #548 A5 — TOCTOU acknowledgment (now centralized in file-lock.mjs's
141
+ * isExistingStale → isPidAliveOnHost): a sub-millisecond race exists between
142
+ * the PID-liveness check and the override write. The window is two sync
143
+ * syscalls; the consequence is bounded (override a stale lock nobody used).
144
+ * Accepted, not eliminated. See file-lock.mjs `isPidAliveOnHost` remarks.
145
+ *
146
+ * @param {string} lockFile
147
+ * @param {number} timeoutMs
148
+ * @returns {Promise<{ ok: true } | { ok: false, reason: 'timeout' | 'fs-error', error?: string }>}
149
+ */
150
+ async function acquireProposalsLock(lockFile, timeoutMs) {
151
+ const deadline = Date.now() + timeoutMs;
152
+
153
+ for (;;) {
154
+ const result = tryAcquireFileLock(lockFile, {
155
+ staleCheck: 'pid',
156
+ signalVanished: true,
157
+ indent: null,
158
+ overrideIndent: null,
159
+ tmpPrefix: '.proposals-write.lock.tmp',
160
+ warn: (msg) => process.stderr.write(msg),
161
+ warnMessage: (reason, _lp, existing) =>
162
+ existing === null
163
+ ? '⚠ memory-proposals lock: stale lock detected (unparseable body) — reclaiming\n'
164
+ : `⚠ memory-proposals lock: stale lock detected (pid=${existing.pid}, host=${existing.host}) — reclaiming\n`,
165
+ });
166
+
167
+ if (result.acquired) return { ok: true };
168
+ if (result.reason === 'fs-error') return { ok: false, reason: 'fs-error', error: result.error };
169
+
170
+ // reason === 'vanished' (concurrent release race) OR 'held' (live holder /
171
+ // cross-host). Both defer to the next poll iteration without overriding.
172
+ if (Date.now() >= deadline) {
173
+ return { ok: false, reason: 'timeout', error: 'lock-timeout' };
174
+ }
175
+ await new Promise((resolve) => setTimeout(resolve, LOCK_POLL_MS));
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Release (unlink) the proposals write-lock. Idempotent — ENOENT is ignored.
181
+ *
182
+ * Delegates to releaseFileLock with `ownerGuard:false` — reproducing the
183
+ * original unconditional unlink. PRESERVE this: the memory-proposals release is
184
+ * intentionally NOT owner-guarded (it always runs inside the same process that
185
+ * acquired, immediately after the JSONL append, so the on-disk lock is always
186
+ * ours). Non-ENOENT fs errors are logged to stderr, never re-thrown.
187
+ *
188
+ * @param {string} lockFile
189
+ */
190
+ function releaseProposalsLock(lockFile) {
191
+ releaseFileLock(lockFile, {
192
+ ownerGuard: false,
193
+ warn: (errToken) => process.stderr.write(`store.mjs: lock release failed (${errToken})\n`),
194
+ });
195
+ }
196
+
197
+ /**
198
+ * Count lines in a JSONL file where `wave_id === waveId`.
199
+ * Returns 0 if the file does not exist.
200
+ *
201
+ * Complexity: O(lines) — acceptable for quota ≤ 5 in a session.
202
+ *
203
+ * @param {string} jsonlPath
204
+ * @param {string} waveId
205
+ * @returns {number}
206
+ */
207
+ function countWaveLines(jsonlPath, waveId) {
208
+ let raw;
209
+ try {
210
+ raw = fs.readFileSync(jsonlPath, 'utf8');
211
+ } catch (err) {
212
+ if (err.code === 'ENOENT') return 0;
213
+ throw err;
214
+ }
215
+
216
+ let count = 0;
217
+ for (const line of raw.split('\n')) {
218
+ if (line.trim().length === 0) continue;
219
+ try {
220
+ const obj = JSON.parse(line);
221
+ if (obj && obj.wave_id === waveId) count++;
222
+ } catch {
223
+ /* skip malformed lines */
224
+ }
225
+ }
226
+ return count;
227
+ }
228
+
229
+ /**
230
+ * Read the per-wave summary JSON, or return a zeroed summary if absent.
231
+ * @param {string} summaryPath
232
+ * @returns {{ queued: number, dropped: number, below_floor: number, fs_error: number }}
233
+ */
234
+ function readSummary(summaryPath) {
235
+ try {
236
+ const raw = fs.readFileSync(summaryPath, 'utf8');
237
+ const parsed = JSON.parse(raw);
238
+ if (parsed && typeof parsed === 'object') {
239
+ return {
240
+ queued: parsed.queued ?? 0,
241
+ dropped: parsed.dropped ?? 0,
242
+ below_floor: parsed.below_floor ?? 0,
243
+ fs_error: parsed.fs_error ?? 0,
244
+ };
245
+ }
246
+ } catch {
247
+ /* ENOENT or malformed — return zeros */
248
+ }
249
+ return { queued: 0, dropped: 0, below_floor: 0, fs_error: 0 };
250
+ }
251
+
252
+ /**
253
+ * Atomically increment one field in the per-wave summary JSON.
254
+ * Uses a tmp + rename to avoid partial-write races.
255
+ * Failures are silently swallowed — the summary is diagnostic, not critical.
256
+ *
257
+ * @param {string} summaryPath
258
+ * @param {keyof ReturnType<typeof readSummary>} field
259
+ */
260
+ function incrementSummary(summaryPath, field) {
261
+ try {
262
+ ensureDir(summaryPath);
263
+ const current = readSummary(summaryPath);
264
+ current[field] = (current[field] ?? 0) + 1;
265
+ const tmpSuffix = crypto.randomBytes(4).toString('hex');
266
+ const tmpFile = `${summaryPath}.tmp.${tmpSuffix}`;
267
+ fs.writeFileSync(tmpFile, JSON.stringify(current, null, 2) + '\n', 'utf8');
268
+ fs.renameSync(tmpFile, summaryPath);
269
+ } catch {
270
+ /* best-effort: summary writes are non-critical */
271
+ }
272
+ }
273
+
274
+ // ---------------------------------------------------------------------------
275
+ // Exported API
276
+ // ---------------------------------------------------------------------------
277
+
278
+ /**
279
+ * Append a validated ProposalRecord to the proposals JSONL, subject to
280
+ * per-wave quota and confidence floor enforcement.
281
+ *
282
+ * Flow:
283
+ * 1. Confidence check BEFORE lock (no I/O needed).
284
+ * 2. Acquire lock (spin-poll 50ms, timeout 1000ms default).
285
+ * 3. Inside lock: count + decide + append.
286
+ * 4. Release lock in finally.
287
+ *
288
+ * @param {object} opts
289
+ * @param {object} opts.record — validated ProposalRecord (from schema.mjs)
290
+ * @param {string} opts.repoRoot — absolute path to repo working directory
291
+ * @param {string} opts.waveId — e.g. 'W2'; must match record.wave_id
292
+ * @param {number} [opts.quotaPerWave=5]
293
+ * @param {number} [opts.confidenceFloor=0.5]
294
+ * @param {number} [opts.lockTimeoutMs=1000]
295
+ * @returns {Promise<{
296
+ * status: 'queued' | 'quota-exceeded' | 'below-floor' | 'fs-error',
297
+ * position?: string,
298
+ * quota?: number,
299
+ * dropped?: number,
300
+ * error?: string
301
+ * }>}
302
+ */
303
+ export async function appendProposal({
304
+ record,
305
+ repoRoot,
306
+ waveId,
307
+ quotaPerWave = DEFAULT_QUOTA_PER_WAVE,
308
+ confidenceFloor = DEFAULT_CONFIDENCE_FLOOR,
309
+ lockTimeoutMs = DEFAULT_LOCK_TIMEOUT_MS,
310
+ }) {
311
+ const summaryPath = summaryPathFor(repoRoot, waveId);
312
+
313
+ // ------------------------------------------------------------------
314
+ // Step 1: Confidence floor — before lock, no I/O needed.
315
+ // ------------------------------------------------------------------
316
+ if (
317
+ typeof record.confidence !== 'number' ||
318
+ record.confidence < confidenceFloor
319
+ ) {
320
+ incrementSummary(summaryPath, 'below_floor');
321
+ return { status: 'below-floor' };
322
+ }
323
+
324
+ const lockFile = lockPathFor(repoRoot);
325
+ const jsonlPath = jsonlPathFor(repoRoot);
326
+
327
+ // ------------------------------------------------------------------
328
+ // Step 2: Acquire lock. Early return if acquire fails (no lock held → no
329
+ // release needed). After this point any path that reaches the finally
330
+ // below holds the lock and must release it.
331
+ // ------------------------------------------------------------------
332
+ const lockResult = await acquireProposalsLock(lockFile, lockTimeoutMs);
333
+ if (!lockResult.ok) {
334
+ incrementSummary(summaryPath, 'fs_error');
335
+ return { status: 'fs-error', error: lockResult.error ?? lockResult.reason };
336
+ }
337
+
338
+ try {
339
+ // ------------------------------------------------------------------
340
+ // Step 3a: Count current proposals for this wave.
341
+ // ------------------------------------------------------------------
342
+ let currentCount;
343
+ try {
344
+ currentCount = countWaveLines(jsonlPath, waveId);
345
+ } catch (err) {
346
+ incrementSummary(summaryPath, 'fs_error');
347
+ return { status: 'fs-error', error: err.message };
348
+ }
349
+
350
+ // ------------------------------------------------------------------
351
+ // Step 3b: Quota check.
352
+ // ------------------------------------------------------------------
353
+ if (currentCount >= quotaPerWave) {
354
+ const summary = readSummary(summaryPath);
355
+ const droppedSoFar = summary.dropped;
356
+ incrementSummary(summaryPath, 'dropped');
357
+ return {
358
+ status: 'quota-exceeded',
359
+ quota: quotaPerWave,
360
+ dropped: droppedSoFar + 1,
361
+ };
362
+ }
363
+
364
+ // ------------------------------------------------------------------
365
+ // Step 3c: Append to JSONL (O_APPEND via appendFileSync).
366
+ // ------------------------------------------------------------------
367
+ try {
368
+ ensureDir(jsonlPath);
369
+ fs.appendFileSync(jsonlPath, serializeProposal(record) + '\n', 'utf8');
370
+ } catch (err) {
371
+ incrementSummary(summaryPath, 'fs_error');
372
+ return { status: 'fs-error', error: err.message };
373
+ }
374
+
375
+ incrementSummary(summaryPath, 'queued');
376
+ const position = `${currentCount + 1}/${quotaPerWave}`;
377
+ return { status: 'queued', position };
378
+ } finally {
379
+ // ------------------------------------------------------------------
380
+ // Step 4: Always release lock (idempotent unlink). Reaching this finally
381
+ // implies the lock was acquired above — early-return covers the
382
+ // non-acquired branch.
383
+ // ------------------------------------------------------------------
384
+ releaseProposalsLock(lockFile);
385
+ }
386
+ }
387
+
388
+ /**
389
+ * Count the number of proposals recorded for a given wave_id.
390
+ * Reads proposals.jsonl without acquiring the lock — suitable for diagnostics
391
+ * and reads where eventual consistency is acceptable.
392
+ *
393
+ * Returns 0 when the file does not exist.
394
+ *
395
+ * @param {object} opts
396
+ * @param {string} opts.repoRoot
397
+ * @param {string} opts.waveId
398
+ * @returns {Promise<number>}
399
+ */
400
+ export async function countProposalsForWave({ repoRoot, waveId }) {
401
+ const jsonlPath = jsonlPathFor(repoRoot);
402
+ try {
403
+ return countWaveLines(jsonlPath, waveId);
404
+ } catch {
405
+ return 0;
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Read the per-wave summary for diagnostic purposes.
411
+ * Returns null when the summary file does not exist.
412
+ *
413
+ * @param {object} opts
414
+ * @param {string} opts.repoRoot
415
+ * @param {string} opts.waveId
416
+ * @returns {Promise<{
417
+ * queued: number,
418
+ * dropped: number,
419
+ * below_floor: number,
420
+ * fs_error: number
421
+ * } | null>}
422
+ */
423
+ export async function readWaveSummary({ repoRoot, waveId }) {
424
+ const summaryPath = summaryPathFor(repoRoot, waveId);
425
+ try {
426
+ const raw = fs.readFileSync(summaryPath, 'utf8');
427
+ const parsed = JSON.parse(raw);
428
+ if (parsed && typeof parsed === 'object') {
429
+ return {
430
+ queued: parsed.queued ?? 0,
431
+ dropped: parsed.dropped ?? 0,
432
+ below_floor: parsed.below_floor ?? 0,
433
+ fs_error: parsed.fs_error ?? 0,
434
+ };
435
+ }
436
+ return null;
437
+ } catch (err) {
438
+ if (err.code === 'ENOENT') return null;
439
+ return null;
440
+ }
441
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Mission-Status Enum + Transition Validator (issue #340).
3
+ *
4
+ * Pure ESM, no I/O, no external dependencies. All functions are deterministic.
5
+ *
6
+ * Five-state lifecycle for wave-plan items:
7
+ *
8
+ * brainstormed → validated → in-dev → testing → completed
9
+ *
10
+ * Rollbacks to `brainstormed` are permitted from any state (user can reset scope).
11
+ * Idempotent self-transitions are always allowed.
12
+ * All other transitions are rejected.
13
+ */
14
+
15
+ /**
16
+ * Canonical ordered list of valid mission-status values.
17
+ *
18
+ * @type {readonly string[]}
19
+ */
20
+ export const MISSION_STATUS_VALUES = Object.freeze([
21
+ 'brainstormed',
22
+ 'validated',
23
+ 'in-dev',
24
+ 'testing',
25
+ 'completed',
26
+ ]);
27
+
28
+ /**
29
+ * Allowed forward transitions (excluding self-transitions and rollback to
30
+ * 'brainstormed', which are handled separately in isValidMissionStatusTransition).
31
+ *
32
+ * @type {ReadonlyMap<string, string>}
33
+ */
34
+ const FORWARD_TRANSITIONS = new Map([
35
+ ['brainstormed', 'validated'],
36
+ ['validated', 'in-dev'],
37
+ ['in-dev', 'testing'],
38
+ ['testing', 'completed'],
39
+ ]);
40
+
41
+ /**
42
+ * Returns true when `value` is one of the five canonical status strings.
43
+ *
44
+ * @param {unknown} value
45
+ * @returns {boolean}
46
+ */
47
+ export function isValidMissionStatus(value) {
48
+ return typeof value === 'string' && MISSION_STATUS_VALUES.includes(value);
49
+ }
50
+
51
+ /**
52
+ * Returns true when the transition from `from` → `to` is permitted.
53
+ *
54
+ * Allowed transitions:
55
+ * - Forward: brainstormed→validated, validated→in-dev, in-dev→testing, testing→completed
56
+ * - Idempotent: any state → itself
57
+ * - Rollback: any state → brainstormed
58
+ *
59
+ * All other transitions return false.
60
+ *
61
+ * @param {string} from - current status value
62
+ * @param {string} to - target status value
63
+ * @returns {boolean}
64
+ */
65
+ export function isValidMissionStatusTransition(from, to) {
66
+ if (!isValidMissionStatus(from) || !isValidMissionStatus(to)) return false;
67
+ // Idempotent self-transition
68
+ if (from === to) return true;
69
+ // Rollback to brainstormed from any state
70
+ if (to === 'brainstormed') return true;
71
+ // Forward transition
72
+ return FORWARD_TRANSITIONS.get(from) === to;
73
+ }
74
+
75
+ /**
76
+ * Validates a mission-status entry object.
77
+ *
78
+ * Expected shape:
79
+ * { id: string, task: string, wave: number, status: <MISSION_STATUS_VALUES> }
80
+ *
81
+ * @param {unknown} obj
82
+ * @returns {{ ok: boolean, errors: string[] }}
83
+ */
84
+ export function validateMissionStatusEntry(obj) {
85
+ const errors = [];
86
+ if (obj === null || typeof obj !== 'object' || Array.isArray(obj)) {
87
+ return { ok: false, errors: ['entry must be a non-null, non-array object'] };
88
+ }
89
+ const entry = /** @type {Record<string, unknown>} */ (obj);
90
+
91
+ // id — non-empty string
92
+ if (typeof entry.id !== 'string' || entry.id.trim() === '') {
93
+ errors.push('id must be a non-empty string');
94
+ }
95
+
96
+ // task — non-empty string
97
+ if (typeof entry.task !== 'string' || entry.task.trim() === '') {
98
+ errors.push('task must be a non-empty string');
99
+ }
100
+
101
+ // wave — integer >= 1
102
+ if (!Number.isInteger(entry.wave) || /** @type {number} */ (entry.wave) < 1) {
103
+ errors.push('wave must be a positive integer');
104
+ }
105
+
106
+ // status — one of MISSION_STATUS_VALUES
107
+ if (!isValidMissionStatus(entry.status)) {
108
+ errors.push(
109
+ `status must be one of [${MISSION_STATUS_VALUES.join(', ')}], got: ${JSON.stringify(entry.status)}`,
110
+ );
111
+ }
112
+
113
+ return { ok: errors.length === 0, errors };
114
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Alternatives-list builder for the mode-selector heuristic (W1A2 split, issue #358).
3
+ *
4
+ * Exports: buildAlternatives, hasActiveSignals.
5
+ * Imports: scoring (for scoreMode + helpers), constants (for ALL_MODES).
6
+ *
7
+ * Pure functions: no I/O, no side effects.
8
+ */
9
+
10
+ import { ALL_MODES } from './constants.mjs';
11
+ import { scoreMode, safeArray, safeBootstrapLock } from './scoring.mjs';
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Public API
15
+ // ---------------------------------------------------------------------------
16
+
17
+ /**
18
+ * Return true if any "active signal" field is non-trivially present.
19
+ * Used to decide whether alternatives should be computed (branch 3).
20
+ *
21
+ * "Non-trivially present" means:
22
+ * - learnings: non-empty array
23
+ * - recentSessions: non-empty array
24
+ * - bootstrapLock: non-null object
25
+ *
26
+ * When no active signals exist, all modes score identically at base 0.5
27
+ * so alternatives carry no information. Return [] to keep the passthrough
28
+ * contract stable (existing tests assert alternatives === [] for bare calls).
29
+ *
30
+ * @param {object} signals
31
+ * @returns {boolean}
32
+ */
33
+ export function hasActiveSignals(signals) {
34
+ const ls = safeArray(signals.learnings);
35
+ const rs = safeArray(signals.recentSessions);
36
+ return ls.length > 0 || rs.length > 0 || safeBootstrapLock(signals) !== null;
37
+ }
38
+
39
+ /**
40
+ * Build the alternatives array for branch 3 (passthrough-weighted).
41
+ * Returns 0–3 entries sorted by confidence descending, each with confidence >= 0.1.
42
+ *
43
+ * When no active signals are present, returns [] (all modes score identically
44
+ * at 0.5, so alternatives carry no information — and this preserves the
45
+ * existing passthrough test contract).
46
+ *
47
+ * @param {string} chosenMode
48
+ * @param {object} signals
49
+ * @returns {Array<{mode: string, confidence: number}>}
50
+ */
51
+ export function buildAlternatives(chosenMode, signals) {
52
+ if (!hasActiveSignals(signals)) {
53
+ return [];
54
+ }
55
+
56
+ const otherModes = ALL_MODES.filter((m) => m !== chosenMode);
57
+ const scored = otherModes
58
+ .map((m) => ({ mode: m, confidence: scoreMode(m, signals) }))
59
+ .filter((e) => e.confidence >= 0.1)
60
+ .sort((a, b) => b.confidence - a.confidence)
61
+ .slice(0, 3);
62
+
63
+ return scored;
64
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Mode-selector constants (W1A2 split, issue #358).
3
+ *
4
+ * Leaf module — no imports from other mode-selector submodules.
5
+ * Pure data: DEFAULT_MODE, ALL_MODES, TIER_MODE_MAP.
6
+ */
7
+
8
+ /** @type {'feature'} */
9
+ export const DEFAULT_MODE = 'feature';
10
+
11
+ /** All valid mode values — mirrors VALID_MODES in recommendations-v0.mjs. */
12
+ export const ALL_MODES = Object.freeze([
13
+ 'housekeeping',
14
+ 'feature',
15
+ 'deep',
16
+ 'discovery',
17
+ 'evolve',
18
+ 'plan-retro',
19
+ ]);
20
+
21
+ /**
22
+ * Bootstrap tier → preferred mode alignment map.
23
+ * @type {Readonly<Record<string, string>>}
24
+ */
25
+ export const TIER_MODE_MAP = Object.freeze({
26
+ fast: 'housekeeping',
27
+ standard: 'feature',
28
+ deep: 'deep',
29
+ });