session-orchestrator 5.0.0 → 5.2.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 (298) hide show
  1. package/.agents/skills/autopilot/SKILL.md +1 -0
  2. package/.agents/skills/bootstrap/SKILL.md +2 -0
  3. package/.agents/skills/brainstorm/SKILL.md +3 -0
  4. package/.agents/skills/close/SKILL.md +17 -0
  5. package/.agents/skills/debug/SKILL.md +2 -0
  6. package/.agents/skills/discovery/SKILL.md +2 -1
  7. package/.agents/skills/dispatcher/SKILL.md +2 -0
  8. package/.agents/skills/eli5/SKILL.md +2 -0
  9. package/.agents/skills/eval/SKILL.md +1 -0
  10. package/.agents/skills/evolve/SKILL.md +2 -1
  11. package/.agents/skills/go/SKILL.md +18 -0
  12. package/.agents/skills/grill/SKILL.md +2 -0
  13. package/.agents/skills/harness-audit/SKILL.md +16 -0
  14. package/.agents/skills/memory-cleanup/SKILL.md +1 -0
  15. package/.agents/skills/persona-panel/SKILL.md +1 -0
  16. package/.agents/skills/plan/SKILL.md +3 -1
  17. package/.agents/skills/portfolio/SKILL.md +17 -0
  18. package/.agents/skills/reconcile/SKILL.md +1 -0
  19. package/.agents/skills/release/SKILL.md +18 -0
  20. package/.agents/skills/repo-audit/SKILL.md +1 -0
  21. package/.agents/skills/spinout/SKILL.md +1 -0
  22. package/.agents/skills/sunset-review/SKILL.md +2 -0
  23. package/.agents/skills/test/SKILL.md +17 -0
  24. package/.agents/skills/ux-grill/SKILL.md +2 -0
  25. package/.claude-plugin/marketplace.json +3 -3
  26. package/.claude-plugin/plugin.json +2 -2
  27. package/.codex-plugin/plugin.json +2 -2
  28. package/.codex-plugin/skills/autopilot/SKILL.md +5 -4
  29. package/.codex-plugin/skills/bootstrap/SKILL.md +8 -4
  30. package/.codex-plugin/skills/brainstorm/SKILL.md +11 -4
  31. package/.codex-plugin/skills/close/SKILL.md +3 -3
  32. package/.codex-plugin/skills/convergence-monitoring/SKILL.md +2 -0
  33. package/.codex-plugin/skills/convergence-monitoring/agents/openai.yaml +5 -0
  34. package/.codex-plugin/skills/debug/SKILL.md +11 -4
  35. package/.codex-plugin/skills/discovery/SKILL.md +8 -4
  36. package/.codex-plugin/skills/dispatcher/SKILL.md +4 -4
  37. package/.codex-plugin/skills/eli5/SKILL.md +9 -4
  38. package/.codex-plugin/skills/eval/SKILL.md +9 -4
  39. package/.codex-plugin/skills/evolve/SKILL.md +9 -4
  40. package/.codex-plugin/skills/go/SKILL.md +3 -3
  41. package/.codex-plugin/skills/grill/SKILL.md +11 -4
  42. package/.codex-plugin/skills/harness-audit/SKILL.md +4 -3
  43. package/.codex-plugin/skills/memory-cleanup/SKILL.md +9 -4
  44. package/.codex-plugin/skills/npm-publish/SKILL.md +2 -0
  45. package/.codex-plugin/skills/npm-publish/agents/openai.yaml +5 -0
  46. package/.codex-plugin/skills/persona-panel/SKILL.md +5 -5
  47. package/.codex-plugin/skills/plan/SKILL.md +8 -4
  48. package/.codex-plugin/skills/portfolio/SKILL.md +3 -3
  49. package/.codex-plugin/skills/reconcile/SKILL.md +9 -4
  50. package/.codex-plugin/skills/release/SKILL.md +3 -3
  51. package/.codex-plugin/skills/repo-audit/SKILL.md +6 -4
  52. package/.codex-plugin/skills/spinout/SKILL.md +4 -4
  53. package/.codex-plugin/skills/sunset-review/SKILL.md +5 -4
  54. package/.codex-plugin/skills/test/SKILL.md +3 -3
  55. package/.codex-plugin/skills/ux-grill/SKILL.md +11 -4
  56. package/.cursor/commands/autopilot.md +4 -4
  57. package/.cursor/commands/bootstrap.md +5 -4
  58. package/.cursor/commands/brainstorm.md +5 -4
  59. package/.cursor/commands/close.md +4 -3
  60. package/.cursor/commands/convergence-monitoring.md +13 -0
  61. package/.cursor/commands/debug.md +4 -4
  62. package/.cursor/commands/discovery.md +4 -4
  63. package/.cursor/commands/dispatcher.md +4 -4
  64. package/.cursor/commands/eli5.md +4 -4
  65. package/.cursor/commands/eval.md +4 -4
  66. package/.cursor/commands/evolve.md +4 -4
  67. package/.cursor/commands/go.md +4 -3
  68. package/.cursor/commands/grill.md +4 -4
  69. package/.cursor/commands/harness-audit.md +3 -3
  70. package/.cursor/commands/memory-cleanup.md +4 -4
  71. package/.cursor/commands/npm-publish.md +13 -0
  72. package/.cursor/commands/persona-panel.md +4 -4
  73. package/.cursor/commands/plan.md +5 -4
  74. package/.cursor/commands/portfolio.md +3 -3
  75. package/.cursor/commands/reconcile.md +4 -4
  76. package/.cursor/commands/release.md +4 -3
  77. package/.cursor/commands/repo-audit.md +4 -4
  78. package/.cursor/commands/spinout.md +4 -4
  79. package/.cursor/commands/sunset-review.md +4 -4
  80. package/.cursor/commands/test.md +3 -3
  81. package/.cursor/commands/ux-grill.md +4 -4
  82. package/.cursor/rules/010-session-workflow.mdc +2 -2
  83. package/.cursor/skills/bootstrap/SKILL.md +1 -0
  84. package/.cursor/skills/close/SKILL.md +13 -0
  85. package/.cursor/skills/debug/SKILL.md +0 -1
  86. package/.cursor/skills/discovery/SKILL.md +0 -1
  87. package/.cursor/skills/dispatcher/SKILL.md +0 -1
  88. package/.cursor/skills/eli5/SKILL.md +0 -1
  89. package/.cursor/skills/evolve/SKILL.md +0 -1
  90. package/.cursor/skills/go/SKILL.md +13 -0
  91. package/.cursor/skills/grill/SKILL.md +0 -1
  92. package/.cursor/skills/harness-audit/SKILL.md +12 -0
  93. package/.cursor/skills/portfolio/SKILL.md +12 -0
  94. package/.cursor/skills/release/SKILL.md +13 -0
  95. package/.cursor/skills/repo-audit/SKILL.md +0 -1
  96. package/.cursor/skills/sunset-review/SKILL.md +0 -1
  97. package/.cursor/skills/test/SKILL.md +12 -0
  98. package/.cursor/skills/ux-grill/SKILL.md +0 -1
  99. package/.cursor-plugin/plugin.json +2 -2
  100. package/.orchestrator/policy/blocked-commands.json +10 -0
  101. package/AGENTS.md +1 -1
  102. package/CHANGELOG.md +80 -0
  103. package/README.md +74 -235
  104. package/commands/session.md +10 -0
  105. package/docs/USER-GUIDE.md +24 -0
  106. package/docs/ci-setup.md +53 -0
  107. package/docs/codex-setup.md +1 -1
  108. package/docs/components.md +12 -5
  109. package/docs/events-schema.md +5 -1
  110. package/docs/install.md +128 -0
  111. package/docs/persona-panel.md +1 -1
  112. package/docs/pi-setup.md +1 -1
  113. package/docs/rule-authoring.md +83 -14
  114. package/docs/scope-collision-guard.md +2 -0
  115. package/docs/session-config-reference.md +6 -4
  116. package/docs/session-config-template.md +38 -0
  117. package/docs/telemetry.md +15 -0
  118. package/hooks/_lib/hook-import-set.json +46 -6
  119. package/hooks/_lib/subagent-paths.mjs +15 -0
  120. package/hooks/_lib/vcs-create-matcher.mjs +217 -62
  121. package/hooks/enforce-scope.mjs +42 -1
  122. package/hooks/hooks-codex.json +1 -1
  123. package/hooks/hooks.json +1 -1
  124. package/hooks/on-session-end.mjs +14 -2
  125. package/hooks/on-stop.mjs +43 -1
  126. package/hooks/post-bash-write-verify.mjs +3 -0
  127. package/hooks/pre-auq-clarity.mjs +3 -0
  128. package/hooks/pre-bash-issue-budget.mjs +103 -17
  129. package/hooks/pre-task-scope-disjoint.mjs +152 -3
  130. package/hooks/skill-invocation-telemetry.mjs +2 -1
  131. package/package.json +3 -2
  132. package/pi/prompts/autopilot.md +3 -3
  133. package/pi/prompts/bootstrap.md +3 -3
  134. package/pi/prompts/brainstorm.md +3 -3
  135. package/pi/prompts/close.md +2 -2
  136. package/pi/prompts/convergence-monitoring.md +11 -0
  137. package/pi/prompts/debug.md +3 -3
  138. package/pi/prompts/discovery.md +3 -3
  139. package/pi/prompts/dispatcher.md +3 -3
  140. package/pi/prompts/eli5.md +3 -3
  141. package/pi/prompts/eval.md +3 -3
  142. package/pi/prompts/evolve.md +3 -3
  143. package/pi/prompts/go.md +2 -2
  144. package/pi/prompts/grill.md +3 -3
  145. package/pi/prompts/harness-audit.md +2 -3
  146. package/pi/prompts/memory-cleanup.md +3 -3
  147. package/pi/prompts/npm-publish.md +11 -0
  148. package/pi/prompts/persona-panel.md +3 -3
  149. package/pi/prompts/plan.md +3 -3
  150. package/pi/prompts/portfolio.md +2 -2
  151. package/pi/prompts/reconcile.md +3 -3
  152. package/pi/prompts/release.md +3 -3
  153. package/pi/prompts/repo-audit.md +3 -4
  154. package/pi/prompts/session.md +1 -1
  155. package/pi/prompts/spinout.md +3 -3
  156. package/pi/prompts/sunset-review.md +3 -3
  157. package/pi/prompts/templates-ack.md +1 -1
  158. package/pi/prompts/test.md +3 -3
  159. package/pi/prompts/ux-grill.md +3 -3
  160. package/scripts/archive-closed-prds.mjs +2 -2
  161. package/scripts/auq-audit.mjs +2 -3
  162. package/scripts/backfill-abandoned-sessions.mjs +57 -3
  163. package/scripts/backfill-evidence-digest.mjs +2 -1
  164. package/scripts/backfill-learnings-from-vault.mjs +2 -2
  165. package/scripts/check-package-manager.mjs +2 -2
  166. package/scripts/ci/assert-vitest-green.mjs +2 -1
  167. package/scripts/emit-session.mjs +2 -3
  168. package/scripts/export-hw-learnings.mjs +2 -1
  169. package/scripts/express-path.mjs +1 -1
  170. package/scripts/gc-stale-worktrees.mjs +2 -1
  171. package/scripts/generate-codex-skills.mjs +48 -4
  172. package/scripts/generate-cursor-adapter.mjs +173 -9
  173. package/scripts/generate-hook-import-set.mjs +12 -27
  174. package/scripts/generate-pi-prompts.mjs +183 -13
  175. package/scripts/github-protection-audit.mjs +2 -3
  176. package/scripts/lib/agent-frontmatter.mjs +23 -1
  177. package/scripts/lib/claude-md-budget-lint.mjs +2 -5
  178. package/scripts/lib/command-blocker.mjs +209 -9
  179. package/scripts/lib/config/drift-check.mjs +19 -0
  180. package/scripts/lib/convergence-monitor.mjs +2 -2
  181. package/scripts/lib/cursor-hook-bridge.mjs +2 -2
  182. package/scripts/lib/description-surface.mjs +2 -5
  183. package/scripts/lib/dispatcher/cli.mjs +2 -1
  184. package/scripts/lib/ecosystem-wizard.mjs +2 -1
  185. package/scripts/lib/fetch-baseline.mjs +3 -8
  186. package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +2 -1
  187. package/scripts/lib/gitlab-portfolio/cli.mjs +2 -1
  188. package/scripts/lib/instruction-budget-guard.mjs +186 -46
  189. package/scripts/lib/is-main-module.mjs +82 -0
  190. package/scripts/lib/locks/index.mjs +32 -25
  191. package/scripts/lib/maintenance-due-banner.mjs +69 -3
  192. package/scripts/lib/peer-discovery.mjs +2 -5
  193. package/scripts/lib/playwright-driver/runner.mjs +63 -2
  194. package/scripts/lib/reconcile/rule-expiry-sweep.mjs +642 -0
  195. package/scripts/lib/rules-sync.mjs +2 -5
  196. package/scripts/lib/scope-echo.mjs +392 -7
  197. package/scripts/lib/session-close-backfill.mjs +58 -6
  198. package/scripts/lib/state-md.mjs +84 -3
  199. package/scripts/lib/sunset/walker.mjs +31 -4
  200. package/scripts/lib/tests-src-ratio.mjs +2 -6
  201. package/scripts/lib/tmux-layout/telemetry-stats.mjs +2 -1
  202. package/scripts/lib/user-invocable-skills.mjs +185 -0
  203. package/scripts/lib/validate/check-banner-parity.mjs +2 -2
  204. package/scripts/lib/validate/check-cursor-adapter.mjs +2 -2
  205. package/scripts/lib/validate/check-dead-bridge.mjs +2 -2
  206. package/scripts/lib/validate/check-doc-cli-commands.mjs +2 -2
  207. package/scripts/lib/validate/check-entry-guard.mjs +366 -0
  208. package/scripts/lib/validate/check-guard-requires-parity.mjs +2 -2
  209. package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +2 -2
  210. package/scripts/lib/validate/check-learning-provenance.mjs +2 -2
  211. package/scripts/lib/validate/check-skill-links.mjs +27 -6
  212. package/scripts/lib/validate/check-skill-script-paths.mjs +2 -2
  213. package/scripts/lib/validate/check-test-git-config-target.mjs +2 -2
  214. package/scripts/lib/validate/check-unicode-safety.mjs +2 -2
  215. package/scripts/lib/validate/check-untracked-test-deps.mjs +2 -2
  216. package/scripts/lib/validate/check-unwired-features.mjs +266 -11
  217. package/scripts/lib/validate/check-validator-registration.mjs +2 -2
  218. package/scripts/lib/validate/check-vcs-repo-flag.mjs +2 -2
  219. package/scripts/lib/validate-vendored-rules.mjs +35 -9
  220. package/scripts/lib/wave-transcript-tail.mjs +2 -2
  221. package/scripts/lock-reaper.mjs +2 -1
  222. package/scripts/materialize-wave-scope.mjs +87 -4
  223. package/scripts/migrate-sessions-jsonl.mjs +2 -1
  224. package/scripts/migrate-vault-paths.mjs +2 -3
  225. package/scripts/release.mjs +124 -35
  226. package/scripts/relocate-vault-corpus.mjs +2 -3
  227. package/scripts/repair-invalid-sessions.mjs +2 -2
  228. package/scripts/session-shape.mjs +2 -2
  229. package/scripts/site-numbers.mjs +35 -11
  230. package/scripts/sweep-expired-rules.mjs +216 -0
  231. package/scripts/validate-plugin.mjs +9 -0
  232. package/scripts/vault-consolidate.mjs +2 -2
  233. package/scripts/vault-mirror.mjs +2 -3
  234. package/scripts/wave-scope-binding.mjs +2 -3
  235. package/skills/_shared/bootstrap-gate.md +1 -1
  236. package/skills/_shared/monitor-patterns.md +1 -1
  237. package/skills/_shared/research-evidence.md +53 -0
  238. package/skills/_shared/state-ownership.md +3 -0
  239. package/skills/autopilot/SKILL.md +58 -4
  240. package/skills/bootstrap/SKILL.md +51 -1
  241. package/skills/brainstorm/SKILL.md +16 -0
  242. package/skills/claude-md-drift-check/checker.mjs +49 -11
  243. package/{commands/close.md → skills/close/SKILL.md} +9 -3
  244. package/skills/debug/SKILL.md +10 -0
  245. package/skills/discovery/SKILL.md +24 -1
  246. package/skills/discovery/probes-session.md +2 -2
  247. package/skills/dispatcher/SKILL.md +38 -7
  248. package/skills/eli5/SKILL.md +11 -0
  249. package/skills/eval/SKILL.md +14 -0
  250. package/skills/evolve/SKILL.md +8 -1
  251. package/skills/evolve/references/evolve-dialectic-mode.md +6 -2
  252. package/{commands/go.md → skills/go/SKILL.md} +9 -1
  253. package/skills/grill/SKILL.md +19 -0
  254. package/{commands/harness-audit.md → skills/harness-audit/SKILL.md} +7 -2
  255. package/skills/hook-development/SKILL.md +46 -41
  256. package/skills/memory-cleanup/SKILL.md +7 -0
  257. package/skills/npm-publish/SKILL.md +1 -1
  258. package/skills/persona-panel/SKILL.md +56 -1
  259. package/skills/persona-panel/persona-format.md +1 -1
  260. package/skills/plan/SKILL.md +28 -1
  261. package/skills/playwright-driver/SKILL.md +7 -10
  262. package/{commands/portfolio.md → skills/portfolio/SKILL.md} +8 -2
  263. package/skills/reconcile/SKILL.md +10 -0
  264. package/{commands/release.md → skills/release/SKILL.md} +16 -2
  265. package/skills/repo-audit/SKILL.md +7 -0
  266. package/skills/session-end/plan-verification.md +2 -2
  267. package/skills/session-plan/SKILL.md +1 -1
  268. package/skills/session-start/SKILL.md +5 -4
  269. package/skills/session-start/phase-8-5-express-path.md +6 -6
  270. package/skills/session-start/references/phase-1-5-session-continuity.md +1 -1
  271. package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +1 -1
  272. package/skills/session-start/references/phase-4-ssot-environment-check.md +4 -3
  273. package/skills/spinout/SKILL.md +12 -1
  274. package/skills/sunset-review/SKILL.md +13 -0
  275. package/{commands/test.md → skills/test/SKILL.md} +10 -4
  276. package/skills/ux-grill/SKILL.md +19 -1
  277. package/skills/wave-executor/SKILL.md +7 -4
  278. package/skills/wave-executor/references/wave-executor-state-init.md +13 -1
  279. package/skills/wave-executor/references/wave-loop-dispatch.md +3 -1
  280. package/skills/wave-executor/references/wave-loop-review.md +17 -1
  281. package/commands/autopilot.md +0 -80
  282. package/commands/bootstrap.md +0 -56
  283. package/commands/brainstorm.md +0 -48
  284. package/commands/debug.md +0 -36
  285. package/commands/discovery.md +0 -32
  286. package/commands/dispatcher.md +0 -59
  287. package/commands/eli5.md +0 -33
  288. package/commands/eval.md +0 -28
  289. package/commands/evolve.md +0 -10
  290. package/commands/grill.md +0 -45
  291. package/commands/memory-cleanup.md +0 -26
  292. package/commands/persona-panel.md +0 -121
  293. package/commands/plan.md +0 -15
  294. package/commands/reconcile.md +0 -23
  295. package/commands/repo-audit.md +0 -24
  296. package/commands/spinout.md +0 -15
  297. package/commands/sunset-review.md +0 -27
  298. package/commands/ux-grill.md +0 -51
