session-orchestrator 5.2.0 → 5.4.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 (427) hide show
  1. package/.agents/skills/architecture/SKILL.md +3 -1
  2. package/.agents/skills/autopilot/SKILL.md +5 -1
  3. package/.agents/skills/autopilot/agents/openai.yaml +5 -0
  4. package/.agents/skills/bootstrap/SKILL.md +5 -1
  5. package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
  6. package/.agents/skills/brainstorm/SKILL.md +5 -1
  7. package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
  8. package/.agents/skills/claude-md-drift-check/SKILL.md +4 -2
  9. package/.agents/skills/close/SKILL.md +5 -1
  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 +5 -1
  13. package/.agents/skills/debug/agents/openai.yaml +5 -0
  14. package/.agents/skills/discovery/SKILL.md +5 -1
  15. package/.agents/skills/discovery/agents/openai.yaml +5 -0
  16. package/.agents/skills/dispatcher/SKILL.md +5 -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 +5 -1
  21. package/.agents/skills/eli5/agents/openai.yaml +5 -0
  22. package/.agents/skills/eval/SKILL.md +6 -2
  23. package/.agents/skills/eval/agents/openai.yaml +5 -0
  24. package/.agents/skills/evolve/SKILL.md +6 -2
  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 +5 -1
  30. package/.agents/skills/go/agents/openai.yaml +5 -0
  31. package/.agents/skills/grill/SKILL.md +5 -1
  32. package/.agents/skills/grill/agents/openai.yaml +5 -0
  33. package/.agents/skills/harness-audit/SKILL.md +5 -1
  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 +5 -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/navigator/SKILL.md +22 -0
  41. package/.agents/skills/navigator/agents/openai.yaml +5 -0
  42. package/.agents/skills/npm-publish/SKILL.md +4 -2
  43. package/.agents/skills/peekaboo-driver/SKILL.md +3 -1
  44. package/.agents/skills/persona-panel/SKILL.md +5 -1
  45. package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
  46. package/.agents/skills/plan/SKILL.md +5 -1
  47. package/.agents/skills/plan/agents/openai.yaml +5 -0
  48. package/.agents/skills/playwright-driver/SKILL.md +3 -1
  49. package/.agents/skills/portfolio/SKILL.md +5 -1
  50. package/.agents/skills/portfolio/agents/openai.yaml +5 -0
  51. package/.agents/skills/quality-gates/SKILL.md +3 -1
  52. package/.agents/skills/reconcile/SKILL.md +5 -1
  53. package/.agents/skills/reconcile/agents/openai.yaml +5 -0
  54. package/.agents/skills/release/SKILL.md +5 -1
  55. package/.agents/skills/release/agents/openai.yaml +5 -0
  56. package/.agents/skills/remote-offload/SKILL.md +3 -1
  57. package/.agents/skills/repo-audit/SKILL.md +5 -1
  58. package/.agents/skills/repo-audit/agents/openai.yaml +5 -0
  59. package/.agents/skills/session/SKILL.md +21 -0
  60. package/.agents/skills/session/agents/openai.yaml +5 -0
  61. package/.agents/skills/session-end/SKILL.md +3 -1
  62. package/.agents/skills/session-plan/SKILL.md +3 -1
  63. package/.agents/skills/session-start/SKILL.md +3 -1
  64. package/.agents/skills/spinout/SKILL.md +5 -1
  65. package/.agents/skills/spinout/agents/openai.yaml +5 -0
  66. package/.agents/skills/sunset-review/SKILL.md +5 -1
  67. package/.agents/skills/sunset-review/agents/openai.yaml +5 -0
  68. package/.agents/skills/templates-ack/SKILL.md +21 -0
  69. package/.agents/skills/templates-ack/agents/openai.yaml +5 -0
  70. package/.agents/skills/test/SKILL.md +5 -1
  71. package/.agents/skills/test/agents/openai.yaml +5 -0
  72. package/.agents/skills/test-audit/SKILL.md +21 -0
  73. package/.agents/skills/test-audit/agents/openai.yaml +5 -0
  74. package/.agents/skills/test-runner/SKILL.md +3 -1
  75. package/.agents/skills/tmux-layout/SKILL.md +3 -1
  76. package/.agents/skills/using-orchestrator/SKILL.md +3 -1
  77. package/.agents/skills/ux-grill/SKILL.md +5 -1
  78. package/.agents/skills/ux-grill/agents/openai.yaml +5 -0
  79. package/.agents/skills/vault-mirror/SKILL.md +3 -1
  80. package/.agents/skills/vault-sync/SKILL.md +3 -1
  81. package/.agents/skills/wave-executor/SKILL.md +3 -1
  82. package/.agents/skills/write-executable-plan/SKILL.md +3 -1
  83. package/.claude-plugin/marketplace.json +1 -1
  84. package/.claude-plugin/plugin.json +1 -1
  85. package/.codex-plugin/plugin.json +4 -4
  86. package/.codex-plugin/skills/claude-md-drift-check/SKILL.md +1 -1
  87. package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -3
  88. package/.codex-plugin/skills/eval/SKILL.md +1 -1
  89. package/.codex-plugin/skills/evolve/SKILL.md +1 -1
  90. package/.codex-plugin/skills/navigator/SKILL.md +22 -0
  91. package/.codex-plugin/skills/{convergence-monitoring → navigator}/agents/openai.yaml +2 -2
  92. package/.codex-plugin/skills/npm-publish/SKILL.md +1 -3
  93. package/.codex-plugin/skills/session/SKILL.md +1 -1
  94. package/.codex-plugin/skills/test-audit/SKILL.md +21 -0
  95. package/.codex-plugin/skills/{npm-publish → test-audit}/agents/openai.yaml +1 -1
  96. package/.cursor/commands/eval.md +1 -1
  97. package/.cursor/commands/navigator.md +15 -0
  98. package/.cursor/commands/session.md +1 -1
  99. package/.cursor/commands/test-audit.md +14 -0
  100. package/.cursor/rules/000-session-orchestrator.mdc +0 -2
  101. package/.cursor/rules/050-plan.mdc +1 -1
  102. package/.cursor/rules/060-evolve.mdc +27 -12
  103. package/.cursor/skills/claude-md-drift-check/SKILL.md +1 -1
  104. package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
  105. package/.cursor/skills/eval/SKILL.md +1 -1
  106. package/.cursor/skills/navigator/SKILL.md +13 -0
  107. package/.cursor/skills/npm-publish/SKILL.md +1 -0
  108. package/.cursor/skills/test-audit/SKILL.md +12 -0
  109. package/.cursor-plugin/plugin.json +1 -1
  110. package/.orchestrator/policy/blocked-commands.json +12 -3
  111. package/AGENTS.md +9 -30
  112. package/CHANGELOG.md +206 -0
  113. package/README.md +13 -9
  114. package/SECURITY.md +12 -0
  115. package/agents/dialectic-deriver.md +13 -10
  116. package/agents/eval-judge.md +67 -45
  117. package/agents/skill-applied-judge.md +34 -19
  118. package/commands/session.md +7 -3
  119. package/docs/agent-authoring.md +2 -2
  120. package/docs/baseline.md +22 -7
  121. package/docs/ci-setup.md +57 -4
  122. package/docs/codex-setup.md +14 -2
  123. package/docs/components.md +12 -10
  124. package/docs/events-schema.md +65 -15
  125. package/docs/persona-panel.md +5 -4
  126. package/docs/rule-authoring.md +84 -17
  127. package/docs/scope-collision-guard.md +26 -4
  128. package/docs/session-config-reference.md +154 -12
  129. package/docs/session-config-template.md +41 -3
  130. package/docs/telemetry.md +2 -0
  131. package/hooks/_lib/hook-import-set.json +87 -6
  132. package/hooks/_lib/subagent-transcript.mjs +582 -31
  133. package/hooks/config-protection.mjs +11 -3
  134. package/hooks/cwd-change-restore.mjs +11 -3
  135. package/hooks/enforce-commands.mjs +70 -23
  136. package/hooks/enforce-scope.mjs +143 -33
  137. package/hooks/hooks-codex.json +1 -1
  138. package/hooks/hooks-cursor.json +5 -0
  139. package/hooks/hooks-pi.json +5 -0
  140. package/hooks/hooks.json +6 -1
  141. package/hooks/loop-guard.mjs +11 -3
  142. package/hooks/on-session-end.mjs +58 -23
  143. package/hooks/on-session-start.mjs +101 -11
  144. package/hooks/on-stop.mjs +170 -22
  145. package/hooks/operator-steer.mjs +11 -3
  146. package/hooks/post-bash-issue-budget-refund.mjs +18 -8
  147. package/hooks/post-bash-write-verify.mjs +3 -2
  148. package/hooks/post-edit-import-probe.mjs +17 -9
  149. package/hooks/post-edit-validate.mjs +13 -5
  150. package/hooks/post-subagent-discovery-validator.mjs +98 -13
  151. package/hooks/post-tool-batch-wave-signal.mjs +202 -38
  152. package/hooks/post-tool-failure-corrective-context.mjs +11 -5
  153. package/hooks/post-tooluse-frontend-slop.mjs +10 -4
  154. package/hooks/pre-auq-clarity.mjs +15 -2
  155. package/hooks/pre-bash-destructive-guard.mjs +80 -9
  156. package/hooks/pre-bash-issue-budget.mjs +16 -11
  157. package/hooks/pre-bash-memory-propose-audit.mjs +137 -56
  158. package/hooks/pre-bash-sessions-ledger-guard.mjs +419 -22
  159. package/hooks/pre-bash-staging-fence.mjs +335 -31
  160. package/hooks/pre-bash-templates-first.mjs +19 -14
  161. package/hooks/pre-edit-sessions-ledger-guard.mjs +162 -0
  162. package/hooks/pre-task-scope-disjoint.mjs +792 -74
  163. package/hooks/subagent-telemetry.mjs +111 -38
  164. package/hooks/wave-scope-commit-guard.mjs +197 -100
  165. package/monitors/monitors.json +1 -1
  166. package/output-styles/wave-summary.md +1 -1
  167. package/package.json +3 -3
  168. package/pi/prompts/eval.md +1 -1
  169. package/pi/prompts/navigator.md +12 -0
  170. package/pi/prompts/session.md +1 -1
  171. package/pi/prompts/test-audit.md +12 -0
  172. package/rules/README.md +7 -2
  173. package/rules/_index.md +3 -3
  174. package/rules/always-on/bash-harness-pitfalls.md +7 -1
  175. package/rules/opt-in-domain/prompt-caching.md +6 -2
  176. package/rules/opt-in-stack/backend-data.md +10 -2
  177. package/rules/opt-in-stack/backend.md +10 -4
  178. package/rules/opt-in-stack/frontend.md +7 -2
  179. package/rules/opt-in-stack/security-web.md +10 -4
  180. package/rules/opt-in-stack/swift.md +6 -2
  181. package/scripts/apply-session-learnings.mjs +492 -0
  182. package/scripts/archive-closed-prds.mjs +2 -1
  183. package/scripts/autopilot.mjs +70 -53
  184. package/scripts/backfill-abandoned-sessions.mjs +118 -16
  185. package/scripts/baseline-archetypes.mjs +1 -1
  186. package/scripts/check-package-manager.mjs +62 -5
  187. package/scripts/check-sessions-integrity.mjs +300 -0
  188. package/scripts/cursor-install.mjs +1 -4
  189. package/scripts/dialectic-deriver.mjs +50 -13
  190. package/scripts/emit-session.mjs +371 -31
  191. package/scripts/eval-session.mjs +65 -3
  192. package/scripts/export-hw-learnings.mjs +22 -3
  193. package/scripts/fleet-checkin.mjs +147 -0
  194. package/scripts/generate-agents-skills.mjs +102 -29
  195. package/scripts/generate-cursor-adapter.mjs +61 -16
  196. package/scripts/lib/agent-frontmatter.mjs +3 -3
  197. package/scripts/lib/agent-status.mjs +2 -31
  198. package/scripts/lib/auq/clarity.mjs +10 -2
  199. package/scripts/lib/auq/parse.mjs +12 -31
  200. package/scripts/lib/auq/schema.mjs +56 -41
  201. package/scripts/lib/auto-dialectic.mjs +304 -15
  202. package/scripts/lib/auto-dream.mjs +7 -6
  203. package/scripts/lib/autopilot/flags.mjs +18 -2
  204. package/scripts/lib/autopilot/kill-switches.mjs +6 -3
  205. package/scripts/lib/autopilot/loop.mjs +19 -2
  206. package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
  207. package/scripts/lib/baseline-archetypes.mjs +41 -22
  208. package/scripts/lib/ci-status-banner.mjs +376 -16
  209. package/scripts/lib/command-blocker.mjs +275 -28
  210. package/scripts/lib/config/dialectic.mjs +12 -3
  211. package/scripts/lib/config/gate.mjs +74 -0
  212. package/scripts/lib/config/host-paths.mjs +79 -0
  213. package/scripts/lib/config/persona-gate-wave.mjs +3 -3
  214. package/scripts/lib/config/reaper.mjs +163 -0
  215. package/scripts/lib/config-schema.mjs +13 -0
  216. package/scripts/lib/config.mjs +85 -10
  217. package/scripts/lib/convergence-monitor.mjs +74 -11
  218. package/scripts/lib/ecosystem-health.mjs +11 -0
  219. package/scripts/lib/eval/engine.mjs +421 -53
  220. package/scripts/lib/eval/judge.mjs +463 -40
  221. package/scripts/lib/eval/schema.mjs +10 -1
  222. package/scripts/lib/events-rotation.mjs +221 -25
  223. package/scripts/lib/events-schema.mjs +114 -0
  224. package/scripts/lib/events.mjs +545 -7
  225. package/scripts/lib/fleet-protocol.mjs +270 -0
  226. package/scripts/lib/frontmatter-guard.mjs +21 -10
  227. package/scripts/lib/gates/gate-baseline.mjs +40 -5
  228. package/scripts/lib/gates/gate-full.mjs +35 -5
  229. package/scripts/lib/gates/gate-helpers.mjs +336 -23
  230. package/scripts/lib/gates/gate-incremental.mjs +49 -23
  231. package/scripts/lib/gates/gate-per-file.mjs +42 -8
  232. package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
  233. package/scripts/lib/instruction-budget-guard.mjs +208 -8
  234. package/scripts/lib/io.mjs +42 -8
  235. package/scripts/lib/issue-close-strip-labels.mjs +207 -49
  236. package/scripts/lib/js-mask.mjs +197 -0
  237. package/scripts/lib/learnings/evolve-telemetry.mjs +11 -7
  238. package/scripts/lib/learnings/expiry-sweep.mjs +34 -3
  239. package/scripts/lib/learnings/io.mjs +154 -26
  240. package/scripts/lib/learnings/schema.mjs +163 -15
  241. package/scripts/lib/learnings/sizing-subject.mjs +2 -2
  242. package/scripts/lib/maintenance-due-banner.mjs +53 -88
  243. package/scripts/lib/memory-proposals/schema.mjs +11 -5
  244. package/scripts/lib/named-baseline-resolver.mjs +53 -28
  245. package/scripts/lib/named-vault-resolver.mjs +104 -5
  246. package/scripts/lib/orphan-reaper/defaults.mjs +41 -0
  247. package/scripts/lib/orphan-reaper/ps-snapshot.mjs +181 -0
  248. package/scripts/lib/orphan-reaper/reaper-audit.mjs +291 -0
  249. package/scripts/lib/orphan-reaper/reaper-decide.mjs +395 -0
  250. package/scripts/lib/orphan-reaper/scan-throttle.mjs +92 -0
  251. package/scripts/lib/orphan-reaper/scan.mjs +506 -0
  252. package/scripts/lib/orphan-reaper.mjs +316 -0
  253. package/scripts/lib/owner-yaml.mjs +104 -6
  254. package/scripts/lib/peer-cards/merger.mjs +48 -10
  255. package/scripts/lib/peer-cards/reader.mjs +78 -2
  256. package/scripts/lib/persona-panel/catalog-loader.mjs +1 -1
  257. package/scripts/lib/process-group.mjs +950 -0
  258. package/scripts/lib/project-hygiene.mjs +21 -1
  259. package/scripts/lib/quality-gate.mjs +152 -33
  260. package/scripts/lib/reconcile/backlog.mjs +368 -0
  261. package/scripts/lib/reconcile/engine.mjs +55 -188
  262. package/scripts/lib/reconcile/rule-expiry-sweep.mjs +302 -60
  263. package/scripts/lib/reconcile/sanitize.mjs +69 -3
  264. package/scripts/lib/reconcile-nudge-banner.mjs +138 -45
  265. package/scripts/lib/resource-probe/parsers.mjs +31 -0
  266. package/scripts/lib/resource-probe.mjs +1 -1
  267. package/scripts/lib/rule-loader.mjs +141 -74
  268. package/scripts/lib/rules-sync.mjs +27 -12
  269. package/scripts/lib/scope-baseline.mjs +3 -4
  270. package/scripts/lib/scope-echo.mjs +39 -2
  271. package/scripts/lib/scope-gate.mjs +605 -1
  272. package/scripts/lib/session-close-backfill.mjs +33 -6
  273. package/scripts/lib/session-id.mjs +9 -20
  274. package/scripts/lib/session-invocation.mjs +20 -0
  275. package/scripts/lib/session-record-repair.mjs +183 -26
  276. package/scripts/lib/session-registry.mjs +27 -2
  277. package/scripts/lib/session-schema/constants.mjs +56 -2
  278. package/scripts/lib/session-schema/normalizer.mjs +56 -4
  279. package/scripts/lib/session-schema/validator.mjs +28 -1
  280. package/scripts/lib/session-schema.mjs +8 -3
  281. package/scripts/lib/session-start-probes.mjs +95 -10
  282. package/scripts/lib/session-token-rollup.mjs +245 -52
  283. package/scripts/lib/sessions-canonical.mjs +118 -4
  284. package/scripts/lib/sessions-integrity-banner.mjs +7 -1
  285. package/scripts/lib/sessions-staleness-banner.mjs +193 -51
  286. package/scripts/lib/sh-quote.mjs +29 -0
  287. package/scripts/lib/skill-evidence-window.mjs +891 -0
  288. package/scripts/lib/skill-evolution/candidate-intake.mjs +133 -12
  289. package/scripts/lib/skill-evolution/engine.mjs +18 -9
  290. package/scripts/lib/skill-judge.mjs +45 -3
  291. package/scripts/lib/subagents-schema.mjs +30 -3
  292. package/scripts/lib/tail-window.mjs +56 -0
  293. package/scripts/lib/telemetry/pricing.mjs +118 -71
  294. package/scripts/lib/telemetry/schema.mjs +30 -0
  295. package/scripts/lib/telemetry/sync.mjs +61 -6
  296. package/scripts/lib/telemetry-flush-health-banner.mjs +4 -22
  297. package/scripts/lib/test-runner/issue-reconcile.mjs +48 -16
  298. package/scripts/lib/tests-src-ratio.mjs +140 -23
  299. package/scripts/lib/tmux-layout/layouts.mjs +1 -11
  300. package/scripts/lib/tmux-layout/telemetry-stats.mjs +72 -13
  301. package/scripts/lib/tmux-layout/tmux-shell.mjs +2 -11
  302. package/scripts/lib/user-invocable-skills.mjs +23 -3
  303. package/scripts/lib/ux-grill/reconcile.mjs +48 -22
  304. package/scripts/lib/validate/check-agents-skills.mjs +26 -15
  305. package/scripts/lib/validate/check-agents.mjs +1 -1
  306. package/scripts/lib/validate/check-cursor-adapter.mjs +1 -0
  307. package/scripts/lib/validate/check-entry-guard.mjs +13 -50
  308. package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
  309. package/scripts/lib/validate/check-hooks-symmetry.mjs +5 -0
  310. package/scripts/lib/validate/check-learnings-shell-writes.mjs +214 -0
  311. package/scripts/lib/validate/check-pi-prompts.mjs +1 -0
  312. package/scripts/lib/validate/check-rules.mjs +7 -5
  313. package/scripts/lib/validate/check-skill-links.mjs +9 -1
  314. package/scripts/lib/validate/check-skill-script-paths.mjs +239 -27
  315. package/scripts/lib/validate/check-test-git-config-target.mjs +24 -34
  316. package/scripts/lib/validate/check-test-value-bans.mjs +87 -14
  317. package/scripts/lib/validate/check-unicode-safety.mjs +1 -0
  318. package/scripts/lib/validate/check-untracked-test-deps.mjs +7 -102
  319. package/scripts/lib/validate/check-unwired-features.mjs +130 -27
  320. package/scripts/lib/validate/check-validator-registration.mjs +34 -10
  321. package/scripts/lib/validate/confidential-names.mjs +10 -0
  322. package/scripts/lib/validate-vendored-rules.mjs +210 -84
  323. package/scripts/lib/vault-mirror/namespace.mjs +46 -8
  324. package/scripts/lib/vault-mirror/process.mjs +10 -3
  325. package/scripts/lib/vault-mirror/render-sessions.mjs +12 -2
  326. package/scripts/lib/vault-mirror/telemetry.mjs +8 -1
  327. package/scripts/lib/vault-status/board-writer.mjs +48 -11
  328. package/scripts/lib/vault-status/narrative-mirror.mjs +71 -16
  329. package/scripts/lib/vault-yaml.mjs +118 -0
  330. package/scripts/lib/wave-executor/dispatch-common.mjs +10 -18
  331. package/scripts/lib/wave-executor/foreign-dispatch.mjs +1 -1
  332. package/scripts/lib/wave-executor/remote-dispatch.mjs +1 -1
  333. package/scripts/lib/wave-resource-gate.mjs +34 -8
  334. package/scripts/lib/worktree/lifecycle.mjs +153 -1
  335. package/scripts/memory-propose.mjs +18 -5
  336. package/scripts/release-session-lock.mjs +306 -0
  337. package/scripts/release.mjs +40 -8
  338. package/scripts/repair-invalid-sessions.mjs +37 -4
  339. package/scripts/resolve-session-invocation.mjs +59 -0
  340. package/scripts/run-migrate-v2-cross-repo.mjs +158 -30
  341. package/scripts/run-quality-gate.mjs +357 -197
  342. package/scripts/site-numbers.mjs +163 -5
  343. package/scripts/sweep-expired-learnings.mjs +56 -5
  344. package/scripts/sweep-expired-rules.mjs +16 -4
  345. package/scripts/validate-plugin.mjs +19 -0
  346. package/scripts/validate-wave-scope.mjs +32 -105
  347. package/scripts/vault-integration-watcher.mjs +2 -1
  348. package/scripts/vault-mirror.mjs +81 -47
  349. package/scripts/wave-scope-binding.mjs +2 -1
  350. package/skills/_shared/config-reading.md +3 -3
  351. package/skills/_shared/fleet-protocol.md +131 -0
  352. package/skills/_shared/platform-tools.md +23 -11
  353. package/skills/_shared/state-ownership.md +5 -0
  354. package/skills/autopilot/SKILL.md +47 -18
  355. package/skills/bootstrap/SKILL.md +1 -1
  356. package/skills/bootstrap/_shared-template.md +1 -1
  357. package/skills/bootstrap/fast-template.md +1 -1
  358. package/skills/bootstrap/private-contract.md +2 -1
  359. package/skills/bootstrap/public-fallback.md +6 -3
  360. package/skills/bootstrap/references/bootstrap-retroactive-flow.md +2 -1
  361. package/skills/claude-md-drift-check/SKILL.md +10 -10
  362. package/skills/claude-md-drift-check/checker.mjs +26 -18
  363. package/skills/convergence-monitoring/README.md +8 -1
  364. package/skills/convergence-monitoring/SIGNALS.md +50 -6
  365. package/skills/convergence-monitoring/SKILL.md +15 -6
  366. package/skills/eval/SKILL.md +39 -24
  367. package/skills/eval/rubric-v1.md +1 -0
  368. package/skills/eval/rubric-v2.md +457 -0
  369. package/skills/evolve/SKILL.md +3 -3
  370. package/skills/evolve/references/evolve-analyze-mode.md +10 -6
  371. package/skills/evolve/references/evolve-dialectic-mode.md +42 -25
  372. package/skills/gitlab-ops/SKILL.md +3 -2
  373. package/skills/memory-cleanup/SKILL.md +1 -1
  374. package/skills/navigator/SKILL.md +193 -0
  375. package/skills/navigator/references/account-check.sh +87 -0
  376. package/skills/navigator/references/fleet-decisions.mjs +231 -0
  377. package/skills/navigator/references/slot-check.sh +114 -0
  378. package/skills/npm-publish/SKILL.md +1 -1
  379. package/skills/persona-panel/SKILL.md +2 -2
  380. package/skills/persona-panel/persona-format.md +2 -2
  381. package/skills/persona-panel/presets/designer-lens.md +1 -1
  382. package/skills/persona-panel/presets/engineer-lens.md +1 -1
  383. package/skills/persona-panel/presets/pm-lens.md +1 -1
  384. package/skills/plan/mode-retro.md +19 -4
  385. package/skills/quality-gates/SKILL.md +6 -4
  386. package/skills/reconcile/SKILL.md +11 -0
  387. package/skills/remote-offload/SKILL.md +4 -2
  388. package/skills/session-end/SKILL.md +13 -16
  389. package/skills/session-end/discovery-scan.md +1 -1
  390. package/skills/session-end/learning-patterns.md +22 -12
  391. package/skills/session-end/metrics-collection.md +5 -6
  392. package/skills/session-end/phase-3-6-tail.md +55 -9
  393. package/skills/session-end/references/phase-3-documentation-updates.md +2 -2
  394. package/skills/session-end/references/phase-4a-worktree-cleanup.md +2 -0
  395. package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
  396. package/skills/session-end/references/session-summary-template.md +1 -1
  397. package/skills/session-end/session-metrics-write.md +32 -60
  398. package/skills/session-plan/SKILL.md +17 -8
  399. package/skills/session-plan/references/session-plan-task-classification.md +2 -2
  400. package/skills/session-start/SKILL.md +6 -0
  401. package/skills/session-start/references/operations-contract.md +4 -0
  402. package/skills/session-start/references/phase-1-7-vault-status-board.md +1 -1
  403. package/skills/session-start/references/phase-4-ssot-environment-check.md +4 -3
  404. package/skills/test-audit/SKILL.md +152 -0
  405. package/skills/test-audit/references/campaign.md +111 -0
  406. package/skills/test-audit/references/mutate.sh +50 -0
  407. package/skills/ux-grill/SKILL.md +1 -1
  408. package/skills/wave-executor/SKILL.md +9 -5
  409. package/skills/wave-executor/circuit-breaker.md +2 -0
  410. package/skills/wave-executor/references/wave-executor-state-init.md +8 -3
  411. package/skills/wave-executor/references/wave-loop-dispatch.md +5 -2
  412. package/skills/wave-executor/references/wave-loop-review.md +3 -5
  413. package/templates/personas/README.md +5 -5
  414. package/templates/personas/accounting-compliance.v1.md +1 -1
  415. package/templates/personas/accounting-tax-advisor.v1.md +1 -1
  416. package/templates/personas/buyer-p1-cto.v1.md +1 -1
  417. package/templates/personas/buyer-p2-kanzlei.v1.md +1 -1
  418. package/templates/personas/buyer-p3-build.v1.md +1 -1
  419. package/templates/personas/buyer-p4-tech-veto.v1.md +1 -1
  420. package/templates/personas/buyer-p5-solo.v1.md +1 -1
  421. package/templates/personas/buyer-p6-ld.v1.md +1 -1
  422. package/templates/personas/klima-ai-expert.v1.md +1 -1
  423. package/templates/personas/klima-physicist.v1.md +1 -1
  424. package/.cursor/commands/convergence-monitoring.md +0 -13
  425. package/.cursor/commands/npm-publish.md +0 -13
  426. package/pi/prompts/convergence-monitoring.md +0 -11
  427. package/pi/prompts/npm-publish.md +0 -11
