session-orchestrator 5.11.0 → 5.13.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 (394) hide show
  1. package/.claude-plugin/marketplace.json +12 -1
  2. package/.claude-plugin/plugin.json +4 -1
  3. package/.codex-plugin/agents/explorer.toml +1 -1
  4. package/.codex-plugin/agents/session-reviewer.toml +2 -2
  5. package/.codex-plugin/agents/wave-worker.toml +1 -1
  6. package/.codex-plugin/plugin.json +1 -1
  7. package/.cursor-plugin/plugin.json +1 -1
  8. package/.orchestrator/policy/blocked-commands.json +8 -0
  9. package/.orchestrator/policy/public-export.json +30 -0
  10. package/CHANGELOG.md +180 -3
  11. package/README.md +126 -117
  12. package/agents/analyst.md +7 -2
  13. package/agents/architect-reviewer.md +7 -2
  14. package/agents/code-implementer.md +4 -2
  15. package/agents/db-specialist.md +9 -2
  16. package/agents/dialectic-deriver.md +9 -3
  17. package/agents/docs-writer.md +7 -2
  18. package/agents/eval-judge.md +7 -1
  19. package/agents/qa-strategist.md +7 -2
  20. package/agents/security-reviewer.md +9 -2
  21. package/agents/session-reviewer.md +18 -2
  22. package/agents/skill-applied-judge.md +7 -1
  23. package/agents/test-writer.md +9 -2
  24. package/agents/ui-developer.md +9 -2
  25. package/agents/ux-evaluator.md +6 -1
  26. package/commands/session.md +4 -2
  27. package/commands/templates-ack.md +89 -18
  28. package/docs/agent-authoring.md +30 -11
  29. package/docs/ci-setup.md +38 -2
  30. package/docs/codex-setup.md +33 -9
  31. package/docs/components.md +13 -2
  32. package/docs/cursor-setup.md +3 -3
  33. package/docs/events-schema.md +5 -4
  34. package/docs/evidence-candidates.md +63 -0
  35. package/docs/fleet-checkin-audit.md +7 -4
  36. package/docs/github-mirror-protection.md +150 -3
  37. package/docs/install.md +102 -25
  38. package/docs/instruction-delivery.md +1 -1
  39. package/docs/migration-v4.md +2 -2
  40. package/docs/mods.md +118 -0
  41. package/docs/owner-config-schema.md +1 -0
  42. package/docs/plugin-architecture-v3.md +1 -1
  43. package/docs/pm-skills-marketplace.md +4 -3
  44. package/docs/rule-authoring.md +24 -13
  45. package/docs/scope-collision-guard.md +2 -1
  46. package/docs/session-config-reference.md +55 -21
  47. package/docs/session-config-template.md +32 -24
  48. package/docs/sessions-ledger-repair.md +137 -0
  49. package/docs/vault-docs-architecture.md +1 -1
  50. package/hooks/_lib/hook-import-set.json +406 -73
  51. package/hooks/_lib/hook-root.mjs +301 -0
  52. package/hooks/_lib/lock-bootstrap.mjs +434 -4
  53. package/hooks/_lib/orchestrator-ignore.mjs +3 -0
  54. package/hooks/_lib/subagent-transcript.mjs +2 -2
  55. package/hooks/_lib/vcs-create-matcher.mjs +13 -17
  56. package/hooks/cwd-change-restore.mjs +5 -1
  57. package/hooks/enforce-commands.mjs +4 -3
  58. package/hooks/enforce-scope.mjs +11 -1
  59. package/hooks/hooks-codex.json +48 -2
  60. package/hooks/hooks.json +31 -31
  61. package/hooks/on-session-end.mjs +38 -15
  62. package/hooks/on-session-start.mjs +158 -59
  63. package/hooks/on-stop.mjs +11 -6
  64. package/hooks/post-bash-issue-budget-refund.mjs +6 -1
  65. package/hooks/post-bash-write-verify.mjs +8 -4
  66. package/hooks/post-edit-validate.mjs +45 -52
  67. package/hooks/post-subagent-discovery-validator.mjs +2 -2
  68. package/hooks/post-tool-batch-wave-signal.mjs +8 -4
  69. package/hooks/post-tool-failure-corrective-context.mjs +5 -1
  70. package/hooks/pre-bash-destructive-guard.mjs +220 -8
  71. package/hooks/pre-bash-issue-budget.mjs +6 -1
  72. package/hooks/pre-bash-sessions-ledger-guard.mjs +691 -123
  73. package/hooks/pre-bash-staging-fence.mjs +258 -14
  74. package/hooks/pre-bash-templates-first.mjs +28 -27
  75. package/hooks/run-node.sh +1 -1
  76. package/hooks/skill-invocation-telemetry.mjs +15 -8
  77. package/hooks/subagent-telemetry.mjs +77 -14
  78. package/hooks/wave-scope-commit-guard.mjs +143 -14
  79. package/mods/so-hud/.claude-plugin/plugin.json +11 -0
  80. package/mods/so-hud/README.md +57 -0
  81. package/mods/so-hud/hooks/beacon-io.mjs +101 -0
  82. package/mods/so-hud/hooks/heredoc-scan.ts +394 -0
  83. package/mods/so-hud/hooks/hooks.json +1 -0
  84. package/mods/so-hud/hooks/logic.ts +182 -0
  85. package/mods/so-hud/hooks/register.tsx +670 -0
  86. package/mods/so-hud/hooks/rules.ts +2998 -0
  87. package/mods/so-hud/tsconfig.json +8 -0
  88. package/mods/so-hud/types/index.d.ts +36 -0
  89. package/package.json +4 -3
  90. package/rules/_index.md +1 -1
  91. package/rules/always-on/ask-via-tool.md +1 -1
  92. package/rules/always-on/loop-and-monitor.md +17 -194
  93. package/rules/always-on/parallel-sessions.md +3 -1
  94. package/rules/opt-in-stack/backend-data.md +1 -1
  95. package/rules/opt-in-stack/backend.md +1 -1
  96. package/rules/opt-in-stack/security-web.md +5 -1
  97. package/scripts/agent-usage-census.mjs +79 -0
  98. package/scripts/apply-session-learnings.mjs +34 -6
  99. package/scripts/archive-closed-prds.mjs +21 -5
  100. package/scripts/autopilot.mjs +11 -5
  101. package/scripts/backfill-abandoned-sessions.mjs +13 -3
  102. package/scripts/backfill-evidence-digest.mjs +2 -1
  103. package/scripts/backfill-learnings-from-vault.mjs +61 -6
  104. package/scripts/backfill-sessions.mjs +16 -2
  105. package/scripts/check-package-manager.mjs +26 -8
  106. package/scripts/check-sessions-integrity.mjs +4 -1
  107. package/scripts/ci/assert-vitest-green.mjs +3 -0
  108. package/scripts/ci/windows-smoke.mjs +201 -0
  109. package/scripts/close-precheck.mjs +55 -0
  110. package/scripts/dialectic-deriver.mjs +20 -12
  111. package/scripts/emit-event.mjs +2 -2
  112. package/scripts/emit-session.mjs +239 -20
  113. package/scripts/eval-session.mjs +17 -3
  114. package/scripts/export-hw-learnings.mjs +29 -2
  115. package/scripts/fleet-checkin-audit.mjs +8 -4
  116. package/scripts/fleet-checkin.mjs +73 -3
  117. package/scripts/gc-stale-worktrees.mjs +6 -1
  118. package/scripts/github-export.mjs +128 -0
  119. package/scripts/github-protection-audit.mjs +3 -1
  120. package/scripts/handover-queue-check.mjs +209 -0
  121. package/scripts/harness-audit.mjs +6 -4
  122. package/scripts/lib/agent-frontmatter.mjs +57 -10
  123. package/scripts/lib/agent-status.mjs +125 -7
  124. package/scripts/lib/agent-usage-census.mjs +394 -0
  125. package/scripts/lib/auto-dialectic.mjs +17 -10
  126. package/scripts/lib/auto-dream.mjs +22 -14
  127. package/scripts/lib/autopilot/loop.mjs +3 -1
  128. package/scripts/lib/autopilot/recent-runs.mjs +4 -2
  129. package/scripts/lib/autopilot/stall-sampler.mjs +2 -1
  130. package/scripts/lib/autopilot/telemetry.mjs +5 -1
  131. package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
  132. package/scripts/lib/baseline-archetypes.mjs +10 -2
  133. package/scripts/lib/blocked-commands-policy.mjs +4 -3
  134. package/scripts/lib/build-live-signals.mjs +13 -9
  135. package/scripts/lib/ci-status-banner.mjs +329 -24
  136. package/scripts/lib/codex/plugin-contract.mjs +8 -0
  137. package/scripts/lib/cold-start-detector.mjs +5 -2
  138. package/scripts/lib/command-blocker.mjs +603 -43
  139. package/scripts/lib/common.mjs +3 -2
  140. package/scripts/lib/config/cross-repo.mjs +74 -7
  141. package/scripts/lib/config/host-paths.mjs +6 -2
  142. package/scripts/lib/config/remote-hosts.mjs +2 -2
  143. package/scripts/lib/config/section-extractor.mjs +65 -11
  144. package/scripts/lib/config/template-keys.mjs +153 -0
  145. package/scripts/lib/config/vault-integration.mjs +1 -0
  146. package/scripts/lib/config.mjs +3 -3
  147. package/scripts/lib/coordinator-snapshot.mjs +1 -1
  148. package/scripts/lib/discovery/triage-state.mjs +2 -1
  149. package/scripts/lib/dispatcher/rank.mjs +5 -7
  150. package/scripts/lib/ecosystem-health.mjs +2 -2
  151. package/scripts/lib/eval/engine.mjs +3 -2
  152. package/scripts/lib/eval/session-resolve.mjs +6 -2
  153. package/scripts/lib/eval/sink.mjs +4 -3
  154. package/scripts/lib/events-retention-banner.mjs +3 -2
  155. package/scripts/lib/events-schema.mjs +10 -0
  156. package/scripts/lib/events.mjs +23 -12
  157. package/scripts/lib/evolve/autonomy-verdict.mjs +4 -9
  158. package/scripts/lib/evolve/autopilot-effectiveness.mjs +8 -18
  159. package/scripts/lib/fleet-protocol.mjs +21 -0
  160. package/scripts/lib/gates/gate-full.mjs +172 -11
  161. package/scripts/lib/gates/gate-helpers.mjs +236 -23
  162. package/scripts/lib/git-config-drift.mjs +141 -7
  163. package/scripts/lib/github-export.mjs +447 -0
  164. package/scripts/lib/gitlab-portfolio/cli.mjs +16 -5
  165. package/scripts/lib/handover-queue-marks.mjs +159 -0
  166. package/scripts/lib/hardware-pattern-detector.mjs +103 -13
  167. package/scripts/lib/harness-audit/categories/category1.mjs +16 -4
  168. package/scripts/lib/harness-audit/categories/category4.mjs +11 -8
  169. package/scripts/lib/harness-audit/categories/category6.mjs +25 -5
  170. package/scripts/lib/harness-audit/categories/category9.mjs +10 -4
  171. package/scripts/lib/host-identity.mjs +9 -5
  172. package/scripts/lib/http-request.mjs +64 -0
  173. package/scripts/lib/instruction-budget-guard.mjs +30 -22
  174. package/scripts/lib/io.mjs +6 -3
  175. package/scripts/lib/issue-budget-reconcile.mjs +3 -2
  176. package/scripts/lib/issue-budget.mjs +36 -7
  177. package/scripts/lib/learnings/evidence-candidates.mjs +154 -0
  178. package/scripts/lib/learnings/expiry-sweep.mjs +99 -7
  179. package/scripts/lib/learnings/io.mjs +289 -15
  180. package/scripts/lib/learnings/select.mjs +53 -4
  181. package/scripts/lib/learnings/store-role.mjs +209 -0
  182. package/scripts/lib/learnings/surface.mjs +46 -7
  183. package/scripts/lib/ledger-path.mjs +113 -0
  184. package/scripts/lib/maintenance-due-banner.mjs +63 -12
  185. package/scripts/lib/memory-banner.mjs +5 -6
  186. package/scripts/lib/memory-proposals/collector.mjs +5 -2
  187. package/scripts/lib/memory-proposals/sink.mjs +7 -4
  188. package/scripts/lib/memory-proposals/store.mjs +22 -5
  189. package/scripts/lib/mode-selector-accuracy.mjs +2 -1
  190. package/scripts/lib/no-follow-open.mjs +57 -0
  191. package/scripts/lib/orphan-reaper/reaper-audit.mjs +4 -2
  192. package/scripts/lib/own-lock-root.mjs +95 -0
  193. package/scripts/lib/owner-interview.mjs +2 -1
  194. package/scripts/lib/owner-yaml.mjs +70 -7
  195. package/scripts/lib/platform.mjs +161 -9
  196. package/scripts/lib/plugin-update-banner.mjs +15 -11
  197. package/scripts/lib/pre-push-local-partial.mjs +117 -0
  198. package/scripts/lib/pre-push-remote-gate.mjs +151 -25
  199. package/scripts/lib/process-group.mjs +270 -34
  200. package/scripts/lib/project-hygiene.mjs +28 -0
  201. package/scripts/lib/quality-gate.mjs +33 -9
  202. package/scripts/lib/reconcile/backlog.mjs +29 -15
  203. package/scripts/lib/reconcile/engine.mjs +26 -2
  204. package/scripts/lib/reconcile/idempotency.mjs +65 -3
  205. package/scripts/lib/reconcile/rule-expiry-sweep.mjs +53 -12
  206. package/scripts/lib/reconcile/verdict-ledger.mjs +224 -0
  207. package/scripts/lib/reconcile-nudge-banner.mjs +40 -6
  208. package/scripts/lib/resource-probe/evaluate.mjs +32 -9
  209. package/scripts/lib/resource-probe/probe-platform.mjs +3 -1
  210. package/scripts/lib/rule-loader.mjs +20 -10
  211. package/scripts/lib/scope-baseline.mjs +123 -8
  212. package/scripts/lib/scope-echo.mjs +5 -2
  213. package/scripts/lib/scope-gate.mjs +55 -20
  214. package/scripts/lib/session-close-backfill.mjs +75 -7
  215. package/scripts/lib/session-end/close-precheck.mjs +151 -0
  216. package/scripts/lib/session-end/phase-skip.mjs +8 -7
  217. package/scripts/lib/session-end/tail-runner.mjs +7 -2
  218. package/scripts/lib/session-end/vault-orphans.mjs +205 -0
  219. package/scripts/lib/session-id.mjs +9 -1
  220. package/scripts/lib/session-identity/own-session.mjs +63 -0
  221. package/scripts/lib/session-invocation.mjs +14 -3
  222. package/scripts/lib/session-lock.mjs +114 -0
  223. package/scripts/lib/session-record-repair.mjs +245 -35
  224. package/scripts/lib/session-registry.mjs +85 -12
  225. package/scripts/lib/session-schema/constants.mjs +9 -0
  226. package/scripts/lib/session-schema/filters.mjs +56 -1
  227. package/scripts/lib/session-schema/validator.mjs +15 -5
  228. package/scripts/lib/session-start-probes.mjs +149 -22
  229. package/scripts/lib/session-token-rollup.mjs +2 -1
  230. package/scripts/lib/session-work-trees.mjs +166 -0
  231. package/scripts/lib/sessions-canonical.mjs +61 -3
  232. package/scripts/lib/sessions-integrity-banner.mjs +17 -39
  233. package/scripts/lib/sessions-staleness-banner.mjs +8 -5
  234. package/scripts/lib/sh-quote.mjs +1 -0
  235. package/scripts/lib/skill-evidence-window.mjs +5 -1
  236. package/scripts/lib/skill-health/join.mjs +10 -9
  237. package/scripts/lib/skill-invocations-schema.mjs +13 -0
  238. package/scripts/lib/skill-judgments-schema.mjs +6 -2
  239. package/scripts/lib/snapshot-probe.mjs +414 -0
  240. package/scripts/lib/so-hud/collect.mjs +174 -0
  241. package/scripts/lib/so-hud/runtime.mjs +87 -0
  242. package/scripts/lib/spiral-carryover.mjs +8 -1
  243. package/scripts/lib/state-md/frontmatter-mutators.mjs +266 -8
  244. package/scripts/lib/state-md/idle-reset.mjs +248 -0
  245. package/scripts/lib/state-md/mission-status.mjs +5 -1
  246. package/scripts/lib/state-md/recommendations.mjs +26 -4
  247. package/scripts/lib/state-md-peer-guard.mjs +2 -0
  248. package/scripts/lib/state-md.mjs +31 -8
  249. package/scripts/lib/sunset/walker.mjs +4 -3
  250. package/scripts/lib/telemetry/consent.mjs +4 -2
  251. package/scripts/lib/telemetry/queue.mjs +3 -3
  252. package/scripts/lib/telemetry/sync.mjs +45 -9
  253. package/scripts/lib/test-runner/artifact-paths.mjs +10 -2
  254. package/scripts/lib/tmux-layout/layouts.mjs +20 -3
  255. package/scripts/lib/ux-grill/paths.mjs +5 -1
  256. package/scripts/lib/validate/check-agents.mjs +37 -6
  257. package/scripts/lib/validate/check-component-paths.mjs +25 -1
  258. package/scripts/lib/validate/check-guard-requires-parity.mjs +3 -3
  259. package/scripts/lib/validate/check-hooks-symmetry.mjs +53 -32
  260. package/scripts/lib/validate/check-json-files.mjs +3 -1
  261. package/scripts/lib/validate/check-learning-provenance.mjs +3 -2
  262. package/scripts/lib/validate/check-learnings-shell-writes.mjs +10 -4
  263. package/scripts/lib/validate/check-metrics-ledger-root.mjs +234 -0
  264. package/scripts/lib/validate/check-owner-leakage.mjs +159 -11
  265. package/scripts/lib/validate/check-test-posix-shell.mjs +246 -0
  266. package/scripts/lib/validate/check-unwired-features.mjs +4 -2
  267. package/scripts/lib/validate/check-vcs-repo-flag-positional.mjs +58 -0
  268. package/scripts/lib/validate/check-vcs-repo-flag.mjs +259 -73
  269. package/scripts/lib/validate/check-workflow-continue-on-error.mjs +63 -0
  270. package/scripts/lib/validate/confidential-names.mjs +3 -1
  271. package/scripts/lib/vault-mirror/migrate-ids.mjs +321 -0
  272. package/scripts/lib/vault-mirror/namespace.mjs +22 -9
  273. package/scripts/lib/vault-mirror/process.mjs +197 -65
  274. package/scripts/lib/vault-mirror/prune.mjs +5 -2
  275. package/scripts/lib/vault-mirror/render-learnings.mjs +8 -7
  276. package/scripts/lib/vault-mirror/render-sessions.mjs +36 -53
  277. package/scripts/lib/vault-mirror/retention.mjs +2 -2
  278. package/scripts/lib/vault-mirror/utils.mjs +106 -13
  279. package/scripts/lib/vault-staleness-banner.mjs +4 -7
  280. package/scripts/lib/vault-status/board-writer.mjs +34 -5
  281. package/scripts/lib/vault-status/narrative-mirror.mjs +66 -7
  282. package/scripts/lib/wave-executor/foreign-dispatch.mjs +20 -0
  283. package/scripts/lib/win-process-tree.mjs +137 -0
  284. package/scripts/lib/workspace.mjs +1 -1
  285. package/scripts/lib/worktree/constants.mjs +1 -1
  286. package/scripts/lib/worktree/lifecycle.mjs +1 -1
  287. package/scripts/lib/worktree/listing.mjs +1 -1
  288. package/scripts/lib/zx.mjs +50 -0
  289. package/scripts/mcp-server.mjs +16 -7
  290. package/scripts/mcp-server.sh +13 -83
  291. package/scripts/measure-policy-cache-effectiveness.mjs +7 -5
  292. package/scripts/memory-propose.mjs +10 -3
  293. package/scripts/migrate-cold-start-seed.mjs +7 -0
  294. package/scripts/migrate-legacy-learnings.sh +22 -0
  295. package/scripts/migrate-sessions-jsonl.mjs +16 -2
  296. package/scripts/migrate-vault-paths.mjs +29 -13
  297. package/scripts/parse-config.mjs +71 -14
  298. package/scripts/print-learnings-index.mjs +25 -2
  299. package/scripts/release-session-lock.mjs +94 -7
  300. package/scripts/release.mjs +451 -24
  301. package/scripts/relocate-vault-corpus.mjs +11 -7
  302. package/scripts/repair-invalid-sessions.mjs +68 -3
  303. package/scripts/resolve-session-invocation.mjs +57 -8
  304. package/scripts/run-migrate-v2-cross-repo.mjs +56 -1
  305. package/scripts/run-quality-gate.mjs +33 -7
  306. package/scripts/self-update.mjs +56 -7
  307. package/scripts/session-commit-files.mjs +133 -0
  308. package/scripts/site-numbers.mjs +52 -9
  309. package/scripts/snapshot-probe.mjs +156 -0
  310. package/scripts/so-hud.mjs +68 -0
  311. package/scripts/state-md-idle-reset.mjs +82 -0
  312. package/scripts/sweep-expired-learnings.mjs +54 -6
  313. package/scripts/sweep-expired-rules.mjs +4 -1
  314. package/scripts/validate-plugin.mjs +22 -0
  315. package/scripts/vault-integration-watcher.mjs +54 -6
  316. package/scripts/vault-mirror.mjs +9 -0
  317. package/scripts/vault-orphans.mjs +171 -0
  318. package/skills/_shared/fleet-protocol.md +166 -3
  319. package/skills/_shared/instruction-file-resolution.md +1 -1
  320. package/skills/_shared/monitor-patterns.md +44 -25
  321. package/skills/_shared/platform-tools.md +7 -0
  322. package/skills/_shared/state-ownership.md +5 -4
  323. package/skills/architecture/references/domain-model.md +1 -1
  324. package/skills/autopilot/SKILL.md +3 -2
  325. package/skills/bootstrap/SKILL.md +3 -1
  326. package/skills/bootstrap/_shared-template.md +4 -4
  327. package/skills/bootstrap/fast-template.md +1 -1
  328. package/skills/bootstrap/private-contract.md +1 -1
  329. package/skills/bootstrap/references/bootstrap-retroactive-flow.md +17 -9
  330. package/skills/bootstrap/templates/agents/project-code-review.md +5 -1
  331. package/skills/bootstrap/templates/agents/project-discovery.md +5 -1
  332. package/skills/bootstrap/templates/agents/project-quality-gate.md +5 -1
  333. package/skills/claude-md-drift-check/SKILL.md +3 -3
  334. package/skills/claude-md-drift-check/checker.mjs +61 -101
  335. package/skills/close/SKILL.md +20 -7
  336. package/skills/discovery/probes/docs-staleness.mjs +7 -5
  337. package/skills/discovery/probes/frontend-slop.mjs +7 -3
  338. package/skills/discovery/probes/ssot-code-diff.mjs +5 -3
  339. package/skills/discovery/probes/supply-chain-slopcheck.mjs +10 -8
  340. package/skills/discovery/probes/vault-narrative-staleness.mjs +30 -10
  341. package/skills/discovery/probes/vault-staleness.mjs +5 -3
  342. package/skills/discovery/probes-session.md +3 -0
  343. package/skills/discovery/probes-vault.md +2 -0
  344. package/skills/dispatcher/SKILL.md +1 -1
  345. package/skills/eval/SKILL.md +3 -1
  346. package/skills/evolve/SKILL.md +2 -0
  347. package/skills/evolve/references/evolve-analyze-mode.md +3 -3
  348. package/skills/evolve/references/evolve-dialectic-mode.md +1 -1
  349. package/skills/gitlab-ops/SKILL.md +7 -6
  350. package/skills/gitlab-portfolio/SKILL.md +2 -2
  351. package/skills/go/SKILL.md +13 -1
  352. package/skills/npm-publish/SKILL.md +5 -2
  353. package/skills/plan/SKILL.md +23 -10
  354. package/skills/plan/mode-feature.md +13 -8
  355. package/skills/plan/mode-new.md +6 -14
  356. package/skills/plan/prd-feature-template.md +19 -0
  357. package/skills/plan/prd-full-template.md +12 -5
  358. package/skills/plan/prd-premortem-panel.md +48 -0
  359. package/skills/plan/prd-reviewer-prompt.md +11 -3
  360. package/skills/plan/soul.md +1 -1
  361. package/skills/portfolio/SKILL.md +5 -6
  362. package/skills/quality-gates/SKILL.md +2 -4
  363. package/skills/reconcile/SKILL.md +27 -4
  364. package/skills/release/SKILL.md +16 -8
  365. package/skills/session-end/SKILL.md +97 -13
  366. package/skills/session-end/phase-3-2-docs-verification.md +1 -0
  367. package/skills/session-end/phase-3-6-tail.md +11 -7
  368. package/skills/session-end/phase-3-7a-recommendations.md +9 -2
  369. package/skills/session-end/plan-verification.md +16 -1
  370. package/skills/session-end/references/phase-3-7e-vault-orphans.md +63 -0
  371. package/skills/session-end/references/phase-3-documentation-updates.md +25 -13
  372. package/skills/session-end/references/phase-5-issue-cleanup.md +1 -1
  373. package/skills/session-end/references/session-summary-template.md +8 -0
  374. package/skills/session-end/session-metrics-write.md +7 -4
  375. package/skills/session-plan/SKILL.md +5 -3
  376. package/skills/session-start/SKILL.md +10 -4
  377. package/skills/session-start/phase-4-5-resource-health.md +11 -5
  378. package/skills/session-start/phase-7-5-mode-selector.md +3 -1
  379. package/skills/session-start/phase-8-5-express-path.md +4 -3
  380. package/skills/session-start/references/phase-1-2-session-lock.md +7 -5
  381. package/skills/session-start/references/phase-1-5-session-continuity.md +13 -5
  382. package/skills/session-start/references/phase-1-7-vault-status-board.md +1 -1
  383. package/skills/session-start/references/phase-4-ssot-environment-check.md +3 -2
  384. package/skills/tmux-layout/SKILL.md +2 -2
  385. package/skills/vault-sync/validator.mjs +3 -2
  386. package/skills/wave-executor/SKILL.md +5 -4
  387. package/skills/wave-executor/references/wave-executor-state-init.md +3 -3
  388. package/skills/wave-executor/references/wave-loop-dispatch.md +11 -5
  389. package/skills/wave-executor/references/wave-loop-review.md +13 -5
  390. package/skills/wave-executor/references/wave-loop-scope-manifest.md +1 -1
  391. package/AGENTS.md +0 -207
  392. package/assets/og-card.png +0 -0
  393. package/assets/og-card.svg +0 -47
  394. package/assets/wave-lifecycle.svg +0 -98
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ // Local partial pre-push gate (#1642): the route the hook takes when no remote host
3
+ // could run the full gate (router exit 10) AND the push goes to an origin MR branch.
4
+ // Exit 0 verified, 1 blocked, 10 not determinable (the hook then runs the full gate
5
+ // locally — fail-closed, same contract as LSO scripts/pre-push-gate.sh, Owner #801).
6
+ //
7
+ // It checks what this push changes since the merge-base with origin's default branch:
8
+ // `node --check` + ESLint on the changed source files and `vitest related` for them.
9
+ // The full suite runs in the MR pipeline on the pushed head SHA.
10
+ //
11
+ // CEILING (BV-004): `vitest related` follows the static import graph. A test that
12
+ // reaches a changed file only by spawning it (hooks, .husky/, CLIs run via
13
+ // spawnSync) or by reading it from disk (policy JSON, Markdown, fixtures — edited OR
14
+ // deleted) is not selected here; the MR pipeline is the net for that class. Only a
15
+ // deleted or renamed SOURCE file falls back to the full gate, because its importers
16
+ // break while nothing is left to select (#1643: falling back on every deletion would
17
+ // send each archive move of a doc to the full local suite).
18
+ // Revisit trigger: an MR pipeline red that a selected-by-path test would have caught
19
+ // locally on a push routed through this gate.
20
+ import { spawnSync } from 'node:child_process';
21
+ import { existsSync } from 'node:fs';
22
+ import { join } from 'node:path';
23
+ import { isMainModule } from './is-main-module.mjs';
24
+
25
+ const log = (message) => process.stderr.write(`pre-push: ${message}\n`);
26
+
27
+ /** Changed files that make a selection meaningless: they reconfigure every test or lint run. */
28
+ const FULL_SUITE_TRIGGERS = [/^package(-lock)?\.json$/, /^vitest\.config\.[cm]?[jt]s$/, /^eslint\.config\.[cm]?[jt]s$/];
29
+ const SOURCE_RE = /\.(mjs|cjs|js)$/;
30
+
31
+ function git(repoRoot, args) {
32
+ // core.quotePath=false: git otherwise prints a non-ASCII path as "\303\244.mjs" (quoted,
33
+ // escaped), and every suffix test below would miss it (#1643).
34
+ const r = spawnSync('git', ['-C', repoRoot, '-c', 'core.quotePath=false', ...args], { encoding: 'utf8', timeout: 30_000 });
35
+ return r.status === 0 ? r.stdout.trim() : null;
36
+ }
37
+
38
+ /**
39
+ * Merge-base of the pushed SHA with origin's default branch, or null when no
40
+ * origin default ref resolves in the source repo (the materialised clone has none).
41
+ */
42
+ export function resolveBase(repoRoot, sha) {
43
+ const head = git(repoRoot, ['symbolic-ref', '-q', '--short', 'refs/remotes/origin/HEAD']);
44
+ for (const ref of [head, 'origin/main'].filter(Boolean)) {
45
+ if (git(repoRoot, ['rev-parse', '-q', '--verify', `${ref}^{commit}`]) === null) continue;
46
+ return git(repoRoot, ['merge-base', sha, ref]) || null;
47
+ }
48
+ return null;
49
+ }
50
+
51
+ /** Runs one tool from the tree's own node_modules; null when the tool is not installed there. */
52
+ function runTool(tree, bin, args, timeout) {
53
+ const path = join(tree, 'node_modules/.bin', bin);
54
+ if (!existsSync(path)) return null;
55
+ const r = spawnSync(process.execPath, [path, ...args], { cwd: tree, stdio: ['ignore', 2, 2], timeout });
56
+ return r.status === 0 && !r.error;
57
+ }
58
+
59
+ async function emit(ledgerRoot, passed, fields) {
60
+ try {
61
+ const { emitEvent, sessionAttribution } = await import('./events.mjs');
62
+ await emitEvent(`orchestrator.quality_gate.${passed ? 'passed' : 'failed'}`, {
63
+ variant: 'local-partial', exit_code: passed ? 0 : 1, host: 'local', host_source: 'fallback',
64
+ ...fields, ...sessionAttribution(ledgerRoot),
65
+ }, { repoRoot: ledgerRoot });
66
+ } catch { /* telemetry cannot change the blocking verdict */ }
67
+ }
68
+
69
+ export async function main(argv = process.argv.slice(2)) {
70
+ const [tree, sha, repoRoot] = argv;
71
+ if (!tree || !sha || !repoRoot) return 10;
72
+ const base = resolveBase(repoRoot, sha);
73
+ if (!base) {
74
+ log('local partial gate: no merge-base with origin\'s default branch — full gate locally (fail-closed).');
75
+ return 10;
76
+ }
77
+ // --no-renames: a rename reads as delete + add, so the old path stays visible.
78
+ const diff = git(repoRoot, ['diff', '--name-only', '--no-renames', '--diff-filter=d', base, sha]);
79
+ const deleted = git(repoRoot, ['diff', '--name-only', '--no-renames', '--diff-filter=D', base, sha]);
80
+ if (diff === null || deleted === null) return 10;
81
+ // A deleted or renamed module breaks its importers, and `vitest related` cannot
82
+ // select tests for a file that no longer exists.
83
+ const gone = deleted.split('\n').find((f) => SOURCE_RE.test(f));
84
+ if (gone) {
85
+ log(`local partial gate: ${gone} deleted or renamed — full gate locally (fail-closed).`);
86
+ return 10;
87
+ }
88
+ const changed = diff.split('\n').filter(Boolean);
89
+ const trigger = changed.find((f) => FULL_SUITE_TRIGGERS.some((re) => re.test(f)));
90
+ if (trigger) {
91
+ log(`local partial gate: ${trigger} changed — full gate locally (fail-closed).`);
92
+ return 10;
93
+ }
94
+ const sources = changed.filter((f) => SOURCE_RE.test(f) && existsSync(join(tree, f)));
95
+ log(`local partial gate: ${changed.length} changed file(s) since ${base.slice(0, 12)}; the full suite runs in the MR pipeline on ${sha.slice(0, 12)} (#1642).`);
96
+ const fields = { pushed_sha: sha, base_sha: base, changed_count: changed.length };
97
+ if (changed.length === 0) {
98
+ await emit(repoRoot, true, fields);
99
+ return 0;
100
+ }
101
+ const syntaxFailed = sources.filter((f) =>
102
+ spawnSync(process.execPath, ['--check', f], { cwd: tree, stdio: ['ignore', 2, 2], timeout: 30_000 }).status !== 0);
103
+ if (syntaxFailed.length) log(`local partial gate: node --check failed: ${syntaxFailed.join(', ')}`);
104
+ const lint = sources.length ? runTool(tree, 'eslint', ['--no-warn-ignored', ...sources], 300_000) : true;
105
+ const test = runTool(tree, 'vitest', ['related', '--run', '--passWithNoTests', ...changed], 900_000);
106
+ if (lint === null || test === null) {
107
+ log('local partial gate: eslint or vitest missing in node_modules — full gate locally (fail-closed).');
108
+ return 10;
109
+ }
110
+ const passed = syntaxFailed.length === 0 && lint && test;
111
+ const failedChecks = [syntaxFailed.length && 'typecheck', !lint && 'lint', !test && 'test'].filter(Boolean);
112
+ await emit(repoRoot, passed, { ...fields, ...(failedChecks.length ? { failed_checks: failedChecks } : {}) });
113
+ log(`local partial gate ${passed ? 'passed' : 'FAILED — Push blocked'}.`);
114
+ return passed ? 0 : 1;
115
+ }
116
+
117
+ if (isMainModule(import.meta.url)) process.exitCode = await main();
@@ -1,7 +1,11 @@
1
1
  #!/usr/bin/env node
