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
@@ -20,19 +20,24 @@
20
20
  * resolve to the wrong GitLab instance on a multi-host machine, turning
21
21
  * this whole module into a silent no-op.
22
22
  *
23
- * Usage (from session-end Phase 5 issue-close loop):
23
+ * Usage (session-end Phase 5 issue-close step) — ONE command strips, closes
24
+ * and verifies every resolved issue:
24
25
  *
25
- * import { stripStatusLabels } from '${PLUGIN_ROOT}/scripts/lib/issue-close-strip-labels.mjs';
26
+ * node scripts/lib/issue-close-strip-labels.mjs --close --vcs gitlab 1388 1387
26
27
  *
27
- * const { stripped, error } = await stripStatusLabels({ issueId: 42, vcs: 'gitlab' });
28
- * if (error) console.warn(`⚠ label strip failed for #${issueId}: ${error}`);
29
- * else if (stripped.length) console.log(`Stripped ${stripped.join(', ')} from #${issueId}`);
30
- * // then close the issue with glab/gh as usual
28
+ * It prints one JSON line per id and exits 1 when any id did not verify as
29
+ * closed. The mechanical call exists because the earlier prose-only step
30
+ * ("call stripStatusLabels, then close") was skipped often enough that 339
31
+ * closed issues still carried `status:in-progress` (measured 2026-09-19).
32
+ * `closeIssues()` is the same sequence as a library call; `stripStatusLabels()`
33
+ * stays exported for callers that only strip.
31
34
  *