@@ -29,6 +29,12 @@
29
29
  * generic pattern path — its `pattern: ">"` would FP-match nearly
30
30
  * every redirect. Unresolved targets (variable/substitution) warn
31
31
  * on stderr (fail-visible) and never block.
32
+ * rule.type === 'path-delete' (#1401): decided by the DELETE/RENAME
33
+ * operand via firstDeniedDeleteTarget (same target-denylist globs,
34
+ * same compiler), likewise shadowing the generic pattern path. The
35
+ * redirect class answers "what does this command WRITE"; nothing
36
+ * answered "what does it REMOVE", so `rm -f <ledger>` was allowed
37
+ * while `> <ledger>` was denied — 53,896 lines of telemetry lost.
32
38
  * G6 no match → exit 0
33
39
  *
34
40
  * Telemetry (Epic #803 process-safety dimension): best-effort
@@ -46,8 +52,7 @@ import crypto from 'node:crypto';
46
52
  import { pathToFileURL } from 'node:url';
47
53
 
48
54
  import { shouldRunHook } from './_lib/profile-gate.mjs';
49
- // #211: exit 0 immediately (silent allow) when this hook is disabled via profile/env
50
- if (!shouldRunHook('pre-bash-destructive-guard')) process.exit(0);
55
+ import { isMainModule } from '../scripts/lib/is-main-module.mjs';
51
56
 
52
57
  // ---------------------------------------------------------------------------
53
58
  // #992 — late-bound repo dependencies
@@ -179,6 +184,8 @@ async function bootstrap() {
179
184
  'redirectSpanEnd',
180
185
  'resolveSegmentVerb',
181
186
  'splitChainSegments',
187
+ 'extractDeleteTargets',
188
+ 'firstDeniedDeleteTarget',
182
189
  ],
183
190
  },
184
191
  },
