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
@@ -53,7 +53,7 @@
53
53
  import { readStdin, emitAllow, emitDeny, emitWarn } from '../scripts/lib/io.mjs';
54
54
  import { resolveProjectDir } from '../scripts/lib/platform.mjs';
55
55
  import { readJson } from '../scripts/lib/common.mjs';
56
- import { findIssueCreateStatements, isLoopedIssueCreate } from './_lib/vcs-create-matcher.mjs';
56
+ import { findIssueCreateStatements, findLoopedIssueCreates } from './_lib/vcs-create-matcher.mjs';
57
57
  import {
58
58
  loadIssueBudgetConfig,
59
59
  resolveIssueBudgetSessionId,
@@ -67,13 +67,10 @@ import {
67
67
  } from '../scripts/lib/issue-budget.mjs';
68
68
 
69
69
  import { shouldRunHook } from './_lib/profile-gate.mjs';
70
+ import { isMainModule } from '../scripts/lib/is-main-module.mjs';
70
71
  import { existsSync } from 'node:fs';
71
72
  import path from 'node:path';
72
73
 
73
- // Opt-out per session via SO_DISABLED_HOOKS=pre-bash-issue-budget; the
74
- // "minimal"/"off" profiles disable it like every other non-core hook.
75
- if (!shouldRunHook('pre-bash-issue-budget')) process.exit(0);
76
-
77
74
  // ---------------------------------------------------------------------------
78
75
  // Helpers
79
76
  // ---------------------------------------------------------------------------
@@ -187,15 +184,39 @@ function resolveToolCallId(input) {
187
184
  * entry #N … nothing is lost", which would be false here — an uncountable bulk
188
185
  * request is not parked, it is handed back whole.
189
186
  *
187
+ * ## Why the lane is a parameter (#1379)
188
+ *
189
+ * Until 2026-09-17 this text said "sits inside a shell loop body (`do … done`)"
190
+ * on BOTH lanes, so the `xargs` deny (`echo b | xargs -I% glab issue create
191
+ * --title junk%` — no loop anywhere) sent the operator looking for a loop the
192
+ * command does not have. Only the lane SENTENCE varies; every other line is
193
+ * byte-identical across lanes, and tests pin them.
194
+ *
190
195
  * @param {{ "max-per-session": number }} config
196
+ * @param {{ lane?: "loop"|"xargs"|"mixed" }} [opts]
191
197
  * @returns {string}
192
198
  */
193
- function formatLoopDenyReason(config) {
199
+ function formatLoopDenyReason(config, { lane = 'loop' } = {}) {
200
+ const LANE_SENTENCES = {
201
+ loop: [
202
+ 'The `issue create` call sits inside a shell loop body (`do … done`), so the cap cannot',
203
+ 'charge it honestly: the word list is expanded by the shell AFTER this hook runs, so',
204
+ '`for i in $(seq 1 50)` would file 50 issues against a count of 1.',
205
+ ],
206
+ xargs: [
207
+ 'The `issue create` call is driven by `xargs`, so the cap cannot charge it honestly: the',
208
+ 'word list arrives on stdin AFTER this hook runs, so `seq 1 50 | xargs` would file 50',
209
+ 'issues against a count of 1.',
210
+ ],
211
+ mixed: [
212
+ 'The `issue create` calls are driven by a shell loop body (`do … done`) AND by `xargs`,',
213
+ 'so the cap cannot charge them honestly: both word lists are produced AFTER this hook',
214
+ 'runs, so `seq 1 50 | xargs` would file 50 issues against a count of 1.',
215
+ ],
216
+ };
194
217
  return [
195
218
  'issue-budget: this command creates an UNKNOWN number of issues — refusing to guess.',
196
- 'The `issue create` call sits inside a shell loop body (`do … done`), so the cap cannot',
197
- 'charge it honestly: the word list is expanded by the shell AFTER this hook runs, so',
198
- '`for i in $(seq 1 50)` would file 50 issues against a count of 1.',
219
+ ...(LANE_SENTENCES[lane] ?? LANE_SENTENCES.loop),
199
220
  '',
200
221
  'Nothing was parked as overflow, because nothing is lost: re-issue the create calls as',
201
222
  'SEPARATE commands and each one is counted normally against the cap',
@@ -302,18 +323,59 @@ async function main() {
302
323
  const config = loadIssueBudgetConfig(projectDir);
303
324
  if (config.mode === 'off') return emitAllow();
304
325
 
305
- // G3b — bulk creation whose multiplicity is not computable (#1145). The
306
- // exemption is asked FIRST, through the same classifier chargeIssueBudget
307
- // uses, so a looped carryover sweep keeps its unconditional pass. It is asked
308
- // on the FIRST issue-create statement's text, which is the very statement
309
- // `isLoopedIssueCreate` judges classifying it on the whole command would
310
- // let an exempt NEIGHBOUR statement lift the loop deny.
311
- const uncountableBulk =
312
- isLoopedIssueCreate(command) && !classifyExemption(statements[0].text).exempt;
326
+ // G3b — bulk creation whose multiplicity is not computable (#1145). Two
327
+ // sources, one policy: a LOOP BODY (#1145) and an `xargs`-driven create
328
+ // (#1289), where the word list arrives on stdin. Both file N issues for one
329
+ // statement, so both are denied rather than charged 1 — the fix had to land
330
+ // here and not only in `isLoopedIssueCreate`, because G3 above short-circuits
331
+ // on `statements.length === 0` and an xargs create used to produce zero
332
+ // statements, so no loop-side fix could ever run.
333
+ //
334
+ // THE INVARIANT (rewritten 2026-09-16): the exemption is classified on the
335
+ // statement that CAUSED the bulk classification — never on `statements[0]`,
336
+ // and never on the whole command. A `[Carryover]` create standing NEXT TO an
337
+ // uncountable one is an unrelated neighbour and must not lift the deny; a
338
+ // `[Carryover]` create that IS the bulk statement keeps its documented
339
+ // unconditional pass (session-end's "those are never deferred" promise, which
340
+ // holds inside a loop too). Measured 2026-09-16 against the previous
341
+ // `statements[0]` binding, both lanes ALLOW where they must DENY:
342
+ // glab issue create --title "[Carryover] real"; echo X | xargs -I% glab issue create --title %
343
+ // glab issue create --title "[Carryover] real"; for i in 1 2 3; do glab issue create --title junk$i; done
344
+ // Same class as the bypass-scoping regression on `matchesBypass` — a matcher
345
+ // widened per statement while its exemption stayed whole-command
346
+ // (`.claude/rules/guard-design.md` § "Widening a matcher without narrowing
347
+ // its bypass", #1106).
348
+ //
349
+ // Fail-CLOSED when a command carries SEVERAL bulk statements and only some are
350
+ // exempt: `some()` over the non-exempt ones denies, because the command as a
351
+ // whole still files an uncountable number of untemplated issues.
352
+ //
353
+ // EVERY loop, not the first (#1379): the loop lane used to contribute at most
354
+ // ONE entry here, so an exempt FIRST loop was the only loop classified and
355
+ // every later loop went unjudged. Reproduced 2026-09-17 @ `9e8146b4`:
356
+ // for i in 1 2 3; do glab issue create --label carryover --title x$i; done;
357
+ // for j in 1 2 3; do glab issue create --title junk$j; done → ALLOW (count=1)
358
+ const bulkEntries = [
359
+ ...findLoopedIssueCreates(command).map((tokens) => ({
360
+ lane: 'loop',
361
+ text: tokens.map((t) => t.text).join(' '),
362
+ })),
363
+ ...statements.filter((s) => s.bulk).map((s) => ({ lane: 'xargs', text: s.text })),
364
+ ];
365
+ const uncountableEntries = bulkEntries.filter((e) => !classifyExemption(e.text).exempt);
366
+ const uncountableBulk = uncountableEntries.length > 0;
367
+ // ONE lane resolution for BOTH reports (#1379 follow-up). The deny already
368
+ // named the lane it fired on; the `warn` undercount notice at the bottom of
369
+ // main() said "inside a loop body" unconditionally, so the xargs lane — where
370
+ // no loop exists anywhere in the command — sent the operator looking for one.
371
+ // Resolved here rather than twice, so the two reports can never disagree.
372
+ const uncountableLanes = new Set(uncountableEntries.map((e) => e.lane));
373
+ const uncountableLane =
374
+ uncountableLanes.size > 1 ? 'mixed' : ([...uncountableLanes][0] ?? 'loop');
313
375
  if (uncountableBulk && config.mode === 'strict') {
314
376
  // Nothing is charged and nothing is parked — the command is handed back
315
377
  // whole, which is what makes unrolling it the correct next action.
316
- return emitDeny(formatLoopDenyReason(config));
378
+ return emitDeny(formatLoopDenyReason(config, { lane: uncountableLane }));
317
379
  }
318
380
 
319
381
  const sessionId = await resolveSessionId(input, projectDir);
@@ -401,11 +463,32 @@ async function main() {
401
463
  // to name the undercount out loud — a silent 1-for-N is the exact failure the
402
464
  // deny above exists to prevent, and `warn` must not reintroduce it quietly.
403
465
  // emitWarn, not stderr: under exit 0 stderr reaches only the debug log (#916).
404
- if (uncountableBulk && verdict.decision === 'allow') {
466
+ //
467
+ // GATED ON THE BULK STATEMENTS, NEVER ON THE LAST VERDICT (2026-09-17). The
468
+ // condition used to read `verdict.decision === 'allow'`, i.e. the verdict of
469
+ // the LAST statement of the chain — so one exempt statement written AFTER an
470
+ // uncountable one silenced the notice the two paragraphs above declare
471
+ // mandatory. Reproduced through this hook binary in `mode: warn`:
472
+ // for i in 1 2; do glab issue create --title j$i; done; \
473
+ // glab issue create --title "[Carryover] z"
474
+ // → stdout EMPTY, ledger count=1 exempt=1 (the trailing exempt statement is
475
+ // an unrelated neighbour, exactly as in G3b's own invariant); without it,
476
+ // the identical loop reported the UNDERCOUNT.
477
+ // Reaching this point already means no statement blocked and none warned
478
+ // (both branches above return), so the uncountable bulk statement — non-exempt
479
+ // by construction of `uncountableEntries` — was charged as 1. The two negated
480
+ // conditions are kept explicit so a future reordering of those branches cannot
481
+ // turn this back into a report about the wrong statement.
482
+ if (uncountableBulk && !blocked && !verdicts.some((v) => v.decision === 'warn')) {
483
+ const LANE_PHRASES = {
484
+ loop: 'inside a loop body',
485
+ xargs: 'driven by `xargs`',
486
+ mixed: 'inside a loop body and by `xargs`',
487
+ };
405
488
  return emitWarn(
406
- `pre-bash-issue-budget: bulk create inside a loop body charged as 1 ` +
489
+ `pre-bash-issue-budget: bulk create ${LANE_PHRASES[uncountableLane]} charged as 1 ` +
407
490
  `(${verdict.count}/${verdict.max}) — the real number of issues this files is not ` +
408
- `knowable before the shell expands the word list, so the count is an UNDERCOUNT. ` +
491
+ `knowable before the word list is expanded, so the count is an UNDERCOUNT. ` +
409
492
  `Set \`issue-budget.mode: strict\` to deny this shape instead.`,
410
493
  );
411
494
  }
@@ -414,10 +497,18 @@ async function main() {
414
497
  return emitAllow();
415
498
  }
416
499
 
417
- // Top-level error handler fail open, same posture as the sibling hooks.
418
- main().catch((e) => {
419
- process.stderr.write(
420
- `⚠ pre-bash-issue-budget: internal error — ${e?.message || e}\n`,
421
- );
422
- process.exit(0);
423
- });
500
+ // Entry guard (#1393): run only as the node script the harness execs — a bare
501
+ // `import()` must run no handler and must not exit the importing process.
502
+ if (isMainModule(import.meta.url)) {
503
+ // Opt-out per session via SO_DISABLED_HOOKS=pre-bash-issue-budget; the
504
+ // "minimal"/"off" profiles disable it like every other non-core hook.
505
+ if (!shouldRunHook('pre-bash-issue-budget')) process.exit(0);
506
+
507
+ // Top-level error handler — fail open, same posture as the sibling hooks.
508
+ main().catch((e) => {
509
+ process.stderr.write(
510
+ `⚠ pre-bash-issue-budget: internal error — ${e?.message || e}\n`,
511
+ );
512
+ process.exit(0);
513
+ });
514
+ }
@@ -13,7 +13,8 @@
13
13
  * G3 — regex match /\bnode\b.*\bmemory-propose\.mjs\b/i (no match → exit 0)
14
14
  * G4 — resolve session_id (from stdin payload or .orchestrator/current-session.json)
15
15
  * G5 — resolve wave (from the active wave-scope.json `wave` field, default 0)
16
- * G6 — redact argv: strip --insight/--subject/--evidence/--content/--reason values
16
+ * G6 — derive a NON-REVERSIBLE argv summary: sha256 command_hash + known flag
17
+ * NAMES + raw length. The command text itself never leaves this process.
17
18
  * G7 — append event JSON line to .orchestrator/metrics/events.jsonl
18
19
  * Always: exit 0 (never block)
19
20
  *
@@ -22,6 +23,7 @@
22
23
  */
23
24
 
24
25
  import { readStdin, emitAllow } from '../scripts/lib/io.mjs';
26
+ import crypto from 'node:crypto';
25
27
  import { existsSync } from 'node:fs';
26
28
  import { readFile } from 'node:fs/promises';
27
29
  import path from 'node:path';
@@ -29,8 +31,7 @@ import { emitEvent } from '../scripts/lib/events.mjs';
29
31
  import { findScopeFile } from '../scripts/lib/hardening.mjs';
30
32
 
31
33
  import { shouldRunHook } from './_lib/profile-gate.mjs';
32
- // exit 0 immediately when this hook is disabled via profile/env
33
- if (!shouldRunHook('pre-bash-memory-propose-audit')) process.exit(0);
34
+ import { isMainModule } from '../scripts/lib/is-main-module.mjs';
34
35
 
35
36
  // ---------------------------------------------------------------------------
36
37
  // Constants
@@ -48,59 +49,78 @@ if (!shouldRunHook('pre-bash-memory-propose-audit')) process.exit(0);
48
49
  const MEMORY_PROPOSE_REGEX = /\bnode\b.*\bmemory-propose\.mjs\b/i;
49
50
 
50
51
  /**
51
- * Flags whose VALUES are privacy-sensitive and must be redacted from the log.
52
- * The flag name itself is preserved; only the value is replaced with [REDACTED].
52
+ * The flag NAMES `scripts/memory-propose.mjs` accepts (its `parseArgs` options
53
+ * block, plus `--help`). CLOSED LIST BY DESIGN: `flags_present` is built by
54
+ * intersecting the command with this constant, never by scraping `--\S+` tokens
55
+ * out of the command. A scrape would re-open the leak this file just closed —
56
+ * `--insight --my-secret-value` would publish the value as if it were a flag.
53
57
  *
54
- * Matches both forms:
55
- * --insight=value → --insight=[REDACTED]
56
- * --insight "quoted value" → --insight [REDACTED]
57
- * --insight unquoted → --insight [REDACTED]
58
+ * A flag the CLI does not know is simply not reported. That is the correct
59
+ * trade: the field exists to say WHICH of the audited CLI's switches were used,
60
+ * not to mirror the command line.
58
61
  */
59
- // CAVEAT (#554 A3, low severity): The regex operates on the unparsed Bash
60
- // tool_input.command string. Exotic shell forms bypass redaction by hiding
61
- // the value behind shell expansion the regex cannot see:
62
- // --insight=$VAR (env-var indirection)
63
- // --insight=$(cat secret) (command substitution)
64
- // --insight <<<heredoc (here-string)
65
- // The literal token (e.g. `$VAR`) is logged, NOT the resolved value — so an
66
- // audit-trail leak requires the resolved value to also appear literally
67
- // elsewhere in the command. Acceptable per the project's local-trust model.
68
- //
69
- // Issue #546: `\S+` would match an opening quote `"` or `'` (non-whitespace),
70
- // and on malformed inputs (e.g. unbalanced or shell-already-unescaped values)
71
- // could partial-match the value, leaving the tail unredacted. The negative
72
- // lookahead `(?!["'])` forces `\S+` to be tried only on values that do not
73
- // start with a quote, preserving the quoted-alt as the sole path for quoted
74
- // values and preventing tail-leaks on malformed inputs.
75
- const SENSITIVE_FLAGS_REGEX = /--(?:insight|subject|evidence|content|reason)(?:=("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|(?!["'])\S+)|\s+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|(?!["'])\S+))/g;
62
+ const KNOWN_FLAGS = Object.freeze([
63
+ 'type',
64
+ 'subject',
65
+ 'insight',
66
+ 'evidence',
67
+ 'confidence',
68
+ 'dry-run',
69
+ 'file-paths',
70
+ 'help',
71
+ ]);
76
72
 
77
73
  // ---------------------------------------------------------------------------
78
74
  // Helpers
79
75
  // ---------------------------------------------------------------------------
80
76
 
81
77
  /**
82
- * Redact values of privacy-sensitive flags from a command string.
83
- * Preserves flag names; replaces values with [REDACTED].
78
+ * sha256(command), truncated to 16 hex characters.
79
+ *
80
+ * WORD-FOR-WORD the `hashCommand()` of `hooks/enforce-commands.mjs:69` (itself
81
+ * taken from `hooks/pre-bash-destructive-guard.mjs:253`, which mirrors
82
+ * `loop-guard.mjs` `hashArgs()`). Deliberately duplicated rather than shared:
83
+ * a PreToolUse hook must not gain another loadable module that can fail at
84
+ * start — three lines are cheaper than a load error on the hot path. Verified
85
+ * before duplicating that no helper is EXPORTED at any of those sites.
86
+ *
87
+ * WHY THE HASH IS HERE (#1415, 2026-09-21): until today this hook wrote a
88
+ * merely flag-redacted `argv_truncated` (512 chars of command text) into
89
+ * `orchestrator.memory.propose_invoked` — a TRACKED `events.jsonl` line and,
90
+ * with the webhook configured, a network payload. Redaction covered only the
91
+ * five `--insight/--subject/--evidence/--content/--reason` values and said so
92
+ * in its own caveat: `$VAR`, `$(cat secret)` and any value written elsewhere
93
+ * on the line passed through. This was the THIRD and last open site of the
94
+ * class closed by `8f15f77b` (enforce-commands) and #1404 (staging-fence).
95
+ *
96
+ * The hash keeps the event COUNTABLE and GROUPABLE — repeat invocations of the
97
+ * same command still collapse to one key — which is the only property any
98
+ * consumer needed. Measured 2026-09-21 (`rg argv_truncated` over `scripts/
99
+ * hooks/ skills/ docs/`): ZERO production readers, the field's only consumers
100
+ * were this hook's own tests.
84
101
  *
85
102
  * @param {string} command
86
103
  * @returns {string}
87
104
  */
88
- function redactArgv(command) {
89
- return command.replace(SENSITIVE_FLAGS_REGEX, (match, eqValue, spaceValue) => {
90
- if (eqValue !== undefined) {
91
- // --insight=value form: preserve up to and including '=', replace value
92
- const eqIndex = match.indexOf('=');
93
- return match.slice(0, eqIndex + 1) + '[REDACTED]';
94
- }
95
- if (spaceValue !== undefined) {
96
- // --insight value form: preserve flag + whitespace, replace value
97
- const valueIndex = match.lastIndexOf(spaceValue);
98
- return match.slice(0, valueIndex) + '[REDACTED]';
99
- }
100
- // Fallback: keep flag name, drop value
101
- const flagMatch = match.match(/^(--[\w-]+)/);
102
- return flagMatch ? flagMatch[1] + ' [REDACTED]' : match;
103
- });
105
+ function hashCommand(command) {
106
+ return crypto.createHash('sha256').update(command).digest('hex').slice(0, 16);
107
+ }
108
+
109
+ /**
110
+ * The subset of KNOWN_FLAGS that appears in the command, in KNOWN_FLAGS order.
111
+ *
112
+ * Names only — a value can never reach this array, because the array is built
113
+ * from the constant above and the command is only ever TESTED against it. Both
114
+ * spellings count (`--insight=x` and `--insight x`); the trailing boundary
115
+ * `(?![\w-])` keeps `--insightful` from reporting `insight`.
116
+ *
117
+ * @param {string} command
118
+ * @returns {string[]}
119
+ */
120
+ function flagsPresent(command) {
121
+ return KNOWN_FLAGS.filter((flag) =>
122
+ new RegExp(`--${flag}(?![\\w-])`).test(command),
123
+ );
104
124
  }
105
125
 
106
126
  /**
@@ -180,8 +200,11 @@ async function main() {
180
200
  // G5 — resolve wave
181
201
  const wave = await resolveWave(projectDir);
182
202
 
183
- // G6 — redact argv: strip sensitive flag values, keep flag names
184
- const argvRedacted = redactArgv(command);
203
+ // G6 — derive the non-reversible argv summary (#1415). Nothing downstream
204
+ // ever sees the command text: hash for grouping, known flag NAMES for
205
+ // shape, raw length for size — no operands, no paths, no values.
206
+ const commandHash = hashCommand(command);
207
+ const flags = flagsPresent(command);
185
208
 
186
209
  // G7 — emit canonical event via emitEvent (single emission path: schema + webhook,
187
210
  // replacing the local hand-rolled appendFileSync bypass).
@@ -193,7 +216,9 @@ async function main() {
193
216
  await emitEvent('orchestrator.memory.propose_invoked', {
194
217
  session_id: sessionId,
195
218
  wave,
196
- argv_truncated: argvRedacted.slice(0, 512),
219
+ command_hash: commandHash,
220
+ flags_present: flags,
221
+ argv_length: command.length,
197
222
  cwd: process.cwd(),
198
223
  exit_code: null,
199
224
  });
@@ -203,10 +228,17 @@ async function main() {
203
228
  return emitAllow();
204
229
  }
205
230
 
206
- // Top-level error handler never let exit 1 leak
207
- main().catch((e) => {
208
- process.stderr.write(
209
- `⚠ pre-bash-memory-propose-audit: internal error ${e?.message || e}\n`,
210
- );
211
- process.exit(0); // fail-open on internal errors
212
- });
231
+ // Entry guard (#1393): run only as the node script the harness execs — a bare
232
+ // `import()` must run no handler and must not exit the importing process.
233
+ if (isMainModule(import.meta.url)) {
234
+ // exit 0 immediately when this hook is disabled via profile/env
235
+ if (!shouldRunHook('pre-bash-memory-propose-audit')) process.exit(0);
236
+
237
+ // Top-level error handler — never let exit 1 leak
238
+ main().catch((e) => {
239
+ process.stderr.write(
240
+ `⚠ pre-bash-memory-propose-audit: internal error — ${e?.message || e}\n`,
241
+ );
242
+ process.exit(0); // fail-open on internal errors
243
+ });
244
+ }