session-orchestrator 5.1.0 → 5.3.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 (484) hide show
  1. package/.agents/skills/architecture/SKILL.md +3 -1
  2. package/.agents/skills/autopilot/SKILL.md +6 -1
  3. package/.agents/skills/autopilot/agents/openai.yaml +5 -0
  4. package/.agents/skills/bootstrap/SKILL.md +7 -1
  5. package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
  6. package/.agents/skills/brainstorm/SKILL.md +8 -1
  7. package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
  8. package/.agents/skills/claude-md-drift-check/SKILL.md +3 -1
  9. package/.agents/skills/close/SKILL.md +21 -0
  10. package/.agents/skills/close/agents/openai.yaml +5 -0
  11. package/.agents/skills/convergence-monitoring/SKILL.md +4 -2
  12. package/.agents/skills/debug/SKILL.md +7 -1
  13. package/.agents/skills/debug/agents/openai.yaml +5 -0
  14. package/.agents/skills/discovery/SKILL.md +7 -2
  15. package/.agents/skills/discovery/agents/openai.yaml +5 -0
  16. package/.agents/skills/dispatcher/SKILL.md +7 -1
  17. package/.agents/skills/dispatcher/agents/openai.yaml +5 -0
  18. package/.agents/skills/docs-orchestrator/SKILL.md +3 -1
  19. package/.agents/skills/ecosystem-health/SKILL.md +3 -1
  20. package/.agents/skills/eli5/SKILL.md +7 -1
  21. package/.agents/skills/eli5/agents/openai.yaml +5 -0
  22. package/.agents/skills/eval/SKILL.md +7 -2
  23. package/.agents/skills/eval/agents/openai.yaml +5 -0
  24. package/.agents/skills/evolve/SKILL.md +8 -3
  25. package/.agents/skills/evolve/agents/openai.yaml +5 -0
  26. package/.agents/skills/frontmatter-guard/SKILL.md +3 -1
  27. package/.agents/skills/gitlab-ops/SKILL.md +3 -1
  28. package/.agents/skills/gitlab-portfolio/SKILL.md +3 -1
  29. package/.agents/skills/go/SKILL.md +22 -0
  30. package/.agents/skills/go/agents/openai.yaml +5 -0
  31. package/.agents/skills/grill/SKILL.md +7 -1
  32. package/.agents/skills/grill/agents/openai.yaml +5 -0
  33. package/.agents/skills/harness-audit/SKILL.md +20 -0
  34. package/.agents/skills/harness-audit/agents/openai.yaml +5 -0
  35. package/.agents/skills/hook-development/SKILL.md +3 -1
  36. package/.agents/skills/mcp-builder/SKILL.md +3 -1
  37. package/.agents/skills/memory-cleanup/SKILL.md +6 -1
  38. package/.agents/skills/memory-cleanup/agents/openai.yaml +5 -0
  39. package/.agents/skills/mode-selector/SKILL.md +3 -1
  40. package/.agents/skills/npm-publish/SKILL.md +4 -2
  41. package/.agents/skills/peekaboo-driver/SKILL.md +3 -1
  42. package/.agents/skills/persona-panel/SKILL.md +6 -1
  43. package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
  44. package/.agents/skills/plan/SKILL.md +8 -2
  45. package/.agents/skills/plan/agents/openai.yaml +5 -0
  46. package/.agents/skills/playwright-driver/SKILL.md +3 -1
  47. package/.agents/skills/portfolio/SKILL.md +21 -0
  48. package/.agents/skills/portfolio/agents/openai.yaml +5 -0
  49. package/.agents/skills/quality-gates/SKILL.md +3 -1
  50. package/.agents/skills/reconcile/SKILL.md +6 -1
  51. package/.agents/skills/reconcile/agents/openai.yaml +5 -0
  52. package/.agents/skills/release/SKILL.md +22 -0
  53. package/.agents/skills/release/agents/openai.yaml +5 -0
  54. package/.agents/skills/remote-offload/SKILL.md +3 -1
  55. package/.agents/skills/repo-audit/SKILL.md +6 -1
  56. package/.agents/skills/repo-audit/agents/openai.yaml +5 -0
  57. package/.agents/skills/session/SKILL.md +21 -0
  58. package/.agents/skills/session/agents/openai.yaml +5 -0
  59. package/.agents/skills/session-end/SKILL.md +3 -1
  60. package/.agents/skills/session-plan/SKILL.md +3 -1
  61. package/.agents/skills/session-start/SKILL.md +3 -1
  62. package/.agents/skills/spinout/SKILL.md +6 -1
  63. package/.agents/skills/spinout/agents/openai.yaml +5 -0
  64. package/.agents/skills/sunset-review/SKILL.md +7 -1
  65. package/.agents/skills/sunset-review/agents/openai.yaml +5 -0
  66. package/.agents/skills/templates-ack/SKILL.md +21 -0
  67. package/.agents/skills/templates-ack/agents/openai.yaml +5 -0
  68. package/.agents/skills/test/SKILL.md +21 -0
  69. package/.agents/skills/test/agents/openai.yaml +5 -0
  70. package/.agents/skills/test-runner/SKILL.md +3 -1
  71. package/.agents/skills/tmux-layout/SKILL.md +3 -1
  72. package/.agents/skills/using-orchestrator/SKILL.md +3 -1
  73. package/.agents/skills/ux-grill/SKILL.md +7 -1
  74. package/.agents/skills/ux-grill/agents/openai.yaml +5 -0
  75. package/.agents/skills/vault-mirror/SKILL.md +3 -1
  76. package/.agents/skills/vault-sync/SKILL.md +3 -1
  77. package/.agents/skills/wave-executor/SKILL.md +3 -1
  78. package/.agents/skills/write-executable-plan/SKILL.md +3 -1
  79. package/.claude-plugin/marketplace.json +1 -1
  80. package/.claude-plugin/plugin.json +1 -1
  81. package/.codex-plugin/plugin.json +4 -4
  82. package/.codex-plugin/skills/autopilot/SKILL.md +5 -4
  83. package/.codex-plugin/skills/bootstrap/SKILL.md +8 -4
  84. package/.codex-plugin/skills/brainstorm/SKILL.md +11 -4
  85. package/.codex-plugin/skills/close/SKILL.md +3 -3
  86. package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -1
  87. package/.codex-plugin/skills/debug/SKILL.md +11 -4
  88. package/.codex-plugin/skills/discovery/SKILL.md +8 -4
  89. package/.codex-plugin/skills/dispatcher/SKILL.md +4 -4
  90. package/.codex-plugin/skills/eli5/SKILL.md +9 -4
  91. package/.codex-plugin/skills/eval/SKILL.md +9 -4
  92. package/.codex-plugin/skills/evolve/SKILL.md +9 -4
  93. package/.codex-plugin/skills/go/SKILL.md +3 -3
  94. package/.codex-plugin/skills/grill/SKILL.md +11 -4
  95. package/.codex-plugin/skills/harness-audit/SKILL.md +4 -3
  96. package/.codex-plugin/skills/memory-cleanup/SKILL.md +9 -4
  97. package/.codex-plugin/skills/npm-publish/SKILL.md +1 -1
  98. package/.codex-plugin/skills/persona-panel/SKILL.md +5 -5
  99. package/.codex-plugin/skills/plan/SKILL.md +8 -4
  100. package/.codex-plugin/skills/portfolio/SKILL.md +3 -3
  101. package/.codex-plugin/skills/reconcile/SKILL.md +9 -4
  102. package/.codex-plugin/skills/release/SKILL.md +3 -3
  103. package/.codex-plugin/skills/repo-audit/SKILL.md +6 -4
  104. package/.codex-plugin/skills/session/SKILL.md +1 -1
  105. package/.codex-plugin/skills/spinout/SKILL.md +4 -4
  106. package/.codex-plugin/skills/sunset-review/SKILL.md +5 -4
  107. package/.codex-plugin/skills/test/SKILL.md +3 -3
  108. package/.codex-plugin/skills/ux-grill/SKILL.md +11 -4
  109. package/.cursor/commands/autopilot.md +4 -4
  110. package/.cursor/commands/bootstrap.md +5 -4
  111. package/.cursor/commands/brainstorm.md +5 -4
  112. package/.cursor/commands/close.md +4 -3
  113. package/.cursor/commands/debug.md +4 -4
  114. package/.cursor/commands/discovery.md +4 -4
  115. package/.cursor/commands/dispatcher.md +4 -4
  116. package/.cursor/commands/eli5.md +4 -4
  117. package/.cursor/commands/eval.md +4 -4
  118. package/.cursor/commands/evolve.md +4 -4
  119. package/.cursor/commands/go.md +4 -3
  120. package/.cursor/commands/grill.md +4 -4
  121. package/.cursor/commands/harness-audit.md +3 -3
  122. package/.cursor/commands/memory-cleanup.md +4 -4
  123. package/.cursor/commands/persona-panel.md +4 -4
  124. package/.cursor/commands/plan.md +5 -4
  125. package/.cursor/commands/portfolio.md +3 -3
  126. package/.cursor/commands/reconcile.md +4 -4
  127. package/.cursor/commands/release.md +4 -3
  128. package/.cursor/commands/repo-audit.md +4 -4
  129. package/.cursor/commands/session.md +1 -1
  130. package/.cursor/commands/spinout.md +4 -4
  131. package/.cursor/commands/sunset-review.md +4 -4
  132. package/.cursor/commands/test.md +3 -3
  133. package/.cursor/commands/ux-grill.md +4 -4
  134. package/.cursor/rules/000-session-orchestrator.mdc +0 -2
  135. package/.cursor/rules/010-session-workflow.mdc +2 -2
  136. package/.cursor/rules/050-plan.mdc +1 -1
  137. package/.cursor/skills/bootstrap/SKILL.md +1 -0
  138. package/.cursor/skills/close/SKILL.md +13 -0
  139. package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
  140. package/.cursor/skills/debug/SKILL.md +0 -1
  141. package/.cursor/skills/discovery/SKILL.md +0 -1
  142. package/.cursor/skills/dispatcher/SKILL.md +0 -1
  143. package/.cursor/skills/eli5/SKILL.md +0 -1
  144. package/.cursor/skills/eval/SKILL.md +1 -1
  145. package/.cursor/skills/evolve/SKILL.md +0 -1
  146. package/.cursor/skills/go/SKILL.md +13 -0
  147. package/.cursor/skills/grill/SKILL.md +0 -1
  148. package/.cursor/skills/harness-audit/SKILL.md +12 -0
  149. package/.cursor/skills/npm-publish/SKILL.md +1 -0
  150. package/.cursor/skills/portfolio/SKILL.md +12 -0
  151. package/.cursor/skills/release/SKILL.md +13 -0
  152. package/.cursor/skills/repo-audit/SKILL.md +0 -1
  153. package/.cursor/skills/sunset-review/SKILL.md +0 -1
  154. package/.cursor/skills/test/SKILL.md +12 -0
  155. package/.cursor/skills/ux-grill/SKILL.md +0 -1
  156. package/.cursor-plugin/plugin.json +1 -1
  157. package/.orchestrator/policy/blocked-commands.json +13 -4
  158. package/AGENTS.md +3 -2
  159. package/CHANGELOG.md +197 -0
  160. package/README.md +11 -9
  161. package/SECURITY.md +12 -0
  162. package/agents/dialectic-deriver.md +13 -10
  163. package/agents/eval-judge.md +67 -45
  164. package/agents/skill-applied-judge.md +34 -19
  165. package/commands/session.md +17 -3
  166. package/docs/baseline.md +12 -6
  167. package/docs/ci-setup.md +53 -0
  168. package/docs/codex-setup.md +15 -3
  169. package/docs/components.md +13 -6
  170. package/docs/events-schema.md +59 -9
  171. package/docs/install.md +16 -0
  172. package/docs/persona-panel.md +1 -1
  173. package/docs/pi-setup.md +1 -1
  174. package/docs/rule-authoring.md +135 -14
  175. package/docs/scope-collision-guard.md +2 -0
  176. package/docs/session-config-reference.md +106 -11
  177. package/docs/session-config-template.md +31 -2
  178. package/docs/telemetry.md +2 -0
  179. package/hooks/_lib/hook-import-set.json +125 -8
  180. package/hooks/_lib/subagent-paths.mjs +15 -0
  181. package/hooks/_lib/subagent-transcript.mjs +582 -31
  182. package/hooks/_lib/vcs-create-matcher.mjs +217 -62
  183. package/hooks/config-protection.mjs +11 -3
  184. package/hooks/cwd-change-restore.mjs +11 -3
  185. package/hooks/enforce-commands.mjs +70 -23
  186. package/hooks/enforce-scope.mjs +143 -33
  187. package/hooks/hooks-codex.json +1 -1
  188. package/hooks/hooks.json +1 -1
  189. package/hooks/loop-guard.mjs +11 -3
  190. package/hooks/on-session-end.mjs +72 -25
  191. package/hooks/on-session-start.mjs +48 -11
  192. package/hooks/on-stop.mjs +211 -23
  193. package/hooks/operator-steer.mjs +11 -3
  194. package/hooks/post-bash-issue-budget-refund.mjs +18 -8
  195. package/hooks/post-bash-write-verify.mjs +6 -2
  196. package/hooks/post-edit-import-probe.mjs +17 -9
  197. package/hooks/post-edit-validate.mjs +13 -5
  198. package/hooks/post-subagent-discovery-validator.mjs +98 -13
  199. package/hooks/post-tool-batch-wave-signal.mjs +200 -38
  200. package/hooks/post-tool-failure-corrective-context.mjs +11 -5
  201. package/hooks/post-tooluse-frontend-slop.mjs +10 -4
  202. package/hooks/pre-auq-clarity.mjs +18 -2
  203. package/hooks/pre-bash-destructive-guard.mjs +80 -9
  204. package/hooks/pre-bash-issue-budget.mjs +119 -28
  205. package/hooks/pre-bash-memory-propose-audit.mjs +86 -54
  206. package/hooks/pre-bash-sessions-ledger-guard.mjs +391 -20
  207. package/hooks/pre-bash-staging-fence.mjs +335 -31
  208. package/hooks/pre-bash-templates-first.mjs +19 -14
  209. package/hooks/pre-task-scope-disjoint.mjs +385 -5
  210. package/hooks/skill-invocation-telemetry.mjs +2 -1
  211. package/hooks/subagent-telemetry.mjs +15 -19
  212. package/hooks/wave-scope-commit-guard.mjs +197 -100
  213. package/monitors/monitors.json +1 -1
  214. package/output-styles/wave-summary.md +1 -1
  215. package/package.json +2 -1
  216. package/pi/prompts/autopilot.md +3 -3
  217. package/pi/prompts/bootstrap.md +3 -3
  218. package/pi/prompts/brainstorm.md +3 -3
  219. package/pi/prompts/close.md +2 -2
  220. package/pi/prompts/debug.md +3 -3
  221. package/pi/prompts/discovery.md +3 -3
  222. package/pi/prompts/dispatcher.md +3 -3
  223. package/pi/prompts/eli5.md +3 -3
  224. package/pi/prompts/eval.md +3 -3
  225. package/pi/prompts/evolve.md +3 -3
  226. package/pi/prompts/go.md +2 -2
  227. package/pi/prompts/grill.md +3 -3
  228. package/pi/prompts/harness-audit.md +2 -3
  229. package/pi/prompts/memory-cleanup.md +3 -3
  230. package/pi/prompts/persona-panel.md +3 -3
  231. package/pi/prompts/plan.md +3 -3
  232. package/pi/prompts/portfolio.md +2 -2
  233. package/pi/prompts/reconcile.md +3 -3
  234. package/pi/prompts/release.md +3 -3
  235. package/pi/prompts/repo-audit.md +3 -4
  236. package/pi/prompts/session.md +2 -2
  237. package/pi/prompts/spinout.md +3 -3
  238. package/pi/prompts/sunset-review.md +3 -3
  239. package/pi/prompts/templates-ack.md +1 -1
  240. package/pi/prompts/test.md +3 -3
  241. package/pi/prompts/ux-grill.md +3 -3
  242. package/rules/README.md +1 -1
  243. package/rules/opt-in-domain/prompt-caching.md +1 -1
  244. package/rules/opt-in-stack/backend-data.md +1 -1
  245. package/rules/opt-in-stack/backend.md +3 -3
  246. package/rules/opt-in-stack/frontend.md +1 -1
  247. package/rules/opt-in-stack/security-web.md +3 -3
  248. package/rules/opt-in-stack/swift.md +1 -1
  249. package/scripts/archive-closed-prds.mjs +2 -2
  250. package/scripts/auq-audit.mjs +2 -3
  251. package/scripts/autopilot.mjs +23 -2
  252. package/scripts/backfill-abandoned-sessions.mjs +171 -15
  253. package/scripts/backfill-evidence-digest.mjs +2 -1
  254. package/scripts/backfill-learnings-from-vault.mjs +2 -2
  255. package/scripts/check-package-manager.mjs +2 -2
  256. package/scripts/check-sessions-integrity.mjs +300 -0
  257. package/scripts/ci/assert-vitest-green.mjs +2 -1
  258. package/scripts/dialectic-deriver.mjs +50 -13
  259. package/scripts/emit-session.mjs +77 -32
  260. package/scripts/eval-session.mjs +65 -3
  261. package/scripts/export-hw-learnings.mjs +2 -1
  262. package/scripts/express-path.mjs +1 -1
  263. package/scripts/gc-stale-worktrees.mjs +2 -1
  264. package/scripts/generate-agents-skills.mjs +102 -29
  265. package/scripts/generate-codex-skills.mjs +48 -4
  266. package/scripts/generate-cursor-adapter.mjs +220 -11
  267. package/scripts/generate-hook-import-set.mjs +12 -27
  268. package/scripts/generate-pi-prompts.mjs +183 -13
  269. package/scripts/github-protection-audit.mjs +2 -3
  270. package/scripts/lib/agent-frontmatter.mjs +23 -1
  271. package/scripts/lib/agent-status.mjs +2 -31
  272. package/scripts/lib/auq/clarity.mjs +10 -2
  273. package/scripts/lib/auq/parse.mjs +12 -31
  274. package/scripts/lib/auq/schema.mjs +56 -41
  275. package/scripts/lib/auto-dialectic.mjs +304 -15
  276. package/scripts/lib/autopilot/flags.mjs +12 -1
  277. package/scripts/lib/autopilot/kill-switches.mjs +6 -3
  278. package/scripts/lib/autopilot/loop.mjs +14 -1
  279. package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
  280. package/scripts/lib/ci-status-banner.mjs +376 -16
  281. package/scripts/lib/claude-md-budget-lint.mjs +2 -5
  282. package/scripts/lib/command-blocker.mjs +408 -33
  283. package/scripts/lib/config/dialectic.mjs +12 -3
  284. package/scripts/lib/config/drift-check.mjs +19 -0
  285. package/scripts/lib/config/gate.mjs +74 -0
  286. package/scripts/lib/config/reaper.mjs +162 -0
  287. package/scripts/lib/config.mjs +14 -0
  288. package/scripts/lib/convergence-monitor.mjs +76 -13
  289. package/scripts/lib/cursor-hook-bridge.mjs +2 -2
  290. package/scripts/lib/description-surface.mjs +2 -5
  291. package/scripts/lib/dispatcher/cli.mjs +2 -1
  292. package/scripts/lib/ecosystem-health.mjs +11 -0
  293. package/scripts/lib/ecosystem-wizard.mjs +2 -1
  294. package/scripts/lib/eval/engine.mjs +421 -53
  295. package/scripts/lib/eval/judge.mjs +463 -40
  296. package/scripts/lib/eval/schema.mjs +10 -1
  297. package/scripts/lib/events-rotation.mjs +221 -25
  298. package/scripts/lib/events-schema.mjs +114 -0
  299. package/scripts/lib/events.mjs +524 -5
  300. package/scripts/lib/fetch-baseline.mjs +3 -8
  301. package/scripts/lib/frontmatter-guard.mjs +21 -10
  302. package/scripts/lib/gates/gate-baseline.mjs +27 -2
  303. package/scripts/lib/gates/gate-full.mjs +28 -3
  304. package/scripts/lib/gates/gate-helpers.mjs +243 -21
  305. package/scripts/lib/gates/gate-incremental.mjs +28 -3
  306. package/scripts/lib/gates/gate-per-file.mjs +27 -2
  307. package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +2 -1
  308. package/scripts/lib/gitlab-portfolio/cli.mjs +2 -1
  309. package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
  310. package/scripts/lib/instruction-budget-guard.mjs +332 -50
  311. package/scripts/lib/io.mjs +42 -8
  312. package/scripts/lib/is-main-module.mjs +82 -0
  313. package/scripts/lib/issue-close-strip-labels.mjs +207 -49
  314. package/scripts/lib/js-mask.mjs +197 -0
  315. package/scripts/lib/learnings/evolve-telemetry.mjs +11 -7
  316. package/scripts/lib/locks/index.mjs +32 -25
  317. package/scripts/lib/maintenance-due-banner.mjs +122 -91
  318. package/scripts/lib/orphan-reaper.mjs +1588 -0
  319. package/scripts/lib/peer-cards/merger.mjs +48 -10
  320. package/scripts/lib/peer-cards/reader.mjs +78 -2
  321. package/scripts/lib/peer-discovery.mjs +2 -5
  322. package/scripts/lib/playwright-driver/runner.mjs +2 -1
  323. package/scripts/lib/process-group.mjs +899 -0
  324. package/scripts/lib/quality-gate.mjs +107 -28
  325. package/scripts/lib/reconcile/backlog.mjs +368 -0
  326. package/scripts/lib/reconcile/engine.mjs +55 -188
  327. package/scripts/lib/reconcile/rule-expiry-sweep.mjs +884 -0
  328. package/scripts/lib/reconcile/sanitize.mjs +69 -3
  329. package/scripts/lib/reconcile-nudge-banner.mjs +138 -45
  330. package/scripts/lib/resource-probe/parsers.mjs +31 -0
  331. package/scripts/lib/rule-loader.mjs +41 -12
  332. package/scripts/lib/rules-sync.mjs +2 -5
  333. package/scripts/lib/scope-echo.mjs +429 -7
  334. package/scripts/lib/scope-gate.mjs +605 -1
  335. package/scripts/lib/session-close-backfill.mjs +91 -12
  336. package/scripts/lib/session-id.mjs +9 -20
  337. package/scripts/lib/session-invocation.mjs +20 -0
  338. package/scripts/lib/session-schema/constants.mjs +30 -2
  339. package/scripts/lib/session-schema/normalizer.mjs +56 -4
  340. package/scripts/lib/session-schema.mjs +8 -3
  341. package/scripts/lib/session-start-probes.mjs +95 -10
  342. package/scripts/lib/sessions-canonical.mjs +23 -0
  343. package/scripts/lib/sessions-integrity-banner.mjs +7 -1
  344. package/scripts/lib/sessions-staleness-banner.mjs +193 -51
  345. package/scripts/lib/skill-evidence-window.mjs +891 -0
  346. package/scripts/lib/skill-evolution/candidate-intake.mjs +133 -12
  347. package/scripts/lib/skill-evolution/engine.mjs +18 -9
  348. package/scripts/lib/skill-judge.mjs +45 -3
  349. package/scripts/lib/state-md.mjs +84 -3
  350. package/scripts/lib/sunset/walker.mjs +31 -4
  351. package/scripts/lib/tail-window.mjs +56 -0
  352. package/scripts/lib/telemetry/schema.mjs +30 -0
  353. package/scripts/lib/telemetry/sync.mjs +61 -6
  354. package/scripts/lib/telemetry-flush-health-banner.mjs +4 -22
  355. package/scripts/lib/test-runner/issue-reconcile.mjs +48 -16
  356. package/scripts/lib/tests-src-ratio.mjs +2 -6
  357. package/scripts/lib/tmux-layout/telemetry-stats.mjs +74 -14
  358. package/scripts/lib/user-invocable-skills.mjs +205 -0
  359. package/scripts/lib/ux-grill/reconcile.mjs +48 -22
  360. package/scripts/lib/validate/check-agents-skills.mjs +26 -15
  361. package/scripts/lib/validate/check-banner-parity.mjs +2 -2
  362. package/scripts/lib/validate/check-cursor-adapter.mjs +3 -2
  363. package/scripts/lib/validate/check-dead-bridge.mjs +2 -2
  364. package/scripts/lib/validate/check-doc-cli-commands.mjs +2 -2
  365. package/scripts/lib/validate/check-entry-guard.mjs +329 -0
  366. package/scripts/lib/validate/check-guard-requires-parity.mjs +2 -2
  367. package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
  368. package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +2 -2
  369. package/scripts/lib/validate/check-learning-provenance.mjs +2 -2
  370. package/scripts/lib/validate/check-pi-prompts.mjs +1 -0
  371. package/scripts/lib/validate/check-rules.mjs +7 -5
  372. package/scripts/lib/validate/check-skill-links.mjs +35 -6
  373. package/scripts/lib/validate/check-skill-script-paths.mjs +241 -29
  374. package/scripts/lib/validate/check-test-git-config-target.mjs +26 -36
  375. package/scripts/lib/validate/check-unicode-safety.mjs +2 -2
  376. package/scripts/lib/validate/check-untracked-test-deps.mjs +9 -104
  377. package/scripts/lib/validate/check-unwired-features.mjs +220 -33
  378. package/scripts/lib/validate/check-validator-registration.mjs +36 -12
  379. package/scripts/lib/validate/check-vcs-repo-flag.mjs +2 -2
  380. package/scripts/lib/validate/confidential-names.mjs +10 -0
  381. package/scripts/lib/validate-vendored-rules.mjs +39 -12
  382. package/scripts/lib/vault-mirror/namespace.mjs +46 -8
  383. package/scripts/lib/vault-mirror/process.mjs +10 -3
  384. package/scripts/lib/vault-mirror/render-sessions.mjs +12 -2
  385. package/scripts/lib/vault-status/narrative-mirror.mjs +31 -7
  386. package/scripts/lib/vault-yaml.mjs +118 -0
  387. package/scripts/lib/wave-transcript-tail.mjs +2 -2
  388. package/scripts/lib/worktree/lifecycle.mjs +153 -1
  389. package/scripts/lock-reaper.mjs +2 -1
  390. package/scripts/materialize-wave-scope.mjs +87 -4
  391. package/scripts/migrate-sessions-jsonl.mjs +2 -1
  392. package/scripts/migrate-vault-paths.mjs +2 -3
  393. package/scripts/release-session-lock.mjs +305 -0
  394. package/scripts/release.mjs +109 -39
  395. package/scripts/relocate-vault-corpus.mjs +2 -3
  396. package/scripts/repair-invalid-sessions.mjs +2 -2
  397. package/scripts/resolve-session-invocation.mjs +59 -0
  398. package/scripts/run-quality-gate.mjs +156 -17
  399. package/scripts/session-shape.mjs +2 -2
  400. package/scripts/site-numbers.mjs +35 -11
  401. package/scripts/sweep-expired-rules.mjs +227 -0
  402. package/scripts/validate-plugin.mjs +21 -0
  403. package/scripts/validate-wave-scope.mjs +32 -105
  404. package/scripts/vault-consolidate.mjs +2 -2
  405. package/scripts/vault-mirror.mjs +11 -4
  406. package/scripts/wave-scope-binding.mjs +2 -3
  407. package/skills/_shared/bootstrap-gate.md +1 -1
  408. package/skills/_shared/monitor-patterns.md +1 -1
  409. package/skills/_shared/platform-tools.md +23 -11
  410. package/skills/_shared/research-evidence.md +53 -0
  411. package/skills/_shared/state-ownership.md +3 -0
  412. package/skills/autopilot/SKILL.md +80 -11
  413. package/skills/bootstrap/SKILL.md +51 -1
  414. package/skills/brainstorm/SKILL.md +16 -0
  415. package/skills/claude-md-drift-check/SKILL.md +1 -1
  416. package/skills/claude-md-drift-check/checker.mjs +49 -11
  417. package/{commands/close.md → skills/close/SKILL.md} +9 -3
  418. package/skills/convergence-monitoring/README.md +8 -1
  419. package/skills/convergence-monitoring/SIGNALS.md +50 -6
  420. package/skills/convergence-monitoring/SKILL.md +15 -6
  421. package/skills/debug/SKILL.md +10 -0
  422. package/skills/discovery/SKILL.md +24 -1
  423. package/skills/discovery/probes-session.md +2 -2
  424. package/skills/dispatcher/SKILL.md +38 -7
  425. package/skills/eli5/SKILL.md +11 -0
  426. package/skills/eval/SKILL.md +52 -23
  427. package/skills/eval/rubric-v1.md +1 -0
  428. package/skills/eval/rubric-v2.md +457 -0
  429. package/skills/evolve/SKILL.md +9 -2
  430. package/skills/evolve/references/evolve-dialectic-mode.md +46 -25
  431. package/skills/gitlab-ops/SKILL.md +3 -2
  432. package/{commands/go.md → skills/go/SKILL.md} +9 -1
  433. package/skills/grill/SKILL.md +19 -0
  434. package/{commands/harness-audit.md → skills/harness-audit/SKILL.md} +7 -2
  435. package/skills/hook-development/SKILL.md +46 -41
  436. package/skills/memory-cleanup/SKILL.md +7 -0
  437. package/skills/npm-publish/SKILL.md +2 -2
  438. package/skills/persona-panel/SKILL.md +56 -1
  439. package/skills/persona-panel/persona-format.md +1 -1
  440. package/skills/plan/SKILL.md +28 -1
  441. package/{commands/portfolio.md → skills/portfolio/SKILL.md} +8 -2
  442. package/skills/reconcile/SKILL.md +21 -0
  443. package/{commands/release.md → skills/release/SKILL.md} +16 -2
  444. package/skills/repo-audit/SKILL.md +7 -0
  445. package/skills/session-end/SKILL.md +13 -16
  446. package/skills/session-end/discovery-scan.md +1 -1
  447. package/skills/session-end/phase-3-6-tail.md +55 -9
  448. package/skills/session-end/plan-verification.md +2 -2
  449. package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
  450. package/skills/session-end/session-metrics-write.md +10 -0
  451. package/skills/session-plan/SKILL.md +18 -6
  452. package/skills/session-plan/references/session-plan-task-classification.md +2 -2
  453. package/skills/session-start/SKILL.md +5 -4
  454. package/skills/session-start/phase-8-5-express-path.md +6 -6
  455. package/skills/session-start/references/phase-1-5-session-continuity.md +1 -1
  456. package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +1 -1
  457. package/skills/session-start/references/phase-4-ssot-environment-check.md +6 -4
  458. package/skills/spinout/SKILL.md +12 -1
  459. package/skills/sunset-review/SKILL.md +13 -0
  460. package/{commands/test.md → skills/test/SKILL.md} +10 -4
  461. package/skills/ux-grill/SKILL.md +20 -2
  462. package/skills/wave-executor/SKILL.md +14 -7
  463. package/skills/wave-executor/circuit-breaker.md +2 -0
  464. package/skills/wave-executor/references/wave-executor-state-init.md +18 -4
  465. package/skills/wave-executor/references/wave-loop-dispatch.md +5 -2
  466. package/skills/wave-executor/references/wave-loop-review.md +17 -1
  467. package/commands/autopilot.md +0 -80
  468. package/commands/bootstrap.md +0 -56
  469. package/commands/brainstorm.md +0 -48
  470. package/commands/debug.md +0 -36
  471. package/commands/discovery.md +0 -32
  472. package/commands/dispatcher.md +0 -59
  473. package/commands/eli5.md +0 -33
  474. package/commands/eval.md +0 -28
  475. package/commands/evolve.md +0 -10
  476. package/commands/grill.md +0 -45
  477. package/commands/memory-cleanup.md +0 -26
  478. package/commands/persona-panel.md +0 -121
  479. package/commands/plan.md +0 -15
  480. package/commands/reconcile.md +0 -23
  481. package/commands/repo-audit.md +0 -24
  482. package/commands/spinout.md +0 -15
  483. package/commands/sunset-review.md +0 -27
  484. package/commands/ux-grill.md +0 -51