@@ -877,6 +884,61 @@ async function main() {
877
884
  continue; // unreachable (blockCommand never returns) — kept for clarity
878
885
  }
879
886
 
887
+ // #1401 — path-delete rules are decided by the DELETE/RENAME operand via
888
+ // firstDeniedDeleteTarget, never by the generic pattern path below (same
889
+ // full-shadow contract as redirect-truncate above: this rule's `pattern` is
890
+ // a human-readable label, and routing it through commandMatchesBlocked
891
+ // would match either nothing or every `rm`).
892
+ //
893
+ // Why a second surface at all: the redirect rule answers "what does this
894
+ // command WRITE". Nothing answered "what does it REMOVE" — so `rm -f
895
+ // .orchestrator/metrics/events.jsonl.1` was ALLOWED while `> ` on the same
896
+ // file was DENIED (measured 2026-09-19, #1401: 53,896 ledger lines lost).
897
+ if (rule.type === 'path-delete') {
898
+ const collected = blocker.extractDeleteTargets(command);
899
+ const cutOff = collected.filter(
900
+ (e) => e.unresolved && (e.reason === 'budget-exhausted' || e.reason === 'depth-exceeded')
901
+ );
902
+ if (cutOff.length > 0) {
903
+ // A recursion cap that drops payloads silently is a bypass, not a cap
904
+ // (#988 T2) — surface it on the VISIBLE channel (#995).
905
+ //
906
+ // Deliberately NARROWER than the redirect branch above, which also
907
+ // surfaces `variable/substitution` operands: `rm -rf "$WORK"` is the
908
+ // ORDINARY spelling of a temp cleanup, so notifying on it would fire on
909
+ // a large share of benign commands and train the operator to ignore the
910
+ // channel (host-resources.md § HR-101 — a signal may only warn if it is
911
+ // rare). A cut-off marker, by contrast, needs 32+ payload segments or
912
+ // 3 levels of nesting: pathological by construction. Unresolved
913
+ // operands stay fail-visible on stderr below; neither class ever blocks.
914
+ const reasons = [...new Set(cutOff.map((e) => e.reason))].join(', ');
915
+ const msg = `pre-bash-destructive-guard: delete-target traversal cut off (${reasons}) — not matched (fail-visible)`;
916
+ process.stderr.write(`⚠ ${msg}\n`);
917
+ notices.push(msg);
918
+ }
919
+ const unresolvedOperands = collected.filter(
920
+ (e) => e.unresolved && e.reason === 'variable/substitution'
921
+ );
922
+ if (unresolvedOperands.length > 0) {
923
+ process.stderr.write(
924
+ `ℹ pre-bash-destructive-guard: ${unresolvedOperands.length} delete target(s) carry a variable/substitution — not matched (rule: ${id})\n`
925
+ );
926
+ }
927
+ const hit = blocker.firstDeniedDeleteTarget(rule, collected, { repoRoot: projectDir });
928
+ if (!hit) continue;
929
+ if (severity !== 'block') {
930
+ const msg = `pre-bash-destructive-guard: protected delete target matched (rule: ${id}) — ${rationale}`;
931
+ process.stderr.write(`⚠ ${msg}\n`);
932
+ notices.push(msg);
933
+ continue;
934
+ }
935
+ // Reason stays short (stdout-budget): verb + target, never the command.
936
+ // The policy `rationale` is long by design (it carries the measurement
937
+ // trail); blockCommand puts only its first line in the systemMessage.
938
+ await blockCommand(`${hit.verb} ${hit.target}`, id, rationale, command, sessionId);
939
+ continue; // unreachable (blockCommand never returns) — kept for clarity
940
+ }
941
+
880
942
  // The rm-rf-destructive rule also fires for recursive+force rm flag variants
