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
@@ -0,0 +1,162 @@
1
+ import { matchBlockHeader } from './block-header.mjs';
2
+ import { preprocessBlockLines } from './block-preprocess.mjs';
3
+
4
+ /**
5
+ * reaper.mjs — Parser for the top-level `reaper:` YAML block
6
+ * (PRD `docs/prd/2026-09-20-prozessgruppen-kill-und-waisen-waechter.md` §4,
7
+ * Epic #1425 / issue #1432 B4).
8
+ *
9
+ * Configures the orphan watchdog that `hooks/post-tool-batch-wave-signal.mjs`
10
+ * (PostToolBatch) and `hooks/on-stop.mjs` (Stop + SubagentStop) trigger as a
11
+ * detached fire-and-forget scan. The scan itself lives in
12
+ * `scripts/lib/orphan-reaper.mjs`; this module only reads the numbers.
13
+ *
14
+ * DEFAULT OFF. `enabled` defaults to `false` — unlike `loop-guard`, this signal
15
+ * can SEND SIGNALS to processes, so it ships inert and is armed per repo only
16
+ * after its firing rate has been measured (`.claude/rules/host-resources.md`
17
+ * HR-101: a class above ~10 % is a broken instrument; HR-105: a rule nothing
18
+ * records is unfalsifiable).
19
+ *
20
+ * Tolerant parser: malformed values silently fall back to defaults.
21
+ *
22
+ * Returns `{ enabled, mode, 'min-age-seconds', 'min-scan-interval-seconds',
23
+ * 'kill-grace-ms', 'verify-wait-ms', 'max-hook-latency-ms', 'false-alarm-window' }`.
24
+ */
25
+
26
+ /**
27
+ * Startwerte für Stufe 1 — NOT calibrated. Every number carries its provenance
28
+ * from the PRD §4 parameter table ("keine Zahl ohne ihre Population").
29
+ *
30
+ * RUNTIME SSOT is `REAPER_DEFAULTS` in `scripts/lib/orphan-reaper.mjs`; these
31
+ * literals mirror it deliberately rather than importing it, because that module
32
+ * pulls `process-group.mjs` + `resource-probe/parsers.mjs` into the config
33
+ * import graph, which every `parseSessionConfig()` caller would then pay for.
34
+ * The parity is held by a test, not by an import
35
+ * (`tests/lib/config/reaper.test.mjs` § defaults mirror REAPER_DEFAULTS).
36
+ */
37
+ /** 300 s — DevWatchdog's hard limit for `tsgo`; the 2026-09-20 orphans were 7–17 min old. */
38
+ const DEFAULT_MIN_AGE_SECONDS = 300;
39
+ /** 30 s — DevWatchdog's normal scan cadence; keeps a PostToolBatch storm from taxing every tool call. */
40
+ const DEFAULT_MIN_SCAN_INTERVAL_SECONDS = 30;
41
+ /** 10 000 ms — `DEFAULT_KILL_GRACE_MS` from `dispatch-common.mjs:61`; repo convention, not newly invented. */
42
+ const DEFAULT_KILL_GRACE_MS = 10_000;
43
+ /** 500 ms — wait before reading the effect back; without it the 2026-09-20 check falsely reported "still alive". */
44
+ const DEFAULT_VERIFY_WAIT_MS = 500;
45
+ /** 50 ms — ceiling a scan may delay a hook by; above that the scan counts as too expensive. */
46
+ const DEFAULT_MAX_HOOK_LATENCY_MS = 50;
47
+ /** 50 decisions — rolling window over the JSONL audit (B5), so the rate has a population on quiet hosts too. */
48
+ const DEFAULT_FALSE_ALARM_WINDOW = 50;
49
+
50
+ /** `report` = decide + audit only (dryRun); `kill` = send signals (PRD Stufe 2, after calibration). */
51
+ const MODE_VALUES = new Set(['report', 'kill']);
52
+ const DEFAULT_MODE = 'report';
53
+
54
+ /**
55
+ * Coerce a raw string into a finite integer >= `min`, else `fallback`.
56
+ *
57
+ * @param {string} raw
58
+ * @param {number} fallback
59
+ * @param {number} min
60
+ * @returns {number}
61
+ */
62
+ function parseBoundedInt(raw, fallback, min) {
63
+ if (!/^-?\d+$/.test(raw)) return fallback;
64
+ const n = Number.parseInt(raw, 10);
65
+ if (!Number.isFinite(n) || n < min) return fallback;
66
+ return n;
67
+ }
68
+
69
+ /**
70
+ * Parse the top-level `reaper:` YAML block from markdown content.
71
+ * Independent of the `## Session Config` section boundary.
72
+ *
73
+ * @param {string} content — full file contents
74
+ * @returns {{enabled: boolean, mode: string, 'min-age-seconds': number,
75
+ * 'min-scan-interval-seconds': number, 'kill-grace-ms': number,
76
+ * 'verify-wait-ms': number, 'max-hook-latency-ms': number,
77
+ * 'false-alarm-window': number}}
78
+ */
79
+ export function _parseReaper(content) {
80
+ let enabled = false;
81
+ let mode = DEFAULT_MODE;
82
+ let minAgeSeconds = DEFAULT_MIN_AGE_SECONDS;
83
+ let minScanIntervalSeconds = DEFAULT_MIN_SCAN_INTERVAL_SECONDS;
84
+ let killGraceMs = DEFAULT_KILL_GRACE_MS;
85
+ let verifyWaitMs = DEFAULT_VERIFY_WAIT_MS;
86
+ let maxHookLatencyMs = DEFAULT_MAX_HOOK_LATENCY_MS;
87
+ let falseAlarmWindow = DEFAULT_FALSE_ALARM_WINDOW;
88
+
89
+ const lines = preprocessBlockLines(content);
90
+ let inBlock = false;
91
+ const blockLines = [];
92
+
93
+ for (const rawLine of lines) {
94
+ const line = rawLine.replace(/\r$/, '');
95
+ if (!inBlock) {
96
+ if (matchBlockHeader(line, 'reaper')) inBlock = true;
97
+ continue;
98
+ }
99
+ if (line.length > 0 && !/^\s/.test(line)) break;
100
+ blockLines.push(line);
101
+ }
102
+
103
+ for (const rawLine of blockLines) {
104
+ const clean = rawLine.replace(/\s*#.*$/, '').replace(/\s+$/, '');
105
+ if (!clean.trim()) continue;
106
+
107
+ const kvMatch = clean.match(/^\s+([a-zA-Z_-]+):\s*(.*)/);
108
+ if (!kvMatch) continue;
109
+
110
+ const k = kvMatch[1];
111
+ let v = kvMatch[2].trim();
112
+ if (v.startsWith('"') && v.endsWith('"') && v.length >= 2) v = v.slice(1, -1);
113
+ else if (v.startsWith("'") && v.endsWith("'") && v.length >= 2) v = v.slice(1, -1);
114
+
115
+ switch (k) {
116
+ case 'enabled':
117
+ // Default is false → only an explicit "true" arms the watchdog.
118
+ enabled = v.toLowerCase() === 'true';
119
+ break;
120
+ case 'mode':
121
+ // An unknown mode falls back to the INERT one — never to `kill`.
122
+ mode = MODE_VALUES.has(v.toLowerCase()) ? v.toLowerCase() : DEFAULT_MODE;
123
+ break;
124
+ case 'min-age-seconds':
125
+ minAgeSeconds = parseBoundedInt(v, DEFAULT_MIN_AGE_SECONDS, 0);
126
+ break;
127
+ case 'min-scan-interval-seconds':
128
+ minScanIntervalSeconds = parseBoundedInt(v, DEFAULT_MIN_SCAN_INTERVAL_SECONDS, 1);
129
+ break;
130
+ case 'kill-grace-ms':
131
+ killGraceMs = parseBoundedInt(v, DEFAULT_KILL_GRACE_MS, 0);
132
+ break;
133
+ case 'verify-wait-ms':
134
+ verifyWaitMs = parseBoundedInt(v, DEFAULT_VERIFY_WAIT_MS, 0);
135
+ break;
136
+ case 'max-hook-latency-ms':
137
+ maxHookLatencyMs = parseBoundedInt(v, DEFAULT_MAX_HOOK_LATENCY_MS, 1);
138
+ break;
139
+ case 'false-alarm-window':
140
+ falseAlarmWindow = parseBoundedInt(v, DEFAULT_FALSE_ALARM_WINDOW, 1);
141
+ break;
142
+ }
143
+ }
144
+
145
+ // Self-healing clamp: a `min-age-seconds` below the scan interval is dead
146
+ // config — a process younger than one scan period can be born and reaped
147
+ // between two scans, so the watchdog would fire on processes it has never
148
+ // seen before. Widen the age floor to at least one scan period (the same
149
+ // shape as loop-guard's window >= threshold clamp).
150
+ if (minAgeSeconds < minScanIntervalSeconds) minAgeSeconds = minScanIntervalSeconds;
151
+
152
+ return {
153
+ enabled,
154
+ mode,
155
+ 'min-age-seconds': minAgeSeconds,
156
+ 'min-scan-interval-seconds': minScanIntervalSeconds,
157
+ 'kill-grace-ms': killGraceMs,
158
+ 'verify-wait-ms': verifyWaitMs,
159
+ 'max-hook-latency-ms': maxHookLatencyMs,
160
+ 'false-alarm-window': falseAlarmWindow,
161
+ };
162
+ }
@@ -65,6 +65,8 @@ import { _parseHealthEndpoints, _parseEcosystemHealthBlockEnabled } from './conf
65
65
  import { _parseBrokenWindow } from './config/broken-window.mjs';
66
66
  import { _parseSlopcheck } from './config/slopcheck.mjs';
67
67
  import { _parseDiscoveryValidator } from './config/discovery-validator.mjs';
68
+ import { _parseReaper } from './config/reaper.mjs';
69
+ import { _parseGate } from './config/gate.mjs';
68
70
  import { _parseTemplatesFirst } from './config/templates-first.mjs';
69
71
  import { _parseVerificationAutoFix } from './config/verification-auto-fix.mjs';
70
72
  import { _parseDialectic } from './config/dialectic.mjs';
@@ -417,6 +419,16 @@ export function parseSessionConfig(mdContent, { hostPaths } = {}) {
417
419
  // discovery-validator: parsed from full content (PSA-006 enforcement / issue #567)
418
420
  const discoveryValidator = _parseDiscoveryValidator(mdContent);
419
421
 
422
+ // reaper: orphan-process watchdog, default OFF (PRD 2026-09-20 Prozessgruppen-Kill
423
+ // und Waisen-Wächter, Epic #1425 / #1432 B4). Consumed by the two hooks that
424
+ // trigger the detached scan — post-tool-batch-wave-signal.mjs and on-stop.mjs.
425
+ const reaper = _parseReaper(mdContent);
426
+
427
+ // gate: wall-clock ceiling for gate path B (run-quality-gate.mjs →
428
+ // gate-*.mjs), which had no timeout at all before #1425 A3. The operator
429
+ // override SO_GATE_TIMEOUT_MS still wins over this committed value.
430
+ const gate = _parseGate(mdContent);
431
+
420
432
  // templates-first: parsed from full content (PRD gsd Pattern 3 / issues #517, #519)
421
433
  const templatesFirst = _parseTemplatesFirst(mdContent);
422
434
 
@@ -555,6 +567,8 @@ export function parseSessionConfig(mdContent, { hostPaths } = {}) {
555
567
  'skill-evolution': skillEvolution,
556
568
  'dispatcher-autonomy': dispatcherAutonomy,
557
569
  'discovery-validator': discoveryValidator,
570
+ 'reaper': reaper,
571
+ 'gate': gate,
558
572
  'templates-first': templatesFirst,
559
573
  'verification-auto-fix': verificationAutoFix,
560
574
  'dialectic': dialectic,
@@ -45,7 +45,7 @@
45
45
 
46
46
  import { existsSync, statSync, openSync, readSync, closeSync } from 'node:fs';
47
47
  import { join } from 'node:path';
48
- import { pathToFileURL } from 'node:url';
48
+ import { isMainModule } from './is-main-module.mjs';
49
49
 
50
50
  const DEFAULT_INTERVAL_S = 2;
51
51
  const EVENTS_FILE_REL = '.orchestrator/metrics/events.jsonl';
@@ -144,20 +144,62 @@ function tailRead(absPath, prevOffset) {
144
144
  * Event types this monitor is meant to classify. Anything else is ignored even
145
145
  * when it carries a wave number.
146
146
  *
147
- * The allowlist is a wave-LIFECYCLE prefix, the agent-dispatch/-stop counters,
148
- * and the quality-gate envelope — i.e. exactly the records that can carry the
149
- * three measurements `evaluateSignals` compares (`files_changed`, the test
150
- * pass count, the per-wave agent count). It is deliberately fail-CLOSED: a new
151
- * event type that gains a `wave_number` field must be added here consciously.
147
+ * The allowlist is the wave-COMPLETION record, the agent-dispatch/-stop
148
+ * counters, and the quality-gate envelope — i.e. exactly the records that can
149
+ * carry the three measurements `evaluateSignals` compares (`files_changed`, the
150
+ * test pass count, the per-wave agent count). It is deliberately fail-CLOSED: a
151
+ * new event type that gains a `wave_number` field must be added here consciously.
152
152
  *
153
153
  * The gate admission is a TYPE-AND-SHAPE gate, never a bare prefix widening —
154
154
  * see `isWaveScopedEvent` for why.
155
+ *
156
+ * `orchestrator.wave.completed` is admitted by its EXACT name. The
157
+ * `orchestrator.wave.` prefix that stood here until 2026-09-19 also admitted
158
+ * `wave.started` (and `wave.final_refused`), neither of which carries a
159
+ * measurement — and it cost the one signal the diff keys
160
+ * exist for: the batch hook wrote completed{N} and started{N+1} in the same
161
+ * millisecond, so they shared a tail tick, started{N+1} instantiated an EMPTY
162
+ * summary for N+1, the tick's highest wave became N+1, and the (N-1, N)
163
+ * `shrinking_diff` pair was never compared (architect-reviewer repro,
164
+ * 40 -> 10 -> 5 files: same tick `[]`, split ticks `shrinking_diff:2` and `:3`).
155
165
  */
156
- const WAVE_EVENT_PREFIX = 'orchestrator.wave.';
166
+ const WAVE_COMPLETED_EVENT = 'orchestrator.wave.completed';
157
167
  const WAVE_EVENT_NAMES = new Set(['agent.dispatched', 'orchestrator.agent.dispatched']);
158
168
  const AGENT_STOPPED_EVENT = 'orchestrator.agent.stopped';
159
169
  const GATE_EVENT_PREFIX = 'orchestrator.quality_gate.';
160
170
 
171
+ /**
172
+ * True when an `orchestrator.agent.stopped` record names the agent that stopped.
173
+ *
174
+ * The shape half of the type-and-shape gate for the agent counter: the event
175
+ * type is BIMODAL (#939/#949, documented at `hooks/on-stop.mjs:554-580`). Only
176
+ * the records carrying a non-empty `agent` describe a real dispatched agent;
177
+ * the rest are the phantom-stop class the coordinator's own `Stop` emits, and
178
+ * counting them inflates every per-wave agent count and the `velocity_drop`
179
+ * signal derived from it.
180
+ *
181
+ * Measured 2026-09-18 over the then-active `.orchestrator/metrics/events.jsonl`
182
+ * plus the legacy `.jsonl.1` backup beside it (destroyed 2026-09-19):
183
+ * → { total_stopped: 16438, with_wave: 6553, with_wave_no_agent: 5930 }
184
+ * i.e. 90.5% of the wave-scoped stops carry no `agent` — a ~10.5x inflation.
185
+ *
186
+ * To re-measure, read the ledger with `readEventsWithRotations(repoRoot)` from
187
+ * `scripts/lib/events.mjs` — it yields the active file plus every archive in
188
+ * time order and reports a rotation whose archive is gone as a `gaps` entry
189
+ * with `complete: false`, which a `cat` over rotation files cannot. Rotation
190
+ * writes `_archive/events-<firstTs>_<lastTs>.jsonl` and an
191
+ * `orchestrator.events.rotated` first line since #1401; the `.1`..`.N` ring is
192
+ * no longer written (see `events-rotation.mjs` § Why `_archive/<name>`).
193
+ * Recipe with the jq filter: `skills/convergence-monitoring/SIGNALS.md`
194
+ * § Live monitor input.
195
+ *
196
+ * @param {Record<string, unknown>} rec
197
+ * @returns {boolean}
198
+ */
199
+ function namesAnAgent(rec) {
200
+ return typeof rec.agent === 'string' && rec.agent.trim() !== '';
201
+ }
202
+
161
203
  /**
162
204
  * True when a record carries a `counts` object this monitor can fold, i.e. a
163
205
  * plain object with a numeric `passed`. `counts: null`, an array, or a string
@@ -183,13 +225,18 @@ function hasFoldableCounts(rec) {
183
225
  * the real wave record arrives. Hence the discriminator: a gate record is
184
226
  * wave-scoped only when it has BOTH `wave_number` and a well-formed `counts`.
185
227
  *
228
+ * The same discriminator applies to `orchestrator.agent.stopped`: the type is
229
+ * bimodal, so a stop record is admitted only when it NAMES an agent — see
230
+ * `namesAnAgent`.
231
+ *
186
232
  * @param {string} evType
187
233
  * @param {Record<string, unknown>} rec
188
234
  * @returns {boolean}
189
235
  */
190
236
  function isWaveScopedEvent(evType, rec = {}) {
191
- if (evType.startsWith(WAVE_EVENT_PREFIX)) return true;
192
- if (WAVE_EVENT_NAMES.has(evType) || evType === AGENT_STOPPED_EVENT) return true;
237
+ if (evType === WAVE_COMPLETED_EVENT) return true;
238
+ if (WAVE_EVENT_NAMES.has(evType)) return true;
239
+ if (evType === AGENT_STOPPED_EVENT) return namesAnAgent(rec);
193
240
  if (evType.startsWith(GATE_EVENT_PREFIX)) {
194
241
  return pickInt(rec.wave_number) !== null && hasFoldableCounts(rec);
195
242
  }
@@ -235,9 +282,16 @@ function isWaveScopedEvent(evType, rec = {}) {
235
282
  *
236
283
  * `orchestrator.agent.stopped` is counted per record toward the wave's agent
237
284
  * count, the same way `agent.dispatched` is — it is the only per-agent record
238
- * this repo actually emits with a wave number (11,754 records measured
239
- * 2026-09-05 in `.orchestrator/metrics/events.jsonl`, against 0 for
240
- * `agent.dispatched`).
285
+ * this repo actually emits with a wave number. But it is counted ONLY when the
286
+ * record NAMES its agent (`namesAnAgent`): the type is bimodal, and the
287
+ * unfiltered count is dominated by the #939/#949 phantom-stop class.
288
+ *
289
+ * Measured 2026-09-18 over `.orchestrator/metrics/events.jsonl{,.1}` (same jq
290
+ * envelope quoted at `namesAnAgent`):
291
+ * agent.stopped, any shape ................. 16438
292
+ * ... carrying a wave number ............... 6553
293
+ * ... of those, WITHOUT an `agent` field ... 5930 (90.5% — not counted)
294
+ * ... of those, WITH a non-empty `agent` ... 623 (what this counter reads)
241
295
  *
242
296
  * @param {Record<string, unknown>} rec
243
297
  * @param {Map<number, WaveSummary>} state
@@ -431,6 +485,15 @@ async function tailLoop(intervalS) {
431
485
  continue;
432
486
  }
433
487
  offset = tick.offset;
488
+ // Only the HIGHEST wave this tick touched is evaluated. NAMED CEILING
489
+ // (BV-004): a wave-(N+1) record that DOES carry a measurement (a named
490
+ // agent.dispatched/-stopped, a gated quality_gate envelope) landing in the
491
+ // same tick as completed{N} still masks the (N-1, N) pair. Evaluating every
492
+ // touched wave would close that, but the first tick reads the whole ledger
493
+ // from offset 0 and wave numbers are not session-scoped, so one stale
494
+ // startup evaluation would become one per wave number in history. Revisit
495
+ // if the ledger shows such a record within one poll interval of a
496
+ // completed{N}.
434
497
  let latestWave = -1;
435
498
  for (const line of tick.lines) {
436
499
  /** @type {Record<string, unknown> | null} */
@@ -502,7 +565,7 @@ function main() {
502
565
 
503
566
  // Run the tail loop only when executed as a script. Importing the module (for
504
567
  // unit tests over `classify`) must not parse vitest's argv and exit 1.
505
- if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
568
+ if (isMainModule(import.meta.url)) {
506
569
  main();
507
570
  }
508
571
 
@@ -18,10 +18,10 @@
18
18
  import { spawnSync } from 'node:child_process';
19
19
  import { existsSync, readFileSync } from 'node:fs';
20
20
  import path from 'node:path';
21
- import { pathToFileURL } from 'node:url';
22
21
 
23
22
  import { matcherMatches, readHookDecision } from './pi-hook-bridge.mjs';
24
23
  import { resolvePluginRoot } from './plugin-root.mjs';
24
+ import { isMainModule } from './is-main-module.mjs';
25
25
 
26
26
  export const CURSOR_TO_CANONICAL_EVENT = Object.freeze({
27
27
  sessionStart: 'SessionStart',
@@ -434,7 +434,7 @@ async function main() {
434
434
  process.stdout.write(`${JSON.stringify(toCursorHookOutput(eventName, result))}\n`);
435
435
  }
436
436
 
437
- const isMain = process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href;
437
+ const isMain =isMainModule(import.meta.url);
438
438
  if (isMain) {
439
439
  main().catch((err) => {
440
440
  process.stderr.write(`cursor-hook-bridge: ${err instanceof Error ? err.message : err}\n`);
@@ -58,6 +58,7 @@
58
58
  import { readdirSync, readFileSync, statSync } from 'node:fs';
59
59
  import { fileURLToPath } from 'node:url';
60
60
  import { join, relative, resolve } from 'node:path';
61
+ import { isMainModule } from './is-main-module.mjs';
61
62
 
62
63
  /** Block-scalar indicator on the `description:` line: '>' (folded) or '|' (literal), optional chomping '+'/'-'. */
63
64
  const BLOCK_SCALAR_RE = /^([>|])([+-]?)\s*$/;
@@ -526,10 +527,6 @@ function main() {
526
527
  }
527
528
 
528
529
  const __filename = fileURLToPath(import.meta.url);
529
- const isMain =
530
- typeof process !== 'undefined' &&
531
- process.argv[1] !== null &&
532
- process.argv[1] !== undefined &&
533
- resolve(process.argv[1]) === resolve(__filename);
530
+ const isMain =isMainModule(import.meta.url);
534
531
 
535
532
  if (isMain) main();
@@ -32,6 +32,7 @@ import { warn } from '../common.mjs';
32
32
  import { acquire } from '../session-lock.mjs';
33
33
  import { enumerateCandidates, freeCandidates } from './enumerate.mjs';
34
34
  import { rankCandidates } from './rank.mjs';
35
+ import { isMainModule } from '../is-main-module.mjs';
35
36
 
36
37
  const VERSION = '1.0.0';
37
38
 
@@ -247,7 +248,7 @@ export async function main(argv) {
247
248
  }
248
249
 
249
250
  // Run main only when executed directly, never on import.
250
- if (import.meta.url === `file://${process.argv[1]}`) {
251
+ if (isMainModule(import.meta.url)) {
251
252
  main(process.argv.slice(2))
252
253
  .then((code) => process.exit(code))
253
254
  .catch((err) => {
@@ -40,6 +40,17 @@
40
40
  import { existsSync, statSync, readFileSync } from 'node:fs';
41
41
  import { join } from 'node:path';
42
42
 
43
+ // Ceiling (BV-004): one process per session that invoked session-start
44
+ // (monitors/monitors.json `when: on-skill-invoke:session-start`), alive until
45
+ // SIGTERM; one stat + one full readFileSync of ONE local file per 900 s
46
+ // (resolved against CLAUDE_PLUGIN_ROOT, not the repo); no network, no repo
47
+ // enumeration, no Session Config read (runs even with `ecosystem-health: false`).
48
+ // The watched file has NO producer (0 writers, measured 2026-09-18:
49
+ // `rg -l "ecosystem-health\.jsonl"` → this file only) — every run emits
50
+ // `no-state-yet` and idles. Revisit when: (1) code starts writing
51
+ // .orchestrator/metrics/ecosystem-health.jsonl → switch to a tail read and gate
52
+ // on `ecosystem-health` enabled; or (2) peer sessions regularly exceed ~5 (one
53
+ // idle node process per live session).
43
54
  const DEFAULT_INTERVAL_S = 15 * 60; // 15min
44
55
  const STATE_FILE_REL = '.orchestrator/metrics/ecosystem-health.jsonl';
45
56
 
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import { runEcosystemWizard } from './ecosystem-wizard/wizard-prompt.mjs';
10
+ import { isMainModule } from './is-main-module.mjs';
10
11
 
11
12
  export { detectCiProvider } from './ecosystem-wizard/ci-detector.mjs';
12
13
  export {
@@ -31,7 +32,7 @@ export { runEcosystemWizard } from './ecosystem-wizard/wizard-prompt.mjs';
31
32
  // CLI entry
32
33
  // ---------------------------------------------------------------------------
33
34
 
34
- if (process.argv[1] && process.argv[1].endsWith('ecosystem-wizard.mjs')) {
35
+ if (isMainModule(import.meta.url)) {
35
36
  const args = process.argv.slice(2);
36
37
  const repoRootIdx = args.indexOf('--repo-root');
37
38
  const dryRun = args.includes('--dry-run');