@@ -1,6 +1,6 @@
1
1
  {
2
- "generated_at": "2026-09-13T09:10:50.839Z",
3
- "head": "ff1ed19177795b743186eebfc387319de0e90c31",
2
+ "generated_at": "2026-09-16T17:00:47.173Z",
3
+ "head": "1ef26415ce80164bd7e27705a4e7d97c37c72126",
4
4
  "entries": [
5
5
  {
6
6
  "file": "hooks/_lib/atomic-json.mjs",
@@ -268,7 +268,8 @@
268
268
  "reachable_from": [
269
269
  "on-session-end.mjs",
270
270
  "on-session-start.mjs",
271
- "post-edit-validate.mjs"
271
+ "post-edit-validate.mjs",
272
+ "skill-invocation-telemetry.mjs"
272
273
  ]
273
274
  },
274
275
  {
@@ -358,6 +359,7 @@
358
359
  "pre-auq-clarity.mjs",
359
360
  "pre-bash-issue-budget.mjs",
360
361
  "pre-bash-memory-propose-audit.mjs",
362
+ "pre-task-scope-disjoint.mjs",
361
363
  "skill-invocation-telemetry.mjs"
362
364
  ]
363
365
  },
@@ -381,6 +383,7 @@
381
383
  "pre-auq-clarity.mjs",
382
384
  "pre-bash-issue-budget.mjs",
383
385
  "pre-bash-memory-propose-audit.mjs",
386
+ "pre-task-scope-disjoint.mjs",
384
387
  "skill-invocation-telemetry.mjs"
385
388
  ]