881
943
  // the literal "rm -rf" pattern misses (`rm -r -f`, `rm -fr`) — #641 gap closure.
882
944
  const matched = id === 'rm-rf-destructive'
@@ -984,10 +1046,19 @@ try {
984
1046
  process.exit(0); // fail-open, but no longer fail-silent
985
1047
  }
986
1048
 
987
- // Top-level error handler — never let exit 1 leak
988
- main().catch((e) => {
989
- process.stderr.write(
990
- `⚠ pre-bash-destructive-guard: internal error — ${e?.message || e}\n`
991
- );
992
- process.exit(0); // fail-open on internal errors to avoid blocking legitimate work
993
- });
1049
+ // Entry guard (#1393): run only as the node script the harness execs — a bare
1050
+ // `import()` must run no handler and must not exit the importing process.
1051
+ // Placed strictly AFTER all decision logic: it gates whether main() is invoked,
1052
+ // never what main() decides, so no deny path can be short-circuited by it.
1053
+ if (isMainModule(import.meta.url)) {
1054
+ // #211: exit 0 immediately (silent allow) when this hook is disabled via profile/env
1055
+ if (!shouldRunHook('pre-bash-destructive-guard')) process.exit(0);
1056
+
1057
+ // Top-level error handler — never let exit 1 leak
1058
+ main().catch((e) => {
1059
+ process.stderr.write(
1060
+ `⚠ pre-bash-destructive-guard: internal error — ${e?.message || e}\n`
1061
+ );
1062
+ process.exit(0); // fail-open on internal errors to avoid blocking legitimate work
1063
+ });
1064
+ }
@@ -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
  // ---------------------------------------------------------------------------
