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
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "A repeatable Plan, Go, Close workflow for AI coding sessions: /session reads your repo and agrees the scope, /go runs the work in waves with a quality gate between each, /close verifies and commits. Runs on Claude Code, Codex CLI, Cursor and Pi.",
10
- "version": "5.11.0"
10
+ "version": "5.13.0"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -24,6 +24,17 @@
24
24
  "keywords": ["session", "orchestration", "waves", "gitlab", "github", "quality-gates", "subagents"],
25
25
  "category": "productivity",
26
26
  "tags": ["session-management", "wave-execution", "vcs-integration", "code-review", "agent-orchestration"]
27
+ },
28
+ {
29
+ "name": "so-hud",
30
+ "source": "./mods/so-hud",
31
+ "description": "Optional add-on for Claude Code: Session Orchestrator status line, warnings and session beacons, plus protection gates (secret-printing commands refused; pushes to main, merges, releases, publishes and deploys confirmed first). Needs the Session Orchestrator release that ships it, or later.",
32
+ "author": { "name": "session-orchestrator contributors" },
33
+ "homepage": "https://session-orchestrator.com",
34
+ "repository": "https://github.com/Kanevry/session-orchestrator",
35
+ "license": "MIT",
36
+ "category": "productivity",
37
+ "tags": ["status-line", "session-management", "agent-orchestration"]
27
38
  }
28
39
  ]
29
40
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "session-orchestrator",
4
- "version": "5.11.0",
4
+ "version": "5.13.0",
5
5
  "description": "A repeatable Plan, Go, Close workflow for AI coding sessions: /session reads your repo and agrees the scope, /go runs the work in waves with a quality gate between each, /close verifies and commits. Runs on Claude Code, Codex CLI, Cursor and Pi.",
