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,355 @@
1
+ /**
2
+ * session-registry.mjs — Per-session heartbeat files + peer detection.
3
+ *
4
+ * Part of Sub-Epic F (#159) of Epic #157. Backs hooks/on-session-start.mjs
5
+ * peer-detection (F2, #168) and hooks/on-stop.mjs clean deregister + zombie
6
+ * sweep (F3, #169).
7
+ *
8
+ * Registry location: `~/.config/session-orchestrator/sessions/active/<sessionId>.json`
9
+ * Sweep log: `~/.config/session-orchestrator/sessions/sweep.log` (JSONL)
10
+ *
11
+ * Overridable via env var `SO_SESSION_REGISTRY_DIR` (points to the parent
12
+ * `sessions/` directory, not `active/`) — used by tests for isolation.
13
+ *
14
+ * Heartbeat schema (per issue #167):
15
+ * {
16
+ * session_id, pid, platform, repo_path_hash, repo_name, branch,
17
+ * started_at, last_heartbeat, status ('active'|'wave'|'idle'),
18
+ * current_wave, host_class, mode (Epic #583 W2-I3)
19
+ * }
20
+ *
21
+ * Schema v2 (Epic #583, W2-I3): `mode` field added so the exclusivity-matrix
22
+ * can classify cross-repo registry entries correctly. Without it, every
23
+ * registry-sourced peer was bucketed as `mode='session'` → classifyMode threw
24
+ * → fell back to `parallel-ok`, silently bypassing the exclusivity matrix
25
+ * for cross-repo entries (D5 from Epic #583 audit). The field is optional
26
+ * on read for back-compat with v1 entries (defaults to null).
27
+ */
28
+
29
+ import os from 'node:os';
30
+ import path from 'node:path';
31
+ import crypto from 'node:crypto';
32
+ import { promises as fs } from 'node:fs';
33
+ import { digestSha256 } from './crypto-digest-utils.mjs';
34
+
35
+ import { appendFileSync, mkdirSync } from 'node:fs';
36
+
37
+ import { utcTimestamp, appendJsonl } from './common.mjs';
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Paths
41
+ // ---------------------------------------------------------------------------
42
+
43
+ /** Parent directory for all session-registry state. */
44
+ export function registryBaseDir() {
45
+ const override = process.env.SO_SESSION_REGISTRY_DIR;
46
+ if (override && override.length > 0) return override;
47
+ return path.join(os.homedir(), '.config', 'session-orchestrator', 'sessions');
48
+ }
49
+
50
+ /** Directory holding one JSON file per active session. */
51
+ export function activeDir() {
52
+ return path.join(registryBaseDir(), 'active');
53
+ }
54
+
55
+ /** Zombie-sweep observability log (JSONL). */
56
+ export function sweepLogPath() {
57
+ return path.join(registryBaseDir(), 'sweep.log');
58
+ }
59
+
60
+ function entryPath(sessionId) {
61
+ _assertSessionId(sessionId);
62
+ return path.join(activeDir(), `${sessionId}.json`);
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Primitives
67
+ // ---------------------------------------------------------------------------
68
+
69
+ /**
70
+ * Stable SHA-256 hex of an absolute path. Used so registry entries can be
71
+ * correlated by repo without exposing local filesystem layout.
72
+ */
73
+ export function repoPathHash(absPath) {
74
+ if (typeof absPath !== 'string' || absPath.length === 0) {
75
+ throw new TypeError('repoPathHash: absPath must be a non-empty string');
76
+ }
77
+ return digestSha256(path.resolve(absPath));
78
+ }
79
+
80
+ function _assertSessionId(sessionId) {
81
+ if (typeof sessionId !== 'string' || sessionId.length === 0) {
82
+ throw new TypeError('session_id must be a non-empty string');
83
+ }
84
+ // Guard against path separators / traversal in filename.
85
+ if (sessionId.includes('/') || sessionId.includes('\\') || sessionId.includes('\x00') || sessionId === '.' || sessionId === '..') {
86
+ throw new TypeError('session_id must not contain path separators or null bytes');
87
+ }
88
+ }
89
+
90
+ async function _writeJsonAtomic(filePath, data, { mode = 0o600 } = {}) {
91
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
92
+ const tmp = `${filePath}.tmp-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
93
+ await fs.writeFile(tmp, JSON.stringify(data, null, 2) + '\n', 'utf8');
94
+ await fs.rename(tmp, filePath);
95
+ try { await fs.chmod(filePath, mode); } catch { /* best effort */ }
96
+ }
97
+
98
+ async function _readJsonSafe(filePath) {
99
+ try {
100
+ const raw = await fs.readFile(filePath, 'utf8');
101
+ return JSON.parse(raw);
102
+ } catch {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ function _validEntry(obj) {
108
+ if (!obj
109
+ || typeof obj !== 'object'
110
+ || typeof obj.session_id !== 'string'
111
+ || typeof obj.last_heartbeat !== 'string'
112
+ || typeof obj.started_at !== 'string') {
113
+ return false;
114
+ }
115
+ // Schema v2 (Epic #583): `mode` is optional. When present it MUST be a string
116
+ // (no number / object / array smuggling). When absent (v1 entry), it is
117
+ // accepted — back-compat with pre-#583 registry files.
118
+ if ('mode' in obj && obj.mode !== null && typeof obj.mode !== 'string') {
119
+ return false;
120
+ }
121
+ return true;
122
+ }
123
+
124
+ function _ageMinutes(isoTimestamp, now = Date.now()) {
125
+ const t = Date.parse(isoTimestamp);
126
+ if (Number.isNaN(t)) return Infinity;
127
+ return (now - t) / 60_000;
128
+ }
129
+
130
+ // ---------------------------------------------------------------------------
131
+ // Public API
132
+ // ---------------------------------------------------------------------------
133
+
134
+ /**
135
+ * Append a single JSONL observability event to sweep.log. Non-throwing:
136
+ * if writing to sweep.log itself fails the error is silently swallowed so
137
+ * callers never cascade a log-write failure into a blocking error.
138
+ *
139
+ * Uses appendFileSync (sync) intentionally — this is called from a catch
140
+ * branch where fire-and-forget sync writes are simpler and safer than
141
+ * spawning a new async chain. Lines stay well under PIPE_BUF (4 KiB) so
142
+ * the append is effectively atomic on POSIX.
143
+ *
144
+ * @param {{ event: string, session_id: string|null, error: string }} opts
145
+ */
146
+ export function logSweepEvent({ event, session_id, error }) {
147
+ const line = JSON.stringify({
148
+ timestamp: new Date().toISOString(),
149
+ event,
150
+ session_id: session_id ?? null,
151
+ error: typeof error === 'string' ? error : String(error),
152
+ }) + '\n';
153
+ try {
154
+ const logPath = sweepLogPath();
155
+ try { mkdirSync(path.dirname(logPath), { recursive: true }); } catch { /* best effort */ }
156
+ appendFileSync(logPath, line, 'utf8');
157
+ } catch { /* last-resort silent no-op — never let log-write cascade */ }
158
+ }
159
+
160
+ /**
161
+ * Create the heartbeat file for this session.
162
+ *
163
+ * @param {object} opts
164
+ * @param {string} opts.sessionId — unique id (from hook stdin payload preferred; uuid fallback)
165
+ * @param {string} opts.projectRoot — absolute path to the project root
166
+ * @param {string} [opts.branch] — current git branch (optional)
167
+ * @param {string} [opts.platform] — 'claude' | 'codex' | 'cursor' | null
168
+ * @param {string} [opts.hostClass] — from getHostFingerprint().host_class
169
+ * @param {number} [opts.pid] — defaults to process.pid
170
+ * @param {string} [opts.status] — 'active' | 'wave' | 'idle' (default 'active')
171
+ * @param {number} [opts.currentWave] — default 0
172
+ * @param {string} [opts.mode] — session mode (e.g., 'deep', 'feature', 'housekeeping').
173
+ * Schema v2 (Epic #583, W2-I3): when present, propagated into the entry so
174
+ * discovery + exclusivity-matrix classification work for cross-repo peers.
175
+ * Default: `null` (back-compat with v1 entries).
176
+ * @returns {Promise<object>} the written entry
177
+ */
178
+ export async function registerSelf(opts) {
179
+ if (!opts || typeof opts !== 'object') {
180
+ throw new TypeError('registerSelf: opts is required');
181
+ }
182
+ const { sessionId, projectRoot } = opts;
183
+ _assertSessionId(sessionId);
184
+ if (typeof projectRoot !== 'string' || projectRoot.length === 0) {
185
+ throw new TypeError('registerSelf: projectRoot must be a non-empty string');
186
+ }
187
+ // Schema v2 (Epic #583): validate mode shape if provided.
188
+ if (opts.mode !== undefined && opts.mode !== null && typeof opts.mode !== 'string') {
189
+ throw new TypeError('registerSelf: mode must be a string when provided');
190
+ }
191
+ // Schema v2.1 (Epic #583 W5-F1c — Q5 H1): semanticSessionId field for #587 completion.
192
+ if (opts.semanticSessionId !== undefined && opts.semanticSessionId !== null && typeof opts.semanticSessionId !== 'string') {
193
+ throw new TypeError('registerSelf: semanticSessionId must be a string when provided');
194
+ }
195
+ const now = utcTimestamp();
196
+ const entry = {
197
+ session_id: sessionId,
198
+ semantic_session_id: opts.semanticSessionId ?? null,
199
+ pid: opts.pid ?? process.pid,
200
+ platform: opts.platform ?? null,
201
+ repo_path_hash: repoPathHash(projectRoot),
202
+ repo_name: path.basename(path.resolve(projectRoot)),
203
+ branch: opts.branch ?? null,
204
+ started_at: now,
205
+ last_heartbeat: now,
206
+ status: opts.status ?? 'active',
207
+ current_wave: opts.currentWave ?? 0,
208
+ host_class: opts.hostClass ?? null,
209
+ mode: opts.mode ?? null,
210
+ };
211
+ await _writeJsonAtomic(entryPath(sessionId), entry);
212
+ return entry;
213
+ }
214
+
215
+ /**
216
+ * Refresh last_heartbeat on the existing entry (and optionally status /
217
+ * current_wave). No-op if the entry is missing.
218
+ */
219
+ export async function heartbeat(sessionId, patch = {}) {
220
+ const file = entryPath(sessionId);
221
+ const existing = await _readJsonSafe(file);
222
+ if (!_validEntry(existing)) return null;
223
+ const updated = {
224
+ ...existing,
225
+ last_heartbeat: utcTimestamp(),
226
+ ...(patch.status !== undefined ? { status: patch.status } : {}),
227
+ ...(patch.currentWave !== undefined ? { current_wave: patch.currentWave } : {}),
228
+ };
229
+ await _writeJsonAtomic(file, updated);
230
+ return updated;
231
+ }
232
+
233
+ /** Read every valid heartbeat entry (ignoring malformed files). */
234
+ export async function readRegistry() {
235
+ const dir = activeDir();
236
+ let names;
237
+ try {
238
+ names = await fs.readdir(dir);
239
+ } catch {
240
+ return [];
241
+ }
242
+ const entries = [];
243
+ for (const name of names) {
244
+ if (!name.endsWith('.json')) continue;
245
+ const parsed = await _readJsonSafe(path.join(dir, name));
246
+ if (_validEntry(parsed)) entries.push(parsed);
247
+ }
248
+ return entries;
249
+ }
250
+
251
+ /**
252
+ * Determine whether a registry entry is "fresh" — last_heartbeat within
253
+ * `freshnessMin` minutes of `now`. Mirrors detectPeers()'s age filter but
254
+ * exposes the rule as a pure function so callers (session-discovery,
255
+ * exclusivity-matrix consumers) can apply it without re-deriving the formula.
256
+ *
257
+ * Schema v2 (Epic #583, W2-I3): used by discoverActiveSessions() to filter
258
+ * registry-sourced sessions when locks are absent / stale.
259
+ *
260
+ * @param {object} entry Registry entry object
261
+ * @param {object} [opts]
262
+ * @param {number} [opts.freshnessMin=15]
263
+ * @param {number} [opts.now] ms-since-epoch (test seam)
264
+ * @returns {boolean}
265
+ */
266
+ export function isRegistryEntryFresh(entry, { freshnessMin = 15, now = Date.now() } = {}) {
267
+ if (!_validEntry(entry)) return false;
268
+ return _ageMinutes(entry.last_heartbeat, now) <= freshnessMin;
269
+ }
270
+
271
+ /**
272
+ * Live peers on this host — excludes self and entries whose last_heartbeat is
273
+ * older than `freshnessMin` minutes. Stale entries remain on disk (sweep is
274
+ * the only thing that removes them).
275
+ */
276
+ export async function detectPeers({ sessionId, freshnessMin = 15 } = {}) {
277
+ const now = Date.now();
278
+ const all = await readRegistry();
279
+ return all.filter((e) => {
280
+ if (sessionId && e.session_id === sessionId) return false;
281
+ return _ageMinutes(e.last_heartbeat, now) <= freshnessMin;
282
+ });
283
+ }
284
+
285
+ /**
286
+ * Remove heartbeat entries older than `thresholdMin` minutes. Appends one
287
+ * JSONL line per removal to sweepLogPath() for observability.
288
+ *
289
+ * @returns {Promise<{removed: string[], logged: number}>}
290
+ */
291
+ export async function sweepZombies({ thresholdMin = 60, now = Date.now() } = {}) {
292
+ const dir = activeDir();
293
+ let names;
294
+ try {
295
+ names = await fs.readdir(dir);
296
+ } catch {
297
+ return { removed: [], logged: 0 };
298
+ }
299
+ const removed = [];
300
+ let logged = 0;
301
+ for (const name of names) {
302
+ if (!name.endsWith('.json')) continue;
303
+ const full = path.join(dir, name);
304
+ const parsed = await _readJsonSafe(full);
305
+ const valid = _validEntry(parsed);
306
+ let age;
307
+ if (valid) {
308
+ age = _ageMinutes(parsed.last_heartbeat, now);
309
+ } else {
310
+ // Malformed files can be fresh semantic-session claim files. Use mtime so
311
+ // a concurrent SessionStart claim is not swept before registerSelf()
312
+ // overwrites it with the real heartbeat entry.
313
+ try {
314
+ const info = await fs.stat(full);
315
+ age = (now - info.mtimeMs) / 60_000;
316
+ } catch {
317
+ age = Infinity;
318
+ }
319
+ }
320
+ if (age > thresholdMin) {
321
+ try {
322
+ await fs.unlink(full);
323
+ removed.push(name);
324
+ try {
325
+ await appendJsonl(sweepLogPath(), {
326
+ timestamp: new Date(now).toISOString(),
327
+ session_id: parsed?.session_id ?? null,
328
+ file: name,
329
+ age_minutes: Number.isFinite(age) ? Math.round(age) : null,
330
+ reason: valid ? 'stale-heartbeat' : 'malformed-entry',
331
+ });
332
+ logged += 1;
333
+ } catch { /* log best-effort */ }
334
+ } catch { /* file might have been removed by another sweep */ }
335
+ }
336
+ }
337
+ return { removed, logged };
338
+ }
339
+
340
+ /**
341
+ * Remove this session's heartbeat file. Idempotent — missing file is not an
342
+ * error (clean-stop after a crashed-sweep scenario).
343
+ *
344
+ * @returns {Promise<boolean>} true if a file was removed
345
+ */
346
+ export async function deregisterSelf(sessionId) {
347
+ _assertSessionId(sessionId);
348
+ try {
349
+ await fs.unlink(entryPath(sessionId));
350
+ return true;
351
+ } catch (err) {
352
+ if (err && err.code === 'ENOENT') return false;
353
+ throw err;
354
+ }
355
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * session-schema/aliases.mjs — aliasLegacyEndedAt.
3
+ *
4
+ * Extracted from scripts/lib/session-schema.mjs (W1A3 split).
5
+ * Pure function. Stdlib only. No imports from siblings or parent barrel.
6
+ * isPlainObject is duplicated inline (Option 1 — submodule isolation).
7
+ *
8
+ * Exports: aliasLegacyEndedAt
9
+ */
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Internal helper (intentional duplication — Option 1 isolation)
13
+ // ---------------------------------------------------------------------------
14
+
15
+ function isPlainObject(v) {
16
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
17
+ }
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Public API
21
+ // ---------------------------------------------------------------------------
22
+
23
+ /**
24
+ * Migrate legacy `ended_at` + `duration_ms` shape to canonical
25
+ * `completed_at` (issue #321). Some pre-canonical writers produced records
26
+ * with `ended_at` instead of `completed_at`; this helper aliases the field
27
+ * so the canonical validator does not reject the record on a missing
28
+ * required field.
29
+ *
30
+ * Behaviour:
31
+ * - If `completed_at` is absent and `ended_at` is a string, alias
32
+ * `completed_at <- ended_at`.
33
+ * - If both `completed_at` and `ended_at` are present and DIFFER, prefer
34
+ * `completed_at` (do not overwrite) and tag `_completed_at_conflict: true`
35
+ * for forensics.
36
+ * - If both `completed_at` and `started_at` end up present, drop the now-
37
+ * redundant `duration_ms` (derivable; canonical schema uses
38
+ * `duration_seconds`).
39
+ *
40
+ * Never throws. Returns either the original entry (no migration needed) or
41
+ * a NEW object with the migration applied.
42
+ *
43
+ * @param {object} entry — session record
44
+ * @returns {object} either the original entry or a NEW object
45
+ */
46
+ export function aliasLegacyEndedAt(entry) {
47
+ if (!isPlainObject(entry)) return entry;
48
+ const hasCompleted = typeof entry.completed_at === 'string';
49
+ const hasEnded = typeof entry.ended_at === 'string';
50
+ if (!hasCompleted && !hasEnded) return entry;
51
+
52
+ let next = entry;
53
+
54
+ if (!hasCompleted && hasEnded) {
55
+ next = { ...entry, completed_at: entry.ended_at };
56
+ } else if (hasCompleted && hasEnded && entry.completed_at !== entry.ended_at) {
57
+ next = { ...entry, _completed_at_conflict: true };
58
+ }
59
+
60
+ // Drop derivable duration_ms once we have both start + completed.
61
+ if (
62
+ 'duration_ms' in next &&
63
+ typeof next.completed_at === 'string' &&
64
+ typeof next.started_at === 'string'
65
+ ) {
66
+ if (next === entry) next = { ...entry };
67
+ delete next.duration_ms;
68
+ }
69
+
70
+ return next;
71
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * session-schema/constants.mjs — pure data constants for session schema.
3
+ *
4
+ * Extracted from scripts/lib/session-schema.mjs (W1A3 split).
5
+ * Leaf module — no imports from siblings or parent.
6
+ *
7
+ * Exports: CURRENT_SESSION_SCHEMA_VERSION, SESSION_KEY_ALIASES,
8
+ * VALID_SESSION_TYPES, REQUIRED_FIELDS, AGENT_SUMMARY_FIELDS
9
+ */
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Version
13
+ // ---------------------------------------------------------------------------
14
+
15
+ /**
16
+ * Current session-record schema version. New writes are stamped with this
17
+ * value by validateSession. Records read without `schema_version` are tagged
18
+ * as 0 (pre-versioning legacy) by normalizeSession.
19
+ *
20
+ * Bumped 1 -> 2 via issue #372 (2026-07-02): gate evidence — 135/135
21
+ * production entries in .orchestrator/metrics/sessions.jsonl validate
22
+ * cleanly against the accepted-version set in validator.mjs. The bump is
23
+ * additive-only; no required-field or shape change accompanies it.
24
+ */
25
+ export const CURRENT_SESSION_SCHEMA_VERSION = 2;
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Key aliases (safe renames — no value transformation)
29
+ // ---------------------------------------------------------------------------
30
+
31
+ /**
32
+ * Safe key aliases — same-shape renames only (no value transformation).
33
+ * Applied by normalizeSession on read so legacy records can be consumed by
34
+ * canonical-key consumers without rewriting the file.
35
+ *
36
+ * Identity is frozen so callers can do strict-equality checks on the object
37
+ * reference across re-imports.
38
+ */
39
+ export const SESSION_KEY_ALIASES = Object.freeze({
40
+ type: 'session_type',
41
+ mode: 'session_type',
42
+ closed_issues: 'issues_closed',
43
+ new_issues: 'issues_created',
44
+ issues_filed: 'issues_created',
45
+ issues_planned: 'planned_issues',
46
+ files_changed: 'total_files_changed',
47
+ snapshots: 'snapshots_created',
48
+ learnings: 'learnings_added',
49
+ waves_total: 'total_waves',
50
+ waves_completed: 'total_waves',
51
+ head_ref: 'branch',
52
+ isolation_override: 'isolation',
53
+ });
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // Enums / required field lists
57
+ // ---------------------------------------------------------------------------
58
+
59
+ /** Closed set of valid session_type values. */
60
+ export const VALID_SESSION_TYPES = Object.freeze(['feature', 'deep', 'housekeeping']);
61
+
62
+ /**
63
+ * Required fields for a schema_version=1 record. Validated by validateSession
64
+ * before any write reaches disk.
65
+ */
66
+ export const REQUIRED_FIELDS = Object.freeze([
67
+ 'session_id',
68
+ 'session_type',
69
+ 'started_at',
70
+ 'completed_at',
71
+ 'total_waves',
72
+ 'waves',
73
+ 'agent_summary',
74
+ 'total_agents',
75
+ 'total_files_changed',
76
+ ]);
77
+
78
+ /**
79
+ * Required numeric counters inside the agent_summary object. All must be
80
+ * non-negative numbers.
81
+ */
82
+ export const AGENT_SUMMARY_FIELDS = Object.freeze(['complete', 'partial', 'failed', 'spiral']);
83
+
84
+ /**
85
+ * Optional additive fields introduced for the remote-agent substrate (ADR-364 thin-slice).
86
+ * These are NOT in REQUIRED_FIELDS — older entries lacking them validate cleanly.
87
+ * Validator: see `_validateOptionalFields` in validator.mjs.
88
+ */
89
+ export const OPTIONAL_FIELDS = Object.freeze([
90
+ 'agent_identity',
91
+ 'worktree_path',
92
+ 'parent_run_id',
93
+ 'lease_acquired_at',
94
+ 'lease_ttl_seconds',
95
+ 'expected_cost_tier',
96
+ // Epic #644 — session-level token rollup fields (additive, v1-compatible).
97
+ 'total_token_input',
98
+ 'total_token_output',
99
+ 'subagents_with_tokens',
100
+ // Epic #724 C1 — SessionEnd close-through backfill provenance fields.
101
+ // `status` distinguishes normally-closed ('completed') from hook-backfilled
102
+ // ('abandoned') records. The `_backfill_*` markers record how a stub was
103
+ // synthesized when a session terminated without running /close. All are
104
+ // additive-optional: pre-#724 records lacking them validate cleanly.
105
+ 'status',
106
+ '_backfill_source',
107
+ '_backfill_incomplete_fields',
108
+ '_session_type_inferred',
109
+ '_synthetic_session_id',
110
+ // #773 — Handover-Alignment-Gate telemetry (additive, v1-compatible).
111
+ // Non-negative integers; absent = gate did not run / not measured.
112
+ 'open_questions_asked',
113
+ 'open_questions_answered',
114
+ 'open_questions_deferred',
115
+ ]);
@@ -0,0 +1,66 @@
1
+ /**
2
+ * session-schema/normalizer.mjs — normalizeSession (read-path normalization).
3
+ *
4
+ * Extracted from scripts/lib/session-schema.mjs (W1A3 split).
5
+ * Imports: constants.mjs. No imports from siblings (validator, timestamps,
6
+ * aliases) or parent barrel.
7
+ *
8
+ * Exports: normalizeSession
9
+ * Module-private: _warnedMissingSchemaVersion (Set, per-process dedupe)
10
+ */
11
+
12
+ import { SESSION_KEY_ALIASES } from './constants.mjs';
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Module-level state
16
+ // ---------------------------------------------------------------------------
17
+
18
+ // Keyed by session_id (or '<unknown>'). Each unique id warns at most once
19
+ // per process, preventing log-spam on large sessions.jsonl files.
20
+ const _warnedMissingSchemaVersion = new Set();
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Public API
24
+ // ---------------------------------------------------------------------------
25
+
26
+ /**
27
+ * Normalize a session entry read from disk. Applies SAFE key aliases, tags
28
+ * legacy entries without `schema_version` as 0 (distinct from
29
+ * CURRENT_SESSION_SCHEMA_VERSION=2 which is stamped on new writes; bumped
30
+ * 1 -> 2 via #372).
31
+ *
32
+ * Never throws. Malformed input (null, non-object, array) is passed through
33
+ * unchanged. Original keys are preserved alongside their canonical alias for
34
+ * debugging.
35
+ *
36
+ * @param {any} entry
37
+ * @returns {any} normalized entry (or original if non-object)
38
+ */
39
+ export function normalizeSession(entry) {
40
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return entry;
41
+
42
+ const next = { ...entry };
43
+
44
+ // Apply safe key aliases (same-shape renames). Preserve the old key.
45
+ for (const [oldKey, newKey] of Object.entries(SESSION_KEY_ALIASES)) {
46
+ if (oldKey in next && !(newKey in next)) {
47
+ next[newKey] = next[oldKey];
48
+ }
49
+ }
50
+
51
+ // schema_version — legacy entries tagged as 0 (NOT CURRENT_SESSION_SCHEMA_VERSION).
52
+ if ('schema_version' in next && next.schema_version !== undefined) {
53
+ // Preserve existing version.
54
+ } else {
55
+ next.schema_version = 0;
56
+ const warnKey = next.session_id ?? '<unknown>';
57
+ if (!_warnedMissingSchemaVersion.has(warnKey)) {
58
+ _warnedMissingSchemaVersion.add(warnKey);
59
+ console.error(
60
+ `[sessions] WARN: record missing schema_version (session_id=${warnKey}); treating as schema_version=0 (pre-versioning legacy)`
61
+ );
62
+ }
63
+ }
64
+
65
+ return next;
66
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * session-schema/timestamps.mjs — clampTimestampsMonotonic.
3
+ *
4
+ * Extracted from scripts/lib/session-schema.mjs (W1A3 split).
5
+ * Pure function. Stdlib only. No imports from siblings or parent barrel.
6
+ * isPlainObject is duplicated inline (Option 1 — submodule isolation).
7
+ *
8
+ * Exports: clampTimestampsMonotonic
9
+ */
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Internal helper (intentional duplication — Option 1 isolation)
13
+ // ---------------------------------------------------------------------------
14
+
15
+ function isPlainObject(v) {
16
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
17
+ }
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Public API
21
+ // ---------------------------------------------------------------------------
22
+
23
+ /**
24
+ * Clamp `completed_at` to be >= `started_at` when an inversion is detected.
25
+ *
26
+ * Defends against clock skew, manual STATE.md frontmatter edits, and any
27
+ * other writer that produces a `completed_at < started_at` violation. Rather
28
+ * than throw and lose the session record entirely, we mathematically clamp
29
+ * `completed_at` to equal `started_at` (duration = 0) and tag the entry with
30
+ * forensics fields so the original timestamp is recoverable.
31
+ *
32
+ * Behaviour:
33
+ * - If either field is absent, return the entry unchanged (let validate
34
+ * surface the missing-field error).
35
+ * - If either field is unparsable, return the entry unchanged (validate
36
+ * will surface the parse error with a clearer message).
37
+ * - If `completed_at >= started_at`, return the entry unchanged.
38
+ * - Otherwise return a NEW object with:
39
+ * completed_at: <started_at>
40
+ * _clamped: true
41
+ * _original_completed_at: <orig completed_at>
42
+ *
43
+ * Never throws. Caller is responsible for emitting any warning log.
44
+ *
45
+ * @param {object} entry — session record (may be the raw input)
46
+ * @returns {object} either the original entry (no clamp needed) or a NEW
47
+ * object with clamp applied + forensics fields
48
+ */
49
+ export function clampTimestampsMonotonic(entry) {
50
+ if (!isPlainObject(entry)) return entry;
51
+ if (typeof entry.started_at !== 'string' || typeof entry.completed_at !== 'string') {
52
+ return entry;
53
+ }
54
+ const startedMs = Date.parse(entry.started_at);
55
+ const completedMs = Date.parse(entry.completed_at);
56
+ if (Number.isNaN(startedMs) || Number.isNaN(completedMs)) return entry;
57
+ if (completedMs >= startedMs) return entry;
58
+ return {
59
+ ...entry,
60
+ completed_at: entry.started_at,
61
+ _clamped: true,
62
+ _original_completed_at: entry.completed_at,
63
+ };
64
+ }