@@ -500,10 +497,18 @@ async function main() {
500
497
  return emitAllow();
501
498
  }
502
499
 
503
- // Top-level error handler — fail open, same posture as the sibling hooks.
504
- main().catch((e) => {
505
- process.stderr.write(
506
- `⚠ pre-bash-issue-budget: internal error — ${e?.message || e}\n`,
507
- );
508
- process.exit(0);
509
- });
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,15 +23,16 @@
22
23
  */
23
24
 
24
25
  import { readStdin, emitAllow } from '../scripts/lib/io.mjs';
25
- import { existsSync } from 'node:fs';
26
+ import crypto from 'node:crypto';
27
+ import { existsSync, realpathSync } from 'node:fs';
26
28
  import { readFile } from 'node:fs/promises';
29
+ import os from 'node:os';
27
30
  import path from 'node:path';
28
31
  import { emitEvent } from '../scripts/lib/events.mjs';
29
32
  import { findScopeFile } from '../scripts/lib/hardening.mjs';
30
33
 
31
34
  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);
35
+ import { isMainModule } from '../scripts/lib/is-main-module.mjs';
34
36
 
35
37
  // ---------------------------------------------------------------------------
36
38
  // Constants
@@ -48,59 +50,126 @@ if (!shouldRunHook('pre-bash-memory-propose-audit')) process.exit(0);
48
50
  const MEMORY_PROPOSE_REGEX = /\bnode\b.*\bmemory-propose\.mjs\b/i;