@@ -22,6 +22,7 @@
22
22
  */
23
23
 
24
24
  import { reapStaleLocks } from './lib/lock-reaper.mjs';
25
+ import { isMainModule } from './lib/is-main-module.mjs';
25
26
 
26
27
  // ---------------------------------------------------------------------------
27
28
  // Human-readable output
@@ -175,7 +176,7 @@ EXIT CODES
175
176
  // CLI guard — prevents execution during test-time imports.
176
177
  // ---------------------------------------------------------------------------
177
178
 
178
- if (import.meta.url === `file://${process.argv[1]}`) {
179
+ if (isMainModule(import.meta.url)) {
179
180
  main()
180
181
  .then((code) => process.exit(code))
181
182
  .catch((err) => {
@@ -23,10 +23,11 @@
23
23
  */
24
24
 
25
25
  import { existsSync, readdirSync, readFileSync, unlinkSync } from 'node:fs';
26
- import { resolve } from 'node:path';
27
- import { fileURLToPath } from 'node:url';
26
+ import { dirname, resolve } from 'node:path';
28
27
  import { writeJsonAtomicSync } from './lib/io.mjs';
28
+ import { isMainModule } from './lib/is-main-module.mjs';
29
29
  import { isPeerRecordId } from './lib/scope-gate.mjs';
30
+ import { SCOPE_MATERIALIZED_EVENT, scopeDigest } from './lib/scope-echo.mjs';
30
31
  import {
31
32
  MANIFEST_SESSION_KEYS,
32
33
  manifestSessionBinding,
@@ -479,6 +480,83 @@ export function materializeWaveScope(records, {
479
480
  return { aggregatePath, perAgentPaths, removedOrphans: removed, retainedOrphans: retained };
480
481
  }
481
482
 
483
+ /**
484
+ * Is the SEND-side half of the #1092 chain installed on this platform at all?
485
+ *
486
+ * The send-side record only exists because a `PreToolUse` hook with matcher
487
+ * `Agent` observes the dispatch. `hooks-codex.json` / `hooks-cursor.json` /
488
+ * `hooks-pi.json` deliberately carry no such entry (those platforms have no
489
+ * `Agent` dispatch tool — `DOCUMENTED_ASYMMETRIES`), so on them a MISSING
490
+ * dispatch record is not evidence of a missing injection. Recording the answer
491
+ * beside the wave is what lets `--verify` tell those two apart later instead of
492
+ * re-deriving a platform fact from an absence.
493
+ *
494
+ * Fail-CLOSED on any read error: an unreadable hooks file means the guard cannot
495
+ * be shown to be installed, and claiming observability we cannot prove is the
496
+ * direction that produces false `injection-missing` accusations.
497
+ *
498
+ * @param {string} [pluginRoot]
499
+ * @param {typeof readFileSync} [readFile]
500
+ * @returns {boolean}
501
+ */
502
+ export function transportObservable(pluginRoot = resolve(import.meta.dirname, '..'), readFile = readFileSync) {
503
+ try {
504
+ const parsed = JSON.parse(readFile(resolve(pluginRoot, 'hooks', 'hooks.json'), 'utf8'));
505
+ const entries = parsed?.hooks?.PreToolUse;
506
+ if (!Array.isArray(entries)) return false;
507
+ return entries.some((entry) => typeof entry?.matcher === 'string'
508
+ && entry.matcher.split('|').map((m) => m.trim()).includes('Agent'));
509
+ } catch {
510
+ return false;
511
+ }
512
+ }
513
+
514
+ /**
515
+ * One `orchestrator.wave_dispatch.scope_materialized` record per wave (#1092) —
516
+ * the DEGRADATION half of the chain.
517
+ *
518
+ * It runs here, and not in the hook, precisely because this command runs on
519
+ * EVERY platform while the hook runs on one. Without it a wave with zero
520
+ * `scope_checked` records is ambiguous between "the coordinator skipped the
521
+ * manifest step" (the #1020 failure this whole chain exists for) and "this
522
+ * platform has no dispatch hook".
523
+ *
524
+ * `repoRoot` is the state directory's PARENT, never `process.cwd()`: the state
525
+ * dir is the artefact this command owns, and a caller invoking it from a
526
+ * subdirectory (or a test from the repo root against a `$TMPDIR` state dir)
527
+ * must not write its record into a different repo's ledger.
528
+ *
529
+ * Best-effort in both directions and SILENT on failure: the corpus pins
530
+ * byte-empty stderr on this command's success path
531
+ * (`tests/integration/wave-scope-producer.test.mjs`), and a telemetry write must
532
+ * never be the thing that fails a materialization.
533
+ *
534
+ * @param {{stateDir: string, wave: number, records: Array<{id: string, files: string[]}>}} params
535
+ * @returns {Promise<void>}
536
+ */
537
+ async function emitScopeMaterialized({ stateDir, wave, records }) {
538
+ try {
539
+ const repoRoot = dirname(resolve(stateDir));
540
+ const agentRecords = records.filter((r) => !isPeerRecordId(r.id));
541
+ const digests = new Set();
542
+ for (const record of agentRecords) {
543
+ if (Array.isArray(record.files) && record.files.length > 0) digests.add(scopeDigest(record.files));
544
+ }
545
+ const { emitEvent, sessionAttribution } = await import('./lib/events.mjs');
546
+ await emitEvent(
547
+ SCOPE_MATERIALIZED_EVENT,
548
+ {
549
+ wave,
550
+ agent_count: agentRecords.length,
551
+ digest_count: digests.size,
552
+ transport_observable: transportObservable(),
553
+ ...sessionAttribution(repoRoot),
554
+ },
555
+ { repoRoot },
556
+ );
557
+ } catch { /* observability is best-effort — it never fails a materialization */ }
558
+ }
559
+
482
560
  /**
483
561
  * @param {string} message
484
562
  * @param {number} code
@@ -536,6 +614,12 @@ export function main() {
536
614
  ? `${JSON.stringify({ ok: true, aggregatePath, perAgentPaths, removedOrphans, retainedOrphans })}\n`
537
615
  : `${aggregatePath}\n`,
538
616
  );
617
+
618
+ // Fire-and-forget AFTER stdout, so the telemetry append can never reorder or
619
+ // delay this command's own contract. Node drains the pending append before
620
+ // exiting because nothing here calls `process.exit()`; the `.catch` keeps an
621
+ // unhandled rejection from turning observability into an exit code.
622
+ emitScopeMaterialized({ stateDir: args.stateDir, wave: args.wave, records }).catch(() => {});
539
623
  } catch (error) {
540
624
  if (error instanceof InputError) {
541
625
  fail(error.message, 1);
@@ -549,7 +633,6 @@ export function main() {
549
633
  }
550
634
  }
551
635
 
552
- const invokedPath = process.argv[1] ? resolve(process.argv[1]) : null;
553
- if (invokedPath === fileURLToPath(import.meta.url)) {
636
+ if (isMainModule(import.meta.url)) {
554
637
  main();
555
638
  }
@@ -41,6 +41,7 @@
41
41
  import { readFileSync, writeFileSync, renameSync, copyFileSync, existsSync } from 'node:fs';
42
42
  import { join, dirname, basename } from 'node:path';
43
43
  import { validateSession, normalizeSession, ValidationError } from './lib/session-schema.mjs';
44
+ import { isMainModule } from './lib/is-main-module.mjs';
44
45
 
45
46
  const DEFAULT_FILE = '.orchestrator/metrics/sessions.jsonl';
46
47
 
@@ -440,7 +441,7 @@ async function main() {
440
441
 
441
442
  // Run main() only when this file is invoked directly as a CLI, not when imported.
442
443
  // Prevents process.exit during test-time imports (#368).
443
- if (import.meta.url === `file://${process.argv[1]}`) {
444
+ if (isMainModule(import.meta.url)) {
444
445
  main().catch((err) => {
445
446
  process.stderr.write(`migrate-sessions-jsonl: unexpected error: ${err?.stack ?? err}\n`);
446
447
  process.exit(2);
@@ -50,7 +50,6 @@
50
50
 
51
51
  import { promises as fs, existsSync } from 'node:fs';
52
52
  import { spawnSync } from 'node:child_process';
53
- import { pathToFileURL } from 'node:url';
54
53
  import path from 'node:path';
55
54
  import os from 'node:os';
56
55
  import {
@@ -58,6 +57,7 @@ import {
58
57
  VAULT_MIGRATION_RULES_PATH,
59
58
  } from './lib/vault-migration-rules.mjs';
60
59
  import { parseColumnFlags, CliFlagError } from './lib/cli-flags.mjs';
60
+ import { isMainModule } from './lib/is-main-module.mjs';
61
61
 
62
62
  // ---------------------------------------------------------------------------
63
63
  // Constants
@@ -773,8 +773,7 @@ function emit(opts, rec) {
773
773
  // importing the module is never a hard error regardless of how the importer runs.
774
774
  // ---------------------------------------------------------------------------
775
775
 
776
- const invokedDirectly =
777
- process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href;
776
+ const invokedDirectly =isMainModule(import.meta.url);
778
777
 
779
778
  if (invokedDirectly) {
780
779
  main().catch((err) => {
@@ -0,0 +1,305 @@
1
+ /**
2
+ * release-session-lock.mjs — CLI: release THIS session's `session.lock`, emit the
3
+ * terminal `orchestrator.session.lock.released` breadcrumb, and PROVE the lock is
4
+ * gone before exiting 0.
5
+ *
6
+ * ## Why a CLI and not one more `release()` call site (#1395)
7
+ *
8
+ * Measured 2026-09-19: three of the four `release()` call sites already emit
9
+ * `orchestrator.session.lock.released` — `hooks/on-session-end.mjs`,
10
+ * `scripts/lib/autopilot/worktree-pipeline.mjs` and
11
+ * `scripts/lib/session-transition.mjs`. The silent one was never code at all: it
12
+ * is the PROSE step `skills/session-end/SKILL.md` § Phase 3.8, executed by hand
13
+ * by the coordinator LLM. It deletes the lock, so the SessionEnd hook later reads
14
+ * `status: 'absent'` and emits nothing — this repo recorded 5 `lock.acquired`
15
+ * against 0 `lock.released`. A prose "call X, then check Y" step is the shape
16
+ * that gets skipped (`.claude/rules/process-contracts.md`: 339 of 351 issues
17
+ * closed without the prose-mandated pre-step); the repair is ONE command that
18
+ * verifies its own effect and exits non-zero when it cannot.
19
+ *
20
+ * ## Why the emit is not inside `release()`
21
+ *
22
+ * Three structural reasons, none of them fixable here:
23
+ * 1. Import cycle — `scripts/lib/events.mjs` imports `session-lock.mjs`
24
+ * (`sessionAttribution()` reads the lock), so `session-lock.mjs` cannot
25
+ * import `events.mjs`.
26
+ * 2. `release()` is synchronous; `emitEvent()` is async.
27
+ * 3. After the unlink the event envelope can no longer read the lock for
28
+ * correlation — which is exactly why this CLI captures `session_id` and
29
+ * `semantic_session_id` from the lock BEFORE releasing and passes both
30
+ * EXPLICITLY in the payload.
31
+ *
32
+ * ## Reuse note (BV-001.2)
33
+ *
34
+ * `leaveSourceRoot()` in `scripts/lib/session-transition.mjs` is the closest
35
+ * existing helper and is deliberately NOT reused: it also deregisters the
36
+ * host-wide registry entry, which a session closing in place must NOT do at
37
+ * Phase 3.8 (the SessionEnd hook owns deregistration, at the real end). Its
38
+ * `LOCK_RELEASED_EVENT` constant IS reused, so the event name has one writer.
39
+ *
40
+ * Exit codes: 0 verified · 1 user/input or ownership error · 2 system error.
41
+ *
42
+ * Exports (for tests): main.
43
+ */
44
+
45
+ import { parseArgs } from 'node:util';
46
+
47
+ import { readLockDetailed, loadOwnerProof, release } from './lib/session-lock.mjs';
48
+ import { LOCK_RELEASED_EVENT } from './lib/session-transition.mjs';
49
+ import { emitEvent } from './lib/events.mjs';
50
+ import { isMainModule } from './lib/is-main-module.mjs';
51
+
52
+ /**
53
+ * `caller` value stamped into the breadcrumb, so this fourth release site stays
54
+ * distinguishable from `on-session-end`, `session-transition` and the autopilot
55
+ * worktree pipeline in the single `orchestrator.session.lock.*` stream.
56
+ */
57
+ export const PHASE_CALLER = 'session-end-phase-3-8';
58
+
59
+ const HELP = `release-session-lock.mjs — release this session's lock and prove it is gone
60
+
61
+ USAGE
62
+ node scripts/release-session-lock.mjs --session-id <raw-id> [--repo-root <path>] [--json]
63
+
64
+ WHAT IT DOES
65
+ 1. Reads <repo-root>/.orchestrator/session.lock.
66
+ 2. Releases it IFF its raw session_id equals --session-id (owner-proof gated,
67
+ exactly as hooks/on-session-end.mjs gates it).
68
+ 3. Emits orchestrator.session.lock.released with the ids read BEFORE the
69
+ delete, plus caller="${PHASE_CALLER}".
70
+ 4. RE-READS the lock path and exits 0 only when our lock is provably gone.
71
+
72
+ FLAGS
73
+ --session-id <id> REQUIRED. The RAW (physical) session id that owns the lock
74
+ — .orchestrator/session.lock "session_id", never STATE.md
75
+ "session" and never the semantic id. A lock owned by a
76
+ different id is NEVER released.
77
+ --repo-root <path> Repo whose lock is released (default: cwd).
78
+ --json Machine-readable result on stdout.
79
+ -h, --help Show this help and exit 0.
80
+
81
+ OUTCOMES
82
+ absent no lock file — nothing released, NO event, exit 0.
83
+ deleted our lock was removed and re-read as gone.
84
+ already-gone ownership matched but the lock had already vanished; the event
85
+ is still written, because a lock removed by a third party while
86
+ we held it is the forensically interesting case.
87
+
88
+ EXIT CODES
89
+ 0 — the lock is provably absent (released now, or nothing to release)
90
+ 1 — user/input or ownership error: missing/empty --session-id, unknown flag,
91
+ or a lock owned by a different session (nothing is touched)
92
+ 2 — system error: unreadable/corrupt lock, filesystem error, owner-proof
93
+ mismatch, the lock still present after release, or the breadcrumb could
94
+ not be written
95
+ `;
96
+
97
+ /**
98
+ * Print the one-line human summary (stdout = data, stderr = diagnostics).
99
+ *
100
+ * @param {{outcome: string, verified: boolean, session_id: string, event_emitted: boolean}} result
101
+ */
102
+ function printHuman(result) {
103
+ if (result.outcome === 'absent') {
104
+ process.stdout.write('session-lock: nothing to release — no lock file (no event written).\n');
105
+ return;
106
+ }
107
+ const verified = result.verified ? 'verified gone' : 'NOT verified — lock still present';
108
+ const event = result.event_emitted ? 'breadcrumb written' : 'breadcrumb FAILED';
109
+ process.stdout.write(
110
+ `session-lock ${result.outcome} for session ${result.session_id} — ${verified}, ${event}.\n`,
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Did OUR lock really vanish?
116
+ *
117
+ * Computed from a FRESH read after `release()` returned, deliberately not from
118
+ * `release()`'s own `verified` field: that field is absent on the benign
119
+ * `no-lock` branch (so a caller copying it reports `verified: false` for a lock
120
+ * that IS gone), and it was computed before this process did anything else. The
121
+ * CLI's whole contract is "exit 0 proves absence", so it re-reads.
122
+ *
123
+ * A FOREIGN lock present at the re-read counts as verified — ours is gone and a
124
+ * sibling legitimately acquired the path in the race window (same rule as
125
+ * `release()`'s post-delete verify; PSA-005: never touch a lock we do not own).
126
+ *
127
+ * @param {string} repoRoot
128
+ * @param {string} sessionId
129
+ * @returns {boolean}
130
+ */
131
+ function ourLockIsGone(repoRoot, sessionId) {
132
+ const after = readLockDetailed({ repoRoot });
133
+ if (after.status === 'absent') return true;
134
+ if (after.status === 'ok') return after.lock.session_id !== sessionId;
135
+ // unreadable / corrupt — absence cannot be confirmed, so it is not claimed.
136
+ return false;
137
+ }
138
+
139
+ /**
140
+ * CLI entry point.
141
+ *
142
+ * @param {{argv?: string[]}} [opts]
143
+ * @returns {Promise<number>} process exit code
144
+ */
145
+ export async function main({ argv = process.argv.slice(2) } = {}) {
146
+ let values;
147
+ try {
148
+ ({ values } = parseArgs({
149
+ args: argv,
150
+ options: {
151
+ 'repo-root': { type: 'string' },
152
+ 'session-id': { type: 'string' },
153
+ json: { type: 'boolean', default: false },
154
+ help: { type: 'boolean', short: 'h', default: false },
155
+ },
156
+ strict: true,
157
+ allowPositionals: false,
158
+ }));
159
+ } catch (err) {
160
+ process.stderr.write(`release-session-lock: ${err?.message ?? 'bad arguments'}\n`);
161
+ process.stderr.write('Run with --help for usage.\n');
162
+ return 1;
163
+ }
164
+
165
+ if (values.help) {
166
+ process.stdout.write(HELP);
167
+ return 0;
168
+ }
169
+
170
+ const sessionId = values['session-id'];
171
+ if (typeof sessionId !== 'string' || sessionId.length === 0) {
172
+ process.stderr.write('release-session-lock: --session-id <raw-id> is required.\n');
173
+ process.stderr.write('Run with --help for usage.\n');
174
+ return 1;
175
+ }
176
+
177
+ const repoRoot = values['repo-root'] ?? process.cwd();
178
+
179
+ const emitResult = async (result) => {
180
+ if (values.json) process.stdout.write(JSON.stringify(result, null, 2) + '\n');
181
+ else printHuman(result);
182
+ };
183
+
184
+ // -- Step 1: read the lock, and capture the ids BEFORE anything is deleted --
185
+ const lockDetail = readLockDetailed({ repoRoot });
186
+
187
+ if (lockDetail.status === 'absent') {
188
+ // Nothing was released, so nothing terminal happened: NO event. Writing one
189
+ // here would put a fictional lifecycle end into the stream — the idempotent
190
+ // re-run case is not a release.
191
+ const result = { ok: true, outcome: 'absent', verified: true, session_id: sessionId, event_emitted: false };
192
+ await emitResult(result);
193
+ return 0;
194
+ }
195
+
196
+ if (lockDetail.status !== 'ok') {
197
+ process.stderr.write(`release-session-lock: lock is ${lockDetail.status} — ownership cannot be established; nothing touched.\n`);
198
+ await emitResult({
199
+ ok: false, outcome: `lock-${lockDetail.status}`, verified: false, session_id: sessionId, event_emitted: false,
200
+ });
201
+ return 2;
202
+ }
203
+
204
+ const lock = lockDetail.lock;
205
+ if (lock.session_id !== sessionId) {
206
+ // A raw-id mismatch never grants release ownership (same gate as
207
+ // hooks/on-session-end.mjs). Reconciliation of a foreign lease is the
208
+ // reaper's job, not this command's.
209
+ process.stderr.write(`release-session-lock: lock is owned by a different session — not released.\n`);
210
+ await emitResult({
211
+ ok: false, outcome: 'session-mismatch', verified: false, session_id: sessionId, event_emitted: false,
212
+ });
213
+ return 1;
214
+ }
215
+
216
+ const semanticSessionId = typeof lock.semantic_session_id === 'string' && lock.semantic_session_id.length > 0
217
+ ? lock.semantic_session_id
218
+ : null;
219
+
220
+ // -- Step 2: release, owner-proof gated (#987/#989) ------------------------
221
+ // `proof` is null whenever ownership cannot be proven; release() treats null
222
+ // as absent and degrades to the session_id-only path, so it is passed
223
+ // through unguarded.
224
+ const proof = loadOwnerProof({ repoRoot });
225
+ const releaseResult = release({ sessionId, repoRoot, proof });
226
+
227
+ if (releaseResult.ok !== true) {
228
+ process.stderr.write(`release-session-lock: release failed — ${releaseResult.reason ?? 'fs-error'}\n`);
229
+ await emitResult({
230
+ ok: false, outcome: `release-${releaseResult.reason ?? 'fs-error'}`, verified: false, session_id: sessionId, event_emitted: false,
231
+ });
232
+ return 2;
233
+ }
234
+
235
+ const alreadyGone = releaseResult.deleted !== true && releaseResult.reason === 'no-lock';
236
+ if (releaseResult.deleted !== true && !alreadyGone) {
237
+ // 'proof-mismatch' (and any future non-delete reason): ownership matched on
238
+ // the raw id but the second factor contradicted it. Contradictory on-disk
239
+ // state, not a bad argument — exit 2.
240
+ process.stderr.write(`release-session-lock: lock not released — ${releaseResult.reason ?? 'not-deleted'}\n`);
241
+ await emitResult({
242
+ ok: false, outcome: `release-${releaseResult.reason ?? 'not-deleted'}`, verified: false, session_id: sessionId, event_emitted: false,
243
+ });
244
+ return 2;
245
+ }
246
+
247
+ // -- Step 3: prove it ------------------------------------------------------
248
+ const verified = ourLockIsGone(repoRoot, sessionId);
249
+ const outcome = alreadyGone ? 'already-gone' : 'deleted';
250
+
251
+ // -- Step 4: the terminal breadcrumb --------------------------------------
252
+ // The ids are passed EXPLICITLY: the lock is gone by now, so emitEvent's
253
+ // lock-derived correlation would find nothing to attribute this record to.
254
+ let eventEmitted = true;
255
+ let emitError = null;
256
+ try {
257
+ await emitEvent(LOCK_RELEASED_EVENT, {
258
+ session_id: sessionId,
259
+ ...(semanticSessionId !== null ? { semantic_session_id: semanticSessionId } : {}),
260
+ caller: PHASE_CALLER,
261
+ outcome,
262
+ verified,
263
+ }, { repoRoot });
264
+ } catch (err) {
265
+ eventEmitted = false;
266
+ emitError = err?.message ?? String(err);
267
+ }
268
+
269
+ const result = {
270
+ ok: verified && eventEmitted,
271
+ outcome,
272
+ verified,
273
+ session_id: sessionId,
274
+ ...(semanticSessionId !== null ? { semantic_session_id: semanticSessionId } : {}),
275
+ event_emitted: eventEmitted,
276
+ ...(emitError !== null ? { event_error: emitError } : {}),
277
+ };
278
+ await emitResult(result);
279
+
280
+ if (!verified) {
281
+ process.stderr.write('release-session-lock: the lock is STILL present after release — not verified.\n');
282
+ return 2;
283
+ }
284
+ if (!eventEmitted) {
285
+ // The release itself succeeded; what failed is the one thing this command
286
+ // exists to guarantee, so it is reported as a system error rather than
287
+ // swallowed. Phase 3.8 treats a non-zero exit as a WARN and continues.
288
+ process.stderr.write(`release-session-lock: breadcrumb could not be written — ${emitError}\n`);
289
+ return 2;
290
+ }
291
+ return 0;
292
+ }
293
+
294
+ // ---------------------------------------------------------------------------
295
+ // CLI guard — prevents execution during test-time imports.
296
+ // ---------------------------------------------------------------------------
297
+
298
+ if (isMainModule(import.meta.url)) {
299
+ main()
300
+ .then((code) => process.exit(code))
301
+ .catch((err) => {
302
+ process.stderr.write(`release-session-lock: unexpected error: ${err?.stack ?? err}\n`);
303
+ process.exit(2);
304
+ });
305
+ }