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,539 @@
1
+ /**
2
+ * session-close-backfill.mjs — SessionEnd close-through backfill (Epic #724 C1).
3
+ *
4
+ * Fleet-wide only ~27% of started sessions ever reach a /close (this repo: 137
5
+ * distinct `orchestrator.session.started` events vs 36 sessions.jsonl records).
6
+ * When a session terminates without running /close, no session record is ever
7
+ * written — the work is invisible to every downstream sessions.jsonl consumer.
8
+ *
9
+ * This module reconstructs a MINIMAL `status: 'abandoned'` stub from the
10
+ * `.orchestrator/metrics/events.jsonl` breadcrumbs the lifecycle hooks already
11
+ * emit, and appends it through the SAME validate + round-trip + append path the
12
+ * normal writer (`scripts/emit-session.mjs`) uses. It is invoked best-effort
13
+ * from `hooks/on-session-end.mjs` and by the one-time historical migration CLI
14
+ * `scripts/backfill-abandoned-sessions.mjs`.
15
+ *
16
+ * ── ID BRIDGE ────────────────────────────────────────────────────────────────
17
+ * sessions.jsonl records are keyed by SEMANTIC ids (`main-2026-05-27-session-1`).
18
+ * events.jsonl carries the harness UUID on `session.started` / `stop` / `ended`.
19
+ * The bridge is the `orchestrator.session.lock.acquired` event, which is the
20
+ * only record carrying BOTH `session_id` (UUID) and `semantic_session_id`.
21
+ * Only ~1/3 of sessions ever emit a lock.acquired, so when the bridge is
22
+ * missing we fall back to a synthetic id + `_synthetic_session_id: true`.
23
+ *
24
+ * ── SAFETY POSTURE ──────────────────────────────────────────────────────────
25
+ * - No-throw: every path returns a structured `{ action, ... }` result; the
26
+ * hook must never be pushed past its teardown timeout by an exception.
27
+ * - Dedupe: never double-write a session already present in sessions.jsonl.
28
+ * - Liveness guard: never backfill over a FOREIGN live session.lock (PSA).
29
+ * - TOCTOU marker: an atomic `openSync(..., 'wx')` claim file keyed by the
30
+ * final id serialises concurrent backfill attempts (mirrors the
31
+ * on-session-start registry-slot claim, #587).
32
+ * - dryRun: computes + validates the stub without touching disk (used by the
33
+ * migration CLI's `--dry-run` default).
34
+ *
35
+ * Plain Node ESM. Named exports. DI-friendly via `deps` (mirrors enumerate.mjs).
36
+ */
37
+
38
+ import fs from 'node:fs';
39
+ import path from 'node:path';
40
+ import crypto from 'node:crypto';
41
+
42
+ import { appendJsonl as defaultAppendJsonl } from './common.mjs';
43
+ import { readLock as defaultReadLock, isLockLive as defaultIsLockLive, DEFAULT_TTL_HOURS } from './session-lock.mjs';
44
+ import { validateSession as defaultValidateSession } from './session-schema/validator.mjs';
45
+ import { serializeSessionLineChecked as defaultSerialize } from '../emit-session.mjs';
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Constants
49
+ // ---------------------------------------------------------------------------
50
+
51
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
52
+
53
+ /** session_type enum accepted by the schema — lock.mode is coerced against it. */
54
+ const VALID_SESSION_TYPES = new Set(['feature', 'deep', 'housekeeping']);
55
+
56
+ const EVENT_STARTED = 'orchestrator.session.started';
57
+ const EVENT_LOCK_ACQUIRED = 'orchestrator.session.lock.acquired';
58
+ const EVENT_STOPPED = 'orchestrator.session.stopped';
59
+ const EVENT_ENDED = 'orchestrator.session.ended';
60
+
61
+ const EVENTS_REL = ['.orchestrator', 'metrics', 'events.jsonl'];
62
+ const SESSIONS_REL = ['.orchestrator', 'metrics', 'sessions.jsonl'];
63
+ const BACKFILL_LOG_REL = ['.orchestrator', 'metrics', 'session-close-backfill.log'];
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Helpers
67
+ // ---------------------------------------------------------------------------
68
+
69
+ /** True when `s` is a canonical UUID (v4-shaped). */
70
+ export function isUuid(s) {
71
+ return typeof s === 'string' && UUID_RE.test(s);
72
+ }
73
+
74
+ /** Filesystem-safe marker filename for an arbitrary session id. */
75
+ function markerName(id) {
76
+ const cleaned = String(id).replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 120);
77
+ return `.backfilled-${cleaned}.marker`;
78
+ }
79
+
80
+ /**
81
+ * Read a JSONL file into an array of parsed objects. Missing file → []; each
82
+ * malformed line is skipped rather than aborting the whole read. Never throws.
83
+ */
84
+ function readJsonlSafe(readFileSync, filePath) {
85
+ let raw;
86
+ try {
87
+ raw = readFileSync(filePath, 'utf8');
88
+ } catch {
89
+ return [];
90
+ }
91
+ const out = [];
92
+ for (const line of raw.split('\n')) {
93
+ const trimmed = line.trim();
94
+ if (!trimmed) continue;
95
+ try {
96
+ out.push(JSON.parse(trimmed));
97
+ } catch {
98
+ /* skip malformed line */
99
+ }
100
+ }
101
+ return out;
102
+ }
103
+
104
+ /** Normalise any parseable timestamp to canonical ISO-8601 UTC ms form. */
105
+ function canonicalIso(value, fallbackMs) {
106
+ const ms = typeof value === 'string' ? Date.parse(value) : NaN;
107
+ if (!Number.isNaN(ms)) return new Date(ms).toISOString();
108
+ const fb = Number.isFinite(fallbackMs) ? fallbackMs : Date.now();
109
+ return new Date(fb).toISOString();
110
+ }
111
+
112
+ /**
113
+ * Walk events.jsonl and gather everything known about one session, bridging
114
+ * UUID ↔ semantic id via lock.acquired. Returns provenance used to synthesize
115
+ * the record (never throws).
116
+ *
117
+ * @param {Array<object>} events
118
+ * @param {{ sessionId: string|null, semanticSessionId: string|null }} ids
119
+ */
120
+ function collectSessionEvents(events, { sessionId, semanticSessionId }) {
121
+ const uuids = new Set();
122
+ if (isUuid(sessionId)) uuids.add(sessionId);
123
+
124
+ let mode = null;
125
+ let semanticFromLock = null;
126
+
127
+ // First pass — lock.acquired bridges the UUID set + carries mode + semantic.
128
+ for (const ev of events) {
129
+ if (ev.event !== EVENT_LOCK_ACQUIRED) continue;
130
+ const matchesUuid = isUuid(sessionId) && ev.session_id === sessionId;
131
+ const matchesSemantic =
132
+ (semanticSessionId && ev.semantic_session_id === semanticSessionId) ||
133
+ (!isUuid(sessionId) && sessionId && ev.semantic_session_id === sessionId);
134
+ if (!matchesUuid && !matchesSemantic) continue;
135
+ if (typeof ev.session_id === 'string') uuids.add(ev.session_id);
136
+ if (typeof ev.mode === 'string') mode = ev.mode;
137
+ if (typeof ev.semantic_session_id === 'string') semanticFromLock = ev.semantic_session_id;
138
+ }
139
+
140
+ // Second pass — started + terminal timestamps from every matched UUID.
141
+ let startedAt = null;
142
+ let branch = null;
143
+ let project = null;
144
+ let lastTerminalMs = null;
145
+ let earliestMs = null;
146
+ // lastEventMs (#731): max timestamp over ALL matched events (not just the
147
+ // terminal STOPPED/ENDED subset lastTerminalMs tracks) — the dead-by-age
148
+ // relaxation needs "when did we last hear from this candidate at all",
149
+ // since a session that never emitted a terminal event is exactly the
150
+ // abandoned case this module exists to reconstruct.
151
+ let lastEventMs = null;
152
+
153
+ for (const ev of events) {
154
+ if (typeof ev.session_id !== 'string' || !uuids.has(ev.session_id)) continue;
155
+ const ts = typeof ev.timestamp === 'string' ? Date.parse(ev.timestamp) : NaN;
156
+ if (!Number.isNaN(ts)) {
157
+ earliestMs = earliestMs === null ? ts : Math.min(earliestMs, ts);
158
+ lastEventMs = lastEventMs === null ? ts : Math.max(lastEventMs, ts);
159
+ }
160
+
161
+ if (ev.event === EVENT_STARTED) {
162
+ if (typeof ev.timestamp === 'string') startedAt = ev.timestamp;
163
+ if (typeof ev.branch === 'string' && ev.branch.length > 0) branch = ev.branch;
164
+ if (typeof ev.project === 'string') project = ev.project;
165
+ } else if (ev.event === EVENT_STOPPED || ev.event === EVENT_ENDED) {
166
+ if (!Number.isNaN(ts)) {
167
+ lastTerminalMs = lastTerminalMs === null ? ts : Math.max(lastTerminalMs, ts);
168
+ }
169
+ if (typeof ev.branch === 'string' && ev.branch.length > 0 && !branch) branch = ev.branch;
170
+ }
171
+ }
172
+
173
+ return { uuids, mode, semanticFromLock, startedAt, branch, project, lastTerminalMs, earliestMs, lastEventMs };
174
+ }
175
+
176
+ /**
177
+ * Determine whether a candidate should be treated as dead-by-age DESPITE a
178
+ * live FOREIGN session.lock (#731 — the historical migration CLI blocks
179
+ * itself: every run happens FROM an active session, so the current lock is
180
+ * always live and shadows every candidate regardless of how old it is).
181
+ *
182
+ * True when EITHER:
183
+ * - `assumeDeadBeforeMs` is set and the candidate's last known event
184
+ * strictly PREDATES it (operator-supplied cutoff, CLI `--assume-dead-before`).
185
+ * - `relaxDeadByAge` is set and the candidate's last known event is older
186
+ * than the lock's own default TTL window (`DEFAULT_TTL_HOURS`) — a
187
+ * session that stopped emitting events longer ago than a lock can even
188
+ * stay live cannot legitimately be "blocked" by that unrelated lock.
189
+ *
190
+ * Both conditions require a resolvable `lastEventMs` — an unknown last-event
191
+ * time (gap in events.jsonl) never unlocks the relaxation, erring toward the
192
+ * existing conservative (block) behaviour. NOT exported: internal to the
193
+ * liveness guard below; default caller behaviour (both params absent) always
194
+ * returns false, i.e. identical to pre-#731 behaviour.
195
+ *
196
+ * @param {{ relaxDeadByAge: boolean, assumeDeadBeforeMs: number|null, lastEventMs: number|null, nowMs: number }} args
197
+ * @returns {boolean}
198
+ */
199
+ function isCandidateDeadByAge({ relaxDeadByAge, assumeDeadBeforeMs, lastEventMs, nowMs }) {
200
+ if (typeof lastEventMs !== 'number' || Number.isNaN(lastEventMs)) return false;
201
+ if (
202
+ typeof assumeDeadBeforeMs === 'number' &&
203
+ !Number.isNaN(assumeDeadBeforeMs) &&
204
+ lastEventMs < assumeDeadBeforeMs
205
+ ) {
206
+ return true;
207
+ }
208
+ if (relaxDeadByAge === true) {
209
+ const ttlMs = DEFAULT_TTL_HOURS * 3600 * 1000;
210
+ if (nowMs - lastEventMs > ttlMs) return true;
211
+ }
212
+ return false;
213
+ }
214
+
215
+ /**
216
+ * Build the abandoned-session stub record. Required fields with no events
217
+ * source are defaulted to empty/zero and enumerated in
218
+ * `_backfill_incomplete_fields`; the mode → session_type coercion sets
219
+ * `_session_type_inferred`.
220
+ */
221
+ function synthesizeRecord({ recordId, synthetic, gathered, nowMs }) {
222
+ const startedIso = canonicalIso(gathered.startedAt, gathered.earliestMs ?? nowMs);
223
+ const startedMs = Date.parse(startedIso);
224
+ const terminalMs = Number.isFinite(gathered.lastTerminalMs) ? gathered.lastTerminalMs : nowMs;
225
+ // completed_at = last terminal event (or now), never earlier than started_at.
226
+ const completedIso = new Date(Math.max(startedMs, terminalMs)).toISOString();
227
+
228
+ let sessionType = 'housekeeping';
229
+ let inferred = true;
230
+ if (gathered.mode && VALID_SESSION_TYPES.has(gathered.mode)) {
231
+ sessionType = gathered.mode;
232
+ inferred = false;
233
+ }
234
+
235
+ const startedFound = typeof gathered.startedAt === 'string';
236
+ const branchFound = typeof gathered.branch === 'string' && gathered.branch.length > 0;
237
+
238
+ const incomplete = ['total_waves', 'waves', 'agent_summary', 'total_agents', 'total_files_changed'];
239
+ if (!startedFound) incomplete.push('started_at');
240
+ if (!branchFound) incomplete.push('branch');
241
+
242
+ const record = {
243
+ session_id: recordId,
244
+ session_type: sessionType,
245
+ started_at: startedIso,
246
+ completed_at: completedIso,
247
+ total_waves: 0,
248
+ waves: [],
249
+ agent_summary: { complete: 0, partial: 0, failed: 0, spiral: 0 },
250
+ total_agents: 0,
251
+ total_files_changed: 0,
252
+ status: 'abandoned',
253
+ // Issue #773 — an abandoned session never ran Phase 1.65, so its carryover
254
+ // is genuinely UNKNOWN. Emit `null` (not 0) so downstream effectiveness
255
+ // consumers can tell "not measured" apart from "measured zero" — 0 here
256
+ // would resurrect the very carryover=0 blind spot #773 exists to close.
257
+ effectiveness: { carryover: null },
258
+ _backfill_source: 'events-jsonl',
259
+ _backfill_incomplete_fields: incomplete,
260
+ };
261
+ if (branchFound) record.branch = gathered.branch;
262
+ if (inferred) record._session_type_inferred = true;
263
+ if (synthetic) record._synthetic_session_id = true;
264
+ return record;
265
+ }
266
+
267
+ /**
268
+ * Dedupe against sessions.jsonl. Returns a `skipped-already-recorded` result
269
+ * when `recordId` (or a UUID `sessionId` written directly as a key) is already
270
+ * present, else `null`. Reads the (small) sessions.jsonl exactly once.
271
+ */
272
+ function checkAlreadyRecorded(readFileSync, sessionsPath, { recordId, sessionId }) {
273
+ const sessionRecords = readJsonlSafe(readFileSync, sessionsPath);
274
+ const existingIds = new Set(
275
+ sessionRecords.map((r) => (r && typeof r.session_id === 'string' ? r.session_id : null)).filter(Boolean)
276
+ );
277
+ if (existingIds.has(recordId)) {
278
+ return { action: 'skipped-already-recorded', sessionId: recordId };
279
+ }
280
+ // Defensive: a prior record keyed directly by the UUID also counts.
281
+ if (isUuid(sessionId) && existingIds.has(sessionId)) {
282
+ return { action: 'skipped-already-recorded', sessionId };
283
+ }
284
+ return null;
285
+ }
286
+
287
+ // ---------------------------------------------------------------------------
288
+ // Public API
289
+ // ---------------------------------------------------------------------------
290
+
291
+ /**
292
+ * Backfill an abandoned session record into sessions.jsonl from events.jsonl.
293
+ *
294
+ * Never throws. Returns one of:
295
+ * { action: 'backfilled', sessionId, record, deadByAge? } — written to disk
296
+ * { action: 'would-backfill', sessionId, record, deadByAge? } — dryRun only, not written
297
+ * { action: 'skipped-no-identifier' } — neither id known
298
+ * { action: 'skipped-already-recorded', sessionId } — already in sessions.jsonl
299
+ * { action: 'skipped-foreign-live-lock', sessionId, lockSessionId }
300
+ * { action: 'skipped-marker-exists', sessionId } — lost the TOCTOU claim
301
+ * { action: 'error', error, sessionId? } — any failure, swallowed
302
+ *
303
+ * `deadByAge: true` is present on `backfilled` / `would-backfill` ONLY when a
304
+ * foreign live lock was present AND bypassed via `relaxDeadByAge` /
305
+ * `assumeDeadBeforeMs` (#731) — it never appears on the default path, so
306
+ * callers can distinguish "genuinely no conflicting lock" from "we relaxed
307
+ * past one" without re-deriving the guard logic.
308
+ *
309
+ * @param {object} args
310
+ * @param {string} args.repoRoot absolute project root
311
+ * @param {string|null} [args.sessionId] harness UUID (SessionEnd stdin) or semantic id
312
+ * @param {string|null} [args.semanticSessionId] semantic id when already known (current-session.json)
313
+ * @param {number|string} [args.now] ms-since-epoch (test seam) or ISO string
314
+ * @param {boolean} [args.dryRun=false] compute + validate only, no marker/write
315
+ * @param {boolean} [args.relaxDeadByAge=false]
316
+ * #731 — when true, a FOREIGN live lock no longer blocks a candidate whose
317
+ * last known event (`lastEventMs`) is older than `DEFAULT_TTL_HOURS`
318
+ * (session-lock.mjs SSOT). Purely additive: default `false` reproduces the
319
+ * original always-block behaviour EXACTLY. Intended for the one-time
320
+ * historical migration CLI only — `hooks/on-session-end.mjs` must NEVER
321
+ * pass this (a hook-time foreign lock is by definition a real, active
322
+ * session, not stale history).
323
+ * @param {number|null} [args.assumeDeadBeforeMs=null]
324
+ * #731 — operator-supplied cutoff (ms-since-epoch). A candidate whose
325
+ * `lastEventMs` strictly predates this value bypasses a foreign live lock
326
+ * regardless of `relaxDeadByAge`. Corresponds to the CLI's
327
+ * `--assume-dead-before <ISO>` flag.
328
+ * @param {object} [args.deps] DI overrides (fs, appendJsonl, readLock, …)
329
+ * @returns {Promise<object>}
330
+ */
331
+ export async function backfillAbandonedSession({
332
+ repoRoot,
333
+ sessionId = null,
334
+ semanticSessionId = null,
335
+ now = Date.now(),
336
+ dryRun = false,
337
+ relaxDeadByAge = false,
338
+ assumeDeadBeforeMs = null,
339
+ deps = {},
340
+ } = {}) {
341
+ const {
342
+ readFileSync = fs.readFileSync,
343
+ appendJsonl = defaultAppendJsonl,
344
+ readLock = defaultReadLock,
345
+ isLockLive = defaultIsLockLive,
346
+ openSync = fs.openSync,
347
+ closeSync = fs.closeSync,
348
+ validateSession = defaultValidateSession,
349
+ serializeSessionLineChecked = defaultSerialize,
350
+ log = null,
351
+ } = deps;
352
+
353
+ const nowMs = resolveNowMs(now);
354
+ const result = await run();
355
+ logBreadcrumb(result);
356
+ return result;
357
+
358
+ async function run() {
359
+ try {
360
+ if (typeof repoRoot !== 'string' || repoRoot.length === 0) {
361
+ return { action: 'error', error: 'repoRoot must be a non-empty string' };
362
+ }
363
+ if (!sessionId && !semanticSessionId) {
364
+ return { action: 'skipped-no-identifier' };
365
+ }
366
+
367
+ const eventsPath = path.join(repoRoot, ...EVENTS_REL);
368
+ const sessionsPath = path.join(repoRoot, ...SESSIONS_REL);
369
+
370
+ // -- Resolve the SEMANTIC record id (sessions.jsonl key) ----------------
371
+ // Two-phase so the common SessionEnd path stays cheap: when the id is
372
+ // already known WITHOUT scanning events (semanticSessionId given, or
373
+ // sessionId is itself a semantic id), dedupe against the tiny
374
+ // sessions.jsonl FIRST and short-circuit if the record already exists —
375
+ // avoiding the two-pass walk over an events.jsonl that can be MBs. The
376
+ // events read is deferred to the branches that genuinely need it: the
377
+ // UUID→semantic lock bridge, the synthetic-id mint, and record synthesis.
378
+ // (When sessionId is a non-UUID semantic id, the ORIGINAL lock-bridge
379
+ // branch resolved to that SAME id — collectSessionEvents only sets
380
+ // semanticFromLock=sessionId in that case — so resolving it early is
381
+ // behaviour-identical, just cheaper.)
382
+ let recordId = null;
383
+ let synthetic = false;
384
+ if (semanticSessionId) {
385
+ recordId = semanticSessionId;
386
+ } else if (sessionId && !isUuid(sessionId)) {
387
+ // sessionId is already a semantic id (Claude Code generated-semantic path).
388
+ recordId = sessionId;
389
+ }
390
+
391
+ // -- Cheap dedupe FIRST when the id needs no events scan -----------------
392
+ if (recordId !== null) {
393
+ const dupe = checkAlreadyRecorded(readFileSync, sessionsPath, { recordId, sessionId });
394
+ if (dupe) return dupe;
395
+ }
396
+
397
+ // -- Read events (needed to synthesize, and to bridge UUID→semantic) ----
398
+ const events = readJsonlSafe(readFileSync, eventsPath);
399
+ const gathered = collectSessionEvents(events, { sessionId, semanticSessionId });
400
+
401
+ // -- Resolve a deferred id from the lock bridge or a synthetic mint ------
402
+ if (recordId === null) {
403
+ if (gathered.semanticFromLock) {
404
+ recordId = gathered.semanticFromLock;
405
+ } else {
406
+ // No semantic bridge — mint a synthetic id. Both components are STABLE
407
+ // across re-runs so dedupe/marker suppress a double write (idempotency
408
+ // is load-bearing for the one-time CLI, #724 C1):
409
+ // - date ← the session's own started_at (immutable in events), not
410
+ // run-time now, so a migration run on any day is stable.
411
+ // - suffix← sha256 of the source UUID, not random.
412
+ const branchSlug = (gathered.branch || 'unknown').replace(/[^a-zA-Z0-9._-]/g, '-');
413
+ const date = canonicalIso(gathered.startedAt, gathered.earliestMs ?? nowMs).slice(0, 10);
414
+ const suffix = crypto.createHash('sha256').update(String(sessionId)).digest('hex').slice(0, 8);
415
+ recordId = `${branchSlug}-${date}-abandoned-${suffix}`;
416
+ synthetic = true;
417
+ }
418
+
419
+ // Dedupe once the events-derived id is known.
420
+ const dupe = checkAlreadyRecorded(readFileSync, sessionsPath, { recordId, sessionId });
421
+ if (dupe) return dupe;
422
+ }
423
+
424
+ // -- Liveness guard — never overwrite a FOREIGN live lock ---------------
425
+ // deadByAge (#731): set when a foreign live lock was present but the
426
+ // candidate qualified for relaxation — surfaced on the final result so
427
+ // callers (the migration CLI's summary) can count relaxed backfills.
428
+ let deadByAge = false;
429
+ let lock = null;
430
+ try {
431
+ lock = readLock({ repoRoot });
432
+ } catch {
433
+ lock = null;
434
+ }
435
+ if (lock) {
436
+ const ownByUuid = Boolean(sessionId) && lock.session_id === sessionId;
437
+ const ownBySemantic =
438
+ (Boolean(semanticSessionId) && lock.semantic_session_id === semanticSessionId) ||
439
+ (Boolean(recordId) && lock.semantic_session_id === recordId);
440
+ const foreign = !ownByUuid && !ownBySemantic;
441
+ if (foreign && isLockLive(lock, nowMs)) {
442
+ const relaxed = isCandidateDeadByAge({
443
+ relaxDeadByAge,
444
+ assumeDeadBeforeMs,
445
+ lastEventMs: gathered.lastEventMs,
446
+ nowMs,
447
+ });
448
+ if (!relaxed) {
449
+ return { action: 'skipped-foreign-live-lock', sessionId: recordId, lockSessionId: lock.session_id };
450
+ }
451
+ deadByAge = true;
452
+ }
453
+ }
454
+
455
+ // -- Synthesize + validate (round-trip gate) BEFORE any disk mutation ---
456
+ const record = synthesizeRecord({ recordId, synthetic, gathered, nowMs });
457
+ let validated;
458
+ try {
459
+ validated = validateSession(record);
460
+ serializeSessionLineChecked(validated);
461
+ } catch (err) {
462
+ return { action: 'error', error: `validation: ${err?.message ?? String(err)}`, sessionId: recordId };
463
+ }
464
+
465
+ if (dryRun) {
466
+ return {
467
+ action: 'would-backfill',
468
+ sessionId: recordId,
469
+ record: validated,
470
+ ...(deadByAge ? { deadByAge: true } : {}),
471
+ };
472
+ }
473
+
474
+ // -- TOCTOU marker — atomic create-or-fail keyed by the final id --------
475
+ const markerPath = path.join(repoRoot, '.orchestrator', 'metrics', markerName(recordId));
476
+ try {
477
+ const fd = openSync(markerPath, 'wx');
478
+ closeSync(fd);
479
+ } catch (err) {
480
+ if (err && err.code === 'EEXIST') {
481
+ return { action: 'skipped-marker-exists', sessionId: recordId };
482
+ }
483
+ // Any other fs error on the marker → refuse to write without the guard.
484
+ return { action: 'error', error: `marker: ${err?.message ?? String(err)}`, sessionId: recordId };
485
+ }
486
+
487
+ // -- Write via the shared append path -----------------------------------
488
+ try {
489
+ await appendJsonl(sessionsPath, validated);
490
+ } catch (err) {
491
+ return { action: 'error', error: `append: ${err?.message ?? String(err)}`, sessionId: recordId };
492
+ }
493
+ return {
494
+ action: 'backfilled',
495
+ sessionId: recordId,
496
+ record: validated,
497
+ ...(deadByAge ? { deadByAge: true } : {}),
498
+ };
499
+ } catch (err) {
500
+ // Absolute backstop — the hook must never see an exception from here.
501
+ return { action: 'error', error: err?.message ?? String(err) };
502
+ }
503
+ }
504
+
505
+ /** Best-effort JSONL breadcrumb (project-local; never cascades). */
506
+ function logBreadcrumb(res) {
507
+ try {
508
+ if (typeof log === 'function') {
509
+ log(res);
510
+ return;
511
+ }
512
+ if (typeof repoRoot !== 'string' || repoRoot.length === 0) return;
513
+ const logPath = path.join(repoRoot, ...BACKFILL_LOG_REL);
514
+ fs.mkdirSync(path.dirname(logPath), { recursive: true });
515
+ fs.appendFileSync(
516
+ logPath,
517
+ JSON.stringify({
518
+ timestamp: new Date(nowMs).toISOString(),
519
+ action: res.action,
520
+ session_id: res.sessionId ?? null,
521
+ ...(res.error ? { error: res.error } : {}),
522
+ }) + '\n',
523
+ 'utf8'
524
+ );
525
+ } catch {
526
+ /* never let logging cascade into the caller */
527
+ }
528
+ }
529
+ }
530
+
531
+ /** Normalise the `now` arg (number ms | ISO string | undefined) → ms. */
532
+ function resolveNowMs(now) {
533
+ if (typeof now === 'number' && Number.isFinite(now)) return now;
534
+ if (typeof now === 'string') {
535
+ const ms = Date.parse(now);
536
+ if (!Number.isNaN(ms)) return ms;
537
+ }
538
+ return Date.now();
539
+ }