49
51
 
50
52
  /**
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].
53
+ * The flag NAMES `scripts/memory-propose.mjs` accepts (its `parseArgs` options
54
+ * block, plus `--help`). CLOSED LIST BY DESIGN: `flags_present` is built by
55
+ * intersecting the command with this constant, never by scraping `--\S+` tokens
56
+ * out of the command. A scrape would re-open the leak this file just closed —
57
+ * `--insight --my-secret-value` would publish the value as if it were a flag.
53
58
  *
54
- * Matches both forms:
55
- * --insight=value → --insight=[REDACTED]
56
- * --insight "quoted value" → --insight [REDACTED]
57
- * --insight unquoted → --insight [REDACTED]
59
+ * A flag the CLI does not know is simply not reported. That is the correct
60
+ * trade: the field exists to say WHICH of the audited CLI's switches were used,
61
+ * not to mirror the command line.
58
62
  */
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;
63
+ const KNOWN_FLAGS = Object.freeze([
64
+ 'type',
65
+ 'subject',
66
+ 'insight',
67
+ 'evidence',
68
+ 'confidence',
69
+ 'dry-run',
70
+ 'file-paths',
71
+ 'help',
72
+ ]);
76
73
 
77
74
  // ---------------------------------------------------------------------------
78
75
  // Helpers