6
6
  "author": {
7
7
  "name": "Bernhard Goetzendorfer",
@@ -12,6 +12,9 @@
12
12
  "repository": "https://github.com/Kanevry/session-orchestrator",
13
13
  "license": "MIT",
14
14
  "keywords": ["session", "orchestration", "waves", "gitlab", "github", "quality-gates", "verification-gates", "multi-agent", "codex", "cursor", "pi"],
15
+ "mcpServers": {
16
+ "session-orchestrator": { "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/mcp-server.mjs"] }
17
+ },
15
18
  "experimental": {
16
19
  "monitors": "./monitors/monitors.json",
17
20
  "evals": "./evals"
@@ -1,4 +1,4 @@
1
- model = "gpt-5.4"
1
+ model = "gpt-6.1-sol"
2
2
  model_reasoning_effort = "medium"
3
3
  sandbox_mode = "read-only"
4
4
 
@@ -1,5 +1,5 @@
1
- model = "gpt-5.4"
2
- model_reasoning_effort = "high"
1
+ model = "gpt-6.1-sol"
2
+ model_reasoning_effort = "medium"
3
3
  sandbox_mode = "read-only"
4
4
 
5
5
  developer_instructions = """
@@ -1,4 +1,4 @@
1
- model = "gpt-5.4"
1
+ model = "gpt-6.1-sol"
2
2
  model_reasoning_effort = "medium"
3
3
  sandbox_mode = "workspace-write"
4
4
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "session-orchestrator",
3
- "version": "5.11.0+codex.20261010111731",
3
+ "version": "5.13.0+codex.20261020173803",
4
4
  "description": "A repeatable Plan, Go, Close workflow for AI coding sessions: /session reads your repo and agrees the scope, /go runs the work in waves with a quality gate between each, /close verifies and commits. Runs on Claude Code, Codex CLI, Cursor and Pi.",
5
5
  "keywords": [
6
6
  "session",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "session-orchestrator",
3
- "version": "5.11.0",
3
+ "version": "5.13.0",
4
4
  "description": "A repeatable Plan, Go, Close workflow for AI coding sessions: /session reads your repo and agrees the scope, /go runs the work in waves with a quality gate between each, /close verifies and commits. Runs on Claude Code, Codex CLI, Cursor and Pi.",
5
5
  "author": {
6
6
  "name": "Bernhard Goetzendorfer"
@@ -135,6 +135,14 @@
135
135
  "target-denylist": [".orchestrator", ".orchestrator/metrics", ".orchestrator/metrics/**"],
136
136
  "rationale": "Deleting or renaming anything under .orchestrator/metrics/ destroys append-only session telemetry with no recovery path — the ledger IS the history, and nothing re-derives it. Measured 2026-09-19 (#1401): a wave subagent ran `touch .orchestrator/metrics/events.jsonl.1` (which ADOPTED the existing rotated ledger instead of creating a probe file), then `rm -f` on the same path — 53,896 lines / 10 MB covering 2026-04-12 to 2026-09-18 were gone, and the guard said nothing, because `rm` was only ever matched as the literal pattern `rm -rf`. Severity is `block`, not `warn`, on a measured absence of legitimate callers: rotation and ledger repair run through Node fs, never through Bash (`renameSync`/`unlinkSync` at scripts/lib/events-rotation.mjs:16,61,69,74), and a census of every shell rm/mv/unlink in scripts/, hooks/, .husky/, .gitlab-ci.yml and the guard's own 60-command test corpus found ZERO targeting this directory. Intentional maintenance has two named bypasses: `allow-destructive-ops: true` in Session Config (per session), or SO_DISABLED_HOOKS=pre-bash-destructive-guard in the environment of the whole session (session-level: the hook reads it from its own process env, so a `SO_DISABLED_HOOKS=… rm …` prefix inside the command has no effect). Covers `rm`, `mv` (both operand roles — the source vanishes, an existing destination is overwritten) and `unlink`, through transparent wrappers (sudo/env/timeout/nohup/nice/…), every chain position, and literal `bash -c` / `env -S` / `eval` payloads. NAMED CEILING (BV-004), each measured: `find … -delete`, `find … -exec rm`, `… | xargs rm`, `git rm <path>` and in-process `node -e fs.unlinkSync(…)` are NOT covered — in each the path is not lexically an operand of a resolvable destructive verb (see PATH_DESTRUCTIVE_VERBS in scripts/lib/command-blocker.mjs). REVISIT TRIGGER: any of those five shapes appearing in a real deletion incident or a transcript census. `.orchestrator/tmp/**` is outside this denylist by construction, not by an exemption clause, and the match is judged PER TARGET — a sibling tmp operand can never lift the verdict for a metrics operand. SPELLING CEILING (measured 2026-09-19 against this hook, one payload per row): the #1401 incident was an agent re-spelling a blocked command, so the four re-spellings were run back through the guard. Two were closed HERE by adding the exact entry `.orchestrator` to the denylist above — `rm -r .orchestrator` (ALLOW before: only the literal `rm -rf` pattern of `rm-rf-destructive` caught the -rf form, and `rm -r` is one character shorter) and `mv .orchestrator /tmp/x` (ALLOW before). That entry is EXACT, not a prefix: `.orchestrator/tmp/**` deletes stay ALLOW, re-measured after the change (`rm -rf .orchestrator/tmp/*`, `rm -rf .orchestrator/tmp`, `rm -f .orchestrator/tmp/scratch.json` → all ALLOW), so the widening adds no exemption clause that could be lifted. Three remain OPEN and are DECLARED, not papered over: (0) a TRAILING SLASH on the state dir — `rm -r .orchestrator/` → ALLOW (measured 2026-09-19, found while re-measuring this change): the operand keeps its slash and the exact entry `.orchestrator` does not match it, while a `.orchestrator/` entry would be read as a DIRECTORY-PREFIX glob (measured: `pathMatchesPattern('.orchestrator/tmp/x', '.orchestrator/') === true`) and would swallow the tmp exemption — so the fix belongs in operand normalisation in scripts/lib/command-blocker.mjs, never in this list; (1) a wildcard in a DIRECTORY segment — `rm -f .orchestrator/*/events.jsonl` — because the guard is lexical and never expands globs, so the operand it sees is the literal string `.orchestrator/*/events.jsonl`, which no concrete-path glob in this denylist matches; (2) a cwd-changing chain — `cd .orchestrator/metrics && rm -f events.jsonl` — because the guard does not track cwd across chain segments and the operand it sees is the bare `events.jsonl`. Closing either needs glob expansion or cwd tracking in scripts/lib/command-blocker.mjs, i.e. a matcher change, not a policy change. REVISIT TRIGGER for these two: either form appearing in a real deletion incident or a transcript census — or any widening of this denylist beyond exact paths, which would need a per-verb exemption for `.orchestrator/tmp/**` bound to the statement the matcher hit (widening without narrowing the bypass opens a hole the narrow form did not have, `.claude/rules/guard-design.md`).",
137
137
  "sources": ["#1401", "PSA-003"]
138
+ },
139
+ {
140
+ "id": "git-hooks-missing-in-worktree",
141
+ "type": "hooks-path-missing",
142
+ "pattern": "git commit|push in a linked worktree without its hooks directory",
143
+ "severity": "block",
144
+ "rationale": "This linked worktree runs NO git hooks: `core.hooksPath` is relative (husky: `.husky/_`), the main checkout has that directory, this worktree does not. pre-commit (gitleaks, owner-leakage, lint-staged), commit-msg (commitlint) and the pre-push full gate would all be skipped, with exit 0 and no output (#1732, measured 2026-10-22: a push went out without its gate). Fix, each step as its OWN command: (1) install the worktree's dependencies from its lockfile — `npm ci` in an npm repo, `pnpm install --frozen-lockfile` in a pnpm repo; (2) `npx husky`; (3) check that the hooks directory now exists; then retry the commit or push.",
145
+ "sources": ["#1732"]
138
146
  }
139
147
  ]
140
148
  }
@@ -0,0 +1,30 @@
1
+ {
2
+ "version": 1,
3
+ "rationale": "Paths kept out of the curated public GitHub mirror (mirror: curated, #1631/#1594). GitLab stays the full private repo; scripts/github-export.mjs builds one snapshot commit without these paths, and check-owner-leakage.mjs --public-export fails if any of them survives. Each path is an exact file or a directory prefix ending in '/'; no globs.",
4
+ "exclude": [
5
+ { "path": "docs/audits/", "reason": "Audit reports may quote third-party and employer material that is not ours to publish.", "issue": "#1631" },
6
+ { "path": ".orchestrator/peers/", "reason": "Per-user peer preference cards; host-local working data, not product source.", "issue": "#1594" },
7
+ { "path": ".vault.yaml", "reason": "Private knowledge-vault registration; meaningful only to the owner's vault tooling.", "issue": "#1594" },
8
+ { "path": "docs/handover/", "reason": "Session handover notes; internal working state.", "issue": "#1594" },
9
+ { "path": "docs/plans/", "reason": "Internal execution plans; working documents, not documentation.", "issue": "#1594" },
10
+ { "path": "docs/proposals/", "reason": "Internal proposals; working documents, not documentation.", "issue": "#1594" },
11
+ { "path": "docs/prd/", "reason": "Internal product requirement documents (plan-default-visibility: internal); they describe private repositories and fleet state.", "issue": "#1631" },
12
+ { "path": "docs/sunset-review/", "reason": "Internal sunset reviews; working documents, not documentation.", "issue": "#1594" },
13
+ { "path": "marketing/", "reason": "Campaign working material; not part of the published plugin.", "issue": "#1594" },
14
+ { "path": "CODEOWNERS", "reason": "GitLab code-owner file; the account it names is a different person on GitHub.", "issue": "#1594" },
15
+ { "path": ".semgrep.yml", "reason": "Semgrep rules for the private GitLab pipeline; nothing on GitHub reads them.", "issue": "#1594" },
16
+ { "path": "assets/marketing/", "reason": "Campaign working material; not part of the plugin and already excluded from the npm package.", "issue": "#1594" },
17
+ { "path": "site/brand/a-leitstand/", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" },
18
+ { "path": "site/brand/a2-agentenmodule/", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" },
19
+ { "path": "site/brand/b-offenes-s/", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" },
20
+ { "path": "site/brand/c-werkbank/", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" },
21
+ { "path": "site/brand/output/", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" },
22
+ { "path": "site/brand/index.html", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" },
23
+ { "path": "site/brand/initial-directions.html", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" },
24
+ { "path": "site/brand/a-refinement.html", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" },
25
+ { "path": "site/brand/render.html", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" },
26
+ { "path": "site/brand/README.md", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" },
27
+ { "path": "site/brand/geometry.json", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" },
28
+ { "path": "site/brand/preferred.json", "reason": "Logo design studies and their working files; the site and the README use only site/brand/a3-terminalmodule/ and FONT-LICENSE.txt.", "issue": "#1594" }
29
+ ]
30
+ }
package/CHANGELOG.md CHANGED
@@ -7,6 +7,183 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [5.13.0] - 2026-10-23
11
+
12
+ ### Added
13
+
14
+ - **Validate check `check-test-posix-shell`** (#1613, BLOCKING): `validate-plugin` fails when a test under `tests/` spawns `sh`, `bash`, `/bin/sh` or `/bin/bash` by bare name instead of through `posixShell()` from `tests/_helpers/posix-shell.mjs`. On native Windows `sh` is not on `PATH` and `bash` is WSL, which cannot see Windows paths; the native run at `fd8d3673` counted 301 red tests in this class. The existing sites are baselined (WARN) until #1613 converts them, and a baseline entry without a finding fails as `baseline-stale`.
15
+ - **Guard rule `git-hooks-missing-in-worktree`** (#1732): the destructive-command guard now refuses `git commit` and `git push` in a linked worktree whose relative `core.hooksPath` (husky: `.husky/_`) exists in the main checkout but not in the worktree. `npm ci` with `ignore-scripts=true` never runs husky's `prepare`, so such a worktree ran no pre-commit, commit-msg or pre-push hook, with exit 0 and no output; on 2026-10-22 a push went out without its full gate that way. The deny names the fix, each step as its own command: install the dependencies from the lockfile (`npm ci` / `pnpm install --frozen-lockfile`), then `npx husky`. It does not list the forms the rule lets through. The directory follows the payload cwd, literal `cd <dir>` and `git -C <dir>`; anything the guard cannot read (a variable, a `cd` inside a subshell or command substitution, `--git-dir`, `-c core.hooksPath=…`) is not judged, and neither is an explicit skip (`--no-verify`, `commit -n` or `-nm`, `HUSKY=0`). The policy now has 13 blocking and 4 warning rules.
16
+ - **Snapshot probe for the curated GitHub export** (#1712): `node scripts/snapshot-probe.mjs --host <alias>` checks out the exact tree the curated export would publish, outside the repo, as a one-commit repo without `origin`. It runs `validate-plugin` locally and the full gate on the remote host, and writes a receipt keyed by the snapshot tree under `.orchestrator/tmp/snapshot-probe/`. Exit 3 means the snapshot is red. Exit 2 means the suite did not run or did not finish (unknown host, fallback, gate timeout); in that case no receipt is written. `writeSnapshotTree()` is extracted from `buildGithubExport`, so the probe tests the tree the export publishes. `runRemoteGate()` is extracted from the pre-push router; its defaults leave the hook unchanged.
17
+ - **Release preflight rows `snapshot-probe` and `npm-token-publish`** (#1712):
18
+ - Under `mirror: curated`, `release.mjs --check` passes only with a green probe receipt for the release commit's snapshot tree that includes a suite run.
19
+ - `npm-token-publish` reads `npm token list` and goes red when the token in use lacks Bypass-2FA, write permission, a fitting scope or a valid expiry. This is the EOTP case of 5.13.0, where `whoami` was green.
20
+ - An EOTP publish failure now says that nothing was published.
21
+ - **Workflow lint `check-workflow-continue-on-error`** (#1712): `validate-plugin` rejects job-level `continue-on-error` in `.github/workflows/`, because it keeps only the workflow green while the job's check run still concludes failure. The informational Windows smoke now tolerates its individual steps and reports a failure as a `::warning` annotation.
22
+ - **Validate check `check-metrics-ledger-root`** (#1722, BLOCKING): every line under `scripts/`, `hooks/` and `skills/` that builds a `.orchestrator/metrics` path must call a resolver in the same statement, declare a relative UPPER_CASE constant whose joins are checked too, or carry a `// ledger-root: <reason>` marker. Deliberately local modules sit on a reasoned allowlist that fails when an entry goes stale.
23
+ - Explicit `--evidence-candidates` and `--source-repo` options for the learning index. Candidate artifacts resolve to current canonical records before existing selection policies; the feature is off by default and reports fallback diagnostics separately. See `docs/evidence-candidates.md`.
24
+ - **Native Windows smoke and a non-blocking `windows-latest` job** (#1612): `scripts/ci/windows-smoke.mjs` runs the gate process probes (captured UTF-8 output, exit codes, a missing command, a timeout that must end a child and two grandchildren, literal argv with spaces, `'`, `%PATH%` and `&`), every `hooks/hooks.json` command through Git Bash, the destructive-guard deny and five first-run SessionStarts, with registry, telemetry and vault redirected to a throwaway config directory. 49/49 on Windows 11 with Node 24.14 and Windows PowerShell 5.1; on POSIX it prints `nur win32` and exits 0. Two GitHub jobs on `windows-latest`, both `continue-on-error` and outside `gate`: `test-windows-smoke` (lint, typecheck, the smoke; moves into `gate` after ten green main runs) and `test-windows-suite` (the full suite with the fail-closed JSON verifier, informational while the test-portability backlog is open).
25
+ - **Portfolio audit 2026-10-12** (`docs/audits/2026-10-12-portfolio-analyse/`): fleet telemetry over 69 projects and 4,500 session records, GitLab activity on both hosts, the learnings corpus, the lead/navigator layer, the instruction budget and the native Claude Code overlap, consolidated into 15 accepted and 7 rejected decisions (E1 to E15, V1 to V7) plus an executed issue plan (#1583 to #1588 opened, 12 issues closed or folded, 21 labels normalised).
26
+ - **`isMeasuredSession(rec)`** in `scripts/lib/sessions-canonical.mjs` (E1, #1584): one predicate for "a session that actually ran", excluding backfill stubs, `_synthetic` records and typeless zero-wave records. Wired into `build-live-signals` (so the mode-selector no longer counts stubs), the dispatcher staleness ranking, `site-numbers` and the cold-start detector via `countSessionsInJsonl(raw, { measuredOnly: true })`. Fleet census 2026-10-12: 59.1 % of all session records were stubs, and `isRealSession` alone still passed 49 of them. For the same reason the session count on the website drops from 481 to 253 with this release (`site/_census.json`, counted 2026-10-20 at `95136cb8`): the old figure counted the stubs.
27
+ - **`projects-root`, a host-local scan root** (#1562): the cross-repo confinement and scan root (`getConfinementRoot()`, used by the host-wide session sweep for the vault live-status board, `vault-integration-watcher`, `run-migrate-v2-cross-repo` and `promote-vault-strict`) can now point somewhere other than `~/Projects`. Set it with the env variable `SO_PROJECTS_ROOT` or with `paths.projects-root` in `owner.yaml`; the env variable wins over `owner.yaml`, which wins over `~/Projects`, and the test-only `CROSS_REPO_CONFINEMENT_ROOT` still outranks both. A leading `~` is expanded; a value that is still not absolute is ignored with one stderr warning naming its source and is never resolved against the working directory. It is deliberately not a Session Config key, so a personal absolute path never lands in a committed `CLAUDE.md`.
28
+ - **so-hud protection gates** (#1551): so-hud 0.2.0 checks shell commands (`Bash`, `Monitor`, `PowerShell`) and file edits before they run and tool results before they reach the model, and the `strictness` profile (`locker`, `ausgewogen`, `streng`) now changes behaviour. **This is a behaviour change for installed 0.1.0 users: outward actions now ask twice by default, secret-printing commands are refused, and on the default profile `ausgewogen` rule traps such as `git add -A` are refused too.** Commands that would print a secret (SEC-008: `cat .env`, a bare `env`, `echo $TOKEN`, `docker inspect` without `--format`, a token in curl arguments or a curl URL) are refused on every profile, with the safe replacement in the refusal. Rule traps (`git add .`/`-A`/`--all`, `claude -p` without `env -u ANTHROPIC_API_KEY`, `${PIPESTATUS[`, `grep -c … || echo 0`) warn on `locker` and are refused otherwise. Outward actions (push to `main`/`master`, force, tags or delete; `glab mr merge`/`glab mr accept`/`gh pr merge` and API writes to `main`; release create; package publish; production deploys on Vercel, Supabase, Fly and Railway) are held wherever a surface is attached (terminal, desktop, IDE, phone) until the owner confirms in two questions within 30 seconds, then run exactly once. Only a plain feature-branch push runs without a question — `git push [-u] origin feat/x`, `git push [-u] origin HEAD` or a bare `git push`, when every earlier command on the line is a git call from a local allowlist (`fetch` with no option and no `::` word; `log`, `show` and `diff` with display options only, so `--output`, `--ext-diff`, `--textconv` and `--upload-pack` ask); any earlier command that is not git, and any earlier output redirection other than onto exactly `/dev/null` or between stdout and stderr (also on a git step, `cd`, an assignment or `exec`), makes every push form ask, and after `add`, `commit`, `status`, `fetch` or `checkout -- <paths>` on the line a push asks unless so-hud measured that no git hook can run (`core.hooksPath` and every `hook.*` config key unset, no hook file without `.sample`) and the step ran in the push's own folder as written (`git -C ../b commit && git push …` asks; in a husky repository `git commit … && git push …` on one line therefore asks) — with exact options, nothing earlier on the line that changes the branch, the folder or git config, and the standard push configuration (#1569); lowercase `head`, `--no-verify`, push options (`-o`), abbreviated options and every other shape ask. Outward commands are recognised inside `bash -c`, `eval`, `gh`/`glab api …/merge` and workspace publishes, and a line that hides an outward word in a construct the parser cannot read (`$(…)`, backticks, `xargs`, shell functions) asks too. A run with no surface at all (a plain `claude -p`) cannot ask: so-hud writes a receipt and lets the call run, and there merges, releases, publishes and deploys are checked by no Session Orchestrator gate. An edit of a file in another live Session Orchestrator session's wave scope in the same working copy warns, asks or is refused by profile; sessions that do not run Session Orchestrator are invisible to this check. Tokens in tool results reach the model as `[<kind> entfernt · sha256:<12 hex>]`. Each decision on an outward action appends one receipt per held action, and each asked collision one receipt (a hash of tool and command or file path, never the command), to `~/.config/session-orchestrator/hud/aktionen.jsonl`. If the gate fails, the call is refused, except on `locker` for calls that are neither outward nor SEC-008. The command-line gates stay authoritative where they exist. `scripts/so-hud.mjs --repo-root` now resolves to the git toplevel, so a session started in a subdirectory reads its wave scope and its peers correctly. Details: `docs/mods.md`.
29
+ - **so-hud profile band and `/profile`** (#1552): until a profile is chosen, a band above the prompt shows the current one (`Profil: ausgewogen (Vorgabe, noch nicht gewählt)`), its effect, what holds on every profile, and one button per profile. The buttons have no hotkeys, because a digit typed into an empty prompt would press one. A press saves the choice in so-hud's own `strictness` row of `/config` (plus the store key `profile-chosen`), and every new session starts with it. `/profile` shows profile, effect and buttons without a model turn; its text is stored in the conversation like any command output, and `Schließen` hides the band for the session. Band and command work without a Session Orchestrator installation. The profile steers only so-hud's own gates, not Session Orchestrator's guards.
30
+ - **Curated GitHub mirror** (`mirror: curated`, #1631, #1594, #1645): `scripts/github-export.mjs` builds one squashed snapshot commit with a `Source-Commit:` trailer and leaves out the paths listed in `.orchestrator/policy/public-export.json`. It refuses when an excluded path survives, the leakage scan fails or `github/main` moved outside the export. Session-end pushes that snapshot from main, never HEAD. `scripts/release.mjs` tags the release commit on GitLab and its snapshot on GitHub, publishes to GitHub only through the export and honours `mirror: none`.
31
+ - **Inbox store role for learnings** (#1516, #1685, #1686): a host with `learnings.store-role: inbox` in `owner.yaml` (or `SO_LEARNINGS_STORE_ROLE=inbox`) appends new learnings to `learnings-inbox.jsonl` and never rewrites `learnings.jsonl`. Readers, sweeps, migrations, the backfill and the dialectic readers see both files. The writer host ingests inbox records with a receipt, and a sweep that crashed before its receipt is healed on the next run. The role resolves fail-closed, and an unknown key in the `owner.yaml` `learnings` section is rejected.
32
+ - **Session-end lists open `[Abnahme]` issues** (#1678): acceptance issues linked to the plan or epic appear with owner and not-before date in their own "Abnahme offen" section and never count as completed. They close only on oracle evidence, not from a `Closes` trailer.
33
+ - **Open handover points carry an owner-queue mark** (#1639): `scripts/handover-queue-check.mjs` finds open points in a handover without `queue:#<id>` or `issue:#<iid>` and, with `--apply`, files them through `owner-queue add` and writes the mark. Measured on 2026-10-15: 35 of 111 open points in fleet handovers were recorded nowhere.
34
+ - **Session start ref per own work tree** (#1659, #1668, #1652): the start event records the ref of the session's own tree, `emit-session` drops a start ref from an outdated checkout, and a wave with 0 agents counts as coordinator-direct.
35
+ - **`SO_GATE_HOSTS` for the pre-push gate** (#1623, #1625, #1626, #1627): `SO_GATE_HOSTS=<alias,...>` replaces the configured host order for one push; an unknown alias, or a list none of whose hosts can run the gate, blocks with exit 2 instead of falling back to the local gate. A host counts as ready by its `gate-ready` token, not by `offload doctor`'s exit code. After the gate passes, each pushed ref is resolved again and a moved ref warns. The so-hud p95 timing bound warns instead of failing.
36
+
37
+ ### Changed
38
+
39
+ - **`/plan` gains a pre-mortem panel, an oracle per acceptance criterion and separate acceptance issues** (#1677).
40
+ - **New Phase 4.5:** two read-only briefs, Refute and Deepen (`skills/plan/prd-premortem-panel.md`), run against the code before the PRD reviewer. Their re-checked predictions go into § Risks, and every acceptance criterion gets an `Oracle:` line.
41
+ - **External Acceptance:** criteria whose oracle cannot run in the implementing session become `[Abnahme]` issues with owner, date and fallback. The implementing issue does not close them.
42
+ - **Reviewer:** gains criterion 8, Verifiability. "Not a pessimist" becomes "not speculative, carry an evidence pointer".
43
+ - **Research and templates:** research starts from raw material (reproduce the premise, census local equivalents). The feature template gains an Evidence Baseline and a First Real Use / Kill line. The Wave 1 scope question asks what shows "done" before it asks for the appetite.
44
+ - **Removed:** the Opportunity Score is replaced by a ranking on measured signal.
45
+ - **Autonomous runs:** a path for runs without AskUserQuestion.
46
+ - **Fixes:** `/plan new` meets the PRD commit gate in the new repo, and two drifts are gone (the stale repo count, the missing 2×2 triage in `mode-new.md`).
47
+ - **Measured basis (2026-10-19):** three shipped PRDs, replayed in history-truncated clones, anticipated 0 of 43 later surprises. Each brief alone hit 7–8, both together 11. Real-world acceptance stayed undone in 3 of 3.
48
+ - **A gate command that is not found fails instead of being skipped** (#1612, reported in GH#76 by dviolante): `runCheck` reports exit 127 (and 9009 from a batch file on Windows) as `fail` with a hint to fix the command or set it to `skip` explicitly; Full Gate no longer passes a session whose configured test command never ran. An explicit `skip` and the visible stub markers are unchanged. On Windows `cmd /c` exits 1 for an unknown command, which was already a `fail`.
49
+ - **Claude Code starts the MCP server with Node** (#1612): `.claude-plugin/plugin.json` declares `session-orchestrator` as `node ${CLAUDE_PLUGIN_ROOT}/scripts/mcp-server.mjs`, which replaces the `bash -c` entry from `.mcp.json` for Claude Code only (the manifest's server of the same name wins). On native Windows `bash` resolved to the WSL launcher and the server never started. Codex, Cursor and Pi keep `.mcp.json`. Both servers answered `initialize`, `tools/list`, `session_config` and `session_metrics` byte-identically.
50
+ - **`post-edit-validate` reports a timeout as `timeout`** (#1612): the configured `typecheck-command` runs as one shell command instead of being split at spaces, and a run that exceeds the hook's 2 s budget is reported as `status: "timeout"` rather than `fail`.
51
+ - **The event webhook POST no longer uses `fetch`** (#1612): with `CLANK_EVENT_URL` and `CLANK_EVENT_SECRET` set, `emitEvent` still fires the POST without waiting for it (several events per hook at 3 s each would exceed the 5 s hook budget), now through `scripts/lib/http-request.mjs` with a 64 KiB response cap.
52
+ - **`issues_created` accepts qualified cross-repo references** (#1603): `issues_created` in `sessions.jsonl` now accepts `group/repo#n` (for example `products/apple-mail-kit#92`) next to plain numbers; record repair and the serializer are unchanged. The vault-mirror v3 renderer writes such a reference as it is instead of prefixing it with `#`, and still prefixes plain numbers.
53
+ - **AUQ-003 asks for every realistic option** (#1554): owner questions weigh all realistic options (not only two), give each one reason, cost and consequence, put the recommendation first as `(Recommended)` and carry one plain-language sentence without dropping a fact. Changed in both copies, `.claude/rules/ask-via-tool.md` and the consumer rule `rules/always-on/ask-via-tool.md`.
54
+ - **Always-on rules below 65 KB again** (#1583): the always-on value of the instruction-budget guard fell from 65,202 to 64,907 bytes (target ≤ 65,000) by tightening `ask-via-tool.md` and `loop-and-monitor.md` without dropping a rule ID, threshold, path or command; the measurement is appended to `docs/audits/2026-10-12-1583-instruction-budget.md`.
55
+ - **README head and install requirements** (#1595, #1596): the README opens with the one-line purpose, the image, who it is for and the prerequisites before the install table, which now appears once; internal shorthands and GitLab issue numbers are gone from the public text, and a CI badge for the GitHub mirror sits next to the version badge (which `scripts/release.mjs` keeps current). `docs/install.md` lists what stops working without a vault.
56
+ - **`gitlab-ops` points to `/templates-ack`** (#1593): the skill no longer documents writing `templates-acknowledged.json` by hand, which bypassed the invocation check from #1589/#1591 and used a schema the hook does not read.
57
+ - **Every agent declares model and effort** (#1614, #1615, #1616): the 10 agents that inherited the model now name it (opus for implementers and reviewers, sonnet for test, docs, QA and analyst, haiku for the judges), and all 14 carry `effort`. The Codex agent TOMLs in `.codex-plugin/agents/` use `gpt-6.1-sol` with reasoning effort medium (session-reviewer was `gpt-5.4` at high). A Codex user without access to that model changes the `model` line in those files. `session-reviewer` gains an ad-hoc mode for a named diff, branch or MR without a wave plan, and `code-implementer` may commit in its own worktree when the prompt carries a `WORKTREE-MODE:` line.
58
+ - **Pre-push falls back to a partial gate on MR branches** (#1642): when no remote host can run the gate, a push to an origin branch other than main runs `node --check`, ESLint on the changed files and `vitest related` locally; the MR pipeline then runs the full suite. Main, tags, deletions, the GitHub mirror and a changed package, vitest or ESLint config keep the full local gate. Non-ASCII paths are read correctly.
59
+ - **Instruction budget and drift-check follow the `paths:`-only rule scope** (#1638): Claude Code reads only `paths:` from rule frontmatter and silently loads a `globs:`-only rule always-on. The instruction-budget guard now counts by `paths:` alone, and drift-check Check 9 warns per rule with `globs:` but no `paths:`. Wave injection is unchanged.
60
+ - **cursor-agent starts with `--disable-codebase-ref`** (#1683), so no LSP processes stay behind after a Cursor wave.
61
+ - **Site body font is Source Sans 3** (#1514). The legal pages preload only declared fonts and load `site.css` with a cache key.
62
+
63
+ ### Fixed
64
+
65
+ - **Gate events report a process group reaped after a normal exit** (#1708): `orchestrator.quality_gate.{passed,failed}` now carries `leftover_groups` (`command`, `pgid`, `signals`, `survivors`) when a gate command left children behind (#1704). The per-command envelope already had the field, but `joinGateKillFields` dropped it, so the rate could not be measured before a leftover turns a gate red. The key is absent when no command had one.
66
+ - **Lock release, HUD and tmux pane find the session's own lock root** (#1730 items 1, 2 and 7). The new module `scripts/lib/own-lock-root.mjs` (`resolveOwnLockRootFor`) checks four candidates in order: the registry's `session_root`, the checkout of the cwd, its main checkout, and the registered linked worktrees. The first checkout whose `session.lock` carries the raw id wins.
67
+ - `release-session-lock.mjs` without `--repo-root` (as `/close` calls it) used to read only the cwd. With the lock in the other checkout it answered `absent` with exit 0 and the lock stayed. It now releases at the found root, and the JSON names it (`repo_root`, `root_source`). The #1651 marker goes to the lock's root, the cwd's checkout and the registry's `session_root` (existing checkouts only, `release_marker_roots`), because the root a later compaction reads depends on its own cwd.
68
+ - `so-hud --session-id <raw-uuid>`: agents, and the lock that dates them, are read beside the own lock. The mod passes `io.sessionId()`; a collector from before #1730 rejects the flag with exit 2, and the mod then measures without it for the rest of the session. The tmux status pane writes the found root into its command.
69
+ - Skill snippets for heartbeat (session-plan, wave loop, session-end), agent status and Phase 1.2 take the root from the resolver instead of `process.cwd()`. At the wrong root `updateHeartbeat` wrote nothing.
70
+ - **A `/clear` in another tab no longer takes the main checkout from a fleet lead** (#1746): the #1635 lead yield took the raw id in `.orchestrator/current-session.json` for the session's own predecessor, but every session start in the checkout rewrites that file. Once the lead had started there last, any other tab's `/clear` passed as the lead's own tab and took `session.lock`. The file now also records `claude_pid` and `host`. The predecessor id counts only when they name the same Claude process on this host (the #1641 witness); otherwise the session yields. The lead's own tab now also yields when the file carries no witness (no readable `CLAUDE_PID`, or written before this fix), which does not affect the lead, because it works through its lease.
71
+ - **A compaction after a lead yield no longer takes the lock back** (#1747): the #1635 yield now leaves the #1651 release marker for its own raw id, tagged `reason: "lead-yield"`, so a later `compact` or `resume` of the same session stays lockless while the lead's lease is active. Once the lease has ended, the next `compact` or `resume` takes the lock again and drops the marker; an explicit release (`release-session-lock.mjs`) keeps its #1651 behaviour. Before, `compact` is no yield source and found no marker, took the free `session.lock`, and the lead's `slot check` ended with exit 5 again. A session that held its lock from a `startup` before the lead keeps it across a compaction; the next deliberate `acquire()` removes the marker as before.
72
+ - **On Windows, `fleet-checkin` no longer overwrites a check-in owned by another repo** (#1744): Node has no `O_NOFOLLOW`/`O_NONBLOCK` on win32, so the writer skipped its read of the existing target and replaced a foreign check-in with exit 0 instead of 2. The new `scripts/lib/no-follow-open.mjs` opens a regular file without following a final symlink on every platform: the POSIX flags where they exist, otherwise `lstat`, open and a bigint `dev`/`ino` comparison of the handle. A target swapped between check and open now ends the check-in with exit 2 (fail-closed); every other read failure keeps the existing behaviour. `fleet-checkin-audit` uses the same helper instead of refusing to run (`unsupported-safe-open`), and reports a swap as `changed-input-file`.
73
+ - **Windows filesystem primitives behave like POSIX** (#1744): memory-proposal rotation no longer fails with `fs-error` (no directory `fsync` on win32, which rejects it with EPERM); a directory in place of the events or sessions ledger reads as unreadable instead of empty in `scanEventsBackwards` and `backfill-abandoned-sessions`; a baseline path below a regular file is `unsafe-source` instead of `producer-missing`; the auto-dream drift check tolerates a named 5 ms skew between the system clock and filesystem timestamps (Windows stamped MEMORY.md up to 1 ms ahead).
74
+ - **`/close` no longer hands a STATE.md whose owner it cannot prove to session-end** (#1718): new stop verdict `ownership-unproven` (fields `wouldRun`, `confirmToken`, `session`, `startedAt`) for a STATE.md without `session-id` and for processes without a native id. `scripts/close-precheck.mjs --confirm-own <confirmToken>` confirms the unchanged file and returns the original verdict with `ownership: confirmed`; a token from before the file changed returns `ownership-unproven` again. Field case (2026-10-21): a legacy STATE.md of an older, already closed plan read `complete-missing-record`, and session-end would have closed a foreign plan.
75
+ - **Behaviour change: on Cursor, Pi and Codex without a thread id each `/close` now asks once for confirmation, because these platforms expose no native session id.**
76
+ - **Ledgers written or read from a linked worktree reach the right copy** (#1722, #1725): the `.orchestrator/metrics/*.jsonl` ledgers are gitignored in this repo, so a writer that joined them to the cwd, `getProjectDir()` or an unmapped repo root left a copy in the worktree that was lost with it, and its readers saw an empty ledger. Measured before the fix: 43 of 62 sites in 54 modules, `eval.jsonl` and `skill-invocations.jsonl` stranded in live worktrees.
77
+ - New contract `scripts/lib/ledger-path.mjs`: `resolveLedgerPath()` / `resolveLedgerRoot()` map to the main checkout only when the root is a linked worktree's top level and `git check-ignore` reports the ledger ignored there (one memoised spawn, inside worktrees only). A versioned ledger stays on the branch, which is what the 27 fleet repos that track their ledgers need. `events.jsonl` and `sessions.jsonl` keep the unconditional mapping of #1514/#1584.
78
+ - 56 modules route their default ledger path through it (counted 2026-10-22T00:33Z): eval, skill-invocations, skill-judgments, subagents, learnings and archive, proposals with their lock, overflow, summaries and sidecars, autopilot, discovery triage, reaper audit, test-runs rollup, ux-grill, harness audit, the discovery probes and the drift checker. Explicit paths stay as given, and inside-root validators accept the mapped root without accepting the rest of the main checkout. The telemetry batch resolves each ledger of an explicit metrics dir on its own.
79
+ - Six skills snippets now use the resolver, and `session-metrics-write.md` no longer passes `--file .orchestrator/metrics/sessions.jsonl`, which `emit-session` takes verbatim.
80
+ - **The release CI row names every red check run** (#1712): `checkCiStatus` adds `failingJobNames` next to `failingJobName`, and the GitHub check-runs request asks for 100 per page instead of 30.
81
+ - **Worktree sessions read the issue-budget counter the hook wrote** (#1729): the PreToolUse hook charges it under the launch directory (usually the main checkout), session-end reconciled and reaped it under the linked worktree. Every worktree session that filed an issue got a false `no-ledger` verdict, and the reaper swept an empty directory. `budgetStatePath()`, the legacy one-file seed and the reaper now root at `resolveEventsLedgerRoot()`: a linked worktree maps to its main checkout, other roots stay unchanged. Counter files a worktree session wrote before this fix are no longer read; they are per-session and short-lived.
82
+ - **`/templates-ack` works inside a linked worktree** (#1729): the command wrote the acknowledgement under the cwd, the `pre-bash-templates-first` hook read it under `CLAUDE_PROJECT_DIR`, so create commands stayed blocked after the acknowledgement. Both sides now resolve a relative ack file under `resolveEventsLedgerRoot()`, and the writer stops with an error instead of falling back to the cwd when the resolver cannot be loaded. Still open: run from a subdirectory of the checkout, the writer keeps that subdirectory while the hook reads at the checkout top.
83
+ - **The rule expiry sweep resolves archived learnings** (#1711): ids that moved to `learnings-archive.jsonl` no longer stay unresolved forever. The live store wins on a duplicate id. Superseded and merged tombstones stay out of the index.
84
+ - **The GitLab CI row sees past the 20th job** (#1716): both job queries of a GitLab pipeline in `checkCiStatus` ask for 100 per page instead of the API default of 20, so a failed job at position 21 or later is named again in `failingJobName`, `failingJobNames` and `allowFailureJobs`. A pipeline with more than 100 jobs still truncates (named ceiling).
85
+ - **Claude hooks keep the Claude process as their parent under dash** (#1717): every `run-node.sh` command in `hooks/hooks.json` now starts with `exec sh …`. dash 0.5.12, `/bin/sh` on the measured WSL Ubuntu host, forks a single `sh -c` command instead of exec'ing it, so `readClaudePid()` read `null` there and the #1641 own-process lock reclaim after `/clear` never fired. The Codex, Cursor and Pi manifests keep the plain form.
86
+ - **A signal no longer leaves the snapshot probe's tree in `TMPDIR`** (#1715): `process-group.mjs` exports `registerExitCleanup(fn)`, which runs synchronous callbacks after the live process groups are killed, on `exit` and right before a handled SIGINT/SIGTERM/SIGHUP is re-raised. The probe registers its own `mkdtemp` directory only. SIGKILL still runs nothing.
87
+ - **Reconcile verdicts survive fresh worktrees** (#1710): the idempotency sidecar `.orchestrator/runtime/reconcile-candidates.jsonl` is gitignored and checkout-local, so in a fresh worktree an operator's `rejected` verdict was gone and `/reconcile` proposed the declined rule again, while the session-start nudge said "no reconcile run on record" or "N new learnings since the last reconcile run". Decisions now also go to the append-only ledger `.orchestrator/metrics/reconcile-verdicts.jsonl`, resolved via `resolveEventsLedgerRoot` (a linked worktree maps to its main checkout). `mergeCandidates` mirrors each terminal decision (`rejected`, `expired-swept`) there once, which backfills older sidecar decisions on the first merge; the branch-scoped `written` and `already-on-disk` outcomes stay out, because the tracked rule file's provenance marker already witnesses them. The engine appends one run record per non-dry run with the number of learnings it loaded, and the nudge takes run recency (from run records only, never from a verdict) and its "new learnings" baseline from it. A garbled ledger line (once per path and count per process), a failed ledger append and a failed run append each print a stderr WARN. A decision stamped over a kept `written` or `already-on-disk` record (the rule-drop recipe, the expiry sweep's delete) is appended by `markCandidateProcessed` directly, because `mergeCandidates` keeps the branch-scoped record there. The ledger is written only for an explicit `repoRoot`, never a `process.cwd()` fallback. Across hosts the verdicts travel only where `.orchestrator/metrics/` is tracked: here it is gitignored (`.orchestrator/metrics/*.jsonl`), in the vault after its `merge=union` attribute and replica-sync pathspec follow-up (outside this repo).
88
+ - **The snapshot probe's exit-path delete retries** (#1723): the exit handler sends SIGKILL to the gate's process group without awaiting it, so a vitest worker could still write while the probe deleted its tree, and `ENOTEMPTY` left a rest in `TMPDIR`. The registered delete now retries up to 3 times with a real synchronous pause of 50, 100 and 150 ms (`Atomics.wait`), because Node 24's `rmSync` truncates `retryDelay` to whole seconds (measured on v24.22.0: 3 retries at 50 ms waited 0 ms, at 400 ms 2004 ms over two failing operations). The comments in `process-group.mjs` now say signalled, not awaited.
89
+ - **The GitHub CI row reads the local HEAD, not main** (#1727): `checkCiStatus` asked the GitHub API for the literal ref `HEAD`, which GitHub resolves to the tip of the remote default branch. On a feature branch or with unpushed commits, the session-start banner therefore reported main's verdict as the verdict for HEAD, green included. It now queries the commit `git rev-parse HEAD` names, as the GitLab path always did. A commit the remote does not know (HTTP 422 "No commit found for SHA", e.g. unpushed) reads `unknown` with reason `head-sha-not-on-remote` and triggers the existing pushed-commit follow-up, which on GitHub also skips its re-query now when the pushed commit is HEAD.
90
+ - **The GitHub CI row names the last green commit** (#856): a red GitHub reading of the local HEAD carries `redCount` and, when a green commit lies within 16 commits of HEAD, `lastGreen` (`sha`, `ageCommits`, `ageDays`), read from one `gh api graphql` history query with each commit's `statusCheckRollup`. Commits without any CI are skipped. GitHub has no pipelines, so `lastGreen` carries no `pipelineId`, and the banner says `last green: commit <sha7> (N commits ago)` without a pipeline number. If the history query fails, the reading stays red with reason `last-green-query-failed`. Readings for an explicit `sha` (the pushed-commit follow-up, the release preflight) skip the query: nothing reads the fields there, and it would push the follow-up past the probe budget. Named ceilings: the rollup also counts legacy commit statuses while the HEAD verdict counts check runs only, and the history follows all parents, so `N commits ago` can include commits of merged branches.
91
+ - **A red GitHub CI row survives a slow history query** (#1728): with the history query inside `checkCiStatus`, a red GitHub reading took 1.6–1.7 s of the 2000 ms session-start probe budget (measured 2026-10-22), and an overrun replaced the 🚨 line with the generic "timed out" notice. The probe now calls `checkCiStatus({ deferHistory: true })`, which returns the red verdict without the history (reason `last-green-deferred`). Its follow-up delivers that verdict first and then runs only the history query through the new export `readDeferredGithubHistory()`. If the budget runs out, the red line stays and says `last green: unknown (history not read within the probe budget)`. Probe follow-ups receive the probe's imported module as a fifth argument. Every other caller keeps the inline history (named ceiling: `dispatcher/rank.mjs` still pays the query on red).
92
+ - **The Bash guards now run on Codex** (#1691): `hooks/hooks-codex.json` registers `pre-bash-destructive-guard`, `pre-bash-sessions-ledger-guard`, `pre-bash-issue-budget`, `enforce-commands` and `pre-bash-memory-propose-audit` as `PreToolUse` Bash hooks and `post-bash-write-verify` as a `PostToolUse` Bash hook. Measured on 2026-10-20 with codex-cli 0.160.1: `codex exec` honours a deny (`printf x > CLAUDE.md` was blocked and the file stayed unchanged; without the hook it became `x`), and a run with the whole registered group allowed `ls` and blocked the redirect. `pre-bash-templates-first` stays unwired, because Codex sends `transcript_path: null` and the guard could then only deny. `tests/hooks/codex-bash-guards-wiring.test.mjs` runs every registered command on the measured payload.
93
+ - **so-hud reports a secret printed through a heredoc** (#1696): `cat <<EOF` with `$T_TOKEN` in an unquoted body now raises the SEC-008 trap on every profile, through a new single-pass scanner (`mods/so-hud/hooks/heredoc-scan.ts`). It covers several heredocs per line, a body without closing line, delimiters such as `<<1` or `<<END!`, `$'…'` and `${x:-'y'}` quoting, decoys in quotes, arithmetic and comments, `<<-` and `\` continuations, and fails closed on unbalanced quotes and more than 16 heredocs per line. Writing to a plain file (`cat > ~/.npmrc <<EOF`, `tee .env >/dev/null <<EOF`) stays allowed. Device paths (`//dev/stdout`, `..`), MULTIOS, `1<&2` and a moved cwd or HOME withdraw that exemption. The runtime is linear: about 40 ms at 100,000 characters, against 14.5 s for the reverted first attempt at 50,000. Two security reviews found 6 and then 2 bypasses. The remaining ones are deliberate obfuscation or older classes and are tracked in #1706.
94
+ - **The full gate's timeout re-run now fires for merged timeout blocks** (#1673): vitest numbers its errors `[k/N]` before it merges identical ones, so a `beforeEach` or `it.each` timeout printed two blocks under `[1/4]` `[2/4]` and the parser refused the re-run (`parsed-2-of-4`). `extractTimeoutOnlyFailedFiles` now counts FAIL headers against N. Trailing text after `timed out in Nms.` makes a run ineligible.
95
+ - **so-hud linear-time tests hold under bursty host load** (#1673): the growth helper interleaves small and big runs and keeps the best of up to four rounds. Measured A/B in one process: the old helper gave 5 false reds in 140 trials, the new one 0 in 280; a quadratic mutant still reads 43 to 63 against the bound 32.
96
+ - **Session record repair** (#1699): an `agents_total` that disagrees with the `agent_summary` sum is flagged (`_total_agents_conflict`), and `completed_at` taken from a valid `ended_at` and `total_waves` taken from a numeric `waves` no longer count as incomplete.
97
+ - **so-hud sees more ways a command prints a secret** (#1699, #1706): SEC-008 now traps zsh `print`, a secret carried through an assignment or `export` (also into `sh -c '…'`), a printing function body, `builtin echo`, zsh parameter flags such as `${(U)…}`, `printenv`/`declare -p` of a tainted name, `curl -K -` with `-v`, `--trace*` or a `-w` format that prints request data, a written file that is read back or sourced, and `$(printenv …)` in a heredoc body. A harmless `cd ~/repo` before `cat > .env <<EOF` keeps the file exemption; device roots are compared case-folded (APFS reaches `/dev` via `/DEV`), and a second `cd` operand or any `CDPATH` withdraws it. `-w '%{http_code}'` stays quiet. Two security reviews ran against a 64,181-command transcript corpus; remaining forms are tracked in a follow-up.
98
+ - **The destructive guard sees a delete inside a zsh anonymous function or a glued brace** (#1699): `function { unlink … }`, `{unlink …;}` and `f() {unlink …;}` run in zsh and were allowed. Old and new parser agree on all 40,215 unique Bash commands of the transcript corpus.
99
+ - **A process group that outlives a normal exit is reaped** (#1704): after exit 0, `spawnInGroup` probes the group and runs the existing kill ladder on a leftover. Gate results show it as `leftoverGroup` / `leftover_group` plus one output line; pass/fail is unchanged until the rate is measured. Our own `npm test`, `npm run lint` and `npm run typecheck` leave nothing behind.
100
+ - **Eight old ledger records lose a stale `completed_at` flag** (#1705): `repair-invalid-sessions` now heals a valid record whose `completed_at` copies a valid `ended_at` but is still listed as incomplete, keeping the prior list once in `_backfill_incomplete_fields_raw` (counted as `healed_valid`).
101
+ - **Bash guards judge a function body in command position** (#1679): `f() { …; }; f` is read like a bare brace group, and a compact function body counts as a delete target.
102
+ - **A Codex shell is detected by `CODEX_THREAD_ID` before the marker walk** (#1636).
103
+ - **The release preflight fails while the `github` push URL differs from its fetch URL** (#1693).
104
+ - **Session lock and STATE.md ownership** (#1605, #1618, #1622, #1630, #1634, #1635, #1636, #1640, #1641): a session reads and writes only its own STATE.md. Close never completes the STATE.md of a live or still-registered peer, and readers acting for the running session ignore a foreign one, also across harnesses. After `/clear` or `/resume` a session recognises its own predecessor lock by the Claude process id, yields the main checkout to an active fleet lead, and keeps its lock in a worktree it entered. Issue budget, relocation pointer, gate context and the session clock follow the session's own lock, also after `ExitWorktree`.
105
+ - **Bash guards share one tokenizer** (#1575, #1577, #1665, #1667, #1670, #1671): the destructive guard, scope gate, staging fence and ledger guard now see commands inside subshells, `$( )`, backticks and `case` arms. The staging fence no longer skips `git add` in commands over 120 characters. The ledger guard catches the repair CLI under bun, deno, npx and `nodejs` and admits an absolute `repair-invalid-sessions --apply` only for a repo with a ledger outside the vault. A guard error denies.
106
+ - **VCS repo-flag check** (#1024, #1672): `gh pr` subcommands with `-R` but no PR number are reported as their own finding, and heredocs, comments, redirects and non-static arguments are read like the shell reads them.
107
+ - **The end-of-session commit admits earlier waves' declared paths** (#1680, #1681, #1636): files an earlier wave of the same session changed no longer block the final commit. The admission is anchored on the session's first lock acquire and fails closed when the session clock is unknown.
108
+ - **Private config files under umask 000** (#1613, #1629, #1632): the private config directory is created 0700 and its files 0600, an unsafe directory is named as such instead of as a leak, and a UTF-8 BOM in host-written owner and names files is stripped.
109
+ - **Vault-mirror ids are schema-valid and unique across the vault** (#1601, #1602), without re-rendering existing notes.
110
+ - **A released lock stays released** (#1651): after compaction a deliberately released lock is not taken back, and `/close` writes the release marker when it finds no lock.
111
+ - **Session-end uses the lock id only when it is the session's own** (#1663, #1664): session id, lock fallback and the skill judge no longer pick up a foreign lock id. The start-wave detector counts waves, not events.
112
+ - **Resource warnings** (#1650, #1654): the standing peer count is no longer a soft signal; session start warns on a start wave instead.
113
+ - **Smaller fixes**: the drift-check foreign-glob probe skips globs that match the repo's tracked files (#1657); the discovery slop probes load their helpers from the plugin, not the scanned repo (#1653); the rule-sweep banner names only files it can act on (#1682); `handover-queue-check --apply` writes only the own handover (#1647); the update banner says that the Codex copy needs `npm run update:local` (#1578); the backfill does not run in a worktree without its main checkout, and hygiene reports a tracked ledger (#1666); config checks measure missing required fields, the portfolio and watcher use the projects root instead of `~/Projects` (#1567, #1619, #1620, #1621); `/templates-ack` binds the acknowledgement to its own invocation and refuses one for a foreign session (#1589, #1591); `/reconcile` rejects invalid sweep input before apply (#1579).
114
+ - **so-hud catches a secret read into a variable and two redirect forms that undo `>/dev/null`** (#1695, #1697, #1696). In `mods/so-hud/hooks/rules.ts`, the SEC-008 rule now flags printing a variable that a `read` at the end of a pipeline (zsh keeps it in the current shell) or a here-string `read` filled with a secret (`echo "$TOKEN" | read -r x; echo "$x"`, also through `sh -c "echo $x"` and `eval`), a redirected stage followed by a pipe (zsh MULTIOS: `echo "$TOKEN" >/dev/null | cat`), and an fd-1 `<&` (not fd 0) that points fd 1 back at the terminal (`echo "$TOKEN" >/dev/null 1<&2`).
115
+ - **The ledger repair reads the record's own legacy count fields** (#1698): a missing `total_agents` now takes the `agent_summary` sum, else a valid `agents_total`, else 0, and never the wave count (a record with `agents_total: 19` and five waves had been repaired to 5). A missing `total_files_changed` takes a valid `files_changed` instead of 0. A value taken from the alias is not listed in `_backfill_incomplete_fields`.
116
+ - **so-hud catches three more ways a secret reaches the transcript** (#1649). In `mods/so-hud/hooks/rules.ts`, the SEC-008 rule now flags:
117
+ - a group after the pipe whose first command is silent but whose next one prints (`| { true >/dev/null 2>&1; cat; }`);
118
+ - `read` inside a loop or group (`| while read -r l; do echo "$l"; done`);
119
+ - `echo "$TOKEN" >&2 | sha256sum`: a stage whose fd 1 is redirected (other than the no-op `>&1`) no longer hands the secret to the consumer behind it.
120
+
121
+ A whole group of silent commands (`| { sha256sum; }`) is now flagged as well (fail-closed, ceiling named in the code). `cut` without `-s` and a command with both outputs on `/dev/null` stay as they were. Detecting a secret in a heredoc body moved to #1696: two security reviews showed the attempt opened bypasses and ran quadratic, so heredocs behave as before.
122
+ - **`git-config-drift` names the right reason for an unreadable config** (#1694): after the `--local` read has succeeded, a later `git config` exit 128 is reported as `config-unreadable` instead of `not-a-git-repo`. A failing `ls-files` call of the `hooks-path-unset` check (for example a corrupt index) is reported as `git-error` and is now covered by a test.
123
+ - **Session start reports a checkout whose husky hooks never run** (#1692): the `git-config-drift` probe gains the finding `hooks-path-unset`. It fires when `core.hooksPath` is set in no git config scope although the repo tracks `.husky/<hook>` files. git then runs `<common-dir>/hooks`, which husky never writes, so commit and push run unchecked. This happened on a host clone for three MR pushes on 2026-10-20. The finding also appears in linked worktrees. Its repair command uses the repo's package manager (SEC-020), and a failed query is reported as unknown, never as healthy. A hooks path set only globally is not judged. Measured on one host on 2026-10-20: 4 of 31 husky repos have no hooks path. A `.husky/_` hooks path that is missing in the current worktree is reported too, with the target's package manager in the repair, and `check-package-manager` names it (#1688, #1662).
124
+ - **The full gate re-runs timeout-only failures serially** (#1673): under host load the pre-push gate on m5 blocked four pushes with four different red sets of timeouts, none in the pushed diff. When every failure of the parallel test run is a test or hook timeout (vitest's own wording, checked against its numbered error count; a timeout block carrying anything beyond the hint and frames, any line after the blocks other than section rules, the closing summary and `npm error` lines, an `Unhandled Errors` section or an `Errors N` summary line disqualify the run) and at most 10 files are involved, `gate-full.mjs` runs exactly those files once more through the gate's own test command, so config, projects and environment stay those of the first run: `npm test -- …` or `… vitest run …` with `--no-file-parallelism --reporter=default --reporter=json` appended. Other test-command shapes (pnpm, yarn, chained commands) and Windows temp paths get no re-run and stay red as before. The test step turns green only when that re-run exits 0 and its JSON report lists exactly the named files, each `passed`, with zero failed tests. An assertion failure is never re-run, nor is a first run that did not end with exit 1 after vitest's closing summary. The envelope keeps the first run's counts and adds `test.rerun` (`files`, `status`, counts); stderr names every re-run file; `orchestrator.quality_gate.*` carries `rerun` (`files`, `status`) from both the gate CLI and the remote pre-push route, whose verifier now accepts failed tests only behind a re-run with status `pass`, zero failures, no timeout, no signal, no survivor and exactly the first run's failing files. CI runs without the re-run and stays the strict instance.
125
+ - **`vault-narrative-staleness` skips passive projects** (#1690): a project folder with a `_passive.md` marker is skipped before any narrative is read, like in the sister probe `vault-staleness` (#1238). It no longer counts in `scanned_projects` but in `metrics.passive_skipped`, which the JSONL record carries too.
126
+ - **The learnings backfill names hidden inbox ids** (#1689): on an inbox host, `scripts/backfill-learnings-from-vault.mjs` reports an inbox id that is already receipted or archived with its own status `present-in-inbox-hidden` (summary counter `present_in_inbox_hidden`) instead of `present-in-store`, because no reader sees it. It still counts as present, so `--apply` appends nothing for it. Writer hosts are unchanged.
127
+ - **`pre-bash-staging-fence` runs on Codex** (#1669): the hook is now registered in `hooks/hooks-codex.json` (`PreToolUse`, matcher `Bash`) and no longer listed as a documented asymmetry in `check-hooks-symmetry.mjs`. Measured 2026-10-20 with codex-cli 0.160.1: a shell call arrives as `tool_name: "Bash"` and the matcher fires. The earlier exception rested on a `strings` probe that looked for a JSON-quoted literal the binary does not contain. The other Bash guards stay unwired on Codex until each is ported and its block path reviewed.
128
+ - **`/close` checks whose STATE.md it reads** (#1661): the pre-check is now `scripts/close-precheck.mjs` and compares STATE.md `session-id` with the own native id before it looks at `status`. A foreign STATE.md is never closed: an active one belonged to a peer in the same checkout, and session-end would have closed that peer's session; a completed one from before `/clear` used to read as "already finalized". A session without its own plan now ends `/close` with `Bereit zum Schließen.` when its own handover is written, otherwise with the wait sentence. An abandoned backfill stub no longer counts as a completed record.
129
+ - **Quality gates on native Windows** (#1612, reported in GH#76 by dviolante): `spawnInGroup` no longer spawns `detached` on Windows (captured output was empty and each gate opened a console window) and hides the window; internal gate calls pass argv instead of a POSIX-quoted string, so paths with spaces or `'` work under `cmd.exe`; a timeout ends the whole tree with `taskkill /T /F` and reads survivors back from a CIM snapshot (PID and creation time), reporting `survivors: null` when that cannot be measured instead of an empty list; stdout and stderr decode UTF-8 across chunk boundaries.
130
+ - **First SessionStart per project crashed on Windows** (#1612): `fetch()` followed by `process.exit()` aborts Node 24 on Windows with `Assertion failed: !(handle->flags & UV_HANDLE_CLOSING)` (6/6 in a minimal repro). The plugin-update probe hit it on every first start per project and day (8/8). The three hook-reachable `fetch` calls (update probe, event webhook, telemetry sync) now use `scripts/lib/http-request.mjs` (`node:https`, no keep-alive agent), and a test forbids `fetch` in every hook-reachable module.
131
+ - **`post-edit-validate` no longer installs an unrelated npm package** (#1612): its last fallback `npx tsc --noEmit` downloaded the unrelated `tsc` package in projects without TypeScript. It now runs only a local `node_modules/typescript/bin/tsc` through Node (no `.cmd` shim, so `%` in a path is not expanded by `cmd.exe`) and otherwise reports `skip`.
132
+ - **The test setup runs on Windows** (#1612, GH#76): dynamic imports of absolute paths go through `pathToFileURL` (seven places, guarded by a test), the guard-requires parity check compares `scripts/lib` paths separator-neutrally, and the owner-leakage policy loads on Windows, where every file mode reads `0o666` (the POSIX permission checks now apply on POSIX only). `node scripts/validate-plugin.mjs` on Windows 11: 239 passed, 0 failed (before: 219/4).
133
+ - **No per-run ledger warning on Windows** (#1612): the gate-process ledger is not written on Windows (its only reader, the orphan reaper, is POSIX-only), so every gate run no longer prints two warnings.
134
+ - **A `--config` file with a UTF-8 BOM is honoured** (#1612): `run-quality-gate.mjs` rejected a config file written by Windows PowerShell 5.1 (`Set-Content -Encoding utf8` adds a BOM), warned, and ran the npm default commands instead of the Session Config. Found in a native end-to-end run with Claude Code 2.1.296.
135
+ - **Validators accept an inline `mcpServers` object** (#1612): `check-component-paths` and `check-json-files` crashed on the manifest's object and array forms.
136
+ - **so-hud asks for an outward action it cannot read** (#1570, folding in #1571, #1572, #1574): an outward action behind a wrapper or option so-hud did not know (`time -p`, `command -p`, `exec -a`, `nohup --`, `env -P`, `stdbuf`, `flock … bash -c "…"`, `doas`, `xcrun`, `script`, `coproc`, `trap "…"`), fed to a shell on stdin (`sh <<<'…'`, `echo … | sh`), run by another interpreter (`python3 -c`, `node -e`), inside a `case` branch, behind a zsh element assignment (`HOME[1,-1]=`), or built from a variable (`${X}t push`, `git ${X}sh`) ran without a question (measured against real bare remotes). Instead of more spellings, so-hud now asks for any part of the line that spells an outward action it knows and formed no hit (fail-closed). Comments and heredocs whose body is only text are removed before the line is read, so a push between `# bash -c '` and `# '` is read as the shell runs it, and Claude Code's own `git commit -m "$(cat <<'EOF' … EOF)" && git push origin feat/x` no longer asks. Variables set by `read`, `printf -v`, `mapfile`, `let`, `env -u`, a nameref, `NAME+=` or `NAME[i]=` count like `NAME=`. From the #1573 review: `git commit` and `git add` count as local steps only with options that open no editor and read no patch interactively; a `log`/`show` that verifies signatures, and `log.showSignature` in the global or system config, make `gpg.program` count; a relative `cd` while `CDPATH` is set leaves the folder unknown. The SEC-008 traps now catch a reader between `# eval '` and `# '`, behind `flock … bash -c`, and `printenv` with several names. Measured over 228,541 Claude Code transcript commands, 21,928 of them with an outward word: 305 now ask that did not, 275 no longer ask. Details: `docs/mods.md`.
137
+ - **so-hud shows running agents, not the dispatch list** (#1633 point 1): the status line counts `agents.running` from the collector's live map instead of `wave.agents_total`. An unknown count (a map older than the session) shows `? Agents`; without a Session Orchestrator session, or with a collector from before #1580, the field is left out instead of showing 0.
138
+ - **so-hud asks when the repository's git config can run a program in a local step** (#1573): a key set by an earlier call — `core.fsmonitor`, `diff.external`, a filter or textconv driver, `core.sshCommand`, `remote.<r>.uploadpack`, `credential.helper`, `gpg.program` — made `git status`, `git diff`, `git add` or `git fetch` before a push run a program that could move HEAD after so-hud measured it (measured with git 2.56.0: `core.fsmonitor` running `git switch main` left the repository on `main` after `git status`). so-hud now reads every configured key once per held push behind a git step and lets the push run only when each key of the `local`, `worktree` and `command` scopes is on an allowlist; any other key, an include, or a failed read asks. Global and system keys are not judged. Details: `docs/mods.md`.
139
+ - **Fleet return reports** (#1604, #1606): no-plan `/go`, session-start without work scope and idle or blocked sessions report once to the reachable active navigator without waiting for a reply, or, without an active lease in an integrated fleet, leave a lead to-do through the navigator CLI. The rule lives once in `skills/_shared/fleet-protocol.md`; `/go`, `/close`, session-end and the wave-executor point to it. A handover or train manifest is written with the waiting or finished answer, under the names and in the folder the owner inbox actually reads (the `cwd` of the session's own registry entry), the answer ends with `Bereit zum Schließen.`, and the session never closes itself. `/go` recognises its own plan by the process-local raw session id instead of repo-wide labels.
140
+ - **Stop records carry the agent type, and a census reads agent usage from the transcripts** (#1617): `hooks/subagent-telemetry.mjs` takes `agent_type` from the matching start record (same agent id, same 256 KiB tail scan) when the stop payload has none. Almost all type-less stop records are phantom stops without a subagent (`subagent_transcript_found: false`, #939) and stay type-less; records from before #949 carry no flag at all. Filter on that flag. `node scripts/agent-usage-census.mjs [--days 14] [--json]` reads the Claude Code subagent transcripts read-only and reports per agent type dispatches, observed models, turns and output per run, cache rebuilds after gaps over 5 minutes and the median start context; `/evolve analyze` runs it.
141
+ - **Every waiting turn reports, and a failed report is visible** (#1611): the once-per-wait marker (`claimRuecklauf()` / `releaseRuecklauf()`) is gone, because the owner inbox lets a session rest only when its last answer reported in that same turn; a marker that suppressed the second report made the inbox nudge hourly. The duty covers turns triggered by an input (inbox nudge, lead or owner message); turns woken by `/loop`, a Monitor event, an own background agent or shell, or ScheduleWakeup do not report again, so a lead reply cannot start a report ping-pong. The fallback chain is SendMessage to the active lease address, then `navigator owner-queue add` in a form that normalises exit 0 and 4 (similar entry already queued) to exit 0 — Claude Code marks every non-zero exit as `is_error`, and the inbox does not count a failed call as a report — and if both fail the last paragraph says `Meldung gescheitert: <Grund>`. Every waiting answer ends with a waiting sentence the inbox recognises (`Bereit zum Schließen.` or `Ich warte auf den nächsten Auftrag.`), including no-plan `/go` and `/close`.
142
+ - **Parallel abandoned-session backfills write one record** (#1609): the TOCTOU marker is keyed on the raw session UUID when known and also honours markers under the older names, so two concurrent runs no longer append one record under the UUID and one under the semantic id; a failed ledger append releases its marker, so the candidate is backfilled on the next run instead of never.
143
+ - **Ledger readers in a linked worktree read the main checkout's `sessions.jsonl`** (#1592): session-id numbering, `autopilot`, `backfill-abandoned-sessions`, harness-audit c1/c9, `check-sessions-integrity`, the sessions-integrity banner, `session-record-repair`, the default target of `repair-invalid-sessions` (an explicit `--repo-root` stays literal), `gc-stale-worktrees` and the eval engine now read the ledger that every writer appends to. From a worktree they previously numbered sessions wrongly, ended autopilot runs as failed, reported missing records and advised a re-emit that would duplicate a record, and `gc-stale-worktrees --apply` could remove a worktree that a recent session still referenced. The backfill's TOCTOU marker and breadcrumb log sit beside that ledger, so the lock holds across worktrees.
144
+ - **Bootstrap accepts the `pkm-vault` archetype with empty lists** (#1607): the baseline contract allows empty `runtimes`/`packageManagers` only for `pkm-vault`, and the plugin had rejected the whole catalogue as `invalid-contract`, which stopped every standard or deep bootstrap with a configured baseline. All other archetypes still need at least one entry.
145
+ - **`update:local` reports a Codex `@local` install instead of skipping it** (#1608): `scripts/self-update.mjs` recognised only `session-orchestrator` without a marketplace suffix, so a `session-orchestrator@local` install (a personal marketplace that copies the whole checkout into the Codex cache, #1518) was skipped silently. It now ends that step as `failed` with the cleanup steps and changes nothing; installs from other marketplaces update as before.
146
+ - **`security-web.md` loads for Next.js 16 `proxy.ts`** (#1576): the opt-in rule now matches `proxy.ts` and `src/proxy.ts` next to `src/middleware*.ts`, and three opt-in rules no longer point to `swift.md`, which ships only with swift archetypes.
147
+ - **SessionEnd no longer backfills an "abandoned" session for `/clear` or `/resume`** (E3, #1584): `hooks/on-session-end.mjs` skips `backfillAbandonedSession` for the documented reasons `clear` and `resume`, where the logical session continues. Every other reason still backfills. Measured basis: the call ran on every SessionEnd and produced 2,493 synthetic abandoned records fleet-wide.
148
+ - **Monitor docs match Claude Code 2.1.271+** (E2, #1585): the removed `persistent` option is gone from `skills/_shared/monitor-patterns.md` (8 places), which now carries the 30-minute deadline and the re-arm rule; LM-002 in `.claude/rules/loop-and-monitor.md` states the same, and ADR-0010 records the 2026-10-12 re-verification against 2.1.296.
149
+ - **A mistyped Session Config key is reported instead of silently ignored** (#1562): `scripts/parse-config.mjs` writes one stderr line with file, line and key for every top-level key of the local `## Session Config` block that neither `docs/session-config-template.md` documents nor the parser reads (e.g. `persistance: true`, whose intended key otherwise kept its default). `project-name` (written by `/bootstrap`, read by nothing) and `vault` (the product-vault registration `/bootstrap` appends, read by `hasVaultConfig()`) are now documented in the template, so the plugin's own scaffolding stays silent. It reads the block the way the runtime parser does, reports under `enforcement: warn` and `strict` without ever refusing the run (a key from a newer plugin version is not a defect), stays silent under `off`, and says in one line when the template could not be read. stdout JSON is unchanged. The starter config now opens the template, and drift-check Check 6 and `parse-config.mjs` share one key extraction (`scripts/lib/config/template-keys.mjs`).
150
+
151
+ - **The npm package ships the so-hud mod** (#1565): `package.json` `files` now includes `mods/so-hud/` (without its tests), so the marketplace entry `./mods/so-hud` resolves when the marketplace is read from the npm package instead of GitHub. The 5.12.0 entry explains the fleet terms it used (`plattform: kopflos`, `Standard-Auflagen`).
152
+ - **The site census no longer republishes old session and learning counts** (#1555): `scripts/site-numbers.mjs` reads the gitignored `sessions.jsonl` and `learnings.jsonl` of the main checkout when it runs in a linked worktree, and `--write` of the served `site/` exits 2 instead of copying those two values from `site/_census.json` when no ledger is readable. 5.11.0 and 5.12.0 had shipped the snapshot's 475 sessions and 220 learnings this way; `counted-sha` keeps its snapshot answer in a tree without `.git`.
153
+ - **A coverage-load timeout on main** (#1566): the memory-proposals G7 case (a 2 MiB lossless chunked copy) has a 120 s limit like the G10 cases, after it exceeded 30 s once under coverage instrumentation on a slow runner.
154
+ - **Bootstrap asks in the operator's language** (#1562): the tier and archetype questions follow `owner.language` from `owner.yaml`, or the language of the conversation, instead of always German.
155
+ - **so-hud: `git push origin HEAD` runs without a question on a feature branch** (#1569): `HEAD`, written in capitals, stands for the measured branch when every earlier command on the line is a local git call (`git tag -l`/`--list` and `git checkout -- <paths>` now count as local); after any other command, every push form asks.
156
+ - **so-hud receipt hash and status line** (#1569, #1552): `argv_sha256` is now the SHA-256 of the UTF-8 of `JSON.stringify([tool, text])` — `["Bash", "<command>"]` for an outward action, `["Edit", "<file path>"]` (or `Write`/`NotebookEdit`) for a collision — instead of the bare command string. The status line says `nicht eingerichtet` for a part that is not set up (no Session Orchestrator, no `vault-integration`) and `noch kein Lauf` for an enabled vault that never ran; a repository where no session has written events yet is no longer `teilweise gemessen`. Without a Session Orchestrator root, `claude plugin list` runs at most once every 5 minutes instead of on every 15-second refresh.
157
+
158
+ ### Security
159
+
160
+ - **The destructive guard reads the zsh brace-group forms it missed** (#1707): zsh runs several forms the shared reader `scripts/lib/command-blocker.mjs` did not see as deletes or truncations, so the guard allowed them on protected paths:
161
+ - a `}` glued to the last word (`{rm -r .orchestrator}`, `{echo x >CLAUDE.md}`), now judged with and without the brace;
162
+ - a quoted or doubled glued opener (`{"unlink" P;}`, `{'unlink' P;}`, `{{unlink P;}}`);
163
+ - the bodies of `repeat <n>`, `always`, `time`, `coproc` and the `f() cmd` short function. After a glued or one-word close (`x}`, `{true}`) an `always` is read both ways: zsh starts a new statement there, bash passes `always` and every word behind it to the command, so `{ rm -f x} always CLAUDE.md; }` still counts CLAUDE.md as a target.
164
+
165
+ A quoted single-word verb (`"unlink" P`) is no longer skipped by the delete collector either; on its own it was a bypass. All changes can only add targets.
166
+ - **so-hud reads the push configuration and closes push-gate bypasses** (#1569): before a push runs without a question, so-hud reads `push.*`, `remote.<r>.push`, `remote.<r>.mirror`, `remote.pushDefault`, `branch.<b>.pushRemote` and `submodule.recurse`; anything but `push.default` unset or `simple` (or `current` for a named branch) and `push.autoSetupRemote` asks, as do a push refspec, a mirror, a push remote that points elsewhere and submodule recursion. The remote must be defined in git config (`remote.<r>.url`); a legacy `.git/remotes` remote asks. Closed: lowercase `head` (git reads it as `HEAD` on a case-insensitive filesystem); an apostrophe in a heredoc and a line break inside quotes hiding a background `&`; git calls inside `$(…)`, backticks and `<(…)`, and `git-<name>` run by path; a `cd` in a subshell, background job, pipeline or `||` branch (the folder is now unknown and the push asks); `git -c` with other keys on an earlier git call; an input redirection in front of the command (`</dev/null git push origin main` was not held); variable assignments on the line before a push, which now ask unless the name is harmless (a single lowercase letter, `rc`, `out`, `msg`, `sha`, `ref`, `head`, `base`, `branch`, `res`, `ok`, `tmp`, or an uppercase letter with at most one more uppercase letter or digit such as `D`, `WT`, `TZ`) — `HOME`, `PATH`, `XDG_*`, `GIT_*`, `SKIP_PRE_PUSH` and `source` ask; an earlier `fetch` with any option or a `::` word, and an earlier `log`, `show` or `diff` with an option that writes a file or runs a program (`--output`, `--ext-diff`, `--textconv`; on `fetch` `--upload-pack`, also abbreviated); a command that is not git before a push that names its branch (`printf … >> .git/config; git push origin feat/x`); an output redirection before any push, on an allowed git step, `cd`, an assignment or `exec` (`git log --format=… >> .git/config`, `cd . > .git/HEAD`, `exec 3>>.git/config`); and a hook fired by an earlier `add`, `commit`, `status`, `fetch` or `checkout -- <paths>`, which now makes a push ask unless so-hud measured that no hook can run — hooks defined in git config (`hook.<name>.command`) count, and such a step in another folder than the push's (`git -C ../b commit -m x && git push origin feat/x`, `cd ../b && git commit -m x && cd ../a && git push …`) always asks. The scanners run in linear time; a 50,000-character line no longer takes seconds. Receipts and beacons are written only into real directories owned by the user and appended only to a regular file with one link (symlinks and hard links are refused); without `O_NOFOLLOW` (native Windows) an `lstat` check stands in, with a race between check and `chmod`; removing a beacon follows no symlink.
167
+
168
+ ## [5.12.0] - 2026-10-11
169
+
170
+ ### Added
171
+
172
+ - **so-hud, a native Claude Code status mod** (#1508): `mods/so-hud` (version 0.1.0) shows the session state (lock, registry, wave scope, agent status, STATE.md, gate and vault events) as a status line and, when something needs attention, a warning band, and writes one beacon per session that other sessions can read through `$.so.beacons()`; the status line shows how many peer sessions are running. It reads everything through `scripts/so-hud.mjs` (JSON v1 with provenance and `degraded`), keeps no second copy of the state and never changes a tool decision. It is listed in this repo's marketplace: `/plugin install so-hud@kanevry` (Claude Code only, tested with 2.1.296, Node.js 24+, Session Orchestrator 5.12.0 or later as the plugin or as a checkout set through `soRoot`). Status texts are German for now; an English table is prepared but not yet selectable.
173
+ - **Mod CI gate** (#1508): the `mods:plugin` job runs `claude plugin validate --strict --json` and `claude plugin test` for every mod under `mods/` with Claude Code pinned once in `CLAUDE_CODE_VERSION` (2.1.296). It verifies the registry signatures of both downloaded packages, calls the platform binary directly so `ignore-scripts` stays on, runs without credentials or nonessential traffic, and is a hard dependency of `pipeline-gate`.
174
+ - **Session-end names untracked vault files modified during the session** (#1563): new Phase 3.7e runs `scripts/vault-orphans.mjs`, which lists untracked files under `vault-dir` modified since the session started (`git ls-files --others --exclude-standard -z`, read-only) and leaves out files carrying a `_generator: session-orchestrator-` marker. The close names them in the final report and records one Open Question with the path list; it never commits, moves or deletes anything in the vault, because a modification time does not prove which session wrote a file. A vault that cannot be read is reported as "could not measure", never as "none". There is no blanket exclusion of `40-learnings/` and `50-sessions/`: an unmarked file there blocks every later mirror auto-commit and is exactly the case to surface.
175
+
176
+ ### Fixed
177
+
178
+ - **The Mode-Selector sees the previous session's recommendation again** (#1560): the session-start Idle Reset was prose only, and its rule 6 deleted the five recommendation keys from the STATE.md frontmatter that Phase 7.5 reads through `buildLiveSignals()`, so after every clean `/close` the selector answered with confidence 0. The reset is now code (`scripts/lib/state-md/idle-reset.mjs`, CLI `scripts/state-md-idle-reset.mjs`, written under the STATE.md lock) and keeps those keys; session-end Phase 3.7a overwrites them at the next close. Without a new Wave History the earlier `## Previous Session` archive stays below the new recommendations copy. `parseRecommendations()` accepts `"#42"` and `"42"` in `top-priorities` and drops free-text entries instead of discarding the whole list.
179
+ - **A first `/session` no longer starts `deep` silently** (#1561): a bare `/session` in a repo whose `.orchestrator/metrics/sessions.jsonl` holds no real session resolves with `defaultReason: "no-session-history"`, and `commands/session.md` then asks for task and mode first (recommending `feature`). A linked worktree reads its main checkout's ledger, and an unreadable ledger counts as not measured, so neither triggers the question.
180
+ - **Solo users see no fleet machinery** (#1559): without fleet integration (`NAVIGATOR_CONFIG_DIR` unset and no `~/.config/navigator/` directory), `scripts/fleet-checkin.mjs` still validates its input, then writes nothing, creates no directory, emits no event and prints `skipped: "no-fleet"`; a headless run (`claude -p`, recorded as `plattform: kopflos` in the check-in) without fleet integration applies the default fleet constraints instead of writing (the `Standard-Auflagen` of `skills/_shared/fleet-protocol.md`: agent caps, a limit on full pipelines, no merge without the owner's order). The SessionStart banner drops the navigator line. In a git repository without `.orchestrator/bootstrap.lock` (neither at the toplevel nor in the session directory) the first banner action is `/bootstrap`, in place of the decision nudge whose `/go` and `/close` do not work before setup; once bootstrapped, the nudge returns and now reads in plain words instead of naming an internal rule file. The hint no longer follows `cold-start.enabled`, which governs only the post-bootstrap first-session nudge.
181
+ - **The npm package no longer ships this repo's own `AGENTS.md`** (#1559): it carried the maintainer's Session Config (vault path, remote host aliases), and no consumer reads an instruction file from the installed package. `tests/scripts/pack-policy-floor.test.mjs` keeps root `AGENTS.md` and `CLAUDE.md` out of the tarball. Config examples use the neutral alias `build-host`.
182
+ - **Install docs match current Claude Code** (#1369, #1559): `docs/install.md` runs `npm install` in the plugin cache only when `node_modules/js-yaml` is missing, since `/plugin install` already provisions the dependencies. The README quickstart says that `/bootstrap` writes the Session Config and names `/bootstrap --fast --no-interview` as the shortest path. The website install step (EN and DE) and the guide use the same conditional command (#1564).
183
+ - **One identity for a repo reached through a symlink** (#1557): `repoPathHash()` and the registry's `repo_name` resolve the real path (`realpathSync.native`, falling back to `path.resolve` when the path is unavailable), so a session in a symlink and one in its target count as the same repo for peer discovery, session transition and the live-status board. Board keys of canonical paths are unchanged. A board row stored under a pre-#1557 alias key is adopted on its first fresh write when it names the same repo; a row of another repo that merely shares the 8-character key stays. Registry entries written before the update keep their old hash until the session registers again.
184
+ - **Umlauts survive in new learning-note slugs** (#1548): vault-mirror transliterates the slugs of new learning notes with a traceable origin (ä to ae, ö to oe, ü to ue, ß to ss, accents after NFC) instead of deleting the characters ("grnes" for "grünes"). Existing notes with a traceable origin keep their path, file name and id, and namespace and tags stay as they were, so auto-commit, relocation and backfill see the same folder. Session notes, tags and records without a traceable origin keep the historical slug, which drops umlauts; the backfill reverse lookup does not find older notes with umlauts; æ, ø, ł and œ are still dropped.
185
+ - **No overwriting another repo's fleet check-in** (#1556): `scripts/fleet-checkin.mjs` exits 2 and writes nothing when the check-in file for the same semantic session id already belongs to another repo; the same repo still updates its own check-in. The existing file is opened without following symlinks and without blocking, read only as a regular file of at most 64 KiB, and the foreign repo name is printed as a JSON string on one stderr line.
186
+
10
187
  ## [5.11.0] - 2026-10-11
11
188
 
12
189
  ### Added
@@ -439,7 +616,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
439
616
 
440
617
  ### Fixed
441
618
 
442
- - Codex portable command discovery now includes command-only `session` alongside skills and retains explicit-only `go`/`close` invocation policies. Session modes are parsed independently of following task text; explicitly requested parallel housekeeping uses the actual wave plan. Generated Codex, portable, Cursor and Pi adapters and setup guidance agree on these contracts. Merge, release and installed-plugin acceptance are tracked in the [release handover](docs/handover/2026-09-19-codex-session-entrypoints.md).
619
+ - Codex portable command discovery now includes command-only `session` alongside skills and retains explicit-only `go`/`close` invocation policies. Session modes are parsed independently of following task text; explicitly requested parallel housekeeping uses the actual wave plan. Generated Codex, portable, Cursor and Pi adapters and setup guidance agree on these contracts. Merge, release and installed-plugin acceptance are tracked in the release handover of 2026-09-19 (internal, not in the public mirror).
443
620
  - **Peer-card data loss on `/evolve dialectic --apply` (#1380).** A managed region wrapping MORE than one `## ` heading cannot be addressed per heading; `mergeDerivedBody` used to target it anyway, so a proposal touching one of its headings either replaced the region (deleting its sibling headings' hand-written text) or appended a duplicate heading. Such a region is now fail-closed: never a target, left byte-unchanged, and every skipped heading surfaced as a `{ type: 'multi-heading-region', region, headings, skipped }` conflict for the operator to resolve by splitting the region.
444
621
  - **Peer-card frontmatter read as body (#1380).** `scripts/lib/peer-cards/reader.mjs` now folds multi-line list values — both the flow form Prettier reformats the writer's output into and a hand-written `- ` block list — before handing the card to `parseStateMd`, and when the frontmatter is still unparseable it strips the block instead of returning it as Markdown. A card's own `---` frontmatter therefore can no longer reach the deriver payload as content.
445
622
  - **Short read decoded as uninitialised memory** in `hooks/subagent-telemetry.mjs`: `findStartTimestampMs()` ignored the byte count `fs.readSync` returned and decoded the whole `allocUnsafe` buffer into `JSON.parse` — fixed by the shared reader above, which loops to fill the window and slices to what it read (#1272).
@@ -929,7 +1106,7 @@ release's commit: 277 working-tree entries at fold time (`git status --short | w
929
1106
  2026-09-06), on top of 13 already-committed predecessors measuring 256 files, +13,537/−1,669
930
1107
  (`git diff --stat v3.24.0..HEAD | tail -1`, same day). Fourteen read-only Discovery agents measured the plugin against its own
931
1108
  telemetry, the 90-day fleet ledger and the vendor docs; the synthesis is
932
- [`docs/audits/2026-09-06-360-audit.md`](docs/audits/2026-09-06-360-audit.md), per-agent
1109
+ the 2026-09-06 360° audit (internal, not in the public mirror), per-agent
933
1110
  evidence under `docs/audits/2026-09-06-360-audit/w1/`. Nothing was removed on judgement: the
934
1111
  rule was 0 telemetry ∧ 0 fleet invocation in 90 days ∧ no runtime consumer, and
935
1112
  prose-invoked skills (0 by construction) were exempt. Upgrade guide:
@@ -962,7 +1139,7 @@ prose-invoked skills (0 by construction) were exempt. Upgrade guide:
962
1139
  `skills/session-plan/SKILL.md:336`. `SESSION_PROFILE_FIELD` in `scripts/lib/state-md.mjs`
963
1140
  is the reader; absent is not empty. Budgets (`ultradeep.max-*`) are deliberately NOT
964
1141
  implemented — the PRD defers thresholds until three runs have been measured. Spec:
965
- [`docs/prd/2026-09-06-ultradeep-session-profile.md`](docs/prd/2026-09-06-ultradeep-session-profile.md).
1142
+ the internal ultradeep session-profile PRD of 2026-09-06 (not in the public mirror).
966
1143
  - **A plugin-update banner at session-start — the first code in this repo that compares
967
1144
  INSTALLED against AVAILABLE.** `scripts/lib/plugin-update-banner.mjs` (`checkPluginUpdate`,
968
1145
  imported lazily from `hooks/on-session-start.mjs:976`) reads the RUNNING package's own