2
- // Optional pre-push router: 0 verified, 1 blocked, 10 transport/readiness fallback.
2
+ // Optional pre-push router: 0 verified, 1 blocked, 2 SO_GATE_HOSTS invalid or none of its
3
+ // hosts could run the gate (blocked, no local fallback), 10 transport/readiness fallback.
3
4
  import { spawnSync } from 'node:child_process';
4
5
  import { randomUUID } from 'node:crypto';
6
+ import { statSync } from 'node:fs';
7
+ import { homedir } from 'node:os';
8
+ import { join } from 'node:path';
5
9
  import { readConfigFile } from './config/io.mjs';
6
10
  import { _parseRemoteHosts } from './config/remote-hosts.mjs';
7
11
  import { isMainModule } from './is-main-module.mjs';
@@ -44,64 +48,186 @@ function remoteCommand(sha, nonce) {
44
48
  `;
45
49
  }
46
50
 
47
- async function main() {
48
- const [tree, sha, ledgerRoot] = process.argv.slice(2);
51
+ const ALIAS_SHAPE = /^[A-Za-z0-9._-]+$/;
52
+
53
+ /**
54
+ * Resolve the `SO_GATE_HOSTS` override (#1626) against the offload host registry.
55
+ *
56
+ * The registry rule mirrors offload's own `load_host`:
57
+ * `${OFFLOAD_CONFIG_DIR:-$HOME/.config/remote-offload}/hosts/<alias>.env` must be a
58
+ * regular file. The alias shape is checked BEFORE any path is built, so `/` and
59
+ * `..` can never reach the filesystem.
60
+ *
61
+ * @param {NodeJS.ProcessEnv} env
62
+ * @returns {{ hosts: string[], source: 'override' } | { error: string } | null}
63
+ * null when the override is unset (whitespace-only counts as unset).
64
+ */
65
+ export function resolveGateHostOverride(env = process.env) {
66
+ const raw = (env.SO_GATE_HOSTS || '').trim();
67
+ if (!raw) return null;
68
+ const configDir = (env.OFFLOAD_CONFIG_DIR || '').trim() || join((env.HOME || '').trim() || homedir(), '.config/remote-offload');
69
+ const registry = join(configDir, 'hosts');
70
+ const hosts = raw.split(',').map((alias) => alias.trim()).filter(Boolean);
71
+ for (const alias of hosts) {
72
+ let known = false;
73
+ if (ALIAS_SHAPE.test(alias) && alias !== '.' && alias !== '..') {
74
+ try { known = statSync(join(registry, `${alias}.env`)).isFile(); } catch { known = false; }
75
+ }
76
+ if (!known) {
77
+ return { error: `SO_GATE_HOSTS names unknown or invalid host '${alias}' — no regular file ${alias}.env in ${registry}. Push blocked (no local fallback).` };
78
+ }
79
+ }
80
+ return hosts.length ? { hosts, source: 'override' } : { error: `SO_GATE_HOSTS='${raw}' contains no host alias. Push blocked.` };
81
+ }
82
+
83
+ /** The `gate-ready=<value>` token of an `offload doctor --brief` run (stdout or stderr), or null. */
84
+ function gateReadyToken(doctor) {
85
+ return /\bgate-ready=(\S+)/.exec(`${doctor.stdout ?? ''}\n${doctor.stderr ?? ''}`)?.[1] ?? null;
86
+ }
87
+
88
+ /**
89
+ * Gate readiness of one host (#1627). doctor's exit code also covers headless
90
+ * readiness, which a gate does not need: a Windows host reports `gate-ready=yes` with
91
+ * exit 1 because `headless-ready=NO`. The token decides when present; an offload
92
+ * without it keeps the old exit-code rule. A spawn error (timeout) is not ready.
93
+ *
94
+ * @param {{ error?: Error, status: number | null, stdout?: string, stderr?: string }} doctor
95
+ * @returns {boolean}
96
+ */
97
+ export function gateReady(doctor) {
98
+ if (doctor.error) return false;
99
+ const token = gateReadyToken(doctor);
100
+ return token === null ? doctor.status === 0 : token === 'yes';
101
+ }
102
+
103
+ /**
104
+ * Did the remote gate re-run its timeout-only failures clean (#1673)? Checked
105
+ * here on the report's own fields, not on `rerun.status` alone: zero failures,
106
+ * no timeout, no signal sent, no survivor, and exactly the first run's failing
107
+ * files — no more, no fewer.
108
+ */
109
+ function cleanRerun(test) {
110
+ const rerun = test?.rerun;
111
+ if (rerun?.status !== 'pass' || rerun.failed !== 0 || rerun.timed_out !== false) return false;
112
+ if (!Array.isArray(rerun.kill_signals) || rerun.kill_signals.length !== 0) return false;
113
+ if (!Array.isArray(rerun.survivors) || rerun.survivors.length !== 0) return false;
114
+ const failedFiles = Array.isArray(test.failed_files) ? test.failed_files : [];
115
+ const files = Array.isArray(rerun.files) ? rerun.files : [];
116
+ return failedFiles.length > 0 && files.length === failedFiles.length &&
117
+ new Set(files).size === files.length && failedFiles.every((f) => files.includes(f));
118
+ }
119
+
120
+ /**
121
+ * Run the full quality gate for `sha` on a remote host via offload (the body of the
122
+ * pre-push router, #1712). Exit-code contract of `code`: 0 verified, 1 blocked,
123
+ * 2 SO_GATE_HOSTS invalid or none of its hosts could run the gate, 10 fallback.
124
+ *
125
+ * @param {object} opts
126
+ * @param {string} opts.tree checkout offload syncs (its basename names the host repo)
127
+ * @param {string} opts.sha commit the remote gate must check out and verify
128
+ * @param {string} opts.ledgerRoot repo root for events and wave attribution
129
+ * @param {NodeJS.ProcessEnv} [opts.env] source of the SO_GATE_HOSTS override
130
+ * @param {boolean} [opts.emit=true] emit `orchestrator.quality_gate.*` — false for
131
+ * callers whose `sha` is not a pushed commit (snapshot probe)
132
+ * @param {string} [opts.jobPrefix='pre-push'] offload job name prefix
133
+ * @param {(message: string) => void} [opts.log]
134
+ * @param {{ write: (chunk: string) => unknown }} [opts.stdout=process.stdout] sink for the remote
135
+ * run's stdout — a caller that owns its own stdout (snapshot probe `--json`) routes it elsewhere
136
+ * @param {{ write: (chunk: string) => unknown }} [opts.stderr=process.stderr] sink for the remote run's stderr
137
+ * @returns {Promise<{ code: number, passed: boolean, alias: string | null, report: object | null, hostSource: string }>}
138
+ */
139
+ export async function runRemoteGate({ tree, sha, ledgerRoot, env = process.env, emit = true, jobPrefix = 'pre-push', log: logFn = log,
140
+ stdout = process.stdout, stderr = process.stderr }) {
49
141
  let hosts;
50
- try {
51
- hosts = _parseRemoteHosts(await readConfigFile(tree)).filter((host) => host['roles-allowed'].includes('test'));
52
- } catch {
53
- log('remote configuration unavailable — using local gate.');
54
- return 10;
142
+ let hostSource = 'config';
143
+ const done = (code) => ({ code, passed: false, alias: null, report: null, hostSource });
144
+ const override = resolveGateHostOverride(env);
145
+ if (override?.error) {
146
+ logFn(override.error);
147
+ return done(2);
148
+ }
149
+ if (override) {
150
+ hosts = override.hosts.map((alias) => ({ alias }));
151
+ hostSource = 'override';
152
+ logFn(`gate host order from SO_GATE_HOSTS (host_source: override): ${override.hosts.join(', ')}.`);
153
+ } else {
154
+ try {
155
+ hosts = _parseRemoteHosts(await readConfigFile(tree)).filter((host) => host['roles-allowed'].includes('test'));
156
+ } catch {
157
+ logFn('remote configuration unavailable — using local gate (host_source: fallback).');
158
+ return done(10);
159
+ }
55
160
  }
56
161
  for (const { alias } of hosts) {
57
- log(`checking remote readiness (${alias}).`);
162
+ logFn(`checking remote readiness (${alias}).`);
58
163
  const doctor = spawnSync('offload', ['doctor', '-H', alias, '--brief', '--no-probe'],
59
164
  { encoding: 'utf8', timeout: 30_000, maxBuffer: 1024 * 1024 });
60
165
  if (doctor.error?.code === 'ENOENT') break;
61
- if (doctor.error || doctor.status !== 0) {
62
- log(`remote ${alias} not ready (exit ${doctor.status ?? doctor.error?.code}) — trying next route.`);
166
+ if (!gateReady(doctor)) {
167
+ const token = gateReadyToken(doctor);
168
+ logFn(`remote ${alias} not ready (exit ${doctor.status ?? doctor.error?.code}${token ? `, gate-ready=${token}` : ''}) — trying next route.`);
63
169
  continue;
64
170
  }
65
171
  const nonce = randomUUID();
66
- log(`running full quality gate remotely on ${alias} against pushed SHA ${sha}.`);
67
- const run = spawnSync('offload', ['run', '-H', alias, tree, '--job', `pre-push-${nonce}`,
68
- '--klasse', 'gate', '--link-modules', '--rm', '--timeout', '1800', '--', 'node', '-e', remoteCommand(sha, nonce)],
172
+ logFn(`running full quality gate remotely on ${alias} (host_source: ${hostSource}) against pushed SHA ${sha}.`);
173
+ // No --link-modules: offload snapshots a linked node_modules symlink into its commit (.gitignore's
174
+ // `node_modules/` matches directories only), and the `git checkout <sha>` in remoteCommand then
175
+ // deletes it, so vitest/eslint exit 127 and the gate reports them as "fail" (push blocked).
176
+ const run = spawnSync('offload', ['run', '-H', alias, tree, '--job', `${jobPrefix}-${nonce}`,
177
+ '--klasse', 'gate', '--rm', '--timeout', '1800', '--', 'node', '-e', remoteCommand(sha, nonce)],
69
178
  { encoding: 'utf8', timeout: 1_860_000, maxBuffer: 64 * 1024 * 1024 });
70
- process.stdout.write(run.stdout ?? '');
71
- process.stderr.write(run.stderr ?? '');
179
+ stdout.write(run.stdout ?? '');
180
+ stderr.write(run.stderr ?? '');
72
181
  const receipts = (run.stdout ?? '').split(/\r?\n/).filter((line) => line.startsWith('SO_PRE_PUSH_RESULT='));
73
182
  let receipt;
74
183
  try { if (receipts.length === 1) receipt = JSON.parse(receipts[0].slice('SO_PRE_PUSH_RESULT='.length)); } catch { /* unknown blocks */ }
75
184
  if (receipts.length === 0 && !run.error && [2, 4, 8, 10, 255].includes(run.status)) {
76
- log(`remote ${alias} transport/readiness failure (exit ${run.status}) — trying next route.`);
185
+ logFn(`remote ${alias} transport/readiness failure (exit ${run.status}) — trying next route.`);
77
186
  continue;
78
187
  }
79
188
  const report = receipt?.report;
80
189
  const stubbedChecks = Object.keys(report?.stubbed ?? {});
81
- if (stubbedChecks.length > 0) log(`remote gate contains stubbed checks: ${stubbedChecks.join(', ')} — Push blocked.`);
190
+ if (stubbedChecks.length > 0) logFn(`remote gate contains stubbed checks: ${stubbedChecks.join(', ')} — Push blocked.`);
82
191
  const passed = !run.error && run.status === 0 && receipt?.nonce === nonce && receipt.sha === sha &&
83
192
  receipt.exit_code === 0 && report?.variant === 'full-gate' && report.test?.status === 'pass' &&
84
- stubbedChecks.length === 0 && !(report.test.failed > 0) && report.test.suite_died !== true &&
193
+ // `failed` stays the first, parallel run's count; it is acceptable only when
194
+ // the gate re-ran exactly those timeout-only failures clean (#1673).
195
+ stubbedChecks.length === 0 && !(report.test.failed > 0 && !cleanRerun(report.test)) &&
196
+ report.test.suite_died !== true &&
85
197
  ['pass', 'skip'].includes(report.typecheck?.status) && ['pass', 'skip'].includes(report.lint?.status) &&
86
198
  ![report.test, report.typecheck, report.lint].some((check) => check.timed_out === true);
87
- try {
199
+ if (emit) try {
88
200
  const { emitEvent, sessionAttribution } = await import('./events.mjs');
89
201
  const counts = admitSuiteCounts(report?.test);
90
202
  const waveNumber = resolveWaveNumber(ledgerRoot);
91
203
  await emitEvent(`orchestrator.quality_gate.${passed ? 'passed' : 'failed'}`, {
92
- variant: 'full-gate', exit_code: passed ? 0 : 1, remote_host: alias,
204
+ variant: 'full-gate', exit_code: passed ? 0 : 1, remote_host: alias, host: alias, host_source: hostSource,
93
205
  pushed_sha: sha, ...sessionAttribution(ledgerRoot),
94
206
  ...joinGateKillFields({ outer: {}, gateStdout: JSON.stringify(report) }),
95
207
  ...(counts ? { counts } : {}),
96
208
  ...(waveNumber !== null ? { wave_number: waveNumber } : {}),
97
209
  ...(report?.test?.failed_files?.length ? { failed_files: report.test.failed_files } : {}),
210
+ ...(Array.isArray(report?.test?.rerun?.files) && report.test.rerun.files.length
211
+ ? { rerun: { files: report.test.rerun.files, status: report.test.rerun.status } } : {}),
98
212
  }, { repoRoot: ledgerRoot });
99
213
  } catch { /* telemetry cannot change the blocking verdict */ }
100
- log(`remote quality gate ${passed ? 'passed' : 'FAILED — Push blocked'} (${alias}).`);
101
- return passed ? 0 : 1;
214
+ logFn(`remote quality gate ${passed ? 'passed' : 'FAILED — Push blocked'} (${alias}).`);
215
+ return { code: passed ? 0 : 1, passed, alias, report: report ?? null, hostSource };
102
216
  }
103
- log('remote routes unavailable — using local full quality gate.');
104
- return 10;
217
+ if (hostSource === 'override') {
218
+ // An explicit override names where the gate may run; silently falling back to the
219
+ // local host is the case the override exists to prevent (#1626).
220
+ logFn(`no SO_GATE_HOSTS route could run the gate (${hosts.map((h) => h.alias).join(', ')}) — Push blocked (no local fallback).`);
221
+ return done(2);
222
+ }
223
+ logFn('remote routes unavailable — local fallback (host: local, host_source: fallback; an origin MR branch takes the partial gate, #1642).');
224
+ return done(10);
225
+ }
226
+
227
+ async function main() {
228
+ const [tree, sha, ledgerRoot] = process.argv.slice(2);
229
+ const r = await runRemoteGate({ tree, sha, ledgerRoot });
230
+ return r.code;
105
231
  }
106
232
 
107
233
  if (isMainModule(import.meta.url)) process.exitCode = await main();