session-orchestrator 5.12.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 (385) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  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 +162 -4
  11. package/README.md +126 -127
  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 +1 -1
  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 +90 -17
  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 +49 -15
  47. package/docs/session-config-template.md +30 -22
  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 +139 -49
  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 -2
  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 +39 -27
  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/section-extractor.mjs +65 -11
  143. package/scripts/lib/config/template-keys.mjs +153 -0
  144. package/scripts/lib/config/vault-integration.mjs +1 -0
  145. package/scripts/lib/config.mjs +2 -2
  146. package/scripts/lib/coordinator-snapshot.mjs +1 -1
  147. package/scripts/lib/discovery/triage-state.mjs +2 -1
  148. package/scripts/lib/dispatcher/rank.mjs +5 -7
  149. package/scripts/lib/ecosystem-health.mjs +2 -2
  150. package/scripts/lib/eval/engine.mjs +3 -2
  151. package/scripts/lib/eval/session-resolve.mjs +6 -2
  152. package/scripts/lib/eval/sink.mjs +4 -3
  153. package/scripts/lib/events-retention-banner.mjs +3 -2
  154. package/scripts/lib/events-schema.mjs +10 -0
  155. package/scripts/lib/events.mjs +23 -12
  156. package/scripts/lib/evolve/autonomy-verdict.mjs +4 -9
  157. package/scripts/lib/evolve/autopilot-effectiveness.mjs +8 -18
  158. package/scripts/lib/fleet-protocol.mjs +4 -0
  159. package/scripts/lib/gates/gate-full.mjs +172 -11
  160. package/scripts/lib/gates/gate-helpers.mjs +236 -23
  161. package/scripts/lib/git-config-drift.mjs +141 -7
  162. package/scripts/lib/github-export.mjs +447 -0
  163. package/scripts/lib/gitlab-portfolio/cli.mjs +16 -5
  164. package/scripts/lib/handover-queue-marks.mjs +159 -0
  165. package/scripts/lib/hardware-pattern-detector.mjs +103 -13
  166. package/scripts/lib/harness-audit/categories/category1.mjs +16 -4
  167. package/scripts/lib/harness-audit/categories/category4.mjs +11 -8
  168. package/scripts/lib/harness-audit/categories/category6.mjs +25 -5
  169. package/scripts/lib/harness-audit/categories/category9.mjs +10 -4
  170. package/scripts/lib/host-identity.mjs +9 -5
  171. package/scripts/lib/http-request.mjs +64 -0
  172. package/scripts/lib/instruction-budget-guard.mjs +30 -22
  173. package/scripts/lib/io.mjs +6 -3
  174. package/scripts/lib/issue-budget-reconcile.mjs +3 -2
  175. package/scripts/lib/issue-budget.mjs +36 -7
  176. package/scripts/lib/learnings/evidence-candidates.mjs +154 -0
  177. package/scripts/lib/learnings/expiry-sweep.mjs +99 -7
  178. package/scripts/lib/learnings/io.mjs +289 -15
  179. package/scripts/lib/learnings/select.mjs +53 -4
  180. package/scripts/lib/learnings/store-role.mjs +209 -0
  181. package/scripts/lib/learnings/surface.mjs +46 -7
  182. package/scripts/lib/ledger-path.mjs +113 -0
  183. package/scripts/lib/maintenance-due-banner.mjs +63 -12
  184. package/scripts/lib/memory-banner.mjs +5 -6
  185. package/scripts/lib/memory-proposals/collector.mjs +5 -2
  186. package/scripts/lib/memory-proposals/sink.mjs +7 -4
  187. package/scripts/lib/memory-proposals/store.mjs +22 -5
  188. package/scripts/lib/mode-selector-accuracy.mjs +2 -1
  189. package/scripts/lib/no-follow-open.mjs +57 -0
  190. package/scripts/lib/orphan-reaper/reaper-audit.mjs +4 -2
  191. package/scripts/lib/own-lock-root.mjs +95 -0
  192. package/scripts/lib/owner-interview.mjs +2 -1
  193. package/scripts/lib/owner-yaml.mjs +70 -7
  194. package/scripts/lib/platform.mjs +161 -9
  195. package/scripts/lib/plugin-update-banner.mjs +15 -11
  196. package/scripts/lib/pre-push-local-partial.mjs +117 -0
  197. package/scripts/lib/pre-push-remote-gate.mjs +151 -25
  198. package/scripts/lib/process-group.mjs +270 -34
  199. package/scripts/lib/project-hygiene.mjs +28 -0
  200. package/scripts/lib/quality-gate.mjs +33 -9
  201. package/scripts/lib/reconcile/backlog.mjs +29 -15
  202. package/scripts/lib/reconcile/engine.mjs +26 -2
  203. package/scripts/lib/reconcile/idempotency.mjs +65 -3
  204. package/scripts/lib/reconcile/rule-expiry-sweep.mjs +53 -12
  205. package/scripts/lib/reconcile/verdict-ledger.mjs +224 -0
  206. package/scripts/lib/reconcile-nudge-banner.mjs +40 -6
  207. package/scripts/lib/resource-probe/evaluate.mjs +32 -9
  208. package/scripts/lib/resource-probe/probe-platform.mjs +3 -1
  209. package/scripts/lib/rule-loader.mjs +20 -10
  210. package/scripts/lib/scope-baseline.mjs +123 -8
  211. package/scripts/lib/scope-echo.mjs +5 -2
  212. package/scripts/lib/scope-gate.mjs +55 -20
  213. package/scripts/lib/session-close-backfill.mjs +75 -7
  214. package/scripts/lib/session-end/close-precheck.mjs +151 -0
  215. package/scripts/lib/session-end/phase-skip.mjs +8 -7
  216. package/scripts/lib/session-end/tail-runner.mjs +7 -2
  217. package/scripts/lib/session-id.mjs +9 -1
  218. package/scripts/lib/session-identity/own-session.mjs +63 -0
  219. package/scripts/lib/session-lock.mjs +114 -0
  220. package/scripts/lib/session-record-repair.mjs +245 -35
  221. package/scripts/lib/session-registry.mjs +72 -9
  222. package/scripts/lib/session-schema/constants.mjs +9 -0
  223. package/scripts/lib/session-schema/filters.mjs +56 -1
  224. package/scripts/lib/session-schema/validator.mjs +15 -5
  225. package/scripts/lib/session-start-probes.mjs +149 -22
  226. package/scripts/lib/session-token-rollup.mjs +2 -1
  227. package/scripts/lib/session-work-trees.mjs +166 -0
  228. package/scripts/lib/sessions-canonical.mjs +61 -3
  229. package/scripts/lib/sessions-integrity-banner.mjs +17 -39
  230. package/scripts/lib/sessions-staleness-banner.mjs +8 -5
  231. package/scripts/lib/sh-quote.mjs +1 -0
  232. package/scripts/lib/skill-evidence-window.mjs +5 -1
  233. package/scripts/lib/skill-health/join.mjs +10 -9
  234. package/scripts/lib/skill-invocations-schema.mjs +13 -0
  235. package/scripts/lib/skill-judgments-schema.mjs +6 -2
  236. package/scripts/lib/snapshot-probe.mjs +414 -0
  237. package/scripts/lib/so-hud/collect.mjs +49 -5
  238. package/scripts/lib/so-hud/runtime.mjs +15 -4
  239. package/scripts/lib/spiral-carryover.mjs +8 -1
  240. package/scripts/lib/state-md/frontmatter-mutators.mjs +266 -8
  241. package/scripts/lib/state-md/idle-reset.mjs +14 -5
  242. package/scripts/lib/state-md/mission-status.mjs +5 -1
  243. package/scripts/lib/state-md-peer-guard.mjs +2 -0
  244. package/scripts/lib/state-md.mjs +31 -8
  245. package/scripts/lib/sunset/walker.mjs +4 -3
  246. package/scripts/lib/telemetry/consent.mjs +4 -2
  247. package/scripts/lib/telemetry/queue.mjs +3 -3
  248. package/scripts/lib/telemetry/sync.mjs +45 -9
  249. package/scripts/lib/test-runner/artifact-paths.mjs +10 -2
  250. package/scripts/lib/tmux-layout/layouts.mjs +20 -3
  251. package/scripts/lib/ux-grill/paths.mjs +5 -1
  252. package/scripts/lib/validate/check-agents.mjs +37 -6
  253. package/scripts/lib/validate/check-component-paths.mjs +25 -1
  254. package/scripts/lib/validate/check-guard-requires-parity.mjs +3 -3
  255. package/scripts/lib/validate/check-hooks-symmetry.mjs +53 -32
  256. package/scripts/lib/validate/check-json-files.mjs +3 -1
  257. package/scripts/lib/validate/check-learning-provenance.mjs +3 -2
  258. package/scripts/lib/validate/check-learnings-shell-writes.mjs +10 -4
  259. package/scripts/lib/validate/check-metrics-ledger-root.mjs +234 -0
  260. package/scripts/lib/validate/check-owner-leakage.mjs +159 -11
  261. package/scripts/lib/validate/check-test-posix-shell.mjs +246 -0
  262. package/scripts/lib/validate/check-unwired-features.mjs +4 -2
  263. package/scripts/lib/validate/check-vcs-repo-flag-positional.mjs +58 -0
  264. package/scripts/lib/validate/check-vcs-repo-flag.mjs +259 -73
  265. package/scripts/lib/validate/check-workflow-continue-on-error.mjs +63 -0
  266. package/scripts/lib/validate/confidential-names.mjs +3 -1
  267. package/scripts/lib/vault-mirror/migrate-ids.mjs +321 -0
  268. package/scripts/lib/vault-mirror/namespace.mjs +19 -7
  269. package/scripts/lib/vault-mirror/process.mjs +56 -27
  270. package/scripts/lib/vault-mirror/prune.mjs +5 -2
  271. package/scripts/lib/vault-mirror/render-learnings.mjs +3 -3
  272. package/scripts/lib/vault-mirror/render-sessions.mjs +36 -53
  273. package/scripts/lib/vault-mirror/retention.mjs +2 -2
  274. package/scripts/lib/vault-mirror/utils.mjs +79 -0
  275. package/scripts/lib/vault-staleness-banner.mjs +4 -7
  276. package/scripts/lib/vault-status/board-writer.mjs +2 -1
  277. package/scripts/lib/vault-status/narrative-mirror.mjs +66 -7
  278. package/scripts/lib/wave-executor/foreign-dispatch.mjs +20 -0
  279. package/scripts/lib/win-process-tree.mjs +137 -0
  280. package/scripts/lib/workspace.mjs +1 -1
  281. package/scripts/lib/worktree/constants.mjs +1 -1
  282. package/scripts/lib/worktree/lifecycle.mjs +1 -1
  283. package/scripts/lib/worktree/listing.mjs +1 -1
  284. package/scripts/lib/zx.mjs +50 -0
  285. package/scripts/mcp-server.mjs +16 -7
  286. package/scripts/mcp-server.sh +13 -83
  287. package/scripts/measure-policy-cache-effectiveness.mjs +7 -5
  288. package/scripts/memory-propose.mjs +10 -3
  289. package/scripts/migrate-cold-start-seed.mjs +7 -0
  290. package/scripts/migrate-legacy-learnings.sh +22 -0
  291. package/scripts/migrate-sessions-jsonl.mjs +16 -2
  292. package/scripts/migrate-vault-paths.mjs +29 -13
  293. package/scripts/parse-config.mjs +71 -14
  294. package/scripts/print-learnings-index.mjs +25 -2
  295. package/scripts/release-session-lock.mjs +94 -7
  296. package/scripts/release.mjs +451 -24
  297. package/scripts/relocate-vault-corpus.mjs +11 -7
  298. package/scripts/repair-invalid-sessions.mjs +68 -3
  299. package/scripts/resolve-session-invocation.mjs +10 -17
  300. package/scripts/run-migrate-v2-cross-repo.mjs +56 -1
  301. package/scripts/run-quality-gate.mjs +33 -7
  302. package/scripts/self-update.mjs +56 -7
  303. package/scripts/session-commit-files.mjs +133 -0
  304. package/scripts/site-numbers.mjs +52 -9
  305. package/scripts/snapshot-probe.mjs +156 -0
  306. package/scripts/so-hud.mjs +29 -6
  307. package/scripts/state-md-idle-reset.mjs +2 -1
  308. package/scripts/sweep-expired-learnings.mjs +54 -6
  309. package/scripts/sweep-expired-rules.mjs +4 -1
  310. package/scripts/validate-plugin.mjs +22 -0
  311. package/scripts/vault-integration-watcher.mjs +54 -6
  312. package/scripts/vault-mirror.mjs +9 -0
  313. package/skills/_shared/fleet-protocol.md +142 -1
  314. package/skills/_shared/instruction-file-resolution.md +1 -1
  315. package/skills/_shared/monitor-patterns.md +44 -25
  316. package/skills/_shared/platform-tools.md +7 -0
  317. package/skills/_shared/state-ownership.md +3 -2
  318. package/skills/architecture/references/domain-model.md +1 -1
  319. package/skills/bootstrap/SKILL.md +3 -1
  320. package/skills/bootstrap/_shared-template.md +4 -4
  321. package/skills/bootstrap/fast-template.md +1 -1
  322. package/skills/bootstrap/private-contract.md +1 -1
  323. package/skills/bootstrap/references/bootstrap-retroactive-flow.md +17 -9
  324. package/skills/bootstrap/templates/agents/project-code-review.md +5 -1
  325. package/skills/bootstrap/templates/agents/project-discovery.md +5 -1
  326. package/skills/bootstrap/templates/agents/project-quality-gate.md +5 -1
  327. package/skills/claude-md-drift-check/SKILL.md +3 -3
  328. package/skills/claude-md-drift-check/checker.mjs +61 -101
  329. package/skills/close/SKILL.md +20 -7
  330. package/skills/discovery/probes/docs-staleness.mjs +7 -5
  331. package/skills/discovery/probes/frontend-slop.mjs +7 -3
  332. package/skills/discovery/probes/ssot-code-diff.mjs +5 -3
  333. package/skills/discovery/probes/supply-chain-slopcheck.mjs +10 -8
  334. package/skills/discovery/probes/vault-narrative-staleness.mjs +30 -10
  335. package/skills/discovery/probes/vault-staleness.mjs +5 -3
  336. package/skills/discovery/probes-session.md +3 -0
  337. package/skills/discovery/probes-vault.md +2 -0
  338. package/skills/dispatcher/SKILL.md +1 -1
  339. package/skills/eval/SKILL.md +3 -1
  340. package/skills/evolve/SKILL.md +2 -0
  341. package/skills/evolve/references/evolve-analyze-mode.md +3 -3
  342. package/skills/evolve/references/evolve-dialectic-mode.md +1 -1
  343. package/skills/gitlab-ops/SKILL.md +7 -6
  344. package/skills/gitlab-portfolio/SKILL.md +2 -2
  345. package/skills/go/SKILL.md +13 -1
  346. package/skills/npm-publish/SKILL.md +5 -2
  347. package/skills/plan/SKILL.md +23 -10
  348. package/skills/plan/mode-feature.md +13 -8
  349. package/skills/plan/mode-new.md +6 -14
  350. package/skills/plan/prd-feature-template.md +19 -0
  351. package/skills/plan/prd-full-template.md +12 -5
  352. package/skills/plan/prd-premortem-panel.md +48 -0
  353. package/skills/plan/prd-reviewer-prompt.md +11 -3
  354. package/skills/plan/soul.md +1 -1
  355. package/skills/portfolio/SKILL.md +5 -6
  356. package/skills/quality-gates/SKILL.md +2 -4
  357. package/skills/reconcile/SKILL.md +27 -4
  358. package/skills/release/SKILL.md +16 -8
  359. package/skills/session-end/SKILL.md +94 -11
  360. package/skills/session-end/phase-3-2-docs-verification.md +1 -0
  361. package/skills/session-end/phase-3-6-tail.md +11 -7
  362. package/skills/session-end/phase-3-7a-recommendations.md +9 -2
  363. package/skills/session-end/plan-verification.md +16 -1
  364. package/skills/session-end/references/phase-3-documentation-updates.md +14 -12
  365. package/skills/session-end/references/phase-5-issue-cleanup.md +1 -1
  366. package/skills/session-end/references/session-summary-template.md +4 -0
  367. package/skills/session-end/session-metrics-write.md +7 -4
  368. package/skills/session-plan/SKILL.md +5 -3
  369. package/skills/session-start/SKILL.md +9 -3
  370. package/skills/session-start/phase-4-5-resource-health.md +11 -5
  371. package/skills/session-start/phase-8-5-express-path.md +4 -3
  372. package/skills/session-start/references/phase-1-2-session-lock.md +7 -5
  373. package/skills/session-start/references/phase-1-5-session-continuity.md +2 -2
  374. package/skills/session-start/references/phase-1-7-vault-status-board.md +1 -1
  375. package/skills/session-start/references/phase-4-ssot-environment-check.md +3 -2
  376. package/skills/tmux-layout/SKILL.md +2 -2
  377. package/skills/vault-sync/validator.mjs +3 -2
  378. package/skills/wave-executor/SKILL.md +5 -4
  379. package/skills/wave-executor/references/wave-executor-state-init.md +3 -3
  380. package/skills/wave-executor/references/wave-loop-dispatch.md +11 -5
  381. package/skills/wave-executor/references/wave-loop-review.md +13 -5
  382. package/skills/wave-executor/references/wave-loop-scope-manifest.md +1 -1
  383. package/assets/og-card.png +0 -0
  384. package/assets/og-card.svg +0 -47
  385. package/assets/wave-lifecycle.svg +0 -98