79
76
  // ---------------------------------------------------------------------------
80
77
 
81
78
  /**
82
- * Redact values of privacy-sensitive flags from a command string.
83
- * Preserves flag names; replaces values with [REDACTED].
79
+ * sha256(command), truncated to 16 hex characters.
80
+ *
81
+ * WORD-FOR-WORD the `hashCommand()` of `hooks/enforce-commands.mjs:69` (itself
82
+ * taken from `hooks/pre-bash-destructive-guard.mjs:253`, which mirrors
83
+ * `loop-guard.mjs` `hashArgs()`). Deliberately duplicated rather than shared:
84
+ * a PreToolUse hook must not gain another loadable module that can fail at
85
+ * start — three lines are cheaper than a load error on the hot path. Verified
86
+ * before duplicating that no helper is EXPORTED at any of those sites.
87
+ *
88
+ * WHY THE HASH IS HERE (#1415, 2026-09-21): until today this hook wrote a
89
+ * merely flag-redacted `argv_truncated` (512 chars of command text) into
90
+ * `orchestrator.memory.propose_invoked` — an `events.jsonl` line (gitignored
91
+ * here via `.gitignore:57`; consumer repos may track it) and,
92
+ * with the webhook configured, a network payload. Redaction covered only the
93
+ * five `--insight/--subject/--evidence/--content/--reason` values and said so
94
+ * in its own caveat: `$VAR`, `$(cat secret)` and any value written elsewhere
95
+ * on the line passed through. This was the THIRD and last open site of the
96
+ * class closed by `8f15f77b` (enforce-commands) and #1404 (staging-fence).
97
+ *
98
+ * The hash keeps the event COUNTABLE and GROUPABLE — repeat invocations of the
99
+ * same command still collapse to one key — which is the only property any
100
+ * consumer needed. Measured 2026-09-21 (`rg argv_truncated` over `scripts/
101
+ * hooks/ skills/ docs/`): ZERO production readers, the field's only consumers
102
+ * were this hook's own tests.
84
103
  *
85
104
  * @param {string} command
86
105
  * @returns {string}
87
106
  */
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
- });
107
+ function hashCommand(command) {
108
+ return crypto.createHash('sha256').update(command).digest('hex').slice(0, 16);
109
+ }
110
+
111
+ /**
112
+ * The subset of KNOWN_FLAGS that appears in the command, in KNOWN_FLAGS order.
113
+ *
114
+ * Names only — a value can never reach this array, because the array is built
115
+ * from the constant above and the command is only ever TESTED against it. Both
116
+ * spellings count (`--insight=x` and `--insight x`); the trailing boundary
117
+ * `(?![\w-])` keeps `--insightful` from reporting `insight`.
118
+ *
119
+ * @param {string} command
120
+ * @returns {string[]}
121
+ */
122
+ function flagsPresent(command) {
123
+ return KNOWN_FLAGS.filter((flag) =>
124
+ new RegExp(`--${flag}(?![\\w-])`).test(command),
125
+ );
126
+ }
127
+
128
+ /**
129
+ * The working directory as a value that carries no absolute host path (#1439).
130
+ *
131
+ * Relative to `projectDir` (`.` when equal); a directory OUTSIDE the project is
132
+ * written as `sha256:<12 hex>` of its resolved path instead — still groupable,
133
+ * never the path itself. Until 2026-09-25 this was `process.cwd()` verbatim,
134
+ * i.e. `/Users/<name>/…` in an events.jsonl line (gitignored here via
135
+ * `.gitignore:57`; consumer repos may track it) and, with the webhook
136
+ * configured, in a network payload. No production reader consumes `cwd`.
137
+ *
138
+ * Both sides are realpath-resolved before comparing: on macOS the tmp dir is
139
+ * `/var/…` while `process.cwd()` reports `/private/var/…`, and a naive
140
+ * `path.relative` then calls the project dir itself `../../../private/var/…`.
141
+ * A path that cannot be resolved falls back to `path.resolve` — never throws.
142
+ *
143
+ * A project dir that is no project boundary — the filesystem root, the home
144
+ * dir, or an ancestor of it (`/Users`) — also gets the hash form: relative to
145
+ * `/` the cwd reads `Users/<name>/…`, i.e. the username in a webhook-bound
146
+ * event, the very leak the relative form exists to prevent. The ancestor test
147
+ * is path-segment-wise (`path.relative`), so `/Users/al` is NOT an ancestor of
148
+ * `/Users/alice` the way a string prefix would claim.
149
+ *
150
+ * @param {string} cwd
151
+ * @param {string} projectDir
152
+ * @returns {string}
153
+ */
154
+ function relativeCwd(cwd, projectDir) {
155
+ const real = (p) => {
156
+ try { return realpathSync(p); } catch { return path.resolve(p); }
157
+ };
158
+ // `..`-prefixed (above the base) or absolute (another Windows drive).
159
+ const escapes = (rel) =>
160
+ rel === '..' || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel);
161
+ const realCwd = real(cwd);
162
+ const realProject = real(projectDir);
163
+ const rel = path.relative(realProject, realCwd);
164
+ if (rel === '') return '.';
165
+ // Same sha256 recipe as `command_hash`, cut to 12 hex.
166
+ const hashed = `sha256:${hashCommand(realCwd).slice(0, 12)}`;
167
+ if (escapes(rel)) return hashed;
168
+ if (path.dirname(realProject) === realProject) return hashed; // filesystem root
169
+ let home = null;
170
+ try { home = real(os.homedir()); } catch { /* no home dir: root check above still holds */ }
171
+ if (home !== null && !escapes(path.relative(realProject, home))) return hashed;
172
+ return rel;
104
173
  }