386
389
  },
@@ -660,6 +663,7 @@
660
663
  "pre-auq-clarity.mjs",
661
664
  "pre-bash-issue-budget.mjs",
662
665
  "pre-bash-memory-propose-audit.mjs",
666
+ "pre-task-scope-disjoint.mjs",
663
667
  "skill-invocation-telemetry.mjs"
664
668
  ]
665
669
  },
@@ -699,6 +703,7 @@
699
703
  "pre-auq-clarity.mjs",
700
704
  "pre-bash-issue-budget.mjs",
701
705
  "pre-bash-memory-propose-audit.mjs",
706
+ "pre-task-scope-disjoint.mjs",
702
707
  "skill-invocation-telemetry.mjs"
703
708
  ]
704
709
  },
@@ -838,7 +843,8 @@
838
843
  "post-tool-batch-wave-signal.mjs",
839
844
  "post-tooluse-frontend-slop.mjs",
840
845
  "pre-auq-clarity.mjs",
841
- "pre-bash-memory-propose-audit.mjs"
846
+ "pre-bash-memory-propose-audit.mjs",
847
+ "pre-task-scope-disjoint.mjs"
842
848
  ]
843
849
  },
844
850
  {
@@ -859,7 +865,8 @@
859
865
  "post-tool-batch-wave-signal.mjs",
860
866
  "post-tooluse-frontend-slop.mjs",
861
867
  "pre-auq-clarity.mjs",
862
- "pre-bash-memory-propose-audit.mjs"
868
+ "pre-bash-memory-propose-audit.mjs",
869
+ "pre-task-scope-disjoint.mjs"
863
870
  ]