package/docs/install.md CHANGED
@@ -12,20 +12,95 @@ uninstall.
12
12
 
13
13
  ## Requirements
14
14
 
15
- | | |
16
- |---|---|
17
- | **Node.js** | **24 or later** (`node --version`) ; `package.json` `engines.node` is `>=24.0.0`. The plugin is ES modules and needs a real Node runtime. [Install Node.js](https://nodejs.org/). |
18
- | **A coding agent** | Claude Code, Codex CLI, Cursor IDE, or Pi. This is a workflow layer *on top of* one of them, not a replacement. |
19
- | **Harness version** | Codex CLI **0.144.4 or later** ([codex-setup.md](codex-setup.md)). No minimum is pinned for Claude Code, Cursor, or Pi; if `/plugin` (or the Cursor/Pi installer) runs, the plugin loads. |
20
- | **OS** | macOS and Linux are tested in CI. Windows is untested and best-effort; shell hooks and the optional Bash/`jq` MCP server need WSL or Git Bash. |
21
- | **Git** | A git repository. Session-orchestrator reads git state at every session start and commits at close. |
15
+ “Required” means the core workflow needs it. “Feature requirement” means you
16
+ need it only when using the named feature. Install Node packages with npm; the
17
+ committed lockfile is `package-lock.json`. Do not run `pnpm install` in this
18
+ checkout. <!-- sources: package.json:26; scripts/check-package-manager.mjs:140-158,193-235 -->
19
+
20
+ Claude Code on native Windows is supported with Node.js 24 and Git for Windows
21
+ (which Claude Code itself requires). Git for Windows supplies `sh` for the hook
22
+ wrappers; the MCP server starts directly with Node and needs neither Bash, jq
23
+ nor `scripts/windows-setup.mjs`. <!-- sources: .claude-plugin/plugin.json:15-17; hooks/hooks.json:14; hooks/run-node.sh:1-29 -->
24
+
25
+ Measured on 2026-10-14 with Windows 11 (10.0.26200), Node 24.14.0, Windows
26
+ PowerShell 5.1, Git for Windows 2.55 and Claude Code 2.1.296:
27
+ <!-- sources: scripts/ci/windows-smoke.mjs:2-3,53-89,101-150; .claude-plugin/plugin.json:15-17 -->
28
+
29
+ - `claude --plugin-dir <checkout> mcp list` reported the Node MCP server as
30
+ connected. <!-- sources: .claude-plugin/plugin.json:15-17 -->
31
+ - All 31 configured hook commands exited 0 through Git Bash, each in under
32
+ 1.5 seconds; the destructive guard denied a hard reset. <!-- sources: hooks/hooks.json:1-270; scripts/ci/windows-smoke.mjs:101-150 -->
33
+ - Quality gates captured UTF-8 output and correct exit codes, reported a missing
34
+ command as `fail`, and ended the process tree on timeout with `taskkill /T /F`.
35
+ Survivor reporting uses measured PIDs, or `null` when measurement is unavailable.
36
+ Windows PowerShell 5.1 suffices; PowerShell 7 is not required.
37
+ <!-- sources: scripts/ci/windows-smoke.mjs:53-89; scripts/lib/process-group.mjs:1010-1040 -->
38
+ - Plugin validation passed 239 checks with 0 failures; the native
39
+ `scripts/ci/windows-smoke.mjs` probes passed 49/49 on the measurement machine.
40
+ <!-- sources: scripts/validate-plugin.mjs:1-37; scripts/ci/windows-smoke.mjs:18-25 -->
41
+
42
+ The contributor test suite does not pass on Windows yet: 19,651 passed and 881
43
+ failed natively (2026-10-14), almost all of them POSIX assumptions inside the
44
+ tests (`sh`, `/` separators, symlinks); tracked in #1613. GitHub Actions runs it
45
+ in the informational `test-windows-suite` job and the product checks above in
46
+ `test-windows-smoke` (`windows-latest`, both non-blocking for now).
47
+
48
+ Not yet measured: a complete Claude Code run with model calls, and Codex CLI /
49
+ Cursor / Pi on native Windows. WSL counts as Linux.
50
+ <!-- sources: .github/workflows/test.yml:153-164,198-214; scripts/ci/windows-smoke.mjs:2-3 -->
51
+
52
+ ### Core and installation
53
+
54
+ | Dependency | Status / version | Without it, you cannot... |
55
+ |---|---|---|
56
+ | **Node.js** | Required, **>=24.0.0**. Check `node --version`. | Run the ES-module scripts and Node hooks. <!-- sources: package.json:25-27,64-78; hooks/run-node.sh:22-29 --> |
57
+ | **Git and a git repository** | Required. | Read session-start repository state or use git-based workflow operations. <!-- sources: hooks/on-session-start.mjs:605-612; scripts/lib/quality-gate.mjs:403-468 --> |
58
+ | **A coding agent** | Required: Claude Code, Codex CLI, Cursor IDE or Pi; use the matching setup guide below. | Load commands, skills and platform integration. <!-- sources: package.json:4,112-121; .claude-plugin/plugin.json:5; scripts/codex-install.mjs:1-18; scripts/cursor-install.mjs:3-9; scripts/pi-install.mjs:1-18 --> |
59
+ | **Codex CLI version** | Feature requirement for the Codex route: **0.144.4 or later**, as documented in its setup guide. | Use the documented marketplace installation route. <!-- source: docs/codex-setup.md:7,37-56 --> |
60
+ | **Claude Code plugin support** | Required for the Claude Code route; **no minimum version is declared** in `package.json` or `.claude-plugin/plugin.json`. | Use `/plugin` installation and the Claude Code hooks. <!-- sources: package.json:25-27; .claude-plugin/plugin.json:1-19; hooks/hooks.json:6-18 --> |
61
+ | **POSIX sh** | Required for shell-wrapped hooks; Claude Code on Windows uses `sh` from Git for Windows. | Launch the configured hook wrappers, including scope and destructive-command guards. <!-- sources: hooks/hooks.json:14,49,69; hooks/run-node.sh:1-29 --> |
62
+ | **npm** | Required for clone-based dependency setup and manual dependency repair; expose it on the harness PATH. | Install missing packages, run npm scripts or update a local clone. <!-- sources: package.json:64-78; scripts/self-update.mjs:200,299; .mcp.json:8 --> |
63
+ | **Runtime Node packages** | Required, installed from `package.json`: `@babel/parser`, `ajv`, `js-yaml`, `mdast-util-to-string`, `picomatch`, `remark`, `remark-parse`, `unified`, `yaml`, `zod`, `zx`. | Load scripts importing these parsers, validators and utilities. No separate global installation is needed. <!-- source: package.json:80-92 --> |
64
+ | **Development Node packages** | Contributor requirement: `@commitlint/cli`, `@commitlint/config-conventional`, `@eslint/js`, `@vitest/coverage-v8`, `eslint`, `husky`, `lint-staged`, `prettier`, `vitest`. | Run the corresponding lint, test, coverage, formatting and git-hook tooling from the checkout. Runtime-only installs can omit them. <!-- sources: package.json:64-78,93-103 --> |
65
+ | **pnpm** | Optional, for consumer projects whose quality commands use it; this plugin checkout is npm-canonical. | Run those consumer quality commands. It is not required to install this plugin. <!-- sources: scripts/check-package-manager.mjs:140-158,193-235; .claude/rules/development.md:36-37 --> |
66
+ | **tsgo** | Optional, for a consumer project's TypeScript gate. Not declared as a dependency here. | Run a consumer gate configured to use `tsgo`; this repo's typecheck uses `node --check` instead. <!-- sources: package.json:73,80-103; scripts/typecheck.mjs:8,28 --> |
67
+
68
+ ### Dependencies for individual features
69
+
70
+ | Dependency | Status / scope | Without it, you cannot... |
71
+ |---|---|---|
72
+ | **Bash** | Feature requirement for the `.mcp.json` MCP server used by Codex CLI, Cursor and Pi. Claude Code starts its MCP server with Node. | Start the shell MCP server. The [native Node MCP setup](#native-windows-mcp-opt-in) avoids Bash and jq for that server. <!-- sources: .mcp.json:4-8; .claude-plugin/plugin.json:15-17; scripts/windows-setup.mjs:7-29 --> |
73
+ | **jq** | Feature requirement for the `.mcp.json` shell MCP server (Codex CLI, Cursor and Pi); Claude Code's Node MCP does not need it. Also used by plugin validation and the shell grounding helper. | Parse MCP requests with the shell server or run `validate-plugin.mjs`; the grounding helper skips when jq is absent. The native Node MCP server does not need jq. <!-- sources: .mcp.json:4-8; .claude-plugin/plugin.json:15-17; scripts/mcp-server.sh:41-55; scripts/validate-plugin.mjs:32-37; scripts/compute-grounding-injection.sh:31-33; scripts/mcp-server.mjs:111-154 --> |
74
+ | **GitLab and authenticated glab** | Optional GitLab integration; only needed for repositories using GitLab. | Use GitLab API-backed issue and merge-request operations. GitLab is not needed for a GitHub repository. <!-- sources: scripts/lib/vault-backfill/glab.mjs:29,53; scripts/lib/autopilot/mr-draft.mjs:144 --> |
75
+ | **GitHub and authenticated gh** | Optional GitHub integration; only needed for repositories using GitHub. | Use GitHub API-backed issue, pull-request and protection-audit operations. <!-- sources: scripts/lib/autopilot/mr-draft.mjs:167; scripts/github-protection-audit.mjs:248-279 --> |
76
+ | **A Markdown notes vault (`vault-dir` / `SO_VAULT_DIR`)** | Optional: enable it in Session Config with `vault-integration.enabled: true` and `vault-integration.vault-dir`. `SO_VAULT_DIR`, a `vault-dirs:` entry in your host-local `owner.yaml` that matches the working directory, or `paths.vault-dir` there (in that order) overrides the committed path on one machine. | Run `/portfolio` (with `gitlab-portfolio` enabled it exits with an error), mirror session and learning records into the vault at `/close` and `/evolve`, keep the live-status board `01-projects/_active-sessions.md`, or get the untracked-vault-files note at `/close`. <!-- sources: scripts/lib/config/host-paths.mjs:38; scripts/lib/config.mjs:88-89,114-133; skills/portfolio/SKILL.md:34,39; scripts/lib/gitlab-portfolio/cli.mjs:193-199,211-216; skills/vault-mirror/SKILL.md:26-32,277-279; skills/session-start/SKILL.md:117-119; skills/session-start/references/phase-1-7-vault-status-board.md:7,42; skills/session-end/SKILL.md:321,323; skills/session-end/references/phase-3-documentation-updates.md:208; skills/session-end/references/phase-3-7e-vault-orphans.md:11 --> |
77
+ | **npx, Playwright and browser binaries** | Feature requirement for web end-to-end tests; install Playwright in the **target project**. | Run the web test driver. It refuses to download a missing Playwright package implicitly; install the target's browser binaries explicitly. <!-- sources: scripts/lib/playwright-driver/runner.mjs:89,258-271,301 --> |
78
+ | **@axe-core/playwright** | Optional package in the web test target. | Collect axe accessibility violations; that check is skipped when the package is absent. <!-- source: scripts/lib/playwright-driver/runner.mjs:61-80,224 --> |
79
+ | **peekaboo, macOS 15+ and UI permissions** | Feature requirement for native macOS UI tests; Screen Recording and Accessibility permissions are required. | Drive native UI tests and capture their screenshots / accessibility tree. Other platforms skip this driver. <!-- sources: skills/peekaboo-driver/SKILL.md:49-58,63-84 --> |
80
+ | **offload and a configured remote host** | Optional remote wave execution and remote pre-push gate. | Dispatch those jobs to the declared host. The coordinator invokes `offload`; its transport setup belongs to that external tool. <!-- sources: scripts/lib/wave-executor/remote-dispatch.mjs:45-47,180-184; scripts/lib/pre-push-remote-gate.mjs:58-70 --> |
81
+ | **tmux** | Optional session side-channel layout. | Create the tmux panes used to observe session progress. <!-- source: scripts/lib/tmux-layout/tmux-shell.mjs:24,64 --> |
82
+ | **ps** | Feature requirement for process and resource probes on supported POSIX hosts. | Collect the process snapshots used by resource monitoring and orphan detection. <!-- sources: scripts/lib/resource-probe/probe-platform.mjs:167; scripts/lib/orphan-reaper/ps-snapshot.mjs:164 --> |
83
+ | **sysctl, memory_pressure and vm_stat (macOS); cat (Linux); tasklist (Windows)** | Optional host resource probes, using the OS-specific tools. | Collect the corresponding swap, memory-pressure and process measurements; unsupported or failed probes return unknown. <!-- sources: scripts/lib/resource-probe/probe-platform.mjs:115-128,245-299 --> |
84
+ | **du** | Feature requirement for project hygiene size measurements. | Measure directories in that hygiene check. <!-- source: scripts/lib/project-hygiene.mjs:284 --> |
85
+ | **tar** | Feature requirement for package staging and vault-consolidation backups. | Extract the npm package during staging or create a consolidation backup. <!-- sources: scripts/lib/plugin-package-stage.mjs:129; scripts/lib/vault-consolidate-fs.mjs:294 --> |
86
+ | **awk, grep, sed and sort** | Feature requirement for the shell documentation-consistency check. | Run that check's text extraction and comparison pipeline. <!-- sources: scripts/check-doc-consistency.sh:5,117-139 --> |
87
+
88
+ The Node and shell binaries must be visible to the agent process, not just your
89
+ interactive terminal. Hook shells do not read your shell startup files; use
90
+ `SO_NODE_BIN=/absolute/path/to/node` if Node cannot be resolved on the inherited
91
+ PATH. <!-- source: hooks/run-node.sh:5-18,22-29 -->
22
92
 
23
93
  ## Install
24
94
 
25
- ### Native Windows MCP (opt-in, #1522)
95
+ ### Native Windows MCP (opt-in)
96
+
97
+ Claude Code does not need this setup: its manifest starts the MCP server with
98
+ Node directly. This opt-in is only for harnesses that read `.mcp.json` (Codex
99
+ CLI, Cursor and Pi); their native Windows operation has not yet been measured.
100
+ <!-- sources: .claude-plugin/plugin.json:15-17; .mcp.json:4-8; scripts/windows-setup.mjs:7-29 -->
26
101
 
27
- With the updated package installed, find its `installPath` using
28
- `claude plugin list --json`, then run in PowerShell:
102
+ For an installed plugin in `plugins/cache`, run from the updated checkout in
103
+ PowerShell: <!-- sources: scripts/windows-setup.mjs:9-18,37-40 -->
29
104
 
30
105
  ```powershell
31
106
  node scripts/windows-setup.mjs "C:\path\to\installed\session-orchestrator"
@@ -42,12 +117,10 @@ Reinstalling the plugin replaces this local
42
117
  configuration; run setup again for the new install path. This opt-in avoids
43
118
  accidentally selecting Windows' `bash.exe` WSL launcher. The native server has
44
119
  the same two read-only tools and needs neither Bash nor jq.
120
+ <!-- sources: scripts/windows-setup.mjs:7-34; .mcp.json:4-8; scripts/mcp-server.mjs:111-154 -->
45
121
 
46
- Windows package staging also launches npm through its JavaScript entrypoint;
47
- Node and npm must be installed together or npm must be on an absolute PATH.
48
- This does **not** establish native Windows hook/guard parity: Git Bash remains
49
- necessary for the existing shell hooks, tracked separately in #1394. The
50
- repository-wide validation currently has a POSIX-only jq discovery step.
122
+ Windows package staging launches npm through its JavaScript entrypoint, so Node
123
+ and npm must be installed together or npm must be on an absolute PATH.
51
124
 
52
125
  | Platform | Install |
53
126
  |---|---|
@@ -120,9 +193,9 @@ into `~/.cache/session-orchestrator/plugin-package/` (`$XDG_CACHE_HOME` if set),
120
193
  updates the Claude Code marketplace and plugin from that packed copy, installs
121
194
  missing runtime dependencies in the plugin directory, and re-runs the Codex
122
195
  installer if the plugin is installed in Codex (it installs from the same packed
123
- copy, #1518). The marketplace must read the
196
+ copy). The marketplace must read the
124
197
  packed copy, never the clone: a directory marketplace on the working checkout
125
- copies every file of it into the plugin cache, `.env.local` included (#1515).
198
+ copies every file of it into the plugin cache, `.env.local` included.
126
199
  The Claude step refuses while any `directory` marketplace reads the clone and
127
200
  prints the exact `claude plugin marketplace remove <name>` and
128
201
  `claude plugin marketplace add ~/.cache/session-orchestrator/plugin-package`
@@ -644,7 +644,7 @@ $ # hooks scope
644
644
  |---|---|---|
645
645
  | Claude Code | `CLAUDE.md` | plugin skills (`.claude-plugin/`) |
646
646
  | Codex CLI | `AGENTS.md` | `.codex-plugin` skills |
647
- | Cursor | `AGENTS.md` | `.agents/skills`, `.cursor/skills` |
647
+ | Cursor | `AGENTS.md` **and** `CLAUDE.md` (superseded 2026-10-21: Cursor's rules help documents `CLAUDE.md`; the survey had `AGENTS.md` only) | `.agents/skills`, `.cursor/skills` |
648
648
  | Copilot CLI | `AGENTS.md` **and** `CLAUDE.md` | `.github/skills`, `.claude/skills`, `.agents/skills` |
649
649
  | OpenCode | `AGENTS.md` | `~/.claude/skills` |
650
650
  | Amp | `AGENTS.md` | `.agents/skills` |
@@ -9,8 +9,8 @@ what happens to your state, and how to go back.
9
9
 
10
10
  The removals follow a two-signal rule measured over a 90-day window — **0 telemetry ∧ 0
11
11
  fleet invocation ∧ no runtime consumer** — not a judgement call. Evidence:
12
- [`docs/audits/2026-09-06-360-audit.md`](./audits/2026-09-06-360-audit.md), per-agent raw
13
- reports under [`audits/2026-09-06-360-audit/w1/`](./audits/2026-09-06-360-audit/w1/). Full
12
+ the 2026-09-06 360° audit and its per-agent raw reports (audit reports stay in the
13
+ private GitLab repository and are not part of the public mirror). Full
14
14
  change list: [CHANGELOG.md](../CHANGELOG.md).
15
15
 
16
16
  ## 1. What Changes
package/docs/mods.md ADDED
@@ -0,0 +1,118 @@
1
+ # Session Orchestrator Mods
2
+
3
+ A mod changes Claude Code's own interface or behaviour: a status line, a band above the prompt, a toast, a hook on tool calls. Session Orchestrator ships one, **so-hud**, in [`mods/so-hud/`](../mods/so-hud/README.md). It is installed separately from the plugin and runs in Claude Code only — Codex, Cursor and Pi do not load it.
4
+
5
+ ```text
6
+ /plugin install so-hud@kanevry
7
+ ```
8
+
9
+ Requirements: Claude Code with mod support (tested with **2.1.296**), Node.js 24 or later, and the Session Orchestrator release that ships the mod, or later.
10
+
11
+ ## so-hud
12
+
13
+ so-hud has two parts: a cockpit that shows state, and protection gates that check shell commands (the `Bash`, `Monitor` and `PowerShell` tools), file edits and tool results before they run or reach the model. A band above the prompt and the `/profile` command pick how strict the gates are (see [Profiles](#profiles)).
14
+
15
+ ### Cockpit
16
+
17
+ A status line for the running session, a warning band when something needs attention, the number of peer sessions, and one beacon file per session that other sessions can read through `$.so.beacons()`. All Session Orchestrator state comes from `scripts/so-hud.mjs`; the mod never writes STATE.md, locks, dispatch records or events. Details: [`mods/so-hud/README.md`](../mods/so-hud/README.md).
18
+
19
+ The status line says `nicht eingerichtet` for a part that is not set up: `SO nicht eingerichtet` when no Session Orchestrator installation is found, `Vault nicht eingerichtet` when the repository's Session Config has no `vault-integration` block (`Vault ?` when `CLAUDE.md` or `AGENTS.md` cannot be read). An enabled vault mirror that has never run shows `Vault noch kein Lauf`. `teilweise gemessen` marks a measurement with a gap; a repository where no session has written events yet is not marked.
20
+
21
+ ### Protection gates (0.2.0, #1551)
22
+
23
+ From 0.2.0 on, so-hud checks tool calls before they run. Installed 0.1.0 users get this with the update: 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. Read [Upgrading from 0.1.0 with scope user](#upgrading-from-010-with-scope-user) before you update.
24
+
25
+ **SEC-008 traps** — commands that would print a secret: reading an env file (`.env`, `.env.local`, `.envrc`, `prod.env`, `.env-prod`) with `cat`, `less`, `head`, `tail`, `grep`, `rg`, `sed`, `awk`, `cut`, `sort`, `xxd`, `base64`, `diff` and similar (`.env.example`, `.env.sample`, `.env.template` and `.env.schema` are fine, and so are `grep -q`/`-c`/`-l` and `sed -n 's/^\([A-Z_][A-Z0-9_]*\)=.*/\1/p'`, which print no values); an environment dump (a bare `env`, `printenv`, `set`, `export`, `export -p`, `readonly`, or `declare`/`typeset` without a name) whose output is not cut down to the key names by the very next stage (`cut -s -d= -f1` or `awk -F= '{print $1}'`); `printenv` of a token variable; `echo`/`printf` of a token variable such as `$TOKEN` or `$GITLAB_TOKEN`, also when later pipe stages only pass it on (`| base64`, `| tr …`); `docker inspect` without `--format`; or a token variable in a curl `Authorization` header argument or in a curl URL query. Commands inside `bash -c "…"` are checked too. Refused on every profile. The refusal names the safe replacement, for example `env | cut -s -d= -f1 | sort` instead of `env`.
26
+
27
+ **Rule traps** — `git add .` / `-A` / `--all` (PSA-004), `claude -p` without `env -u ANTHROPIC_API_KEY`, `${PIPESTATUS[` (empty in zsh), and `grep -c … || echo 0`. A toast on `locker`, refused on `ausgewogen` and `streng`, again with the replacement command.
28
+
29
+ **Outward actions** — `git push` to `main` or `master`, with force, with tags or as a delete; `glab mr merge` / `glab mr accept` / `gh pr merge`, and `gh api` / `glab api` calls that write to a `…/merge` endpoint, run a merge mutation, or write with a `branch=main`/`branch=master` field (for example a file or commit created through the API); `glab release create` / `gh release create`; `npm`, `pnpm`, `yarn`, `changeset` and `cargo publish`, including workspace forms (`npm -w <pkg> publish`, `pnpm --filter <x> publish`) and `npx --yes npm publish`; `vercel --prod`, `vercel promote`, `vercel rollback`; `supabase db push`, `supabase db reset --linked`, `supabase migration up --linked`; `fly deploy`; `railway up`. These are also recognised inside `bash -c "…"`, `sh -c`, `eval`, after `nice`, `timeout` or `sudo`, and across `\` line continuations. A `git push --dry-run`, and a publish or deploy with `--dry-run`, is not held, as long as no later `--no-dry-run` undoes it. A command line that contains an outward word (`git … push`, `publish`, `gh`/`glab … merge`, `deploy`, `--prod`, `gh`/`glab … release`) inside a construct so-hud cannot read — `$(…)`, backticks, `xargs`, a shell function, a shell started by path or with options before `-c`, a git alias defined with `-c alias.…` — is held as well. Where a person can answer (the terminal, or an attached desktop, IDE or phone surface), the call is held and the owner confirms in two steps within 30 seconds — two questions in Claude Code's own question dialog. Before the second question so-hud measures the facts again; if they changed (for example a new commit to push), it refuses and asks for a new confirmation. Only after the second confirmation does the original call run, exactly once. Declining, closing the dialog or letting the 30 seconds run out refuses it.
30
+
31
+ Only a plain push of a feature branch runs without a question (#1569). Plain means all of this holds:
32
+
33
+ - **Options:** only `-u`/`--set-upstream`, `-q`/`--quiet`, `-v`/`--verbose`, `--progress` or `--no-progress`, written out exactly.
34
+ - **Destination:** at most a remote name and one branch name (no `:`, no `+`, not `@`, not `main` or `master`, also not `Main` or `MASTER`, not also a tag name); or no branch and an upstream that is such a branch; or `HEAD`, written in capitals, for the branch checked out. `git push origin HEAD` and `git push -u origin HEAD` therefore run without a question on a feature branch. `HEAD` and a bare `git push` stand for the branch so-hud measured. A branch the push names runs without a question only when git resolves it to exactly `refs/heads/<name>`: a branch name that is a symbolic ref to another branch (`feat/sym` pointing at `main`, which git would push as `feat/sym -> main`), or that a ref like `refs/feat/x` shadows, asks (#1570). Lowercase `head` asks: on a case-insensitive filesystem git reads it as `HEAD`.
35
+ - **Earlier commands that are not git:** so-hud measures the branch and the push configuration before the line runs. As soon as an earlier command on the line is not a git call, every push form asks, also one that names its branch (`npm test && git push origin feat/x`, `./switch.sh; git push origin HEAD`, `printf … >> .git/config; git push origin feat/x`): that command may have changed the branch or the push configuration. A command inside `$(…)` or backticks counts too (`x=$(./switch.sh) && git push`); `h=$(git rev-parse HEAD)` runs only a git call. A `cd` or a plain assignment such as `x=1` runs no command and does not count. An earlier output redirection counts like such a command, also on an allowed git step, a `cd`, an assignment or `exec` (`git log -1 --format=… >> .git/config; git push origin feat/x`, `cd . > .git/HEAD; git push origin HEAD`, `exec 3>>.git/config`): it may write `.git/config` or `.git/HEAD`. Only these write nothing and do not count: input redirections (`<`, `N<`, `<<`, `<<<`), process substitutions (`<(…)`, `>(…)`), `>`, `>>` or `&>` with any descriptor onto exactly `/dev/null` (`2>/dev/null`, `&>/dev/null`), and `>&` from stdout or stderr onto stdout or stderr (`2>&1`, `>&2`). Every other form asks, for example `>&3`, `&>>/dev/null`, `>|`, `<>`, `<&` or a quoted `"/dev/null"`. A `>` inside quotes (`git commit -m "a > b"`) is no redirection.
36
+ - **Remote:** a plain name that is defined in git config (`remote.<name>.url`). A remote that exists only as a legacy `.git/remotes/<name>` file asks.
37
+ - **Earlier git calls on the line** only read or work locally, an allowlist: `add`, `commit`, `status`, `rev-parse`, `ls-files`, `branch --show-current`, `tag -l` or `tag --list` with no other word (listing only), and `checkout -- <paths>` (restoring files; `checkout main -- <path>` asks); `fetch` only with no option and no word containing `::` (`git fetch`, `git fetch origin`, `git fetch origin main`); `log`, `show` and `diff` only when every option is a display option: `--oneline`, `--stat`, `--shortstat`, `--numstat`, `--name-only`, `--name-status`, `--summary`, `--cached`, `--staged`, `--no-color`, `--decorate`, `--graph`, `--abbrev-commit`, `-s`, `--no-patch`, `-p`, `-u`, `--patch`, `-q`, `--quiet`, `--`, `--no-ext-diff`, `--no-textconv`, a count (`-3`, `-n3`), or `--format=`, `--pretty=`, `--color=` or `--max-count=` with its value in the same word. Every other option asks: `--output` (also as `--output <file>`) writes a file such as `.git/HEAD`, `--ext-diff` and `--textconv` run a configured program, and on `fetch` `--upload-pack` (also abbreviated, `--upload=`) runs a program and `--set-upstream` moves the upstream. A git call inside `$(…)`, backticks, `<(…)` or `>(…)` counts as an earlier call (`echo $(git switch main) && git push …` asks), and a `git-<name>` program run by name or path never counts as local. `commit` counts only with options that open no editor and read no patch interactively (`-a`, `-q`, `-s`, `-n`/`--no-verify`, `-o`, `-i`, `-v`, `--allow-empty`, `--amend`, `--no-edit`, `--author`, `--date`, `--cleanup`, `--trailer`, `-S`/`--gpg-sign`, `--no-gpg-sign`, `-u`, `--dry-run`, `--short`, `--porcelain`, short bundles such as `-am`) and with a message (`-m`, `-F`, `-C`, a plain `--fixup=<commit>`, or `--amend --no-edit`): `git commit` alone, `-e`, `--amend` without a message, `-c`, `--fixup=amend:`/`reword:`, `--squash`, `-t`, `-p` and `--interactive` ask, because they run `core.editor`, the pager or `interactive.diffFilter`. `add` counts only with `-A`, `-u`, `-f`, `-v`, `-n`, `-N`, `--all`, `--update`, `--force`, `--dry-run`, `--intent-to-add`, `--ignore-errors`, `--ignore-missing`, `--ignore-removal`, `--sparse`, `--refresh` or `--chmod=±x`; `-p`, `-i`, `-e` and `--renormalize` ask (#1573 input). `log` and `show` may also verify signatures (`--show-signature`, or a `--format=`/`--pretty=` value with a `%G` placeholder): that is local, but runs `gpg.program` (see Repository configuration).
38
+ - **Hooks:** `add`, `commit`, `status`, `diff`, `fetch` and `checkout -- <paths>` can fire a git hook, and a hook can run any program, which may move the branch or rewrite the push configuration. When one of them runs earlier on the line, every push form asks unless so-hud measured in the push's folder that none of the hooks those steps can fire exists (#1574). Only those names are looked up — for `git commit`: pre-commit, prepare-commit-msg, commit-msg, post-commit, post-rewrite, reference-transaction, post-index-change, pre-auto-gc — in the hooks directory (`git rev-parse --git-path hooks`, which follows `core.hooksPath`), as `<name>` or `<name>.<ext>`, never `*.sample`. In a husky v9 repository (`core.hooksPath` ending in `.husky/_`) a hook counts only when the user's own `.husky/<name>` exists, and only after so-hud confirmed that `_/h` and the stubs are husky's own files; any other content counts as a hook. A hook defined in git config (`hook.<name>.command` with `hook.<name>.event`, includes followed) counts when its event is one of those names; an undocumented `hook.*` key asks. A git step the map does not know, an unrecognised layout or a failed read counts as "hooks may run". Such a step in a folder other than the push's, or in an unknown one, makes every push form ask whatever was measured, because it fires the hooks of that folder (`git -C ../b commit -m x && git push origin feat/x`, `cd ../b && git commit -m x && cd ../a && git push origin feat/x`, `git commit -m x && git -C ../a push origin feat/x`). Folders are compared as written, so `git -C . status && git push` asks too; `cd sub && git commit -m x && git push origin HEAD` runs both in `sub`. In a husky repository `git commit … && git push …` on one line asks only when a hook the commit can fire exists under `.husky/` (for example `.husky/pre-commit`); a push in a separate call does not ask for hooks.
39
+ - **Repository configuration** (#1573): a key in the repository's git config can make a local step run a program — `core.fsmonitor` for `status` and `diff`, `diff.external` and `diff.<x>.textconv` for `diff` and `log -p`, `filter.<x>.*` for `add` and `checkout -- <paths>`, `core.sshCommand`, `remote.<r>.uploadpack` and `credential.helper` for `fetch`, `gpg.program` for `commit` — and that program may move the branch after so-hud measured it (measured with git 2.56.0: `core.fsmonitor` pointing at a script that runs `git switch main` left a repository on `main` after `git status`). When any git call runs earlier on the line, so-hud reads every configured key once (`git config --null --show-scope --name-only --list` in the push's folder) and every push form asks unless each key of the scopes `local`, `worktree` and `command` (includes followed) is on an allowlist: `core.repositoryformatversion`, `core.filemode`, `core.bare`, `core.logallrefupdates`, `core.ignorecase`, `core.precomposeunicode`, `core.sparseCheckout`, `core.sparseCheckoutCone`, `index.sparse`, `core.hooksPath` (judged under Hooks), `extensions.worktreeConfig`, `user.name`, `user.email`, `gc.auto`, `maintenance.auto`, `submodule.recurse`, `push.*`, `remote.pushDefault`, `branch.<b>.remote`/`merge`/`pushRemote`/`vscode-merge-base` and `remote.<r>.url`/`pushurl`/`fetch`/`push`/`mirror`/`glab-resolved*`. Any other key asks, `include.path` included; a failed or unparseable read asks too. Because a git step runs the configuration of the folder it runs in, any git step in a folder other than the push's, or in an unknown one, makes every push form ask, also one that fires no hook (`git -C ../b diff && git push origin HEAD`, `cd ../b && git diff; cd ../a && git push origin HEAD`); folders are compared as written, so `git -C . log && git push` asks too. A `remote.<r>.url` or `pushurl` whose value has the `<transport>::` form asks too: `git fetch` starts `git-remote-<transport>` from `PATH`. The values are read in the same call and never shown. The `global` and `system` scopes hold the host's own helpers, so they are judged by a denylist instead: `core.fsmonitor` other than a boolean, `diff.external`, `diff.<x>.textconv`/`command`, `core.sshCommand`, `core.askPass`, `core.gitProxy`, `remote.<r>.uploadpack`/`receivepack`/`vcs`, `uploadpack.packObjectsHook`, a remote URL in the `<transport>::` form or a `url.<base>.insteadOf` rewriting into one, a `filter.<x>.clean`/`smudge`/`process` other than git-lfs's own values (`git-lfs clean -- %f`, `git-lfs smudge -- %f`, `git-lfs filter-process`), a `credential.helper` other than an empty value, `osxkeychain`, `manager`, `manager-core`, `wincred`, `libsecret`, `store`, `cache` or `gh`/`glab auth git-credential` (bare or from `/opt/homebrew/bin`, `/usr/local/bin`, `/usr/bin`), `gpg.program` when a `commit` on the line signs (`commit.gpgSign` on there, or `-S`/`--gpg-sign` on the commit) or a `log`/`show` on the line verifies signatures, and `log.showSignature` on behind any `log` or `show` (it makes them verify). Not judged there: `core.pager` (no step's output is a terminal), `core.editor` (a commit that would open one asks as a step), includes (the included entries are judged themselves). A submodule or embedded repository has a configuration of its own that `git status`, `git diff` and `git fetch` run when they step into it, so a push behind a git step also asks when the index of the push's folder holds any gitlink (mode 160000, also an embedded repository added without `.gitmodules`), counted with `git ls-files --stage`; a failed count asks too.
40
+ - **`git -c`**, on the push and on every earlier git call on the line, sets nothing other than `user.name`, `user.email`, `core.quotepath` or `color.*`.
41
+ - **Variables:** every variable set on the line up to the push — `NAME=value`, `NAME+=value` or `NAME[i]=value` in front of a command or as its own statement, `env -u NAME`, or through `export`, `declare`, `typeset`, `local`, `readonly`, `unset`, `let`, `read`, `printf -v`, `mapfile`, `readarray`, `getopts` or `vared` (#1571; a `declare -n` nameref counts the name it points to) — has a harmless name: a single lowercase letter; `rc`, `out`, `msg`, `sha`, `ref`, `head`, `base`, `branch`, `res`, `ok` or `tmp`; or one or two characters, an uppercase letter followed by at most one uppercase letter or digit (`D`, `WT`, `L1`, `TZ`). Every other name asks — `HOME`, `PATH`, `XDG_*`, `GIT_*`, `SKIP_PRE_PUSH` — and so do `source`, `.`, `alias` and `hash`.
42
+ - **Folder:** a `cd` earlier on the line moves the folder so-hud measures in only when its effect is certain. A `cd` in a subshell `( … )` or a `{ … }` group, in a background job, in a pipeline, after `||`, behind a prefix such as `if` or `command`, or written as `cd -`, `popd` or with an option makes the folder unknown, and the push asks. So does a relative `cd sub` (not starting with `.`) while `CDPATH` is set in so-hud's environment: the shell looks it up in `CDPATH` first (#1573 input). A zsh `cdpath` set only in the session shell's own startup files is not seen.
43
+ - **Push configuration** is the standard one. so-hud reads it with `git config` in the push's folder: every `push.*` key, `remote.<remote>.push`, `remote.<remote>.mirror`, `remote.pushDefault`, `branch.<branch>.pushRemote` and `submodule.recurse`. It passes only when `push.default` is unset or `simple` (or `current`, when the push names its branch), `push.autoSetupRemote` is the only other `push.*` key set, `remote.<remote>.push` is unset, `remote.<remote>.mirror` and `submodule.recurse` are unset or false, and — when the line names no remote — `branch.<branch>.pushRemote` and `remote.pushDefault` are unset or name the same remote. Anything else asks: `push.default` set to `upstream`, `tracking`, `matching` or `nothing`, `push.followTags`, `push.pushOption`, `push.recurseSubmodules`, a push refspec, a mirror remote, a push remote that points elsewhere.
44
+
45
+ An input redirection in front of the command is read through: `</dev/null git push origin feat/x` is `git push origin feat/x`. Everything else is held: for example `git push origin head`, a `src:dst` refspec, `--no-verify`, push options (`-o`, which can merge on GitLab), abbreviated options such as `--mirr`, `--git-dir`/`--work-tree` or `GIT_DIR`-style variables, a push after `git switch`, `git checkout <branch>`, `git tag <name>` or `git config` on the same line, and every push for which one of the `git` reads behind this decision failed.
46
+
47
+ **Collision guard** — before `Edit`, `Write` or `NotebookEdit`, so-hud checks whether the file lies in the declared wave scope of another live Session Orchestrator session in the same working copy. Peers in another worktree never collide, and files outside the repository are not checked. A peer whose scope is not known shows a line in the band instead. The check uses the last measurement of `scripts/so-hud.mjs` (refreshed every 15 seconds); before the first successful measurement, or in a repository without Session Orchestrator, there is nothing to check against and the edit runs.
48
+
49
+ **Secret shield** — a tool result that contains a GitLab, GitHub, Anthropic, OpenAI, Stripe, Slack, AWS or Google token, or a private key, reaches the model as `[<kind> entfernt · sha256:<12 hex>]`, and a toast asks to rotate the token.
50
+
51
+ ### Profiles
52
+
53
+ The `strictness` setting picks the profile; the default is `ausgewogen`.
54
+
55
+ | Check | `locker` | `ausgewogen` | `streng` |
56
+ |---|---|---|---|
57
+ | SEC-008 trap | refused | refused | refused |
58
+ | Rule trap | toast, the call runs | refused | refused |
59
+ | Outward action (interactive) | asked twice | asked twice | asked twice |
60
+ | File in a peer's wave scope | toast, the edit runs | asks: [Trotzdem] [Worktree vorschlagen] [Abbrechen] | refused, with a hint to work in an own worktree |
61
+ | Secret in a tool result | replaced, toast | replaced, toast | replaced, toast |
62
+ | The gate itself fails | refused, except calls that are neither outward nor SEC-008 | refused | refused |
63
+
64
+ Any other `strictness` value falls back to `ausgewogen`, the default. The texts in the dialogs, toasts and refusals are German for now.
65
+
66
+ **Choosing a profile (#1552).** Until a profile is chosen, a band above the prompt shows `Profil: ausgewogen (Vorgabe, noch nicht gewählt)`, one line on what the profile does, one on what holds on every profile, and three buttons: `locker`, `ausgewogen`, `streng`. 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`; so-hud reloads with it, and every new session starts with it, without the band. A `strictness` set by hand in `/config` counts as chosen too.
67
+
68
+ `/profile` answers without a model turn: it shows the profile, its effect, what holds on every profile and where to change it. Its text is stored in the conversation like any command output, so the model reads it on its next turn. In the terminal it also opens the band with the three buttons and `Schließen`, which hides the band for the rest of the session; while no profile is chosen, the band returns at the next session start. Band and `/profile` work without a Session Orchestrator installation.
69
+
70
+ The profile steers only so-hud's own gates. Session Orchestrator's own guards — the destructive-command guard, the pre-push hook, the scope guard — do not read it.
71
+
72
+ ### Upgrading from 0.1.0 with scope user
73
+
74
+ so-hud 0.1.0 only displayed state: its `tool.check` hook passed every call through. 0.2.0 adds the protection gates above. Outward actions such as a push to `main`, a merge (also through `gh api` / `glab api`), a release or a publish ask for two confirmations within 30 seconds, and SEC-008 traps refuse commands that would print a secret. Both hold on every profile.
75
+
76
+ `/plugin install` uses scope `user` unless told otherwise, and an update at that scope changes every Claude Code session on the host at once. That includes coordinator or lead sessions that merge through the API once the owner has said so: from the update on, each such merge waits for the two confirmations. Before you update:
77
+
78
+ 1. Pick the `strictness` per host. 0.1.0 already has this row in `/config` (it changed nothing there yet), so you can set `locker`, `ausgewogen` or `streng` before the update; after it, the band above the prompt and `/profile` do the same. The profile decides rule traps and collisions, not outward actions or SEC-008 traps (see [Profiles](#profiles)).
79
+ 2. Try 0.2.0 in one session first: load it from a checkout with `claude --plugin-dir ./mods/so-hud`, or install it at project scope (`claude plugin install so-hud@kanevry --scope project`) in one repository.
80
+ 3. Only then update the user-scope install (`claude plugin update so-hud@kanevry`) and restart the sessions.
81
+
82
+ ### Receipts
83
+
84
+ Every decision on an outward action appends one line per held action (a command with a push and a publish writes two lines), and every collision that was asked about appends one line, to `~/.config/session-orchestrator/hud/aktionen.jsonl`. The file has mode 0600 and its directory 0700. It is only ever appended to, never rewritten, so other tools may append to it as well.
85
+
86
+ so-hud writes a receipt or a beacon only when `hud/` (and, for beacons, `hud/beacons/`) is a real directory owned by the current user, not a symlink, and appends only to a regular file with exactly one link: a symlink or a hard link in place of `aktionen.jsonl` is refused. The directory is checked through a handle opened with `O_NOFOLLOW`, so the check and the `chmod` hit the same directory. Where Node offers no `O_NOFOLLOW` (native Windows), so-hud checks with `lstat` instead and sets the mode by path; a link swapped in between those two steps would be followed (creating one there needs admin rights or Developer Mode), and the owner check is skipped because Node reports no user id there. Removing the session's beacon at the end follows no symlink. A process that can rename entries in `~/.config/session-orchestrator` itself could still swap `hud/` between the check and the write.
87
+
88
+ | Field | Content |
89
+ |---|---|
90
+ | `zeit` | ISO timestamp |
91
+ | `session_id` | the session that decided |
92
+ | `vorgang` | always `pane-aktion` |
93
+ | `aktion` | `aussenwirkung` (outward action) or `kollision` |
94
+ | `argv_sha256` | SHA-256 (hex) of the UTF-8 of `JSON.stringify([tool, text])`, the tool name standing where a program would stand in an argv: `["Bash", "<command>"]` (or `Monitor`, `PowerShell`) with the command exactly as the model sent it for `aussenwirkung`; `["Edit", "<file path>"]` (or `Write`, `NotebookEdit`) for `kollision`. Neither the command nor the path is written, because a command can carry a token |
95
+ | `exit` | exit code, or `null` |
96
+ | `profil` | `locker`, `ausgewogen` or `streng` |
97
+ | `ergebnis` | `ausgefuehrt` (ran after confirmation), `abgelehnt` (declined, dialog closed, facts changed, or refused by a later check), `verfallen` (30 seconds passed), `ohne-zeichenflaeche` (nobody to ask, see below); `fehler` is reserved and not written by 0.2.0 |
98
+ | `art` | optional short lowercase label |
99
+
100
+ The field set is closed: a receipt with any other field, or larger than 1 KB, is not written.
101
+
102
+ ### Headless runs
103
+
104
+ so-hud asks wherever a surface is attached — the terminal, or a desktop, IDE or phone client. Only a run with no surface at all (a plain `claude -p`, or an Agent SDK run nobody is attached to) has nobody to ask. There so-hud writes a receipt with `ergebnis: ohne-zeichenflaeche` and lets the outward action run. What still checks it is outside so-hud: a push passes the repository's own pre-push hook if it has one, and a force push is blocked by Session Orchestrator's destructive-command guard. **Merges, releases, publishes and deploys are not checked by any Session Orchestrator gate in such a run.** SEC-008 traps, rule traps and the secret shield apply unchanged. An edit that collides with a peer's scope on `ausgewogen` is refused there, because nobody can answer the question.
105
+
106
+ ### Failure direction
107
+
108
+ If the gate itself fails, the call is refused (fail-closed). The one exception is `locker`, where a call that is neither an outward action nor a SEC-008 trap runs.
109
+
110
+ ## Limits
111
+
112
+ - **Sessions that do not run Session Orchestrator are invisible to the collision guard.** It knows peers only from the Session Orchestrator session registry and their declared wave scope. A plain Claude Code session, another agent CLI or an editor in the same working copy can change any file without a warning.
113
+ - **The gates are a hurdle, not the authority.** The command-line gates — pre-push hook, destructive-command guard, CI — stay authoritative where they exist; a command that so-hud lets through still has to pass them. A command run outside Claude Code is never seen by so-hud.
114
+ - **Commands are read by pattern.** The command line is cut at `&&`, `||`, `;`, `|` and newlines; quoted parts of one word are joined. Comments, and heredocs whose body is only text (a quoted delimiter, or an unquoted one whose body holds no `$(`, `$((`, `$[`, backtick or `${…}` other than `${name}`, read by `cat` without writing a file, by `git commit`, or by `gh`/`glab` `issue`/`mr`/`pr`, including Claude Code's own `git commit -m "$(cat <<'EOF' … EOF)"`), are removed before anything is read, so a commit message or an issue body that mentions `git push` is no command (#1572, #1574). Elsewhere a `;` or a line break inside quotes still cuts the line, so `-m "wip; git push origin main"` asks (fail-closed). Constructs it cannot read (`$(…)`, backticks, `xargs`, shell functions, shells started by path) are held when an outward word appears in them. **Fail-closed fallback (#1570):** after the walker, any part of the line that spells an outward action so-hud knows — a tool word with its action in subcommand position: `git … push`, `gh`/`glab … mr|pr merge`, `… release create`, a forge API write to a ref or merge path, `npm`/`pnpm`/`yarn`/`changeset`/`cargo … publish`, `vercel --prod`/`promote`/`rollback`, `supabase db push`, `supabase db reset`/`migration up` with `--linked`, `fly deploy`, `railway up` — and formed no hit asks. That covers a wrapper or option so-hud does not know (`time -p`, `command -p`, `exec -a`, `nohup --`, `env -P`, `stdbuf`, `flock`, `lockf`, `doas`, `xcrun`, `script`, `coproc`, `trap "…"`), a `bash -c` that is not the first word, a shell fed on stdin (`sh <<<'…'`, `echo … | sh`), another interpreter (`python3 -c`, `node -e`, `perl -e`, also as a heredoc), a `case` branch, a command or git subcommand built from a variable (`${X}t push`, `git ${X}sh`), and git subcommands that run a shell command they are given (`rebase -x`, `bisect run`, `submodule foreach`). Not read as code there: the words of commands that never run them (`echo`, `printf`, `grep`, `cat`, `ls`, …) unless piped into one that may (`| sh`, `| awk`, `| sed`); a quoted argument of git or of a forge text command; the code of `node`, `python3`, `perl` and other interpreters on a line with no process API (`child_process`, `exec(`, `spawn(`, `subprocess`, `os.system`, `system(`, `qx`, …), so an analysis script that mentions `git push` runs; and a heredoc body `cat` writes to a file on a line that runs no file. Measured over 228,541 Claude Code transcript commands (21,928 of them hold an outward word): 305 now ask that did not, 275 no longer ask (the commit-message heredocs). Not recognised at all: git aliases from the user's own git config (`git p`), a command name taken from an environment variable set outside the line, scripts that publish or deploy (`npm run release`, `node scripts/release.mjs`, a file written and run without an outward word in its own words), and a process API spelled so the pattern does not match (`require('child' + '_process')`). Changes made by non-git commands earlier on the line (for example a script that edits `.git/config`) are not read either: so-hud measures before the line runs, so every push form behind such a command asks. Not measured: a program key git adds later in the global or system config (judged there by a denylist), and a program the push itself runs from the configuration (`core.sshCommand` on a bare `git push` with no git step before it); see Repository configuration above (#1573). The SEC-008 environment-dump trap goes by the command and its arguments. It catches `env` (also with `-0`/`-i`, in `( )`, or behind `command`/`builtin`/`VAR=x`) when no command follows, bare `set`, bare `export`/`readonly`, `declare`/`typeset` without a name, `typeset <SECRET_NAME>`, and `printenv` with no name or a secret one. Option settings like `set -e` and assignments like `export FOO=bar` stay free. A dump passes only if the stage directly after it cuts it to keys (`cut -s -d= -f1`, `awk -F= '{print $1}'`, `sed 's/=.*//'`); `printenv NAME` can never be filtered, because it prints the bare value. A secret that is echoed or fed as a here-string passes only into an allow list of silent consumers: digests, `openssl dgst`, `wc`, `read`, `curl --config -`, the stdin logins of named tools (`docker`/`podman`/`nerdctl`/`buildah`/`skopeo`/`oras login --password-stdin`, `helm registry login`, `crane auth login`, `gh auth login --with-token`, `glab auth login --stdin`), or a command whose every stdout target and stderr go to `/dev/null` (stdout alone for `echo`/`printf`; zsh writes to every listed target, so `>&2 >/dev/null` still prints). Runners such as `xargs`, `sh -c`, `env`, `sudo` or `timeout` never count as silent; anything else counts as printing it. The env-file readers are the listed ones only. The traps read a `bash -c '…'` behind another word too (`flock /tmp/l bash -c "cat .env"`), and a `printenv` with several names. A command written to avoid the patterns is not recognised.
115
+ - **The push gate leaves the push's own redirections alone.** An output redirection on the push itself (`git push origin feat/x > .git/config`) is not judged: the shell opens it before git starts, so it can only truncate the file, and git writes into it only after it has read its refs and config.
116
+ - **`Monitor` output is not shielded.** so-hud checks a `Monitor` command before it starts, but the lines it streams arrive as notifications, not as a tool result, so the secret shield does not see them.
117
+ - **The secret shield knows only the token formats listed above.** A secret in any other format passes through unchanged.
118
+ - **Engine:** tested with Claude Code 2.1.296. Other builds may differ in the mod API.
@@ -52,6 +52,7 @@ section is replaced by its default and reported via `droppedSections`
52
52
  | `paths.namespace-map-path` | string | no | `''` | Host-local repo-pseudonym JSON map (#725 D5). |
53
53
  | `paths.confidential-names-file` | string | no | `''` | Host-local JSON array of names for the CP11 leakage rule (#728a). Names live in that file only, never inline here. |
54
54
  | `dispatcher.autonomy` | `off` \| `advisory` \| `autonomous-gated` \| `''` | no | `''` | `''` = no override (#679). |
55
+ | `learnings.store-role` | `writer` \| `inbox` | no | absent | Absent, or an empty `learnings:`, = `writer`. Env `SO_LEARNINGS_STORE_ROLE` overrides it. `inbox` = this host (every repo on it) only appends to `learnings-inbox.jsonl` (#1516). Any other value, any other key in `learnings` (e.g. `store_role`), or an unreadable/unparseable owner.yaml that names `learnings`, is `invalid`: canonical-store writes refuse (`LearningsStoreRoleError`), they do not fall back to `writer`. |
55
56
  | `vaults`, `baselines` | lists | no | absent | Passed through untouched; parsed leniently at point of use. |
56
57
 
57
58
  ### Privacy Contract
@@ -84,7 +84,7 @@ Edit `hooks/hooks.json`. Each entry maps an event matcher to the Node command, r
84
84
  {
85
85
  "hooks": {
86
86
  "PreToolUse": [
87
- { "matcher": "Bash", "hooks": [ { "type": "command", "command": "sh \"$CLAUDE_PLUGIN_ROOT/hooks/run-node.sh\" \"$CLAUDE_PLUGIN_ROOT/hooks/example.mjs\"" } ] }
87
+ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "exec sh \"$CLAUDE_PLUGIN_ROOT/hooks/run-node.sh\" \"$CLAUDE_PLUGIN_ROOT/hooks/example.mjs\"" } ] }
88
88
  ]
89
89
  }
90
90
  }
@@ -36,8 +36,9 @@ does not generate:
36
36
  taxonomy, part of the Six Tactics in `skills/grill/soul.md`.
37
37
  - `/brainstorm` — three-lens (PM / Designer / Engineer) divergent ideation
38
38
  pass plus Mom-Test interview discipline, in `skills/brainstorm/SKILL.md`.
39
- - `/plan` — Opportunity Score ranking, Impact×Risk 2×2 triage, and an
40
- optional job-story format, in `skills/plan/SKILL.md` / `mode-feature.md`.
39
+ - `/plan` — evidence-ranked candidates, Impact×Risk 2×2 triage, a two-brief
40
+ pre-mortem panel (`skills/plan/prd-premortem-panel.md`) and an optional
41
+ job-story format, in `skills/plan/SKILL.md` / `mode-feature.md`.
41
42
  - `/discovery feature` — grounded, grep-verified intent-drift and
42
43
  stubbed/dead-feature probes (`skills/discovery/probes-feature.md`),
43
44
  routing judgment-based PM work (OST, personas, market-sizing) out to
@@ -68,7 +69,7 @@ roster, to limit auto-dispatch noise (see § Warnings).
68
69
  |---|---|---|---|
69
70
  | Assumption interrogation / red-teaming a plan | `strategy-red-team`-class pm-skills ↔ `/grill` | `skills/grill/soul.md` (Six Tactics, kill-assumption workup, pre-mortem) | Overlap — prefer `/grill` (session/gate-integrated, codebase-grounded) |
70
71
  | Divergent ideation / brainstorming | `brainstorm-ideas-*`-class pm-skills ↔ `/brainstorm` | `skills/brainstorm/SKILL.md` (three-lens PM/Designer/Engineer pass, Mom-Test) | Overlap — prefer `/brainstorm` (HARD-GATE + PRD hand-off already wired) |
71
- | PRD generation / feature scoping / prioritization | `create-prd`-class pm-skills ↔ `/plan feature` | `skills/plan/SKILL.md`, `mode-feature.md` (Opportunity Score, 2×2 risk triage, job-story option) | Overlap — prefer `/plan` (researched Q&A engine, issue creation, appetite/scope discipline already wired) |
72
+ | PRD generation / feature scoping / prioritization | `create-prd`-class pm-skills ↔ `/plan feature` | `skills/plan/SKILL.md`, `mode-feature.md` (evidence-ranked candidates, 2×2 risk triage, pre-mortem panel, job-story option) | Overlap — prefer `/plan` (researched Q&A engine, issue creation, appetite/scope discipline already wired) |
72
73
  | Documented-vs-enforced intent auditing | No pm-skills equivalent | `/discovery feature` (`skills/discovery/probes-feature.md`) | Unique to this plugin — grep-verified, not judgment-based |
73
74
  | Roadmap documents, stakeholder maps, persona trees as standalone artifacts | No equivalent here — this plugin folds personas/roadmap input into PRDs, not standalone living documents | `phuryn/pm-skills` | Unique to pm-skills — install alongside if a repo needs these as first-class artifacts |
74
75
  | Opportunity Solution Tree (OST) as a maintained document | No equivalent here (OST reasoning is routed out of `/discovery`'s verified pipeline per its Non-Goals) | `phuryn/pm-skills` | Unique to pm-skills |
@@ -309,13 +309,17 @@ them silently loses a learning or regenerates it:
309
309
  unparseable date. Two KNOWN LIMITS, both measured 2026-09-18 and both left
310
310
  unsolved on purpose:
311
311
 
312
- - **Only the on-disk half.** `partitionMaterialized` marks a learning
313
- materialized on `sidecarTerminal || onDisk`; this covers `onDisk` only. On
314
- this repo 79 of 92 provenance keys are ALSO terminal in
312
+ - **Only the on-disk third.** `partitionMaterialized` marks a learning
313
+ materialized on `sidecarTerminal || ledgerTerminal || onDisk`; this covers
314
+ `onDisk` only. On this repo 79 of 92 provenance keys are ALSO terminal in
315
315
  `.orchestrator/runtime/reconcile-candidates.jsonl`, so only 13 keys
316
- actually become re-proposable. The sidecar half stays untouched because it
317
- carries operator DECLINES — expiring a rule must not re-ask a question the
318
- operator already answered.
316
+ actually become re-proposable. The sidecar and the append-only verdict
317
+ ledger `.orchestrator/metrics/reconcile-verdicts.jsonl` (#1710) stay
318
+ untouched because they carry operator DECLINES — expiring a rule must not
319
+ re-ask a question the operator already answered. The ledger holds
320
+ DECISIONS only (`rejected`, `expired-swept`), never the branch-scoped
321
+ `written` / `already-on-disk`, so a rule's own `written` verdict does not
322
+ pin its learning there.
319
323
  - **A file the sweep cannot split re-proposes forever.** A file the sweep
320
324
  skips (`no-1to1-mapping`, `no-provenance-block`, `unreadable`,
321
325
  `no-counter-sentence`) that then
@@ -375,17 +379,21 @@ them silently loses a learning or regenerates it:
375
379
  **Dropping a learning requires a stamp BEFORE the delete.** Deleting a
376
380
  generated rule file whose insight is already carried verbatim by a hand-written
377
381
  always-on rule is legitimate — but `rm` alone does not stick. `engine.mjs`
378
- treats a learning as `alreadyMaterialized` if EITHER the idempotency sidecar
379
- holds a terminal verdict for its `learning_key` (`isProcessed`) OR a
380
- `.claude/rules/*.md` file still carries its marker. Delete the file without
381
- stamping and both conditions go false, so the next `/reconcile` proposes it
382
- again. Stamp it first, via the store's only sanctioned writer:
382
+ treats a learning as `alreadyMaterialized` if the idempotency sidecar holds a
383
+ terminal verdict for its `learning_key` (`isProcessed`), OR the append-only
384
+ verdict ledger `.orchestrator/metrics/reconcile-verdicts.jsonl` does (#1710),
385
+ OR a `.claude/rules/*.md` file still carries its marker. Delete the file
386
+ without stamping and the marker is gone; the `written` sidecar record keeps it
387
+ terminal only in the checkout that wrote the rule, so the next `/reconcile` in
388
+ any other checkout (a fresh worktree) proposes it again. Stamp it first, via the store's only sanctioned writer —
389
+ with `outcome: 'rejected'`: only a decision reaches the ledger, so an
390
+ `already-on-disk` stamp holds in the stamping checkout alone:
383
391
 
384
392
  ```js
385
393
  import { markCandidateProcessed } from '../scripts/lib/reconcile/idempotency.mjs';
386
394
  markCandidateProcessed({
387
395
  learningKey: 'anti-pattern/<subject-slug>',
388
- outcome: 'rejected', // or 'already-on-disk' when it lives elsewhere
396
+ outcome: 'rejected', // 'already-on-disk' (it lives elsewhere) stays checkout-local
389
397
  fallbackSlug: '<the .claude/rules slug>',
390
398
  repoRoot,
391
399
  });
@@ -415,7 +423,10 @@ under four contracts, all of them consequences of the four merge rules above:
415
423
 
416
424
  2. **Three fail-open cases, all reported rather than guessed.** An entry carries
417
425
  no date of its own; its date is recoverable only via `learning-id` →
418
- `.orchestrator/metrics/learnings.jsonl` `expires_at` (measured 2026-09-17 @
426
+ `.orchestrator/metrics/learnings.jsonl` or its sibling
427
+ `learnings-archive.jsonl` `expires_at`, with the live store taking precedence
428
+ for duplicate ids; the archive supplies the learning's own expiry evidence
429
+ (measured 2026-09-17 @
419
430
  `9e8146b4`: 87 of 92 unique ids resolve, 5 do not). An **unresolvable id**
420
431
  keeps its entry and blocks the file delete. An **ambiguous file** — where the
421
432
  `###` headings do not map 1:1 onto the non-`markers only` pairs, because
@@ -84,7 +84,7 @@ Discovery requires the Claude harness agent-directory shape and reciprocal Git w
84
84
 
85
85
  The post-Bash hook reads control state from the coordinator but observes Git changes in the agent checkout. Its saved observations are keyed by both roots and the manifest path, preventing one agent's snapshot from hiding another's changes.
86
86
 
87
- These guarantees require the named hooks to run. Native Codex `exec_command` and `apply_patch` are not wired to these Claude `PreToolUse` matchers. Codex sessions still require explicit scope manifests, disjoint assignments and diff verification; this change does not claim native pre-tool enforcement. The environment-only commit guard and dispatch-ledger retention are separate mechanisms.
87
+ These guarantees require the named hooks to run. On Codex only the Bash half runs: `enforce-commands.mjs` and `post-bash-write-verify.mjs` are wired in `hooks/hooks-codex.json` and read `.codex/wave-scope.json` (deny honoured, measured 2026-10-20 on codex-cli 0.160.1, #1691). The Edit/Write guards are not wired, because `apply_patch` is not mapped to the Edit/Write matchers. Codex sessions still require explicit scope manifests, disjoint assignments and diff verification; there is no native pre-tool enforcement for file edits. The environment-only commit guard and dispatch-ledger retention are separate mechanisms.
88
88
 
89
89
  ## 3. The collision algorithm
90
90
 
@@ -212,6 +212,7 @@ Complete list of what this guard does **not** see, or sees only approximately:
212
212
  10. **The manifest binding is read by the WRITE gates, not by the dispatch hook.** `enforce-scope`, `enforce-commands`, `post-bash-write-verify` and — since #1493.1 — the git pre-commit `wave-scope-commit-guard` (env-tier identity only, it has no hook payload) classify the manifest via `classifyManifestSession()` and stand down on a foreign one. The dispatch ledger of § 4 takes its session from the harness's own `input.session_id` instead — in its file name since #1493 and in `waveKeyOf(projectDir, sessionId, …)` — and reads only `wave` and `role` out of `wave-scope.json`; the `session` field is not consulted there at all. So a peer session's manifest cannot bind this session's writes or commits, but the hooks reach that property by different routes, and a change to one does not carry to the other.
213
213
  11. **A session that published a peer's id reads its OWN manifest as `foreign` (#1194) — the writer's defense is now mechanical (#1207).** Dropping the lock tier (§ 2.3) moved the cost to the other side of the trade: with a raw `sessionAttribution()` read, a session that lost the `bootstrapLock()` race could get the peer's id and write it into its own manifest, and Gate 3b would then classify the manifest `foreign`, standing its own write guard down. Since #1207 the writer calls `attributionForRecord(repoRoot)` instead — `skills/wave-executor/wave-loop.md` § Scope Manifest 1 — which confirms the lock's raw `session_id` against `readProcessLocalSessionIds()` before returning anything and yields `{}` on any mismatch, so the coordinator performs no manual comparison and STATE.md plays no part in it (a shared working-copy artefact written by whichever session holds the lock, not a process-local witness). A filled binding is therefore provably this session's own; unbound (`{}`) = ENFORCE. CEILING (BV-004): on a harness that exports no session env var and puts no `session_id` in the hook payload (Codex CLI, Cursor today) both tiers are empty, so G3b is permanently `unknown` = enforce = pre-#1123 behaviour there. Revisit when Codex/Cursor hook payloads carry a session id.
214
214
  12. **The commit guard's identity is whatever the committing shell exports (#1504 point 7b).** `wave-scope-commit-guard` runs inside `git commit` and gets no hook payload, so `readProcessLocalSessionIds()` reads the env only. `CLAUDE_CODE_SESSION_ID=<foreign id> git commit` therefore makes this session's own manifest read as `foreign` and the guard stands down. Named, not fixed (owner decision 2026-10-09): it is the same power `git commit --no-verify` already grants to the same shell, so reading identity from `session.lock` instead would add a failure mode (lock absent or reaped) without closing the bypass. The guard protects against accidents, not against intent. Revisit if `--no-verify` is ever blocked for coordinator sessions.
215
+ 13. **The commit guard admits earlier waves through their sidecars, bounded only by the session clock (#1680).** The coordinator commits after the last wave, while the active manifest names only that wave's paths. A staged path outside `allowedPaths` therefore still passes — with one `belongs to wave <k> of this session — allowed.` line — when an earlier wave of a non-foreign (own or unknown) manifest declared it in `<state-dir>/filescopes/wave-<k>.scopes.json` (k < `wave`, peer records excluded via `unionFileScopes()`, declared files only, no test-sibling expansion). A sidecar counts only if its mtime is not older than this session's start; with no session clock none counts and the guard blocks as before, so a stale `filescopes/` of an earlier session never widens the scope. Two consequences: commit BEFORE session-end deletes `filescopes/` (`skills/wave-executor/references/wave-loop-scope-manifest.md` § 7), and a peer session sharing the working copy that materializes sidecars in the same state dir after our start widens our coverage — the sidecars carry no session binding. Revisit when they gain one. A compact rewrites both session clocks, so the start is pulled back to the first `orchestrator.session.lock.acquired` event of this process's raw session id in the last 1 MiB of `events.jsonl` (#1681; only ever earlier, never later). A `/clear` mints a new raw id, so waves before it do not count and the guard blocks as before #1680; the same holds when the first acquire has scrolled out of the 1 MiB window.
215
216
 
216
217
  ## 7. Debugging
217
218