32
35
  * Auto-created by `scripts/lib/issue-close-strip-labels.mjs` (#308).
33
36
  */
34
37
 
35
38
  import { execFileSync } from 'node:child_process';
39
+ import { parseArgs } from 'node:util';
40
+ import { isMainModule } from './is-main-module.mjs';
36
41
  import { resolveRepoSpec } from './vcs-repo-spec.mjs';
37
42
 
38
43
  /** Regex that matches any `status:*` label name. */
@@ -43,11 +48,12 @@ const STATUS_LABEL_RE = /^status:/;
43
48
  *
44
49
  * @param {string} cmd
45
50
  * @param {string[]} args
51
+ * @param {typeof execFileSync} [execFile] injectable executor (tests)
46
52
  * @returns {{ ok: boolean, stdout: string, stderr: string }}
47
53
  */
48
- function runCli(cmd, args) {
54
+ function runCli(cmd, args, execFile = execFileSync) {
49
55
  try {
50
- const stdout = execFileSync(cmd, args, {
56
+ const stdout = execFile(cmd, args, {
51
57
  encoding: 'utf8',
52
58
  stdio: ['ignore', 'pipe', 'pipe'],
53
59
  });
@@ -61,50 +67,50 @@ function runCli(cmd, args) {
61
67
  }
62
68
 
63
69
  /**
64
- * Fetch the label names currently applied to an issue.
70
+ * Read an issue as JSON via `glab issue view --output json` (every field) or
71
+ * `gh issue view --json <ghFields>` (only the named fields).
65
72
  *
66
- * @param {{ issueId: number | string, vcs: 'gitlab' | 'github', spec?: string }} opts
73
+ * @param {{ issueId: string, vcs: 'gitlab' | 'github', spec?: string,
74
+ * ghFields: string, execFile?: typeof execFileSync }} opts
67
75
  * `spec` is the resolved `-R`/`--repo` host-pinning value (#839); omitted
68
76
  * (undefined) means no `-R` is appended — never emit `-R undefined`.
69
- * @returns {{ ok: boolean, labels: string[], stderr: string }}
77
+ * @returns {{ ok: boolean, data: any, stderr: string }}
70
78
  */
71
- function fetchLabels({ issueId, vcs, spec }) {
72
- const id = String(issueId);
73
-
74
- if (vcs === 'github') {
75
- // `gh issue view <NUMBER> --json labels` returns { labels: [{name, ...}] }
76
- const args = ['issue', 'view', id, '--json', 'labels'];
77
- if (spec) args.push('-R', spec);
78
- const res = runCli('gh', args);
79
- if (!res.ok) return { ok: false, labels: [], stderr: res.stderr };
80
- let parsed;
81
- try {
82
- parsed = JSON.parse(res.stdout);
83
- } catch (e) {
84
- return { ok: false, labels: [], stderr: `JSON parse failed: ${e && e.message ? e.message : String(e)}` };
85
- }
86
- const labels = Array.isArray(parsed?.labels)
87
- ? parsed.labels.map((l) => (typeof l?.name === 'string' ? l.name : String(l))).filter(Boolean)
88
- : [];
89
- return { ok: true, labels, stderr: '' };
90
- }
91
-
92
- // Default: gitlab via glab.
93
- // `glab issue view <IID> --output json` returns an issue object with a `labels` array.
94
- const args = ['issue', 'view', id, '--output', 'json'];
79
+ function viewIssueJson({ issueId, vcs, spec, ghFields, execFile }) {
80
+ const args =
81
+ vcs === 'github'
82
+ ? ['issue', 'view', issueId, '--json', ghFields]
83
+ : ['issue', 'view', issueId, '--output', 'json'];
95
84
  if (spec) args.push('-R', spec);
96
- const res = runCli('glab', args);
97
- if (!res.ok) return { ok: false, labels: [], stderr: res.stderr };
98
- let parsed;
85
+ const res = runCli(vcs === 'github' ? 'gh' : 'glab', args, execFile);
86
+ if (!res.ok) return { ok: false, data: null, stderr: res.stderr };
99
87
  try {
100
- parsed = JSON.parse(res.stdout);
88
+ return { ok: true, data: JSON.parse(res.stdout), stderr: '' };
101
89
  } catch (e) {
102
- return { ok: false, labels: [], stderr: `JSON parse failed: ${e && e.message ? e.message : String(e)}` };
90
+ return {
91
+ ok: false,
92
+ data: null,
93
+ stderr: `JSON parse failed: ${e && e.message ? e.message : String(e)}`,
94
+ };
103
95
  }
104
- // GitLab JSON shape: { labels: ["status:ready", "priority::high", ...] }
105
- const labels = Array.isArray(parsed?.labels)
106
- ? parsed.labels.map((l) => (typeof l === 'string' ? l : String(l))).filter(Boolean)
107
- : [];
96
+ }
97
+
98
+ /**
99
+ * Fetch the label names currently applied to an issue.
100
+ *
101
+ * GitHub shape: `{ labels: [{ name }] }`; GitLab shape: `{ labels: ["status:ready", ...] }`.
102
+ *
103
+ * @param {{ issueId: string, vcs: 'gitlab' | 'github', spec?: string, execFile?: typeof execFileSync }} opts
104
+ * @returns {{ ok: boolean, labels: string[], stderr: string }}
105
+ */
106
+ function fetchLabels({ issueId, vcs, spec, execFile }) {
107
+ const { ok, data, stderr } = viewIssueJson({ issueId, vcs, spec, ghFields: 'labels', execFile });
108
+ if (!ok) return { ok: false, labels: [], stderr };
109
+ const toName =
110
+ vcs === 'github'
111
+ ? (l) => (typeof l?.name === 'string' ? l.name : String(l))
112
+ : (l) => (typeof l === 'string' ? l : String(l));
113
+ const labels = Array.isArray(data?.labels) ? data.labels.map(toName).filter(Boolean) : [];
108
114
  return { ok: true, labels, stderr: '' };
109
115
  }
110
116
 
@@ -119,12 +125,14 @@ function fetchLabels({ issueId, vcs, spec }) {
119
125
  * issueId: number | string,
120
126
  * vcs?: 'gitlab' | 'github',
121
127
  * repoRoot?: string,
122
- * resolveRepoSpecFn?: (opts: { repoRoot: string, vcs: 'gitlab' | 'github' }) => string | undefined
128
+ * resolveRepoSpecFn?: (opts: { repoRoot: string, vcs: 'gitlab' | 'github' }) => string | undefined,
129
+ * execFile?: typeof execFileSync
123
130
  * }} opts
124
131
  * `repoRoot` defaults to `process.cwd()`. `resolveRepoSpecFn` is the
125
132
  * injectable seam for the `-R`/`--repo` host-pinning resolution (#839) —
126
133
  * defaults to the real `resolveRepoSpec` (shells out to `git remote
127
- * get-url`); tests inject a stub instead of shelling out.
134
+ * get-url`); tests inject a stub instead of shelling out. `execFile`
135
+ * (default `execFileSync`) runs every glab/gh call.
128
136
  * @returns {Promise<{ stripped: string[], error?: string }>}
129
137
  */
130
138
  export async function stripStatusLabels({
@@ -132,6 +140,7 @@ export async function stripStatusLabels({
132
140
  vcs = 'gitlab',
133
141
  repoRoot = process.cwd(),
134
142
  resolveRepoSpecFn = resolveRepoSpec,
143
+ execFile = execFileSync,
135
144
  } = {}) {
136
145
  try {
137
146
  const id = String(issueId ?? '').trim();
@@ -146,7 +155,7 @@ export async function stripStatusLabels({
146
155
  const spec = resolveRepoSpecFn({ repoRoot, vcs: vcsResolved });
147
156
 
148
157
  // Step 1: fetch current labels.
149
- const { ok, labels, stderr } = fetchLabels({ issueId: id, vcs: vcsResolved, spec });
158
+ const { ok, labels, stderr } = fetchLabels({ issueId: id, vcs: vcsResolved, spec, execFile });
150
159
  if (!ok) {
151
160
  return { stripped: [], error: `failed to fetch labels: ${stderr}` };
152
161
  }
@@ -167,13 +176,13 @@ export async function stripStatusLabels({
167
176
  args.push('--remove-label', label);
168
177
  }
169
178
  if (spec) args.push('-R', spec);
170
- stripRes = runCli('gh', args);
179
+ stripRes = runCli('gh', args, execFile);
171
180
  } else {
172
181
  // `glab issue update <IID> --unlabel label1,label2,...`
173
182
  // glab accepts comma-separated list in a single --unlabel flag value.
174
183
  const args = ['issue', 'update', id, '--unlabel', toStrip.join(',')];
175
184
  if (spec) args.push('-R', spec);
176
- stripRes = runCli('glab', args);
185
+ stripRes = runCli('glab', args, execFile);
177
186
  }
178
187
 
179
188
  if (!stripRes.ok) {
@@ -189,3 +198,152 @@ export async function stripStatusLabels({
189
198
  return { stripped: [], error: msg };
190
199
  }
191
200
  }
201
+
202
+ /**
203
+ * Strip `status:*` labels, close, and VERIFY the close for each issue id — in
204
+ * that order, one id at a time.
205
+ *
206
+ * `closed` is true only when a re-read of the issue AFTER the close says
207
+ * `state: closed` (GitLab `closed`, GitHub `CLOSED`). The close command's own
208
+ * exit status is not trusted: a wrong project or a silent 404 can exit 0 while
209
+ * closing nothing (gitlab-ops skill, "Close verification"). The re-read runs
210
+ * even when the close command failed, so an issue that was already closed
211
+ * reports `closed: true` alongside the close `error`.
212
+ *
213
+ * A strip failure is non-fatal: it is reported as `stripError` and the close
214
+ * still runs. An id that is not a positive integer (an optional leading `#` is
215
+ * accepted) gets no CLI call at all — a value starting with `-` would
216
+ * otherwise reach glab/gh as a flag.
217
+ *
218
+ * @param {{
219
+ * ids: Array<number | string>,
220
+ * vcs?: 'gitlab' | 'github',
221
+ * repo?: string,
222
+ * repoRoot?: string,
223
+ * resolveRepoSpecFn?: (opts: { repoRoot: string, vcs: 'gitlab' | 'github' }) => string | undefined,
224
+ * execFile?: typeof execFileSync
225
+ * }} opts
226
+ * `repo` overrides the `-R`/`--repo` spec; otherwise it is resolved once via
227
+ * `resolveRepoSpecFn` exactly as `stripStatusLabels` does (#839).
228
+ * @returns {Promise<Array<{ id: string, stripped: string[], stripError?: string,
229
+ * closed: boolean, state: string | null, error?: string }>>}
230
+ */
231
+ export async function closeIssues({
232
+ ids = [],
233
+ vcs = 'gitlab',
234
+ repo,
235
+ repoRoot = process.cwd(),
236
+ resolveRepoSpecFn = resolveRepoSpec,
237
+ execFile = execFileSync,
238
+ } = {}) {
239
+ const vcsResolved = vcs === 'github' ? 'github' : 'gitlab';
240
+ const spec = repo?.trim() || resolveRepoSpecFn({ repoRoot, vcs: vcsResolved });
241
+ const cli = vcsResolved === 'github' ? 'gh' : 'glab';
242
+ const results = [];
243
+
244
+ for (const rawId of ids) {
245
+ const id = String(rawId ?? '')
246
+ .trim()
247
+ .replace(/^#/, '');
248
+ if (!/^[1-9]\d*$/.test(id)) {
249
+ results.push({
250
+ id: String(rawId),
251
+ stripped: [],
252
+ closed: false,
253
+ state: null,
254
+ error: 'invalid issue id',
255
+ });
256
+ continue;
257
+ }
258
+ const result = { id, stripped: [], closed: false, state: null };
259
+
260
+ // 1. strip — non-fatal. The resolved spec is handed through so strip,
261
+ // close and verify all pin the same repo.
262
+ const strip = await stripStatusLabels({
263
+ issueId: id,
264
+ vcs: vcsResolved,
265
+ resolveRepoSpecFn: () => spec,
266
+ execFile,
267
+ });
268
+ result.stripped = strip.stripped;
269
+ if (strip.error) result.stripError = strip.error;
270
+
271
+ // 2. close
272
+ const closeArgs = ['issue', 'close', id];
273
+ if (spec) closeArgs.push('-R', spec);
274
+ const closeRes = runCli(cli, closeArgs, execFile);
275
+ const errors = closeRes.ok
276
+ ? []
277
+ : [`close failed: ${closeRes.stderr.trim() || 'unknown error'}`];
278
+
279
+ // 3. verify — the platform's state is the only evidence of a close.
280
+ const view = viewIssueJson({
281
+ issueId: id,
282
+ vcs: vcsResolved,
283
+ spec,
284
+ ghFields: 'state',
285
+ execFile,
286
+ });
287
+ if (view.ok) {
288
+ result.state = typeof view.data?.state === 'string' ? view.data.state : null;
289
+ result.closed = result.state?.toLowerCase() === 'closed';
290
+ if (!result.closed) {
291
+ errors.push(`verify: issue state is ${result.state ?? 'unknown'}, not closed`);
292
+ }
293
+ } else {
294
+ errors.push(`verify failed: ${view.stderr.trim() || 'unknown error'}`);
295
+ }
296
+ if (errors.length) result.error = errors.join('; ');
297
+ results.push(result);
298
+ }
299
+ return results;
300
+ }
301
+
302
+ const USAGE =
303
+ 'usage: issue-close-strip-labels.mjs --close [--vcs gitlab|github] [-R|--repo <spec>] <id>...';
304
+
305
+ /**
306
+ * CLI: `--close [--vcs gitlab|github] [-R <spec>] <id>...` — one JSON line per
307
+ * id on stdout. Exit 0 = every id verified closed, 1 = at least one did not,
308
+ * 2 = usage error (no GitLab/GitHub call made).
309
+ *
310
+ * @param {string[]} [argv]
311
+ * @returns {Promise<number>} exit code
312
+ */
313
+ async function main(argv = process.argv.slice(2)) {
314
+ let parsed;
315
+ try {
316
+ parsed = parseArgs({
317
+ args: argv,
318
+ allowPositionals: true,
319
+ options: {
320
+ close: { type: 'boolean', default: false },
321
+ vcs: { type: 'string', default: 'gitlab' },
322
+ repo: { type: 'string', short: 'R' },
323
+ },
324
+ });
325
+ } catch (err) {
326
+ process.stderr.write(`${err?.message ?? err}\n${USAGE}\n`);
327
+ return 2;
328
+ }
329
+ const { values, positionals } = parsed;
330
+ if (!values.close || positionals.length === 0 || !['gitlab', 'github'].includes(values.vcs)) {
331
+ process.stderr.write(`${USAGE}\n`);
332
+ return 2;
333
+ }
334
+
335
+ const results = await closeIssues({ ids: positionals, vcs: values.vcs, repo: values.repo });
336
+ for (const r of results) {
337
+ if (r.stripError) {
338
+ process.stderr.write(
339
+ `⚠ label strip failed for #${r.id}: ${r.stripError} — close attempted anyway\n`,
340
+ );
341
+ }
342
+ process.stdout.write(`${JSON.stringify(r)}\n`);
343
+ }
344
+ return results.every((r) => r.closed) ? 0 : 1;
345
+ }
346
+
347
+ if (isMainModule(import.meta.url)) {
348
+ process.exitCode = await main();
349
+ }
@@ -0,0 +1,197 @@
1
+ // js-mask.mjs — ONE JavaScript lexer for every "blank the comments / separate
2
+ // code from data" scanner in this repo.
3
+ //
4
+ // WHY IT LIVES HERE. `maskSource` grew inside
5
+ // `scripts/lib/validate/check-untracked-test-deps.mjs`, where it is used to
6
+ // separate CODE from DATA. Three OTHER modules had hand-rolled
7
+ // `stripComments` copies with NO regex-literal branch, and each one was
8
+ // measurably wrong for the same input shape (`/\/*$/` — a regex whose body
9
+ // contains `/*`), measured 2026-09-18 @ 20a4cbff:
10
+ // - `check-validator-registration.mjs` — the regex's `/*` opened a block
11
+ // comment that swallowed the rest of the surface text → a FALSE
12
+ // "UNREGISTERED" finding.
13
+ // - `check-test-git-config-target.mjs` — same swallow inside a call tail →
14
+ // `wrapperHasCwd` returned false → a false alarm.
15
+ // - `auq/parse.mjs` — the swallowed region no longer contained
16
+ // `(Recommended)` → a silent FALSE NEGATIVE in the AUQ clarity measurement.
17
+ // One lexer, five consumers: the regex branch and its three named ceilings are
18
+ // maintained once.
19
+ //
20
+ // Import-safety: importing this module executes nothing.
21
+
22
+ /**
23
+ * Keywords after which a `/` opens a REGEX LITERAL, never a division. The
24
+ * single-character `prev` below cannot tell `return /x/` from `n / x`, because
25
+ * the character it sees (`n`) is a word character in both. This set is the
26
+ * one-token lookback that closes the difference; it is deliberately the usual
27
+ * short list rather than a parser (BV-001.6/BV-004 — a full expression parser
28
+ * for a masker is the abstraction this file exists to avoid).
29
+ *
30
+ * Named ceiling (BV-004), in the other direction: a genuine DIVISION CHAIN after
31
+ * one of these words (`return a / b / c`) is now read as a regex and its middle
32
+ * blanked. Measured 2026-09-18 over `git ls-files '*.mjs'` (1245 files): 0
33
+ * occurrences — all 18 files whose masking this lookback changes carry a real
34
+ * `return /…/` literal. REVISIT TRIGGER: the first such division chain.
35
+ */
36
+ const REGEX_PRECEDING_KEYWORDS = new Set([
37
+ 'return', 'typeof', 'case', 'in', 'of', 'new', 'delete', 'void', 'yield',
38
+ 'await', 'instanceof', 'do', 'else',
39
+ ]);
40
+
41
+ /** Word characters, for the one-token lookback above. */
42
+ const WORD_CHAR_RE = /[A-Za-z0-9_$]/;
43
+
44
+ /**
45
+ * Blank out the INTERIOR of every string literal, template literal, comment and
46
+ * regex literal, preserving byte offsets and newlines. Delimiters are kept so
47
+ * paren/comma balance and identifier boundaries survive.
48
+ *
49
+ * This is what separates CODE from DATA. A test that embeds fixture source in a
50
+ * template literal (`check-untracked-test-deps.mjs`'s own test file does) must
51
+ * not be scanned as though that fixture were its own body — masking makes that
52
+ * structural instead of relying on a self-exemption marker.
53
+ *
54
+ * With `keepLiterals: true` only COMMENTS are blanked: string, template and
55
+ * regex literals are still recognised (so a `//` or `/*` inside one is never read
56
+ * as a comment, and a quote inside a regex never opens a string) but their
57
+ * contents stay intact. `check-entry-guard.mjs` needs exactly that — it must see
58
+ * `endsWith('x.mjs')` and `${process.argv[1]}` verbatim (#1383) — and it is the
59
+ * mode the three `stripComments` consumers delegate in (#1388).
60
+ *
61
+ * @param {string} text
62
+ * @param {{ keepLiterals?: boolean }} [options]
63
+ * @returns {string} same-length masked source
64
+ */
65
+ export function maskSource(text, { keepLiterals = false } = {}) {
66
+ const out = text.split('');
67
+ const n = out.length;
68
+ let i = 0;
69
+ // Last significant (non-space) char before `i`, and its offset, used for the
70
+ // regex/division disambiguation below (the offset carries the one-token
71
+ // keyword lookback).
72
+ let prev = '';
73
+ let prevIdx = -1;
74
+
75
+ /** The whole word ending at `idx`, or '' when `idx` is not a word char. */
76
+ const wordEndingAt = (idx) => {
77
+ if (idx < 0 || !WORD_CHAR_RE.test(text[idx])) return '';
78
+ let k = idx;
79
+ while (k > 0 && WORD_CHAR_RE.test(text[k - 1])) k--;
80
+ return text.slice(k, idx + 1);
81
+ };
82
+
83
+ const blank = (from, to) => {
84
+ for (let k = from; k < to && k < n; k++) if (out[k] !== '\n') out[k] = ' ';
85
+ };
86
+ // Literal interiors go through this one; comments always use `blank`.
87
+ const blankLiteral = keepLiterals ? () => {} : blank;
88
+
89
+ while (i < n) {
90
+ const c = text[i];
91
+ const next = text[i + 1];
92
+
93
+ // Line comment.
94
+ if (c === '/' && next === '/') {
95
+ let j = i;
96
+ while (j < n && text[j] !== '\n') j++;
97
+ blank(i, j);
98
+ i = j;
99
+ continue;
100
+ }
101
+ // Block comment — ONLY when it is actually closed.
102
+ //
103
+ // Named ceiling (BV-004), replacing an unbounded blank: an UNTERMINATED
104
+ // `/*` used to blank everything to EOF. In valid source that shape is a
105
+ // syntax error, so in practice it only ever arises from a lexer desync —
106
+ // a `/*` inside a regex literal the branch below failed to recognise
107
+ // (`return /\/*$/`). Blanking the rest of the file then hides every guard
108
+ // under it, which is a validator falling SILENT. The direction is therefore
109
+ // chosen fail-toward-REPORTING: an unclosed `/*` is not a comment at all,
110
+ // and the few bytes of a genuine one get scanned as code — at worst that
111
+ // produces a finding a human dismisses, never a silent pass.
112
+ // REVISIT TRIGGER: a tracked file that legitimately ends inside an
113
+ // unterminated block comment (i.e. the moment `node --check` accepts one).
114
+ if (c === '/' && next === '*') {
115
+ let j = i + 2;
116
+ while (j < n && !(text[j] === '*' && text[j + 1] === '/')) j++;
117
+ if (j < n) {
118
+ blank(i, j + 2);
119
+ i = j + 2;
120
+ continue;
121
+ }
122
+ // Unterminated — fall through and treat the `/` as ordinary source.
123
+ }
124
+ // Single/double-quoted string — keep the quotes, blank the interior.
125
+ if (c === "'" || c === '"') {
126
+ let j = i + 1;
127
+ while (j < n && text[j] !== c) {
128
+ if (text[j] === '\\') j++;
129
+ if (text[j] === '\n') break; // unterminated — bail at EOL
130
+ j++;
131
+ }
132
+ blankLiteral(i + 1, j);
133
+ i = Math.min(j + 1, n);
134
+ prev = c;
135
+ prevIdx = i - 1;
136
+ continue;
137
+ }
138
+ // Template literal — blank everything including `${…}` substitutions.
139
+ if (c === '`') {
140
+ let j = i + 1;
141
+ while (j < n && text[j] !== '`') {
142
+ if (text[j] === '\\') j++;
143
+ j++;
144
+ }
145
+ blankLiteral(i + 1, j);
146
+ i = Math.min(j + 1, n);
147
+ prev = c;
148
+ prevIdx = i - 1;
149
+ continue;
150
+ }
151
+ // Regex literal — only when the previous significant TOKEN cannot end an
152
+ // expression, AND a closing unescaped `/` exists on the SAME line. The
153
+ // same-line bound keeps a misread division from swallowing the file.
154
+ //
155
+ // The token is either the single char `prev`, or — when that char is a word
156
+ // character — the whole word ending at it, checked against
157
+ // REGEX_PRECEDING_KEYWORDS. Without that lookback `return /\/*$/` read as a
158
+ // division and its `/*` reached the block-comment branch above, which blanked
159
+ // the rest of the file: a validator silently passing (measured 2026-09-18).
160
+ //
161
+ // Named ceiling (BV-004): the lookback is exactly ONE token. A regex after
162
+ // any other non-expression-ending context — a `)` that closes `if (…)`, a
163
+ // statement label, `function` — is still read as division and its body
164
+ // scanned as code. That desync is now bounded on BOTH sides: a quote in it
165
+ // opens a string that bails at EOL, and a `/*` in it no longer blanks past
166
+ // the file (unterminated block comments are not comments — see above).
167
+ // REVISIT TRIGGER: a census finding that needs `)`-aware lookback, i.e. a
168
+ // regex used directly as the body of `if (…) /re/.test(x)`.
169
+ const startsRegex = c === '/' && (
170
+ prev === '' || '(,=:[!&|?{};+-*%~^<>'.includes(prev)
171
+ || REGEX_PRECEDING_KEYWORDS.has(wordEndingAt(prevIdx))
172
+ );
173
+ if (startsRegex) {
174
+ let j = i + 1;
175
+ let inClass = false;
176
+ let closed = -1;
177
+ while (j < n && text[j] !== '\n') {
178
+ if (text[j] === '\\') { j += 2; continue; }
179
+ if (text[j] === '[') inClass = true;
180
+ else if (text[j] === ']') inClass = false;
181
+ else if (text[j] === '/' && !inClass) { closed = j; break; }
182
+ j++;
183
+ }
184
+ if (closed > i) {
185
+ blankLiteral(i + 1, closed);
186
+ i = closed + 1;
187
+ prev = '/';
188
+ prevIdx = closed;
189
+ continue;
190
+ }
191
+ }
192
+
193
+ if (!/\s/.test(c)) { prev = c; prevIdx = i; }
194
+ i++;
195
+ }
196
+ return out.join('');
197
+ }
@@ -11,13 +11,17 @@
11
11
  * a CODE call site instead: `emitEvolveCompleted()` is invoked from
12
12
  * `scripts/sweep-expired-learnings.mjs`'s `--prune` exit path (the ONE store
13
13
  * write `/evolve analyze` already performs), and `recordDialecticRun()` is
14
- * invoked from `scripts/dialectic-deriver.mjs::runDialecticDeriver()` for the
15
- * three abort statuses it can determine on its own, plus dry-run success —
16
- * see that module's header for why APPLY-mode success and the two
17
- * throw-based abort classes (`unknown-model`, `subagent-crash`) still call
18
- * `recordDialecticRun()` from `skills/evolve/SKILL.md` prose (the merge and
19
- * the dispatch-agent try/catch both happen one layer up, outside this pure
20
- * pipeline).
14
+ * invoked from `scripts/dialectic-deriver.mjs::runDialecticDeriver()` for
15
+ * every outcome that pipeline can determine on its own: the `ok`/abort
16
+ * returns (`empty-input`, `budget-exceeded`, `would-empty-card`, dry-run
17
+ * success) AND the two throw-based abort classes (`unknown-model`,
18
+ * `subagent-crash`), the latter via that module's `recordThrownAbort()`
19
+ * helper which records at the throw point before the original error is
20
+ * rethrown. Only APPLY-mode success still calls `recordDialecticRun()` from
21
+ * `skills/evolve/SKILL.md` prose (Step 6.4 of
22
+ * `skills/evolve/references/evolve-dialectic-mode.md`) — the merge
23
+ * (`mergePeerCard()`) happens one layer up, outside this pure pipeline, so the
24
+ * real merge-stats deltas are not knowable inside `runDialecticDeriver()`.
21
25
  *
22
26
  * Both emitters are best-effort and try/catch-wrapped — same posture as
23
27
  * `emitReconcileCompleted` in `scripts/lib/reconcile/engine.mjs` — because
@@ -1,31 +1,38 @@
1
1
  /**
2
- * locks/index.mjs — convenience barrel for the short-lived lock protocols split
3
- * out of session-lock.mjs in #630 (A1 barrel-preserving split).
2
+ * locks/index.mjs — DEPRECATED convenience barrel for the lock protocols
3
+ * split out of session-lock.mjs in #630 (A1 barrel-preserving split).
4
4
  *
5
- * Re-exports both protocol modules so a consumer can `import { withStateMdLock,
6
- * withStagingFenceLock } from '@lib/locks/index.mjs'` in one statement. This is
7
- * a nice-to-have: session-lock.mjs remains the canonical barrel that preserves
8
- * the original 22-symbol import surface, and the two protocol modules are also
9
- * importable directly.
5
+ * @deprecated since 5.2.0, removed in 6.0.0. Import directly from
6
+ * `./state-md-lock.mjs` / `./staging-fence-lock.mjs`, or from
7
+ * `scripts/lib/session-lock.mjs` for the canonical 22-symbol barrel that
8
+ * preserves the original import surface for all its importers.
10
9
  *
11
- * Pure re-export leaf imports only the two sibling modules, never
12
- * session-lock.mjs, so there is no import cycle.
10
+ * This file shipped in 5.1.0 with zero in-repo importers and was deleted in
11
+ * dd05e0f8 (`check-unwired-features` S4: no hook, npm script, CI job or husky
12
+ * stage reached it, and it named symbols — not modules — so it did not qualify
13
+ * for the pure `export *` exemption that shape gets). Restored here as a
14
+ * one-cycle deprecation shim (`.claude/rules/development.md` § Package Lifecycle
15
+ * & Versioning) because `package.json` carries no `exports` map: every packed
16
+ * file under `scripts/lib/` is public, so a consumer that deep-imported this
17
+ * path before the deletion has no compile error to warn it, only a runtime
18
+ * "module not found" — a shim buys that consumer one minor cycle to migrate
19
+ * before 6.0.0 removes the path outright.
20
+ *
21
+ * Deliberately the `export *` form with ZERO named exports (matches the
22
+ * `scripts/lib/autopilot-telemetry.mjs` precedent): a named-export re-export
23
+ * barrel is exactly the shape `check-unwired-features.mjs` S4 flags as an
24
+ * unreachable library module (condition 4 in `collectUnreachableLibraryModules`
25
+ * requires named exports before a re-export shim over live code enters that
26
+ * census) — that flag is what got this file deleted the first time. A star
27
+ * re-export names no symbol, so the shim reads as `exports: []` and stays
28
+ * invisible to that census while its target modules stay reachable through it.
13
29
  */
14
30
 
15
- export {
16
- STATE_LOCK_PATH,
17
- DEFAULT_STATE_LOCK_TIMEOUT_MS,
18
- STATE_LOCK_POLL_MS,
19
- acquireStateLock,
20
- releaseStateLock,
21
- withStateMdLock,
22
- } from './state-md-lock.mjs';
31
+ console.warn(
32
+ '[deprecated] scripts/lib/locks/index.mjs is deprecated since 5.2.0 and will be removed in 6.0.0 — ' +
33
+ "import from './state-md-lock.mjs' / './staging-fence-lock.mjs' directly, or from " +
34
+ 'scripts/lib/session-lock.mjs for the canonical surface.',
35
+ );
23
36
 
24
- export {
25
- STAGING_FENCE_LOCK_PATH,
26
- DEFAULT_STAGING_FENCE_LOCK_TIMEOUT_MS,
27
- STAGING_FENCE_LOCK_POLL_MS,
28
- acquireStagingFenceLock,
29
- releaseStagingFenceLock,
30
- withStagingFenceLock,
31
- } from './staging-fence-lock.mjs';
37
+ export * from './state-md-lock.mjs';
38
+ export * from './staging-fence-lock.mjs';