864
871
  },
865
872
  {
@@ -882,6 +889,7 @@
882
889
  "pre-auq-clarity.mjs",
883
890
  "pre-bash-issue-budget.mjs",
884
891
  "pre-bash-memory-propose-audit.mjs",
892
+ "pre-task-scope-disjoint.mjs",
885
893
  "skill-invocation-telemetry.mjs"
886
894
  ]
887
895
  },
@@ -990,6 +998,16 @@
990
998
  "skill-invocation-telemetry.mjs"
991
999
  ]
992
1000
  },
1001
+ {
1002
+ "file": "scripts/lib/is-main-module.mjs",
1003
+ "reachable_from": [
1004
+ "enforce-scope.mjs",
1005
+ "on-session-end.mjs",
1006
+ "on-session-start.mjs",
1007
+ "pre-task-scope-disjoint.mjs",
1008
+ "skill-invocation-telemetry.mjs"
1009
+ ]
1010
+ },
993
1011
  {
994
1012
  "file": "scripts/lib/issue-budget.mjs",
995
1013
  "reachable_from": [
@@ -1024,6 +1042,7 @@
1024
1042
  "pre-auq-clarity.mjs",
1025
1043
  "pre-bash-issue-budget.mjs",
1026
1044
  "pre-bash-memory-propose-audit.mjs",
1045
+ "pre-task-scope-disjoint.mjs",
1027
1046
  "skill-invocation-telemetry.mjs"
1028
1047
  ]
1029
1048
  },
@@ -1047,6 +1066,7 @@
1047
1066
  "pre-auq-clarity.mjs",
1048
1067
  "pre-bash-issue-budget.mjs",
1049
1068
  "pre-bash-memory-propose-audit.mjs",
1069
+ "pre-task-scope-disjoint.mjs",
1050
1070
  "skill-invocation-telemetry.mjs"
1051
1071
  ]
1052
1072
  },
@@ -1070,6 +1090,7 @@
1070
1090
  "pre-auq-clarity.mjs",
1071
1091
  "pre-bash-issue-budget.mjs",
1072
1092
  "pre-bash-memory-propose-audit.mjs",
1093
+ "pre-task-scope-disjoint.mjs",
1073
1094
  "skill-invocation-telemetry.mjs"
1074
1095
  ]
1075
1096
  },
@@ -1196,6 +1217,12 @@
1196
1217
  "on-session-start.mjs"
1197
1218
  ]
1198
1219
  },
1220
+ {
1221
+ "file": "scripts/lib/scope-echo.mjs",
1222
+ "reachable_from": [
1223
+ "pre-task-scope-disjoint.mjs"
1224
+ ]
1225
+ },
1199
1226
  {
1200
1227
  "file": "scripts/lib/scope-gate.mjs",
1201
1228
  "reachable_from": [
@@ -1244,7 +1271,8 @@
1244
1271
  "post-tool-batch-wave-signal.mjs",
1245
1272
  "post-tooluse-frontend-slop.mjs",
1246
1273
  "pre-auq-clarity.mjs",
1247
- "pre-bash-memory-propose-audit.mjs"
1274
+ "pre-bash-memory-propose-audit.mjs",
1275
+ "pre-task-scope-disjoint.mjs"
1248
1276
  ]
1249
1277
  },
1250
1278
  {
@@ -1267,6 +1295,7 @@
1267
1295
  "pre-auq-clarity.mjs",
1268
1296
  "pre-bash-issue-budget.mjs",
1269
1297
  "pre-bash-memory-propose-audit.mjs",
1298
+ "pre-task-scope-disjoint.mjs",
1270
1299
  "skill-invocation-telemetry.mjs"
1271
1300
  ]
1272
1301
  },
@@ -1290,6 +1319,7 @@
1290
1319
  "pre-auq-clarity.mjs",
1291
1320
  "pre-bash-issue-budget.mjs",