105
174
 
106
175
  /**
@@ -180,8 +249,11 @@ async function main() {
180
249
  // G5 — resolve wave
181
250
  const wave = await resolveWave(projectDir);
182
251
 
183
- // G6 — redact argv: strip sensitive flag values, keep flag names
184
- const argvRedacted = redactArgv(command);
252
+ // G6 — derive the non-reversible argv summary (#1415). Nothing downstream
253
+ // ever sees the command text: hash for grouping, known flag NAMES for
254
+ // shape, raw length for size — no operands, no paths, no values.
255
+ const commandHash = hashCommand(command);
256
+ const flags = flagsPresent(command);
185
257
 
186
258
  // G7 — emit canonical event via emitEvent (single emission path: schema + webhook,
187
259
  // replacing the local hand-rolled appendFileSync bypass).
@@ -193,8 +265,10 @@ async function main() {
193
265
  await emitEvent('orchestrator.memory.propose_invoked', {
194
266
  session_id: sessionId,
195
267
  wave,
196
- argv_truncated: argvRedacted.slice(0, 512),
197
- cwd: process.cwd(),
268
+ command_hash: commandHash,
269
+ flags_present: flags,
270
+ argv_length: command.length,
271
+ cwd: relativeCwd(process.cwd(), projectDir),
198
272
  exit_code: null,
199
273
  });
200
274
  } catch { /* telemetry never blocks the hook (#1183) */ }
@@ -203,10 +277,17 @@ async function main() {
203
277
  return emitAllow();
204
278
  }
205
279
 
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
- });
280
+ // Entry guard (#1393): run only as the node script the harness execs — a bare
281
+ // `import()` must run no handler and must not exit the importing process.
282
+ if (isMainModule(import.meta.url)) {
283
+ // exit 0 immediately when this hook is disabled via profile/env
284
+ if (!shouldRunHook('pre-bash-memory-propose-audit')) process.exit(0);
285
+
286
+ // Top-level error handler — never let exit 1 leak
287
+ main().catch((e) => {
288
+ process.stderr.write(
289
+ `⚠ pre-bash-memory-propose-audit: internal error — ${e?.message || e}\n`,
290
+ );
291
+ process.exit(0); // fail-open on internal errors
292
+ });
293
+ }