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
@@ -462,7 +462,9 @@ export function checkStaleArtifacts(repoRoot, ageDays = DEFAULT_ARTIFACT_AGE_DAY
462
462
  * H4 — CI configuration hygiene.
463
463
  *
464
464
  * Measured 5/6. Checks locally declared executable commands without evaluating
465
- * pipeline conditions or external includes. The dependency-audit gap is the load-bearing one: three
465
+ * pipeline conditions or external includes; when no local audit step is found
466
+ * but `.gitlab-ci.yml` declares `include:`, the finding is downgraded to an
467
+ * unverifiable, non-fixable one. The dependency-audit gap is the load-bearing one: three
466
468
  * of the tested repos carried known-vulnerable dependencies that no pipeline
467
469
  * would ever surface.
468
470
  *
@@ -506,6 +508,24 @@ export function checkCiConfig(repoRoot) {
506
508
  }
507
509
 
508
510
  if (ciConfigs.some(({ text }) => text) && !ciConfigs.some(hasAuditStep)) {
511
+ // Jobs supplied via include: (component/local/project/remote/template) are
512
+ // never resolved, so an audit may exist there — report it as unverifiable.
513
+ let includes = 0;
514
+ try {
515
+ const gitlabText = hasGitlab ? ciConfigs[0].text : '';
516
+ const include = yaml.loadAll(gitlabText, undefined, { schema: GITLAB_SCHEMA }).filter(isConfigMap).at(-1)?.include;
517
+ includes = Array.isArray(include) ? include.length : include ? 1 : 0;
518
+ } catch {
519
+ /* unparseable config — keep the plain missing-audit finding */
520
+ }
521
+ if (includes > 0) {
522
+ findings.push({
523
+ check: 'ci-audit-job',
524
+ fixable: false,
525
+ message: `CI dependency-audit step could not be verified: ${includes} include(s) not evaluated (.gitlab-ci.yml include:)`,
526
+ });
527
+ return findings;
528
+ }
509
529
  findings.push({
510
530
  check: 'ci-audit-job',
511
531
  fixable: true,
@@ -26,7 +26,9 @@
26
26
  * - Output collection: each gate captures the last ~50 lines of combined
27
27
  * stdout+stderr (vs. `gate-helpers.mjs::runCheck` which truncates to 5).
28
28
  * The longer tail flows into the diagnostics bundle and the fixer's
29
- * failureContext.
29
+ * failureContext. Since #1427 the tail is cut by `process-group.mjs`'s own
30
+ * `OUTPUT_TAIL_LINES`, which is the same 50, and the full capture is
31
+ * byte-capped there instead of by `spawnSync`'s `maxBuffer`.
30
32
  *
31
33
  * - `last-green-sha.txt` lives at `.orchestrator/runtime/last-green-sha.txt`
32
34
  * and is updated atomically after every successful gate. `changedFiles` is
@@ -61,12 +63,17 @@
61
63
 
62
64
  import { spawnSync } from 'node:child_process';
63
65
  import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from 'node:fs';
64
- import { dirname, join } from 'node:path';
66
+ import { dirname, join, resolve } from 'node:path';
65
67
  import { fileURLToPath } from 'node:url';
66
68
 
67
69
  import { emitEvent, sessionAttribution } from './events.mjs';
68
70
  import { parsePorcelainZ } from './git-porcelain.mjs';
69
71
  import { admitSuiteCounts, extractTestCounts } from './gates/gate-helpers.mjs';
72
+ import {
73
+ DEFAULT_GATE_TIMEOUT_MS,
74
+ buildCommandSignature,
75
+ spawnInGroup,
76
+ } from './process-group.mjs';
70
77
  import { redactDiagnosticsBundle } from './quality-gate/diagnostics.mjs';
71
78
  import { readProcessLocalSessionIds } from './session-identity/own-session.mjs';
72
79
 
@@ -86,17 +93,20 @@ const DEFAULT_COMMANDS = {
86
93
  test: 'npm test',
87
94
  };
88
95
 
89
- /** Max lines of combined stdout+stderr retained per failure. */
90
- const OUTPUT_TAIL_LINES = 50;
91
-
92
96
  /** Max corrective_context entries forwarded to the fixer (most-recent). */
93
97
  const CORRECTIVE_CONTEXT_TAIL = 5;
94
98
 
95
99
  /** Hard ceiling on retries — defensive coercion. */
96
100
  const MAX_RETRIES_HARD_CAP = 10;
97
101
 
98
- /** Per-gate command timeout (15 min). Wave gates can be long; never infinite. */
99
- const GATE_TIMEOUT_MS = 15 * 60 * 1000;
102
+ /**
103
+ * Per-gate command timeout (15 min). Wave gates can be long; never infinite.
104
+ *
105
+ * Re-exported from `process-group.mjs` rather than re-spelled here: both gate
106
+ * paths are allowed exactly as long (PRD parameter `gate.timeout-path-b-ms`),
107
+ * and a second literal would drift from it silently.
108
+ */
109
+ const GATE_TIMEOUT_MS = DEFAULT_GATE_TIMEOUT_MS;
100
110
 
101
111
  // ---------------------------------------------------------------------------
102
112
  // Internal helpers
@@ -110,7 +120,8 @@ const GATE_TIMEOUT_MS = 15 * 60 * 1000;
110
120
  * @returns {string}
111
121
  */
112
122
  function resolveRepoRoot(explicit) {
113
- if (typeof explicit === 'string' && explicit.trim()) return explicit;
123
+ // Absolute on purpose: emitEvent refuses a relative root (#1468).
124
+ if (typeof explicit === 'string' && explicit.trim()) return resolve(explicit);
114
125
  return process.cwd();
115
126
  }
116
127
 
@@ -274,29 +285,48 @@ function resolveCommands(override, repoRoot) {
274
285
  }
275
286
 
276
287
  /**
277
- * Run a shell command, capture stdout+stderr, return last ~50 lines plus exit code.
278
- *
279
- * Does NOT throw — failures are encoded in the return value. Honours
280
- * GATE_TIMEOUT_MS as a hard ceiling.
288
+ * Run a gate command as the leader of its OWN process group, capture
289
+ * stdout+stderr, return the last ~50 lines plus the exit code.
290
+ *
291
+ * ASYNC since #1427 A1/A2, and that is the point rather than an incidental
292
+ * refactor. The previous `spawnSync(cmd, { shell: true, timeout })` made the
293
+ * SHELL the child, so Node's timeout signalled the shell alone and every
294
+ * grandchild — `tsgo --noEmit`, Vitest workers — was reparented to PID 1 and
295
+ * kept running. That is the 2026-09-20 incident: four orphaned `tsgo`
296
+ * processes, two at PPID 1, up to 8.0 GB RSS each, host at 13% free memory.
297
+ * {@link spawnInGroup} spawns `detached: true` (the child becomes a process-GROUP
298
+ * leader) and signals `-pgid` along the SIGTERM → grace → SIGKILL → verify
299
+ * ladder, so the whole group is gone when this resolves — or reported as a
300
+ * survivor, never booked as success (PRD B6).
301
+ *
302
+ * Does NOT throw — failures are encoded in the return value. The three fields
303
+ * the pre-#1427 contract carried (`exitCode`, `output`, `timedOut`) are
304
+ * unchanged; `overflow`, `killSignals`, `survivors` and `pgid` are additive.
305
+ *
306
+ * Exit-code contract, preserved verbatim from the synchronous path:
307
+ * - timeout → `124`
308
+ * - output over the byte cap → `1` (what `spawnSync`'s ENOBUFS produced)
309
+ * - otherwise the child's own code.
281
310
  *
282
311
  * @param {string} cmd
283
312
  * @param {string} cwd
284
- * @returns {{ exitCode: number, output: string, timedOut: boolean }}
313
+ * @param {object} [opts]
314
+ * @param {number} [opts.timeoutMs] — wall-clock ceiling. Defaults to GATE_TIMEOUT_MS.
315
+ * @param {string|null} [opts.sessionId] — owner recorded in the gate-process ledger.
316
+ * @param {object} [opts.seams] — forwarded verbatim to {@link spawnInGroup}
317
+ * (`spawnFn`, `killFn`, `isAliveFn`, `sleepFn`, `killGraceMs`, `verifyWaitMs`).
318
+ * Test seam only; production passes nothing and gets the module defaults.
319
+ * @returns {Promise<{ exitCode: number, output: string, timedOut: boolean,
320
+ * overflow: boolean, killSignals: string[], survivors: number[], pgid: number }>}
285
321
  */
286
- function runGate(cmd, cwd) {
322
+ async function runGate(cmd, cwd, opts = {}) {
287
323
  try {
288
324
  // Gate commands are executable configuration from the caller or local
289
325
  // Session Config, not data interpolated into a command template. Shell
290
326
  // syntax is intentional; callers must trust both command sources (including
291
327
  // uncommitted config). See security.md: Session Config Command Trust.
292
- // nosemgrep: unsafe-shell-spawn
293
- const result = spawnSync(cmd, {
328
+ const result = await spawnInGroup(cmd, {
294
329
  cwd,
295
- shell: true,
296
- encoding: 'utf8',
297
- stdio: ['ignore', 'pipe', 'pipe'],
298
- timeout: GATE_TIMEOUT_MS,
299
- maxBuffer: 16 * 1024 * 1024, // 16 MiB cap
300
330
  // #1360: gate runs are where worker over-subscription was measured to
301
331
  // cause timeout failures (integration fixtures spawn their own Node/npm/
302
332
  // git children on top of Vitest's workers). `vitest.config.mjs` reads this
@@ -305,19 +335,40 @@ function runGate(cmd, cwd) {
305
335
  // Inherited by every gate subprocess, not only the test one — typecheck
306
336
  // and lint ignore it, so setting it unconditionally costs nothing.
307
337
  env: { ...process.env, SO_BOUNDED_WORKERS: '1' },
338
+ timeoutMs: typeof opts.timeoutMs === 'number' ? opts.timeoutMs : GATE_TIMEOUT_MS,
339
+ // The ledger (A4) is what lets the orphan-reaper tell a descendant of OUR
340
+ // gate from a foreign process before it signals anything.
341
+ repoRoot: cwd,
342
+ commandSignature: buildCommandSignature(cmd),
343
+ sessionId: opts.sessionId ?? null,
344
+ ...(opts.seams ?? {}),
308
345
  });
309
- const combined = (result.stdout ?? '') + (result.stderr ?? '');
310
- const tail = combined.split('\n').slice(-OUTPUT_TAIL_LINES).join('\n').trim();
311
- const timedOut = result.signal === 'SIGTERM' && result.error?.code === 'ETIMEDOUT';
312
- const exitCode = typeof result.status === 'number'
313
- ? result.status
314
- : (timedOut ? 124 : 1);
315
- return { exitCode, output: tail, timedOut };
346
+
347
+ // A process that outlived SIGKILL (one that `setsid`-ed out of the group) is
348
+ // SAID OUT LOUD on the channel the operator and the diagnostics bundle both
349
+ // read. Silence here would be the exact failure the ladder exists to expose.
350
+ const output = result.survivors.length > 0
351
+ ? `${result.output}\ngate: ${result.survivors.length} process(es) survived SIGKILL: ${result.survivors.join(', ')}`.trim()
352
+ : result.output;
353
+
354
+ return {
355
+ exitCode: result.exitCode,
356
+ output,
357
+ timedOut: result.timedOut,
358
+ overflow: result.overflow,
359
+ killSignals: result.killSignals,
360
+ survivors: result.survivors,
361
+ pgid: result.pgid,
362
+ };
316
363
  } catch (err) {
317
364
  return {
318
365
  exitCode: 1,
319
366
  output: `quality-gate: failed to spawn command "${cmd}": ${err?.message ?? String(err)}`,
320
367
  timedOut: false,
368
+ overflow: false,
369
+ killSignals: [],
370
+ survivors: [],
371
+ pgid: -1,
321
372
  };
322
373
  }
323
374
  }
@@ -723,13 +774,44 @@ function coerceMaxRetries(n) {
723
774
  // Public API
724
775
  // ---------------------------------------------------------------------------
725
776
 
777
+ /**
778
+ * The kill-ladder fields of a `quality_gate.passed/.failed` record (#1439).
779
+ *
780
+ * ONE builder for BOTH emitters — this library's {@link emitGateEvent} and the
781
+ * `scripts/run-quality-gate.mjs` CLI — so the two can never publish different
782
+ * shapes under the same keys. From THIS builder all three keys are always
783
+ * present: `timed_out` `false`, `survivors` `0` and `kill_signals` `[]` are
784
+ * measurements ("not killed"), unlike `counts`, whose absence means "not
785
+ * measured". The CLI then passes the result through `joinGateKillFields()`
786
+ * (`scripts/lib/gates/gate-helpers.mjs`, #1457), which OR-joins the inner
787
+ * per-command ladders and MAY omit the three keys when that inner side was
788
+ * not measured and the outer ladder did not fire.
789
+ *
790
+ * - `timed_out` — the run hit its wall-clock ceiling and was killed (exit 124).
791
+ * - `survivors` — how many pids were still alive after the SIGKILL step; the
792
+ * pids themselves stay in the gate output / diagnostics bundle.
793
+ * - `kill_signals` — the signals actually sent, in order (`SIGTERM`, `SIGKILL`).
794
+ * Copied, because `spawnInGroup` records them at send time into a live array.
795
+ *
796
+ * @param {{timedOut?: boolean, killSignals?: string[], survivors?: number[]}|null|undefined} run
797
+ * @returns {{timed_out: boolean, survivors: number, kill_signals: string[]}}
798
+ */
799
+ export function gateKillFields(run) {
800
+ return {
801
+ timed_out: run?.timedOut === true,
802
+ survivors: Array.isArray(run?.survivors) ? run.survivors.length : 0,
803
+ kill_signals: Array.isArray(run?.killSignals) ? [...run.killSignals] : [],
804
+ };
805
+ }
806
+
726
807
  /**
727
808
  * Emit exactly one `orchestrator.quality_gate.{passed,failed}` event per
728
809
  * `runQualityGateWithRetry` CALL (#928b).
729
810
  *
730
811
  * NO DOUBLE-COUNTING. The two gate paths never nest:
731
812
  * - `scripts/run-quality-gate.mjs` spawns `scripts/lib/gates/gate-*.mjs`
732
- * directly and does not import this module;
813
+ * directly; it imports only the pure `gateKillFields` helper from this
814
+ * module, never `runQualityGateWithRetry`, so it never reaches this emitter;
733
815
  * - this module spawns the resolved gate COMMANDS (and `parse-config.mjs`)
734
816
  * directly and does not invoke that wrapper.
735
817
  * A single run therefore passes through exactly one emitter.
@@ -783,7 +865,7 @@ function coerceMaxRetries(n) {
783
865
  * wrote it.
784
866
  *
785
867
  * Extraction margin: {@link suiteCountsFromOutput} sees only the
786
- * `OUTPUT_TAIL_LINES` (50) tail `runCheck` retains. Measured on `npm test`
868
+ * 50-line tail `process-group.mjs` retains (`OUTPUT_TAIL_LINES` there). Measured on `npm test`
787
869
  * (vitest 2026-07-31), the `Tests` summary line sits 5 lines from the end — 45
788
870
  * lines of headroom. A runner epilogue longer than that (coverage table, long
789
871
  * unhandled-error dump) pushes the summary out of the window; `counts` is then
@@ -796,8 +878,13 @@ function coerceMaxRetries(n) {
796
878
  * @param {number} attempts
797
879
  * @param {string|null} gate
798
880
  * @param {{passed: number, failed: number, total: number}|null} [counts]
881
+ * @param {{timedOut?: boolean, killSignals?: string[], survivors?: number[]}|null} [killRun]
882
+ * the run that decided the verdict — the last attempt's failing gate; `null`
883
+ * on the passing path, where every gate exited 0 and so none was killed
884
+ * (`spawnInGroup` only starts its kill ladder on timeout or overflow, and
885
+ * both force a non-zero exit code).
799
886
  */
800
- async function emitGateEvent(repoRoot, ok, attempts, gate, counts) {
887
+ async function emitGateEvent(repoRoot, ok, attempts, gate, counts, killRun = null) {
801
888
  try {
802
889
  await emitEvent(
803
890
  `orchestrator.quality_gate.${ok ? 'passed' : 'failed'}`,
@@ -807,6 +894,7 @@ async function emitGateEvent(repoRoot, ok, attempts, gate, counts) {
807
894
  attempts,
808
895
  ...(gate ? { gate } : {}),
809
896
  ...(counts ? { counts } : {}),
897
+ ...gateKillFields(killRun),
810
898
  ...sessionAttribution(repoRoot),
811
899
  },
812
900
  { repoRoot },
@@ -833,6 +921,14 @@ async function emitGateEvent(repoRoot, ok, attempts, gate, counts) {
833
921
  * @param {string} [opts.repoRoot] — defaults to process.cwd().
834
922
  * @param {{lint?: string, typecheck?: string, test?: string}} [opts.commands]
835
923
  * — override individual gate commands.
924
+ * @param {number} [opts.gateTimeoutMs] — per-gate wall-clock ceiling. Defaults to
925
+ * GATE_TIMEOUT_MS (15 min). On expiry the gate's
926
+ * whole process GROUP gets SIGTERM → grace →
927
+ * SIGKILL and the gate reports exit 124.
928
+ * @param {object} [opts._processSeams] — TEST SEAM, forwarded verbatim to
929
+ * `spawnInGroup` (`spawnFn`, `killFn`,
930
+ * `isAliveFn`, `sleepFn`, `killGraceMs`,
931
+ * `verifyWaitMs`). Production passes nothing.
836
932
  *
837
933
  * @returns {Promise<{
838
934
  * ok: boolean,
@@ -851,6 +947,24 @@ export async function runQualityGateWithRetry(opts) {
851
947
  : async () => {};
852
948
  const repoRoot = resolveRepoRoot(safeOpts.repoRoot);
853
949
  const commands = resolveCommands(safeOpts.commands, repoRoot);
950
+ /**
951
+ * Per-gate spawn options, resolved ONCE per call.
952
+ *
953
+ * `sessionId` comes from the PROCESS-LOCAL witness only
954
+ * ({@link readOwnSessionIds} → `CLAUDE_CODE_SESSION_ID`), never from
955
+ * `.orchestrator/session.lock`: the lock is a repo-GLOBAL artefact any
956
+ * session in this working copy may hold, so reading it here would stamp a
957
+ * PEER's id onto our own gate processes and point the reaper at the wrong
958
+ * owner (`.claude/rules/identity-and-locks.md` — rank witnesses, never union
959
+ * them). No witness → `null`, which the ledger records as "owner unknown".
960
+ */
961
+ const gateOpts = {
962
+ timeoutMs: typeof safeOpts.gateTimeoutMs === 'number' ? safeOpts.gateTimeoutMs : undefined,
963
+ sessionId: [...readOwnSessionIds()][0] ?? null,
964
+ seams: (typeof safeOpts._processSeams === 'object' && safeOpts._processSeams !== null)
965
+ ? safeOpts._processSeams
966
+ : undefined,
967
+ };
854
968
 
855
969
  // Accumulate per-attempt failure info for the diagnostics bundle.
856
970
  const allFailures = [];
@@ -889,7 +1003,10 @@ export async function runQualityGateWithRetry(opts) {
889
1003
 
890
1004
  for (const gate of GATE_ORDER) {
891
1005
  const cmd = commands[gate];
892
- const result = runGate(cmd, repoRoot);
1006
+ // `await` inside `for…of` is deliberate: the gates are fail-fast and
1007
+ // SEQUENTIAL (lint → typecheck → test), exactly as the synchronous path
1008
+ // ran them. Parallelising them would change the contract, not the plumbing.
1009
+ const result = await runGate(cmd, repoRoot, gateOpts);
893
1010
  if (gate === 'test') {
894
1011
  // The numbers are in hand right here — capture them at the seam rather
895
1012
  // than letting them travel as prose (#954).
@@ -906,6 +1023,8 @@ export async function runQualityGateWithRetry(opts) {
906
1023
  exitCode: result.exitCode,
907
1024
  output: result.output,
908
1025
  timedOut: result.timedOut,
1026
+ killSignals: result.killSignals,
1027
+ survivors: result.survivors,
909
1028
  command: cmd,
910
1029
  attempt,
911
1030
  };
@@ -984,7 +1103,7 @@ export async function runQualityGateWithRetry(opts) {
984
1103
  `❌ quality-gate exhausted retries (${attempt}), writing diagnostics to ${bundlePath ?? '<unwritable>'}\n`,
985
1104
  );
986
1105
 
987
- await emitGateEvent(repoRoot, false, attempt, lastFailure?.gate ?? null, testCounts);
1106
+ await emitGateEvent(repoRoot, false, attempt, lastFailure?.gate ?? null, testCounts, lastFailure);
988
1107
 
989
1108
  const out = {
990
1109
  ok: false,