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,301 @@
1
+ /**
2
+ * hook-root.mjs — which root holds THIS session's own state (#1605).
3
+ *
4
+ * `$CLAUDE_PROJECT_DIR` stays on the LAUNCH checkout after `EnterWorktree`. A
5
+ * /clear, compact or resume there re-fires SessionStart with the payload `cwd`
6
+ * inside the entered worktree; writing the new session's `session.lock` and
7
+ * `current-session.json` at the launch root blocked whoever works there for the
8
+ * lock's 4 h TTL. Two functions, one per side of the lifecycle:
9
+ *
10
+ * - {@link resolveSessionStartRoot} — the WRITER (SessionStart) decides by rule
11
+ * whether the session's state moves into the entered worktree, and records
12
+ * its answer as `session_root` in the host registry entry of its raw id;
13
+ * - {@link resolveOwnLockRoot} — the READERS (Stop, PostToolBatch, SessionEnd,
14
+ * the current-session.json side writers) never re-derive that rule: they
15
+ * follow the `session_root` pointer first — whatever the current `cwd` — and
16
+ * accept it only when the lock there carries their own raw `session_id`.
17
+ *
18
+ * Both answer the launch root unchanged whenever anything is in doubt.
19
+ */
20
+
21
+ import { readdirSync, readFileSync, realpathSync } from 'node:fs';
22
+ import path from 'node:path';
23
+ import { dotGitAncestor, linkedWorktreeSpot } from '../../scripts/lib/platform.mjs';
24
+ import { activeDir, isRegistryEntryFresh } from '../../scripts/lib/session-registry.mjs';
25
+ import { isLockLive, readLockDetailed } from '../../scripts/lib/session-lock.mjs';
26
+ import { parseSessionId } from '../../scripts/lib/session-id.mjs';
27
+
28
+ /** SessionStart sources that continue in the tree the session already works in. */
29
+ const RELOCATING_SOURCES = new Set(['clear', 'compact', 'resume']);
30
+
31
+ /**
32
+ * The payload's own `session_id`, UUID-only, exactly as the lifecycle hooks
33
+ * derive their raw id — never a value read back from `current-session.json`,
34
+ * which would make every ownership compare below self-fulfilling.
35
+ *
36
+ * @param {object|null} input
37
+ * @returns {string|null}
38
+ */
39
+ export function rawPayloadSessionId(input) {
40
+ const id = input?.session_id ?? input?.sessionId ?? null;
41
+ return parseSessionId(id)?.format === 'uuid' ? id : null;
42
+ }
43
+
44
+ /** `session_id` of the lock at `root`, `null` when absent; `undefined` when unreadable/corrupt. */
45
+ function lockIdAt(root) {
46
+ const detail = readLockDetailed({ repoRoot: root });
47
+ if (detail.status === 'absent') return null;
48
+ return detail.status === 'ok' ? detail.lock.session_id : undefined;
49
+ }
50
+
51
+ /**
52
+ * JSON object at `file`: `{ status: 'absent' }` on ENOENT, `{ status: 'ok', value }`
53
+ * for an object, `{ status: 'unreadable' }` for every other read or parse failure.
54
+ * Callers that judge liveness treat 'unreadable' as LIVE (fail closed, #1605 B6).
55
+ *
56
+ * @param {string} file
57
+ * @returns {{status: 'absent'|'ok'|'unreadable', value?: object}}
58
+ */
59
+ function readJsonState(file) {
60
+ let text;
61
+ try { text = readFileSync(file, 'utf8'); } catch (err) {
62
+ return { status: err?.code === 'ENOENT' ? 'absent' : 'unreadable' };
63
+ }
64
+ try {
65
+ const value = JSON.parse(text);
66
+ return value !== null && typeof value === 'object' ? { status: 'ok', value } : { status: 'unreadable' };
67
+ } catch { return { status: 'unreadable' }; }
68
+ }
69
+
70
+ /**
71
+ * The root the host registry entry of `ownId` points at (`session_root`), when
72
+ * the lock there carries `ownId` — else `null`. Independent of the current
73
+ * `cwd`: after `ExitWorktree` the payload `cwd` no longer names the worktree
74
+ * that holds the lock (#1605 B1), and a compact after a `cd` must not decide
75
+ * anew (B2/B3).
76
+ *
77
+ * With `liveAtMs`, the lock there must also be live by its own heartbeat and
78
+ * TTL (`isLockLive`) — the WRITER's test (#1630): a session whose process died
79
+ * without SessionEnd leaves entry and lock behind, and a `resume` hours later
80
+ * from another checkout was pulled back into that worktree past the target
81
+ * checks of {@link resolveSessionStartRoot}. A lock past its TTL is no claim
82
+ * to continue there; the caller then decides by rule. Only a `resume` passes
83
+ * `liveAtMs` — it is the one source that can come from a new process. A
84
+ * `compact` always comes from the running session, whose own stale lock after
85
+ * a long idle is a refresh, not a takeover (`lock-bootstrap.mjs`); dropping the
86
+ * pointer there orphaned the worktree lock (#1630 review). The READERS pass no
87
+ * `liveAtMs` either: a running session must still find its own stale lock to
88
+ * heartbeat and release it.
89
+ *
90
+ * Ceiling (BV-004): a process that dies without SessionEnd and is resumed from
91
+ * the main checkout WITHIN the lock's TTL still follows the pointer back into
92
+ * the worktree — the lock there is live and indistinguishable from a running
93
+ * session's. Revisit when such a case is reported.
94
+ *
95
+ * @param {string|null} ownId
96
+ * @param {number|null} [liveAtMs]
97
+ * @returns {string|null}
98
+ */
99
+ function pointedOwnRoot(ownId, liveAtMs = null) {
100
+ if (ownId === null) return null;
101
+ const entry = readJsonState(path.join(activeDir(), `${ownId}.json`));
102
+ const root = entry.status === 'ok' ? entry.value.session_root : null;
103
+ if (typeof root !== 'string' || !path.isAbsolute(root)) return null;
104
+ if (liveAtMs === null) return lockIdAt(root) === ownId ? root : null;
105
+ const detail = readLockDetailed({ repoRoot: root });
106
+ return detail.status === 'ok' && detail.lock.session_id === ownId && isLockLive(detail.lock, liveAtMs)
107
+ ? root : null;
108
+ }
109
+
110
+ /** Is `child` equal to or inside `parent`? Plain path compare — both sides are realpaths. */
111
+ function isSameOrInside(child, parent) {
112
+ const rel = path.relative(parent, child);
113
+ return rel === '' || (rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel));
114
+ }
115
+
116
+ /**
117
+ * Does a LIVE host registry entry other than `ownId` hold state in, or work in,
118
+ * the checkout `targetTop`? Reads `session_root` (where its lock lives) AND
119
+ * `work_root` (where it works) of every fresh entry — the second catches a
120
+ * session that entered `targetTop` by EnterWorktree while its lock stayed at its
121
+ * own launch root (#1605 B4). A root that merely CONTAINS `targetTop` (the main
122
+ * checkout around `.claude/worktrees/<name>`) is a different working copy and
123
+ * does not count. Fail closed: an unreadable registry directory or entry counts
124
+ * as a live claim (#1605 B6); only ENOENT means "nobody".
125
+ *
126
+ * @param {string} targetTop realpath of the target checkout's top
127
+ * @param {string|null} ownId
128
+ * @param {number} nowMs
129
+ * @returns {boolean}
130
+ */
131
+ function foreignRegistryClaim(targetTop, ownId, nowMs) {
132
+ let names;
133
+ try { names = readdirSync(activeDir()); } catch (err) { return err?.code !== 'ENOENT'; }
134
+ for (const name of names) {
135
+ if (!name.endsWith('.json') || name === `${ownId}.json`) continue;
136
+ const entry = readJsonState(path.join(activeDir(), name));
137
+ if (entry.status === 'absent') continue;
138
+ if (entry.status === 'unreadable') return true;
139
+ const e = entry.value;
140
+ if (e.session_id === ownId || !isRegistryEntryFresh(e, { now: nowMs })) continue;
141
+ for (const root of [e.session_root, e.work_root]) {
142
+ if (typeof root === 'string' && path.isAbsolute(root) && isSameOrInside(root, targetTop)) return true;
143
+ }
144
+ }
145
+ return false;
146
+ }
147
+
148
+ /** realpath of the checkout top holding `dir`, or '' when it has none or cannot be resolved. */
149
+ function checkoutTopOf(dir) {
150
+ if (typeof dir !== 'string' || !path.isAbsolute(dir)) return '';
151
+ const top = dotGitAncestor(dir);
152
+ if (top === '') return '';
153
+ try { return realpathSync.native(top); } catch { return ''; }
154
+ }
155
+
156
+ /**
157
+ * #1636 Pkt 2 / #1634 Pkt 1 — a MEASUREMENT, not a guard: does a fresh host
158
+ * registry entry other than `ownId` WORK in the checkout `repoRoot` (its
159
+ * `work_root` lies in it) while its lock lives in a DIFFERENT checkout (its
160
+ * `session_root` does not)? That is the session that left its worktree by
161
+ * ExitWorktree and works in the main checkout — `acquire()` cannot see it, so a
162
+ * new session there gets the lock with no conflict. Recorded on
163
+ * `orchestrator.session.lock.acquired` so the rate is countable before any
164
+ * banner is built on it (HR-101/HR-105).
165
+ *
166
+ * Three states, never two (an unreadable registry must not read as "nobody"):
167
+ * `true`, `false`, or `null` when the registry directory or an entry cannot be
168
+ * read (ENOENT of the directory is `false` — no registry, no claim).
169
+ * Spawn-free: `fs` reads only.
170
+ *
171
+ * @param {string} repoRoot
172
+ * @param {string|null} ownId — this session's raw id (its own entry is skipped).
173
+ * @param {number} [nowMs]
174
+ * @returns {boolean|null}
175
+ */
176
+ export function foreignWorkRootIn(repoRoot, ownId, nowMs = Date.now()) {
177
+ const targetTop = checkoutTopOf(repoRoot);
178
+ if (targetTop === '') return null;
179
+ let names;
180
+ try { names = readdirSync(activeDir()); } catch (err) { return err?.code === 'ENOENT' ? false : null; }
181
+ let unreadable = false;
182
+ for (const name of names) {
183
+ if (!name.endsWith('.json') || name === `${ownId}.json`) continue;
184
+ const entry = readJsonState(path.join(activeDir(), name));
185
+ if (entry.status === 'absent') continue;
186
+ if (entry.status === 'unreadable') { unreadable = true; continue; }
187
+ const e = entry.value;
188
+ if (e.session_id === ownId || !isRegistryEntryFresh(e, { now: nowMs })) continue;
189
+ if (checkoutTopOf(e.work_root) === targetTop && checkoutTopOf(e.session_root) !== targetTop) return true;
190
+ }
191
+ return unreadable ? null : false;
192
+ }
193
+
194
+ /**
195
+ * Is session `id` live by the host registry? A non-UUID id cannot be looked up
196
+ * safely (it would become a path segment), so it counts as live — fail closed,
197
+ * like an entry that exists but cannot be read (#1605 B6).
198
+ *
199
+ * @param {string} id
200
+ * @param {number} nowMs
201
+ * @returns {boolean}
202
+ */
203
+ function isRegisteredLive(id, nowMs) {
204
+ if (parseSessionId(id)?.format !== 'uuid') return true;
205
+ const entry = readJsonState(path.join(activeDir(), `${id}.json`));
206
+ if (entry.status === 'absent') return false;
207
+ return entry.status === 'unreadable' || isRegistryEntryFresh(entry.value, { now: nowMs });
208
+ }
209
+
210
+ /**
211
+ * The root SessionStart writes this session's own state into (#1605).
212
+ *
213
+ * First: when the registry entry of the raw payload id already points at a root
214
+ * whose LIVE lock carries that id, that root — a compact or resume continues
215
+ * where the session's lock is, whatever the current `cwd` (#1605 B2/B3). On a
216
+ * `resume` a pointed lock past its TTL is ignored (#1630) and the rule below
217
+ * decides; a `compact` follows its own pointer even to a stale lock. Ceiling:
218
+ * see {@link pointedOwnRoot} (resume within the TTL after a crash).
219
+ *
220
+ * Otherwise the entered worktree wins ONLY when ALL hold, else `launchRoot`:
221
+ * (a) the payload `source` continues a session (`clear`, `compact`, `resume`)
222
+ * — a `startup` belongs to the launch root;
223
+ * (b) the payload `cwd` sits in a back-pointer-confirmed linked worktree of the
224
+ * launch root's repository, other than the launch checkout
225
+ * (`linkedWorktreeSpot`);
226
+ * (c) the session does not already hold its lock at the launch root;
227
+ * (d) the target holds no live lock of ANOTHER session, no unreadable lock, no
228
+ * `current-session.json` naming another session that is live by the host
229
+ * registry, and no live registry entry of another session whose
230
+ * `session_root` or `work_root` lies in the target checkout (B4).
231
+ * Registry read errors other than ENOENT count as live (B6).
232
+ * A /clear's predecessor is released and deregistered by its SessionEnd(clear)
233
+ * first, so it does not count; when that SessionEnd failed, the predecessor
234
+ * still looks live and the session keeps the launch root — the pre-#1605
235
+ * answer (ceiling, BV-004; revisit when a /clear in a worktree reports a lock
236
+ * written to the launch root although its SessionEnd ran).
237
+ *
238
+ * Spawn-free: `fs` reads only.
239
+ *
240
+ * @param {object|null} input the parsed SessionStart payload
241
+ * @param {string} launchRoot `resolveProjectDir()`
242
+ * @param {number} [nowMs]
243
+ * @returns {Promise<string>}
244
+ */
245
+ export async function resolveSessionStartRoot(input, launchRoot, nowMs = Date.now()) {
246
+ const ownId = rawPayloadSessionId(input);
247
+ const pointed = pointedOwnRoot(ownId, input?.source === 'resume' ? nowMs : null);
248
+ if (pointed !== null) return pointed;
249
+ if (!RELOCATING_SOURCES.has(input?.source)) return launchRoot;
250
+ // Without a raw payload id no reader (resolveOwnLockRoot) can ever find a
251
+ // relocated lock again — it would sit orphaned in the worktree for its TTL.
252
+ if (ownId === null) return launchRoot;
253
+ const spot = linkedWorktreeSpot(input?.cwd, launchRoot);
254
+ if (spot === '') return launchRoot;
255
+ if (ownId !== null && lockIdAt(launchRoot) === ownId) return launchRoot;
256
+
257
+ const detail = readLockDetailed({ repoRoot: spot });
258
+ if (detail.status === 'unreadable' || detail.status === 'corrupt') return launchRoot;
259
+ if (detail.status === 'ok' && detail.lock.session_id !== ownId && isLockLive(detail.lock, nowMs)) {
260
+ return launchRoot;
261
+ }
262
+ const current = readJsonState(path.join(spot, '.orchestrator', 'current-session.json'));
263
+ if (current.status === 'unreadable') return launchRoot;
264
+ const named = current.status === 'ok' ? current.value.session_id : null;
265
+ if (typeof named === 'string' && named !== '' && named !== ownId && isRegisteredLive(named, nowMs)) {
266
+ return launchRoot;
267
+ }
268
+ const top = dotGitAncestor(spot);
269
+ if (top === '') return launchRoot;
270
+ let targetTop;
271
+ try { targetTop = realpathSync.native(top); } catch { return launchRoot; }
272
+ if (foreignRegistryClaim(targetTop, ownId, nowMs)) return launchRoot;
273
+ return spot;
274
+ }
275
+
276
+ /**
277
+ * The root whose `.orchestrator/session.lock` carries THIS session's raw
278
+ * `session_id`: the registry pointer (`session_root`) first, then the launch
279
+ * root, then the entered worktree's spot (`linkedWorktreeSpot` of the payload
280
+ * `cwd`) — else `launchRoot` unchanged. For Stop, PostToolBatch, SessionEnd and
281
+ * the hooks that annotate `current-session.json`: they act where SessionStart
282
+ * actually put the lock, and never re-derive SessionStart's rule. Without a raw
283
+ * payload id ownership cannot be established, so the answer is the launch root.
284
+ *
285
+ * Ceiling (BV-004): a session that never got a lock at a relocated root (lost
286
+ * the acquire race) is read at the launch root. Revisit when a relocated
287
+ * `current-session.json` is found without a lock beside it.
288
+ *
289
+ * @param {object|null} input the parsed hook payload
290
+ * @param {string} launchRoot `getProjectDir()`
291
+ * @returns {string}
292
+ */
293
+ export function resolveOwnLockRoot(input, launchRoot) {
294
+ const ownId = rawPayloadSessionId(input);
295
+ if (ownId === null) return launchRoot;
296
+ const pointed = pointedOwnRoot(ownId);
297
+ if (pointed !== null) return pointed;
298
+ if (lockIdAt(launchRoot) === ownId) return launchRoot;
299
+ const spot = linkedWorktreeSpot(input?.cwd, launchRoot);
300
+ return spot !== '' && lockIdAt(spot) === ownId ? spot : launchRoot;
301
+ }