1292
1321
  "pre-bash-memory-propose-audit.mjs",
1322
+ "pre-task-scope-disjoint.mjs",
1293
1323
  "skill-invocation-telemetry.mjs"
1294
1324
  ]
1295
1325
  },
@@ -1431,6 +1461,7 @@
1431
1461
  "pre-auq-clarity.mjs",
1432
1462
  "pre-bash-issue-budget.mjs",
1433
1463
  "pre-bash-memory-propose-audit.mjs",
1464
+ "pre-task-scope-disjoint.mjs",
1434
1465
  "skill-invocation-telemetry.mjs"
1435
1466
  ]
1436
1467
  },
@@ -1462,6 +1493,7 @@
1462
1493
  "pre-auq-clarity.mjs",
1463
1494
  "pre-bash-issue-budget.mjs",
1464
1495
  "pre-bash-memory-propose-audit.mjs",
1496
+ "pre-task-scope-disjoint.mjs",
1465
1497
  "skill-invocation-telemetry.mjs"
1466
1498
  ]
1467
1499
  },
@@ -1527,6 +1559,14 @@
1527
1559
  "skill-invocation-telemetry.mjs"
1528
1560
  ]
1529
1561
  },
1562
+ {
1563
+ "file": "scripts/lib/user-invocable-skills.mjs",
1564
+ "reachable_from": [
1565
+ "on-session-end.mjs",
1566
+ "on-session-start.mjs",
1567
+ "skill-invocation-telemetry.mjs"
1568
+ ]
1569
+ },
1530
1570
  {
1531
1571
  "file": "scripts/lib/wave-context.mjs",
1532
1572
  "reachable_from": [
@@ -21,6 +21,21 @@
21
21
  * override completely unvalidated. That containment check is a NEW rule this
22
22
  * consolidation introduces, not a preserved behaviour.
23
23
  *
24
+ * MEASURED NON-DEFECT (2026-09-16, #1289 Befund 2). The ~90 %
25
+ * `transcript_found:false` rate on `orchestrator.agent.stopped` is NOT a defect
26
+ * of this derivation. Over 15.457 records: 0 of 5077 distinct false `agent_id`s
27
+ * have a sidecar anywhere on the host (8626 sidecar ids found via
28
+ * `find ~/.claude/projects -path '*subagents/agent-*.jsonl'`), 0 have a
29
+ * `SubagentStart` record, and 0 records carrying a valid id got a null result
30
+ * from this function — the false mass is the #939/#949 phantom-stop class (the
31
+ * harness fires SubagentStop for an ephemeral class that never existed; the
32
+ * evidence is in the header of hooks/subagent-telemetry.mjs). Driving this
33
+ * resolver with the 30 newest real false-case payloads resolves 0/30 because the
34
+ * file does not exist, while the 30 newest true-case payloads resolve 26/30 (the
35
+ * other 4 are aged-out transcripts of 2026-09-13 sessions). Widening the
36
+ * derivation — a directory scan, a nested `subagents/` fallback, an unbounded id
37
+ * — would resolve onto a FOREIGN agent's sidecar without recovering one record.
38
+ *
24
39
  * @module hooks/_lib/subagent-paths
25
40
  */
26
41
 
@@ -96,28 +96,25 @@
96
96
  * triage of a per-session counter file
97
97
  * (`.orchestrator/runtime/issue-budget/<hash>.json`, #1141) or in a
98
98
  * transcript census of real create calls.
99
- * - An `xargs`-driven create is NOT matched, and `xargs` is deliberately not
100
- * a transparent wrapper: `command-blocker.mjs` classes it as an INTERPRETER
101
- * (`SHELL_EXEC_INTERPRETERS`), because unwrapping it there would LOOSEN the
102
- * destructive-command guard that shares this lexer. Measured 2026-09-09
103
- * against this file all four shapes yield 0 statements, so neither the
104
- * cap nor the loop-deny sees them:
105
- *
106
- * xargs glab issue create --title X → 0
107
- * echo X | xargs -I% glab issue create --title % → 0
108
- * seq 1 50 | xargs -I% gh api -X POST …/issues -f title=% 0
109
- * xargs -n1 glab issue create 0
110
- *
111
- * Widening this one shape means changing what `resolveSegmentVerb` reports
112
- * for `xargs` — read by the four OTHER consumers of that resolver
113
- * (`grep -rln resolveSegmentVerb scripts/ hooks/`, 2026-09-09:
114
- * `scripts/lib/project-hygiene.mjs`, `scripts/lib/scope-gate.mjs`,
115
- * `hooks/pre-bash-sessions-ledger-guard.mjs`,
116
- * `hooks/pre-bash-destructive-guard.mjs`) so it is a deliberate
117
- * cross-consumer change, never a local patch here.
118
- * Pinned by `tests/hooks/vcs-create-matcher.test.mjs`. Revisit when an
119
- * `xargs`-driven create shows up in a transcript census or in the overflow
120
- * triage of a per-session counter file.
99
+ * - An `xargs`-driven create IS matched since #1289 but LOCALLY, in
100
+ * {@link xargsDrivenRemainder}, never by changing what `resolveSegmentVerb`
101
+ * reports for `xargs`. That resolver still answers `verb: 'xargs'`, because
102
+ * `command-blocker.mjs` classes `xargs` as an INTERPRETER
103
+ * (`SHELL_EXEC_INTERPRETERS`) and unwrapping it there would LOOSEN the
104
+ * destructive-command guard the five positional consumers of that resolver
105
+ * share. Measured 2026-09-16 against this file — the four shapes that
106
+ * yielded 0 statements before now yield 1 each, flagged `bulk: true`:
107
+ *
108
+ * xargs glab issue create --title X 1
109
+ * echo X | xargs -I% glab issue create --title % 1
110
+ * seq 1 50 | xargs -I% gh api -X POST …/issues -f title=% → 1
111
+ * xargs -n1 glab issue create → 1
112
+ *
113
+ * A `bulk` statement is never CHARGED: the multiplicity comes from the
114
+ * stdin word list and is unknowable before the shell runs, so
115
+ * `hooks/pre-bash-issue-budget.mjs` treats it exactly like a loop body —
116
+ * deny in `strict`, report the undercount in `warn`. The remaining ceiling
117
+ * is the option grammar, named at {@link xargsDrivenRemainder}.
121
118
  * - A paren glued to the verb (`(glab issue create …)`) is not reached: it
122
119
  * lexes as the single word `(glab`. This is `command-blocker.mjs`'s own
123
120
  * named ceiling on `COMMAND_POSITION_KEYWORDS` (#1145), inherited here
@@ -164,7 +161,14 @@ export const CREATE_REGEX = /^\s*(gh|glab)\s+(pr|mr|issue)\s+(create|new)\b/;
164
161
  function statementsOf(command) {
165
162
  if (typeof command !== 'string' || command.length === 0) return [];
166
163
  try {
167
- return splitChainSegments(tokenizeCommand(command));
164
+ const segments = splitChainSegments(tokenizeCommand(command));
165
+ const out = [];
166
+ for (const segment of segments) {
167
+ out.push(segment);
168
+ const rest = xargsDrivenRemainder(segment);
169
+ if (rest !== null) out.push(rest);
170
+ }
171
+ return out;
168
172
  } catch {
169
173
  // Fail OPEN, matching every other error path in the two consuming hooks:
170
174
  // a lexer that throws must not wedge every Bash call. Worst case is a
@@ -173,6 +177,64 @@ function statementsOf(command) {
173
177
  }
174
178
  }
175
179
 
180
+ /**
181
+ * xargs' own options, split by whether they consume a SEPARATE next token.
182
+ * Both the separated (`-n 1`) and the attached (`-n1`, `-I%`) spellings are
183
+ * handled — the attached form is what agents actually write.
184
+ */
185
+ const XARGS_VALUE_FLAGS = new Set(['-I', '-i', '-n', '-P', '-L', '-s', '-E', '-d', '-a']);
186
+
187
+ /**
188
+ * The UTILITY xargs drives, as an additional statement — or `null` when the
189
+ * segment is not an `xargs` call, or nothing follows its options (#1289).
190
+ *
191
+ * ## Why here and not in the shared lexer
192
+ *
193
+ * `command-blocker.mjs` classes `xargs` as an INTERPRETER, deliberately NOT a
194
+ * transparent wrapper: unwrapping it there would resolve past it for the FIVE
195
+ * positional consumers of `resolveSegmentVerb` and LOOSEN the destructive-command
196
+ * guard. So the widening stays local to this matcher, where the only question is
197
+ * "does this command file issues?" — the shared verb resolution is untouched,
198
+ * and `resolveSegmentVerb` still reports `verb: 'xargs'`.
199
+ *
200
+ * The returned array carries a non-index `xargsDriven` marker so
201
+ * {@link findIssueCreateStatements} can flag the statement `bulk: true`: the
202
+ * multiplicity is set by the STDIN word list, which does not exist at hook time,
203
+ * so such a create can never be charged — only denied or reported.
204
+ *
205
+ * NAMED CEILING (BV-004): only the option spellings in `XARGS_VALUE_FLAGS` plus
206
+ * bare boolean/unknown short flags are skipped. A SEPARATED long form
207
+ * (`--max-args 5`) leaves its operand in verb position, so the remainder does not
208
+ * match a create shape and the call is missed — the pre-#1289 behaviour, i.e. an
209
+ * under-report, never a false deny. REVISIT TRIGGER: a separated-long-form xargs
210
+ * create in a transcript census.
211
+ *
212
+ * @param {Array<{ text: string, quoted: boolean }>} segment
213
+ * @returns {Array<{ text: string, quoted: boolean }>|null}
214
+ */
215
+ function xargsDrivenRemainder(segment) {
216
+ if (segment.length === 0) return null;
217
+ const head = segment[0];
218
+ if (head.quoted || head.text.replace(/^.*\//, '') !== 'xargs') return null;
219
+
220
+ let i = 1;
221
+ while (i < segment.length) {
222
+ const tok = segment[i];
223
+ if (tok.quoted) break; // a quoted word is the utility, never an option
224
+ const text = tok.text;
225
+ if (text === '--') { i++; break; }
226
+ if (!text.startsWith('-') || text === '-') break;
227
+ if (XARGS_VALUE_FLAGS.has(text)) { i += 2; continue; } // separated: `-n 1`
228
+ if (XARGS_VALUE_FLAGS.has(text.slice(0, 2))) { i += 1; continue; } // attached: `-I%`
229
+ i += 1; // boolean (`-0`, `-r`, `-t`) or an unknown flag
230
+ }
231
+
232
+ const rest = segment.slice(i);
233
+ if (rest.length === 0) return null;
234
+ rest.xargsDriven = true;
235
+ return rest;
236
+ }
237
+
176
238
  /**
177
239
  * `--help` short-circuits a cobra command (both `gh` and `glab` are cobra
178
240
  * CLIs): the help text is printed and the command body never runs, so
@@ -609,13 +671,19 @@ export function findIssueCreateStatements(command) {
609
671
  continue;
610
672
  }
611
673
  literalCatPaths ??= singleQuotedCatPaths(command);
612
- out.push({
674
+ const statement = {
613
675
  shape,
614
676
  tokens,
615
677
  text: tokens.map((t) => t.text).join(' '),
616
678
  ...fieldsFromTokens(tokens, shape, literalCatPaths),
617
679
  cwdChanged,
618
- });
680
+ };
681
+ // `bulk` — the create files an UNKNOWN number of issues (#1289). Today the
682
+ // only source is an `xargs`-driven statement, whose multiplicity is the
683
+ // stdin word list; the key is present ONLY when true, so every existing
684
+ // consumer of this shape is unaffected.
685
+ if (tokens.xargsDriven === true) statement.bulk = true;
686
+ out.push(statement);
619
687
  }
620
688
  return out;
621
689
  }
@@ -689,6 +757,124 @@ export function isIssueCreate(command) {
689
757
  return matchVcsCreate(command)?.kind === 'issue';
690
758
  }
691
759
 
760
+ /**
761
+ * EVERY issue-create STATEMENT that sits inside a LOOP BODY — one token array
762
+ * per implicated loop, `[]` when none (#1145, #1379).
763
+ *
764
+ * Returning the statements rather than a boolean is what lets the consuming hook
765
+ * bind the CAP EXEMPTION to the statement that CAUSED the bulk classification.
766
+ * Measured 2026-09-16 against `hooks/pre-bash-issue-budget.mjs` before this
767
+ * change, which classified the exemption on `statements[0]`:
768
+ *
769
+ * for i in 1 2 3; do glab issue create --title junk$i; done
770
+ * → DENY
771
+ * glab issue create --title "[Carryover] real"; for i in 1 2 3; do \
772
+ * glab issue create --title junk$i; done
773
+ * → ALLOW
774
+ *
775
+ * The second command files an unknowable number of UNTEMPLATED issues, lifted
776
+ * by an unrelated neighbour's exemption. That is `.claude/rules/guard-design.md`
777
+ * § "Widening a matcher without narrowing its bypass" (#1106 class) in the
778
+ * exemption lane instead of the bypass lane — same shape, same fix: judge the
779
+ * exemption on the statement the gate fired on, never on a sibling.
780
+ *
781
+ * Each element is the segment the loop-depth scan implicated, so the caller can
782
+ * rebuild its classification text exactly as {@link findIssueCreateStatements}
783
+ * does (`tokens.map((t) => t.text).join(' ')`).
784
+ *
785
+ * ## Why ALL of them, not the first (#1379)
786
+ *
787
+ * The 2026-09-16 scan below collected every candidate head but `return`ed on
788
+ * the first one found at `depth > 0`. Since the consuming hook classifies the
789
+ * cap exemption PER bulk statement, reporting one loop for a two-loop command
790
+ * means an EXEMPT first loop is the only loop judged and every later loop goes
791
+ * unexamined. Reproduced 2026-09-17 @ `9e8146b4` against the live hook in a
792
+ * strict-mode throwaway repo:
793
+ *
794
+ * for i in 1 2 3; do glab issue create --label carryover --title x$i; done;
795
+ * for j in 1 2 3; do glab issue create --title junk$j; done
796
+ * → ALLOW, ledger count=1 exempt=1 (must DENY)
797
+ *
798
+ * Same class as the exemption-binding fix this docblock already describes, one
799
+ * level up: judging the exemption on ONE of several bulk statements is the same
800
+ * defect as judging it on a sibling.
801
+ *
802
+ * @param {string} command
803
+ * @returns {Array<Array<{ text: string, quoted: boolean }>>} one entry per loop
804
+ */
805
+ export function findLoopedIssueCreates(command) {
806
+ if (typeof command !== 'string' || command.length === 0) return [];
807
+
808
+ let tokens;
809
+ let segments;
810
+ try {
811
+ tokens = tokenizeCommand(command);
812
+ segments = splitChainSegments(tokens);
813
+ } catch {
814
+ return []; // fail OPEN, same posture as statementsOf
815
+ }
816
+
817
+ // EVERY issue-create statement is a candidate, keyed by its HEAD token's
818
+ // object identity — the position in the raw stream at which the loop depth
819
+ // must be read.
820
+ //
821
+ // Until 2026-09-16 this considered only the FIRST create statement of ANY
822
+ // kind and then checked its `kind`, mirroring the pre-#1163 hook whose G3 was
823
+ // a single `isIssueCreate(command)` boolean. That mirror is obsolete and was
824
+ // a MISS: since #1163 the hook judges EVERY statement via
825
+ // {@link findIssueCreateStatements}, and the old first-only scan reported
826
+ // `false` for any command whose first create sits outside the loop. Measured
827
+ // 2026-09-16 against HEAD:
828
+ //
829
+ // glab issue create --title "[Carryover] real"; for i in 1 2 3; do \
830
+ // glab issue create --title junk$i; done → false (should be true)
831
+ // glab mr create --title m; for i in 1 2; do \
832
+ // glab issue create --title j; done → false (should be true)
833
+ // for i in 1 2 3; do glab issue create --title j; done → true
834
+ //
835
+ // The first shape files an unknowable number of issues and was ALLOWED by the
836
+ // budget gate. Scanning all candidates is the fail-CLOSED direction and no
837
+ // longer disagrees with the hook about which statements exist.
838
+ const createHeads = new Map();
839
+ for (const seg of segments) {
840
+ const shape = matchStatement(seg);
841
+ if (shape && shape.kind === 'issue') createHeads.set(seg[0], seg);
842
+ }
843
+ if (createHeads.size === 0) return [];
844
+
845
+ const kept = new Set(segments.flat());
846
+ const found = [];
847
+ let depth = 0;
848
+ for (const tok of tokens) {
849
+ // A create head at depth 0 is a create AFTER (or BEFORE) a loop, not inside
850
+ // one: `while x; do echo a; done; glab issue create` stays unimplicated.
851
+ if (createHeads.has(tok)) {
852
+ if (depth > 0) found.push(createHeads.get(tok));
853
+ continue;
854
+ }
855
+ if (tok.quoted || kept.has(tok)) continue; // an argument, or a separator
856
+ if (tok.text === 'do') depth += 1;
857
+ else if (tok.text === 'done' && depth > 0) depth -= 1;
858
+ }
859
+ return found;
860
+ }
861
+
862
+ /**
863
+ * The FIRST looped issue-create statement, or `null`.
864
+ *
865
+ * @deprecated since #1379 — use {@link findLoopedIssueCreates}, which reports
866
+ * EVERY implicated loop. A caller that classifies anything per loop (a cap
867
+ * exemption, a per-loop deny reason) is WRONG on this API for a command with
868
+ * two loops; kept only because this module has no `exports` map, so every
869
+ * export is a public entrypoint a consumer may already import.
870
+ *
871
+ * @param {string} command
872
+ * @returns {Array<{ text: string, quoted: boolean }>|null}
873
+ */
874
+ export function findLoopedIssueCreate(command) {
875
+ return findLoopedIssueCreates(command)[0] ?? null;
876
+ }
877
+
692
878
  /**
693
879
  * True when the matched issue-create statement sits inside a LOOP BODY, i.e.
694
880
  * the command creates an unknown number of issues (#1145).
@@ -730,47 +916,16 @@ export function isIssueCreate(command) {
730
916
  * to charging 1 for N. The unit test `tests/hooks/vcs-create-matcher.test.mjs`
731
917
  * pins the coupling directly. Revisit if that set is ever narrowed.
732
918
  *
919
+ * Boolean wrapper over {@link findLoopedIssueCreates} — kept as the named
920
+ * question for callers that only need the FACT (and for the sibling suites that
921
+ * pin it). A caller that must classify the cap exemption asks for the STATEMENT
922
+ * instead; see that function's docblock for why the distinction is load-bearing.
923
+ *
733
924
  * @param {string} command
734
925
  * @returns {boolean} false when the command has no issue-create statement at all
735
926
  */
736
927
  export function isLoopedIssueCreate(command) {
737
- if (typeof command !== 'string' || command.length === 0) return false;
738
-
739
- let tokens;
740
- let segments;
741
- try {
742
- tokens = tokenizeCommand(command);
743
- segments = splitChainSegments(tokens);
744
- } catch {
745
- return false; // fail OPEN, same posture as statementsOf
746
- }
747
-
748
- // The create statement's HEAD token, by object identity — the position in the
749
- // raw stream at which the loop depth must be read.
750
- //
751
- // Deliberately the FIRST create statement of any kind, then a kind check —
752
- // exactly what isIssueCreate() does via matchVcsCreate(). Searching for the
753
- // first ISSUE create instead would diverge on `glab mr create … ; glab issue
754
- // create …`: the hook's G3 would look at the mr, this at the issue, and the
755
- // two would disagree about which statement they are judging.
756
- let headTok = null;
757
- for (const seg of segments) {
758
- const shape = matchStatement(seg);
759
- if (!shape) continue;
760
- if (shape.kind === 'issue') headTok = seg[0];
761
- break;
762
- }
763
- if (!headTok) return false;
764
-
765
- const kept = new Set(segments.flat());
766
- let depth = 0;
767
- for (const tok of tokens) {
768
- if (tok === headTok) return depth > 0;
769
- if (tok.quoted || kept.has(tok)) continue; // an argument, or a separator
770
- if (tok.text === 'do') depth += 1;
771
- else if (tok.text === 'done' && depth > 0) depth -= 1;
772
- }
773
- return false;
928
+ return findLoopedIssueCreates(command).length > 0;
774
929
  }
775
930
 
776
931
  /**
@@ -529,7 +529,48 @@ async function main() {
529
529
  // Coordinator carveout (#245): exact-path allowlist for harness-owned files.
530
530
  // STATE.md and wave-scope.json are written by the coordinator between waves;
531
531
  // per-wave allowedPaths lists should not need to enumerate harness infrastructure.
532
- if (isCoordinatorCarveout(normalizedRel, projectRoot, scopePath)) {
532
+ //
533
+ // CALLER DISCRIMINATION (#1361) — the carve-out is the COORDINATOR's, exactly
534
+ // as Gate 5c's is (#1352), and for a sharper reason: the two files it covers
535
+ // are the harness's own control surfaces. STATE.md is coordinator-owned by
536
+ // contract (`skills/_shared/state-ownership.md`), and `wave-scope.json` IS the
537
+ // manifest every later gate of this wave reads — a subagent permitted to write
538
+ // it can rewrite its own file scope and disarm the guard for the rest of the
539
+ // wave. A `'subagent'` therefore falls through to Gate 7, where a manifest that
540
+ // does not grant the path is a DENY.
541
+ //
542
+ // ALLOW-CENSUS (RCR-007, the precondition for forbidding what is allowed
543
+ // today): 1069 transcripts across 46 sessions of this repo carried 4385
544
+ // subagent writes; 0 targeted a carve-out path (2026-09-13 @ `5501f700`). The
545
+ // fix takes nothing away from any observed legitimate writer.
546
+ //
547
+ // FAIL-OPEN on ambiguity, same as Gate 5c and for the same reason: the other
548
+ // three classifications keep the ALLOW, because fail-closed would deny the
549
+ // coordinator's own `/close` STATE.md writes. The classification is emitted so
550
+ // that fail-open stays COUNTABLE (HR-105) — before #1361 this branch called
551
+ // `emitAllow()` (`process.exit(0)`, no log, no event), so whether it ever fired
552
+ // was unfalsifiable after the fact.
553
+ const carveoutCaller = classifyCaller(input);
554
+ if (carveoutCaller !== 'subagent' && isCoordinatorCarveout(normalizedRel, projectRoot, scopePath)) {
555
+ // One event per decision point, awaited before emitAllow() — emitAllow()
556
+ // calls process.exit(), which would discard a pending append.
557
+ try {
558
+ const { emitEvent } = await import('../scripts/lib/events.mjs');
559
+ await emitEvent(
560
+ 'orchestrator.scope.coordinator_carveout_allowed',
561
+ {
562
+ hook: HOOK_NAME,
563
+ manifest: scopePath,
564
+ wave: scope.wave,
565
+ // Repo-RELATIVE by choice: the carve-out set is in-repo by
566
+ // construction, so the relative form carries the whole decision
567
+ // without putting a host-local absolute path into the ledger.
568
+ file_path: normalizedRel,
569
+ discriminator: carveoutCaller,
570
+ },
571
+ { repoRoot: projectRoot },
572
+ );
573
+ } catch { /* observability is best-effort — never blocks the decision */ }
533
574
  return emitAllow();
534
575
  }
535
576
 
@@ -7,7 +7,7 @@
7
7
  "hooks": [
8
8
  {
9
9
  "type": "command",
10
- "command": "echo '🎯 Session Orchestrator v5.0.0 — /session [housekeeping|feature|deep] | /plan [new|feature|retro] | /discovery [scope] | /evolve [analyze|review|list]'",
10
+ "command": "echo '🎯 Session Orchestrator v5.2.0 — /session [housekeeping|feature|deep] | /plan [new|feature|retro] | /discovery [scope] | /evolve [analyze|review|list]'",
11
11
  "async": false
12
12
  },
13
13
  {
package/hooks/hooks.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "echo '🎯 Session Orchestrator v5.0.0 — /session [housekeeping|feature|deep] | /plan [new|feature|retro] | /discovery [scope] | /evolve [analyze|review|list]'",
9
+ "command": "echo '🎯 Session Orchestrator v5.2.0 — /session [housekeeping|feature|deep] | /plan [new|feature|retro] | /discovery [scope] | /evolve [analyze|review|list]'",
10
10
  "async": false
11
11
  },
12
12
  {
@@ -618,8 +618,20 @@ async function main() {
618
618
  // Dead-by-age relaxation (relaxDeadByAge/assumeDeadBeforeMs, #731 — used by the
619
619
  // historical migration CLI) is NEVER passed here: a foreign lock that is live
620
620
  // at hook-time is, by definition, a real active session, not stale history.
621
+ // `ownSessionIsEnding` (#1376) rides the SAME `isRecordedSession`
622
+ // attestation as `duration_ms` / `semantic_session_id` above, for the
623
+ // same reason: it is a claim made ON BEHALF of the recorded session, and
624
+ // a foreign terminating window that inherited this repo's
625
+ // `current-session.json` identity (#863 defect (b)) must never make it.
626
+ // With the attestation false the core keeps its blanket own-live-lock
627
+ // skip, so the #863 behaviour is preserved exactly where #863 applied.
621
628
  try {
622
- const res = await backfillAbandonedSession({ repoRoot: projectRoot, sessionId, semanticSessionId });
629
+ const res = await backfillAbandonedSession({
630
+ repoRoot: projectRoot,
631
+ sessionId,
632
+ semanticSessionId,
633
+ ownSessionIsEnding: isRecordedSession,
634
+ });
623
635
  await emitBackfillOutcome('abandoned', res, { sessionId, semanticSessionId });
624
636
  } catch { /* best-effort — never block teardown */ }
625
637
 
@@ -627,7 +639,7 @@ async function main() {
627
639
  // above and to THIS session's own id: it reads STATE.md directly and
628
640
  // repairs a PAST session whose `status: completed` was set (by hand or
629
641
  // otherwise) without session-end's Phase 3.7 ever writing the matching
630
- // sessions.jsonl record — the exact state `commands/close.md`'s
642
+ // sessions.jsonl record — the exact state `skills/close/SKILL.md`'s
631
643
  // Pre-Check then reads as "already finalized" forever after. Cheap
632
644
  // no-op on the overwhelmingly common path (STATE.md status is
633
645
  // 'active'/'paused'/'idle', or the record already exists).