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
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2023", "lib": ["es2023"], "types": [], "module": "esnext",
4
+ "moduleResolution": "bundler", "strict": true, "noUncheckedIndexedAccess": true,
5
+ "noEmit": true, "skipLibCheck": true, "jsx": "react", "jsxFactory": "h", "jsxFragmentFactory": "Fragment"
6
+ },
7
+ "include": [".claude-plugin/types", "hooks", "types", "tests"]
8
+ }
@@ -0,0 +1,36 @@
1
+ /** Standalone public wire contract; null means unmeasured. Engine nouns are asynchronous. */
2
+ export type SoProfile = 'locker' | 'ausgewogen' | 'streng'
3
+ export type SoState = 'arbeitet' | 'wartet' | 'bereit' | 'blockiert' | 'unbekannt'
4
+ export interface SoBeacon {
5
+ schema_version: 1; session_id: string; name: string | null; repo_name: string | null
6
+ branch: string | null; pid: number | null; zustand: SoState; seit: string
7
+ warte_grund: 'dialog' | 'freigabe' | 'frage' | null; heuristisch: boolean
8
+ kontext_prozent: number | null; letzter_request_at: string | null; modell: string | null
9
+ welle: number | null; aktualisiert_at: string
10
+ }
11
+ export interface SoSnapshot {
12
+ schema_version: 1; measured_at: string; age_ms: number
13
+ repo: { root: string; name: string }
14
+ lock: { session_id: string | null; live: boolean; last_heartbeat: string | null } | null
15
+ session: { session_id: string; semantic_session_id: string | null } | null
16
+ wave: { running: boolean | null; number: number | null; role: string | null; agents_total: number | null; source: string | null }
17
+ agents: { entries: { id: string; typ: string | null; modellfamilie: string | null; status: string | null; tokens: null; letzter_request_at: null; quelle: string }[]; source: string | null; at: string | null; degraded: unknown
18
+ /** Running agents of this session; null when unknown (see stale_reason). Absent from collectors before #1580. */
19
+ running?: number | null
20
+ /** Why `running` is null: 'no-session', 'cache-unverifiable', 'no-session-start' or 'older-than-session'; null when measured. */
21
+ stale_reason?: string | null
22
+ /** Agents still running without a Stop past 24 h, counted apart from `running`; absent from older collectors. */
23
+ decayed?: number | null }
24
+ state: { status: string | null; session_type: string | null; current_wave: number | null; total_waves: number | null }
25
+ gate: { last: { variant: string | null; passed: boolean | null; check_failed: boolean | null; exit_code: number | null; at: string | null } | null; last_green_sha: string | null }
26
+ peers: { session_id: string; branch: string | null; status: string | null; current_wave: number | null; last_heartbeat: string | null; gleiches_working_copy: boolean; scope_paths: string[] | null }[]
27
+ vault: { enabled: boolean | null; mode: string | null; last_run: { at: string | null; created: number | null; updated: number | null; failed: number | null; aborted: string | null } | null; board: { action: string | null; at: string | null } | null }
28
+ degraded: string[]
29
+ }
30
+ export interface So {
31
+ version(): 1
32
+ snapshot(): Promise<SoSnapshot | null>
33
+ beacons(): Promise<SoBeacon[]>
34
+ profile(): SoProfile
35
+ }
36
+ declare module 'claude-code' { interface EngineInterface { so: So } }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "session-orchestrator",
3
- "version": "5.12.0",
4
- "description": "Your coding agents get an agreed plan, a check after each round of changes and a handover to the next session. Plan, Go, Close: /session reads your repo and agrees the scope, /go runs the work in waves with your tests and typecheck after each implementation wave, /close runs the full gate, verifies, commits and files the rest as issues. Scope and destructive-command guards, crash-safe resume, learnings you approve. For Claude Code, Codex CLI, Cursor and Pi.",
3
+ "version": "5.13.0",
4
+ "description": "Your coding agents get an agreed plan, a check after each round of changes and a handover to the next session. For Claude Code, Codex CLI, Cursor and Pi.",
5
5
  "type": "module",
6
6
  "homepage": "https://session-orchestrator.com",
7
7
  "keywords": [
@@ -43,6 +43,8 @@
43
43
  "evals/",
44
44
  "!evals/results/**",
45
45
  "!assets/marketing/**",
46
+ "mods/so-hud/",
47
+ "!mods/so-hud/tests/**",
46
48
  ".claude-plugin/",
47
49
  ".codex-plugin/",
48
50
  ".cursor-plugin/",
package/rules/_index.md CHANGED
@@ -39,7 +39,7 @@ baseline project type (`nextjs-saas`, `express-service`, `docker-service`,
39
39
  - `always-on/test-value.md` — TV-001..005: name the bug a test catches, or do not write it; deletion is a feature
40
40
  - `always-on/build-value.md` — BV-001..004: the seven-rung build ladder, the four protections never simplified away
41
41
  - `always-on/cross-session-messaging.md` — CSM-001..005: messaging is transport, not shared state; no permission laundering
42
- - `always-on/loop-and-monitor.md` — LM-001..008: routing between `/goal`, Workflows, Channels, Monitor, `/loop`, and Routines
42
+ - `always-on/loop-and-monitor.md` — LM-001..008: routing between `/goal`, Workflows, Channels, Monitor, `/loop`, and Routines (short form ≤ 4 kB; long form with filter examples in `docs/reference/loop-and-monitor-routing.md`, not vendored — #1624)
43
43
  - `always-on/bash-harness-pitfalls.md` — six false-green shell-harness failure classes (path-scoped via `globs:` + `paths:` frontmatter carrying identical lists — `paths:` is what Claude Code reads, `globs:` what `rule-loader.mjs` reads first; #1449)
44
44
 
45
45
  ## opt-in-stack (vendored on match)
@@ -30,7 +30,7 @@ AskUserQuestion({ questions: [{
30
30
  ], multiSelect: false }]})
31
31
  ```
32
32
 
33
- Option 1 is the recommendation, labelled `(Recommended)`. 2–4 options, 1–4 questions per call. A description that only restates its label is a rule break.
33
+ Weigh every realistic option, not just two: 2–4 options, 1–4 questions per call. Option 1 is the recommendation, labelled `(Recommended)`. Each description gives reason, cost and consequence; one that only restates its label is a rule break. One plain-words (eli5) sentence in the question or a description carries the core and drops no fact (AUQ-006).
34
34
 
35
35
  - ✓ "Blocker for the next wave; ~20 min; freezes plan scope until it lands."
36
36
  - ✗ "Fix the bug now."
@@ -1,220 +1,43 @@
1
1
  <!-- source: session-orchestrator plugin (canonical: rules/always-on/loop-and-monitor.md) -->
2
2
  # Loop & Monitor Routing (Always-on)
3
3
 
4
- `/goal`, `/loop`, `Monitor`, `Workflow`, Channels, and cloud Routines /
5
- scheduled desktop tasks share the "recurring, polling-style, or keep-going-
6
- until-done work" slot but are **not interchangeable**. Picking the wrong
7
- primitive wastes tokens, masks failures, or loses durability. This rule
8
- encodes the routing decision once so future sessions do not re-derive it.
9
-
10
- Version gates move; the routing does not. Check the primitive's availability in
11
- your harness before wiring anything load-bearing onto it, and keep a fallback.
4
+ `/goal`, Workflow, Channels, Monitor, `/loop` and Routines share the "keep going / keep watching" slot but are not interchangeable. Check each primitive's availability in your harness before relying on it, and keep a fallback.
12
5
 
13
6
  ## LM-001: Decision Tree — Pick the Primitive First
14
-
15
- ```
16
- Is the work a FINITE objective with a recognizable done-condition that
17
- the model's OWN surfaced output can demonstrate (refactor until the tests
18
- referenced in the transcript pass, drain a worklist, reach a state you
19
- can describe in a few thousand characters)?
20
- │
21
- ├─ Yes → /goal.
22
- │ Continuation across turns until the condition is model-evaluated.
23
- │ (Completion-condition axis — see LM-008.)
24
- │ Pair with deterministic gates — /goal judges; it never verifies.
25
- │ NOT: "until CI goes green" — that is an EXTERNAL stream the
26
- │ evaluator cannot see → Monitor (next branch).
27
- │
28
- └─ No → Is this a ONE-SHOT fan-out across many independent units (codebase-
29
- wide audit, large migration, multi-angle cross-checked research)
30
- needing dozens-to-hundreds of subagents one conversation cannot
31
- coordinate?
32
- │
33
- ├─ Yes → dynamic Workflow.
34
- │ Codifies the plan as a rerunnable script; the main context
35
- │ holds only the final result, not the per-agent chatter.
36
- │ NOT a recurring primitive — for repeated polling stay on the
37
- │ axes below. (Fan-out axis — see LM-002b.)
38
- │
39
- └─ No → Is the event PUSHABLE from an external system (CI webhook,
40
- error tracker, chat)?
41
- │
42
- ├─ Yes → Channels.
43
- │ The source pushes the event into the open session via
44
- │ a channel plugin — zero polling, reacts while you're
45
- │ away. (Push-based sibling of Monitor — see LM-002a.)
46
- │
47
- └─ No → Is the watched thing a STREAM I can tail (logs, file
48
- changes, CI status transitions, a JSONL telemetry
49
- file)?
50
- │
51
- ├─ Yes → Monitor.
52
- │ Each stdout line = one notification. Zero
53
- │ polling tokens.
54
- │
55
- └─ No → Is the watched thing PERIODIC and bounded by
56
- THIS conversation (days, not weeks;
57
- resume/continue restoration acceptable)?
58
- │
59
- ├─ Yes → /loop.
60
- │ Use dynamic mode unless the cadence
61
- │ is genuinely fixed.
62
- │
63
- └─ No → Cloud Routines or desktop scheduled
64
- tasks. Daily notes, weekly audits,
65
- cross-repo sweeps. /loop CANNOT
66
- cover these — it fires only while
67
- the agent runtime is running.
68
- ```
7
+ In order, first match wins:
8
+ 1. Finite objective whose done-condition the model's own surfaced output can demonstrate: `/goal` (LM-008). External CI-green is invisible to its evaluator: use Monitor.
9
+ 2. One-shot fan-out over many independent units, too large for one conversation: Workflow (LM-002b).
10
+ 3. Event the source can push (CI webhook, error tracker, chat): Channels (LM-002a).
11
+ 4. Stream you can tail (logs, file changes, CI transitions, JSONL): Monitor (LM-002).
12
+ 5. Periodic and bound to THIS conversation (days, resume acceptable): `/loop`, dynamic unless the cadence is truly fixed (LM-003).
13
+ 6. Durable, offline, overnight or cross-repo: cloud Routines or desktop scheduled tasks (LM-004).
69
14
 
70
15
  ## LM-002: Use Monitor When …
71
-
72
- Use Monitor when the watched thing is a STREAM you can tail — a long-running
73
- build/test suite emitting progress to stdout, a CI pipeline you poll-then-stream
74
- (`glab ci status`, `gh pr checks --watch`), an accumulating error log
75
- (`tail -f` + `grep --line-buffered`), a JSONL telemetry stream, or a filesystem
76
- event (`inotifywait -m`). Each stdout line is one notification; zero polling
77
- tokens.
78
-
79
- **Coverage rule (load-bearing).** A Monitor filter must match every terminal
80
- state, not just the happy path. *Silence is not success.*
81
-
82
- ```bash
83
- # WRONG — silent on crash, hang, or any non-success exit
84
- tail -f run.log | grep --line-buffered "elapsed_steps="
85
-
86
- # RIGHT — alternation covers progress + every failure signature
87
- tail -f run.log | grep -E --line-buffered "elapsed_steps=|Traceback|Error|FAILED|assert|Killed|OOM"
88
- ```
89
-
90
- If you cannot enumerate failure signatures, broaden the alternation rather than
91
- narrow it. Some extra noise beats missing a crashloop.
16
+ Tailing builds, tests, CI, logs or file events. The filter MUST match progress AND every terminal failure state; silence is not success. If failure signatures cannot be enumerated, broaden the filter. A watch expires; expiry is an exit notice, never success, so re-arm if still needed. In unattended `-p` runs, wait in bounded foreground commands and read the real exit: background tasks and Monitor watches do not keep the run alive after the turn ends.
92
17
 
93
18
  ## LM-002a: Use Channels When …
94
-
95
- Channels is the **push-based sibling of Monitor**: an external system pushes the
96
- event into your open session via a channel plugin (CI webhook, error tracker,
97
- chat) — zero polling, reacts while you are away. Choose Channels over Monitor
98
- when the source can PUSH (you register a webhook endpoint) rather than be TAILED
99
- (`tail -f` / `glab ci status`); when it can only be polled or tailed, stay on
100
- Monitor. Treat it as preview-grade — do not wire a load-bearing automation onto
101
- it without a Monitor or `/loop` fallback.
19
+ The source can push. Research preview: never load-bearing without a Monitor or `/loop` fallback. Session-to-session text is messaging, not Channels.
102
20
 
103
21
  ## LM-002b: Use Workflows When …
104
-
105
- Dynamic **Workflows** is the **one-shot fan-out** primitive — distinct from every
106
- recurring/polling axis. Reach for it when a single objective decomposes into many
107
- independent units one conversation cannot coordinate: a codebase-wide audit, a
108
- large migration, a multi-angle research sweep. The model plans once, codifies a
109
- rerunnable script, fans out subagents, and returns only the final result.
110
-
111
- **Never reimplement a one-shot fan-out as `/loop`.** A `/loop` body re-runs a
112
- single coordinator prompt on an interval; it has no native fan-out, no
113
- agent-count cap, and no rerunnable-script artifact.
114
-
115
- **Distinct from in-run agent teams.** Workflows' one-shot fan-out is unrelated to
116
- experimental in-run multi-agent coordination within a single live session — see
117
- `parallel-sessions.md` § PSA Scope Axes for that boundary.
118
-
119
- **Do not swap a purpose-built wave orchestrator for a bare Workflow.** A
120
- Workflow's agent-count caps are bounds, not kill-switches, and it ships no
121
- per-run telemetry artifact. If your orchestration has stop conditions and an
122
- audit trail, a Workflow does not replace them.
22
+ One-shot independent fan-out. Never emulate fan-out with `/loop`; never replace an orchestrator that owns kill-switches and telemetry with a bare Workflow.
123
23
 
124
24
  ## LM-003: Use `/loop` When …
125
-
126
- Use `/loop` when the check is genuinely periodic (no streamable trigger) and
127
- bounded by THIS conversation — a staleness check during a multi-hour session, a
128
- backlog snapshot during long-running work, branch-tending while waiting on
129
- review. Wire a custom maintenance loop into `.claude/loop.md` (project) or
130
- `~/.claude/loop.md` (user). Scheduled tasks fire only while the agent runtime is
131
- running; unexpired tasks restore on `--resume`/`--continue`.
132
-
133
- **Cadence — pick by observation-rate, not by the cache.** Choose the interval
134
- from how fast the watched thing changes: about a minute to a few minutes when a
135
- state transition is imminent, five to twenty minutes for steady-state polling,
136
- twenty minutes to an hour for idle maintenance. The runtime clamps a self-paced
137
- wakeup to roughly [60s, 3600s]. Under a short (5-minute) prompt-cache TTL, avoid
138
- a cadence that lands exactly on the cache cliff — you pay the miss without
139
- amortising it.
140
-
141
- **Limits & kill-switches (load-bearing).** A single environment variable
142
- (`CLAUDE_CODE_DISABLE_CRON=1`) disables the cron scheduler AND `/loop` entirely —
143
- not just one task. Each session is capped at a few dozen scheduled tasks. Both
144
- `.claude/loop.md` and `~/.claude/loop.md` are TRUNCATED past **25,000 bytes** —
145
- keep the loop body lean; a bloated body silently loses its tail rather than
146
- erroring.
25
+ Periodic, no stream, this conversation. Pace by observation rate: imminent change 60–270 s, steady polling 300–1200 s, idle 1200–3600 s. Loop body files truncate past 25,000 bytes; keep them lean. `CLAUDE_CODE_DISABLE_CRON=1` disables `/loop` and the scheduler entirely.
147
26
 
148
27
  ## LM-004: Use Routines / Scheduled Tasks When …
149
-
150
- Use cloud Routines or desktop scheduled tasks when the work must run when no
151
- session is open (overnight, weekly, monthly), spans repos no single session can
152
- witness, or must persist across machine restarts. `/loop` is the wrong tool here
153
- — it fires only while the agent runtime is running; `--resume`/`--continue`
154
- restoration is not durable unattended scheduling.
28
+ Work must survive the session: nightly, weekly, cross-repo, restart-durable. Resume restoration is not unattended scheduling.
155
29
 
156
30
  ## LM-005: Never Reimplement These as `/loop`
157
-
158
- - **A driver that already has its own kill-switches and telemetry.** Wrapping it
159
- in `/loop` loses both. Pair them — never replace.
160
- - **Synchronous inter-wave checkpoints.** They are synchronous by design.
161
- - **Quality gates** (typecheck, test, lint). These block on purpose. Run them
162
- once, sequentially.
163
- - **Hook-served events.** `PostToolUse`, `Stop`, `SubagentStop` already fire at
164
- the right moment. A `/loop` poll on top is redundant.
165
- - **`/goal`.** Do not hand-roll a per-turn Stop-hook prompt evaluator to keep
166
- working until a condition holds — `/goal` IS that mechanism, natively.
167
- Re-implementing it as a custom Stop hook or a `/loop` body duplicates the
168
- machinery and loses the built-in restore and clear lifecycle. See LM-008.
169
- - **One-shot subagent fan-out.** A codebase-wide audit / large migration /
170
- multi-angle research sweep is a dynamic Workflow, not a `/loop`. See LM-002b.
31
+ Autonomous drivers with their own kill-switches, synchronous inter-step quality gates, hook events you already receive, and `/goal` itself (never rebuild it as a custom Stop hook). Pair with them instead.
171
32
 
172
33
  ## LM-006: Destructive-Action Safeguards Apply
173
-
174
- A `/loop` body is a coordinator prompt that runs every iteration. Treat it as you
175
- would any coordinator action:
176
-
177
- - **No destructive operations** (`git push`, `git reset`, `rm`, merging an MR,
178
- closing an issue) without explicit transcript authorisation for the specific
179
- action.
180
- - **Track your footprint.** A loop that "tidies up" untracked files may delete
181
- another session's work-in-progress. See `parallel-sessions.md`.
182
- - **Read-only first.** When in doubt, observe and report.
34
+ Every loop iteration is an ordinary action: no push, reset, delete, merge or issue close without explicit authorization for that action. When unsure, observe and report read-only.
183
35
 
184
36
  ## LM-007: Anti-Patterns
185
-
186
- - Fixed `/loop 5m …` to babysit a CI run — use Monitor on `glab ci status` or `gh pr checks --watch` instead (LM-002).
187
- - `/loop 1d …` for a daily note — use Routines or desktop tasks; `/loop` does not fire while the agent runtime is stopped (LM-004).
188
- - A Monitor filter matching only the success marker — silence from a crash is indistinguishable from success (LM-002 coverage rule).
189
- - `/loop` wrapping an autonomous driver — duplicates loop semantics and hides its kill-switches (LM-005).
190
- - A cadence sitting exactly on a short prompt-cache TTL boundary — you pay the cache miss without amortising it (LM-003).
191
- - Using `/goal` as a quality gate — the evaluator reads the transcript only; pair `/goal` with a deterministic exit-code gate (LM-008).
192
- - Unbounded `/goal` with no turn/time-bound clause — always embed "or stop after N turns / M minutes" (LM-008).
193
- - Hand-rolling a one-shot fan-out as a `/loop` body — use a Workflow (LM-002b).
37
+ Wrong routing, success-only filters, unbounded goals, a goal used as a gate, and caps mistaken for kill-switches.
194
38
 
195
39
  ## LM-008: Use `/goal` When …
196
-
197
- `/goal <condition>` keeps the model working across turns until a stated
198
- completion condition is confirmed — a session-scoped, prompt-based Stop hook
199
- where a small, fast evaluator reads the condition plus the conversation each turn
200
- and returns yes/no with a reason. Cost is typically negligible. Use it for a
201
- **finite objective** that needs multiple turns to converge and whose
202
- done-condition is **demonstrable from the model's own surfaced output** — the
203
- evaluator runs NO tools, so surface the evidence (paste the test summary, echo
204
- the worklist, print the state). Write conditions the transcript can demonstrate
205
- ("all referenced tests show as passing in the output" works; "the production
206
- database is consistent" does not). **Always embed a bound** ("or stop after 20
207
- turns / 30 minutes") so a non-converging goal terminates.
208
-
209
- **The load-bearing caveat — `/goal` provides CONTINUATION plus model-evaluated
210
- JUDGMENT, never deterministic VERIFICATION.** The evaluator judges the condition
211
- from the transcript; it does not run verification tools. Deterministic quality
212
- gates remain the source of truth: the project's test, typecheck, and lint
213
- commands and their **exit codes** decide whether work is correct. Never replace
214
- an exit-code gate with a model vote. The correct pattern is a goal whose
215
- condition references freshly-run gate output ("…until the test command prints 0
216
- failures **in this turn's output**"), backed by an actual gate run each turn —
217
- not a goal that asserts success on its own.
40
+ Finite multi-turn objective. The evaluator runs no tools: surface fresh evidence (test summary, worklist, state) in the transcript, and always embed a bound ("or stop after 20 turns / 30 minutes"). `/goal` gives continuation and model judgment, never verification: fresh test/typecheck/lint exit codes decide, run each turn.
218
41
 
219
42
  ## See Also
220
43
 
@@ -157,6 +157,8 @@ The git index and stash are **shared resources of the working copy**, not a priv
157
157
 
158
158
  **The only write channel a subagent has is editing files inside its own declared file scope.** All VCS operations — staging, committing, pushing — belong to the coordinator. This makes the PSA-004 guarantee structural rather than behavioural at the subagent layer: an agent that never touches the index cannot violate PSA-004 by construction.
159
159
 
160
+ **Sole exception — worktree mode (`code-implementer` only).** It may stage its own files one by one (`git add <file>`) and `git commit` only when its prompt carries a line `WORKTREE-MODE: <absolute path> <branch>` AND `git rev-parse --show-toplevel` and `git branch --show-current` match that path and branch; otherwise the ban applies unchanged. A linked worktree has its own index, so the index race above does not exist there. The commit hook briefly adds an entry to the shared stash via lint-staged; the agent itself never runs `git stash`. Coordinator duty: never grant the coordinator's own worktree, never put two agents in one worktree. The wave-executor never sets the line. `git push`, merge, `git reset`, `git stash` and `git clean` stay banned in every mode, and the coordinator still publishes.
161
+
160
162
  **Anti-patterns:**
161
163
  - A subagent running `git add <its own files>` "to be helpful" before reporting done — even scoped to its own files, it races the coordinator's staging pass and any sibling's concurrent index write.
162
164
  - A subagent running `git stash` to "save progress" before switching tasks — report `blocked` and let the coordinator decide, never stash.
@@ -170,4 +172,4 @@ The git index and stash are **shared resources of the working copy**, not a priv
170
172
  - Using `git add .` in a shared workspace — you will commit another session's partial work.
171
173
  - Reverting "broken" commits without asking — another session may have intentionally pushed incremental progress.
172
174
  - Pausing at PSA-001 signals when your scope is unaffected — unnecessary interruptions slow the session.
173
- - A subagent staging or committing its own work (PSA-007).
175
+ - A subagent staging or committing without a matching `WORKTREE-MODE` line, or as any agent other than `code-implementer` (PSA-007).
@@ -193,4 +193,4 @@ async function updateUser(id: string, data: Partial<User>): Promise<void> {
193
193
  - Batch operations: use `.upsert()` or `.insert()` with arrays instead of individual inserts in loops.
194
194
 
195
195
  ## See Also
196
- security-web.md · frontend.md · backend.md · swift.md · parallel-sessions.md
196
+ security-web.md · frontend.md · backend.md · parallel-sessions.md
@@ -393,4 +393,4 @@ export async function askAI(prompt: string, options?: { model?: string; maxToken
393
393
  - Checking budget after the LLM call (check before, enforce before spending)
394
394
 
395
395
  ## See Also
396
- security-web.md · frontend.md · backend-data.md · swift.md · parallel-sessions.md
396
+ parallel-sessions.md · vendored only for some archetypes, so possibly absent in this repo: security-web.md [nextjs-saas, nextjs-minimal, express-service] · frontend.md [nextjs-saas, nextjs-minimal] · backend-data.md [nextjs-saas]
@@ -1,12 +1,16 @@
1
1
  ---
2
2
  globs:
3
3
  - src/middleware*.ts
4
+ - proxy.ts
5
+ - src/proxy.ts
4
6
  - src/app/**
5
7
  - next.config.*
6
8
  - src/lib/csp*
7
9
  - src/lib/cors*
8
10
  paths:
9
11
  - src/middleware*.ts
12
+ - proxy.ts
13
+ - src/proxy.ts
10
14
  - src/app/**
11
15
  - next.config.*
12
16
  - src/lib/csp*
@@ -197,4 +201,4 @@ securityMiddleware(app);
197
201
  - **Template Literals**: Never use tagged template literals with user input for SQL, HTML, or shell commands. Use parameterized queries, DOMPurify, or `child_process.execFile()` respectively.
198
202
 
199
203
  ## See Also
200
- frontend.md · backend.md · backend-data.md · swift.md · parallel-sessions.md
204
+ parallel-sessions.md · vendored only for some archetypes, so possibly absent in this repo: frontend.md [nextjs-saas, nextjs-minimal] · backend.md [nextjs-saas, express-service, docker-service] · backend-data.md [nextjs-saas]
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * agent-usage-census.mjs — CLI shell over scripts/lib/agent-usage-census.mjs (#1617).
4
+ *
5
+ * Reads Claude Code subagent transcripts (read-only) and reports per agent type:
6
+ * dispatches, observed models, turns and output per run, cache rebuilds after
7
+ * idle gaps, and the median start context. Definitions: see the lib header.
8
+ *
9
+ * Usage: agent-usage-census.mjs [--days N] [--root DIR] [--json] [--top N]
10
+ * Exit: 0 measured · 1 bad argument · 2 root unreadable / measurement failed
11
+ */
12
+
13
+ import os from 'node:os';
14
+ import path from 'node:path';
15
+ import fs from 'node:fs';
16
+
17
+ import { DEFAULT_DAYS, formatTable, runCensus } from './lib/agent-usage-census.mjs';
18
+ import { writeStdoutLineSync } from './lib/io.mjs';
19
+ import { isMainModule } from './lib/is-main-module.mjs';
20
+
21
+ const USAGE = 'Usage: agent-usage-census.mjs [--days N] [--root DIR] [--json] [--top N] [--help]';
22
+
23
+ export function parseArgs(argv) {
24
+ const out = { days: DEFAULT_DAYS, root: path.join(os.homedir(), '.claude', 'projects'), json: false, top: 25, help: false };
25
+ for (let i = 0; i < argv.length; i++) {
26
+ const a = argv[i];
27
+ if (a === '--json') out.json = true;
28
+ else if (a === '--help') out.help = true;
29
+ else if (a === '--days' || a === '--top') {
30
+ const v = Number(argv[++i]);
31
+ if (!Number.isFinite(v) || v <= 0) return { error: `${a} requires a positive number` };
32
+ out[a.slice(2)] = v;
33
+ } else if (a === '--root') {
34
+ const v = argv[++i];
35
+ if (!v) return { error: '--root requires a directory' };
36
+ out.root = path.resolve(v);
37
+ } else return { error: `Unknown argument: ${a}` };
38
+ }
39
+ return out;
40
+ }
41
+
42
+ async function main() {
43
+ const args = parseArgs(process.argv.slice(2));
44
+ if (args.error) {
45
+ console.error(`Error: ${args.error}`);
46
+ console.error(USAGE);
47
+ process.exit(1);
48
+ }
49
+ if (args.help) {
50
+ console.log(USAGE);
51
+ console.log('');
52
+ console.log('Per agent type over the subagent transcripts of the last N days (read-only):');
53
+ console.log(' dispatches, observed models (share of turns), turns/run and output tokens/run (median),');
54
+ console.log(' cache rebuilds after > 5 min gaps, median start context.');
55
+ console.log('');
56
+ console.log(` --days N window in days (default ${DEFAULT_DAYS})`);
57
+ console.log(' --root D transcript root (default ~/.claude/projects)');
58
+ console.log(' --json machine-readable envelope on stdout');
59
+ console.log(' --top N rows in the table (default 25)');
60
+ console.log('');
61
+ console.log('Exit: 0 measured · 1 bad argument · 2 root unreadable / measurement failed');
62
+ process.exit(0);
63
+ }
64
+ if (!fs.existsSync(args.root)) {
65
+ console.error(`Error: transcript root does not exist: ${args.root}`);
66
+ process.exit(2);
67
+ }
68
+ let result;
69
+ try {
70
+ result = await runCensus({ root: args.root, days: args.days });
71
+ } catch (err) {
72
+ console.error(`Error: census failed: ${err?.message ?? err}`);
73
+ process.exit(2);
74
+ }
75
+ writeStdoutLineSync(args.json ? JSON.stringify(result, null, 2) : formatTable(result, { top: args.top }));
76
+ process.exit(0);
77
+ }
78
+
79
+ if (isMainModule(import.meta.url)) main();
@@ -73,12 +73,15 @@
73
73
  * prune/rewrite, store lock not acquired). Unparseable store lines are
74
74
  * NOT an error: they are kept verbatim, counted as `malformed` in the
75
75
  * summary, and WARNed on stderr with their line numbers.
76
+ * 4 Refused: this host's learnings store role is invalid (owner.yaml
77
+ * `learnings` or SO_LEARNINGS_STORE_ROLE, #1516/#1686) — nothing written
76
78
  */
77
79
 
78
80
  import { readFileSync, realpathSync } from 'node:fs';
79
81
  import path from 'node:path';
80
82
  import { validatePathInsideProject } from './lib/path-utils.mjs';
81
83
  import { LearningsLockError, readLearnings, withLearningsLock } from './lib/learnings/io.mjs';
84
+ import { LearningsStoreRoleError } from './lib/learnings/store-role.mjs';
82
85
  import { pruneLearnings } from './lib/learnings/expiry-sweep.mjs';
83
86
  import {
84
87
  CURRENT_SCHEMA_VERSION,
@@ -87,6 +90,8 @@ import {
87
90
  } from './lib/learnings/schema.mjs';
88
91
  import { parseSessionConfig, readConfigFile } from './lib/config.mjs';
89
92
  import { isMainModule } from './lib/is-main-module.mjs';
93
+ import { resolveLedgerPath } from './lib/ledger-path.mjs';
94
+ import { resolveEventsLedgerRoot } from './lib/platform.mjs';
90
95
 
91
96
  const DEFAULT_FILE = '.orchestrator/metrics/learnings.jsonl';
92
97
  const ARCHIVE_BASENAME = 'learnings-archive.jsonl';
@@ -334,15 +339,22 @@ async function emitSessionWriteApplied({ repoRoot, filePath, payload }) {
334
339
  // inside the repo but living outside it is refused, and a root spelled
335
340
  // through a symlink (macOS /var → /private/var) still accepts the store
336
341
  // inside it. Runs after the write, so the store exists and resolves.
342
+ //
343
+ // #1722: the default store of a linked worktree that gitignores it lives in
344
+ // the main checkout, so the ledger root (resolveEventsLedgerRoot) is accepted
345
+ // too; a store outside BOTH roots is still refused.
337
346
  const file = canonicalPath(filePath);
338
- const rel = path.relative(canonicalPath(repoRoot), file);
339
- if (!validatePathInsideProject(file, repoRoot, { canonicalizeRoot: true }).ok) {
347
+ const root = [repoRoot, resolveEventsLedgerRoot(repoRoot)].find(
348
+ (r) => validatePathInsideProject(file, r, { canonicalizeRoot: true }).ok,
349
+ );
350
+ if (root === undefined) {
340
351
  process.stderr.write(
341
352
  `apply-session-learnings: skipped ${SESSION_WRITE_EVENT} — ${filePath} lies outside ` +
342
353
  `--repo-root ${repoRoot}\n`,
343
354
  );
344
355
  return;
345
356
  }
357
+ const rel = path.relative(canonicalPath(root), file);
346
358
  try {
347
359
  const { emitEvent, sessionAttribution } = await import('./lib/events.mjs');
348
360
  await emitEvent(
@@ -385,7 +397,9 @@ async function main(argv) {
385
397
  }
386
398
 
387
399
  const repoRoot = path.resolve(opts.repoRoot ?? process.cwd());
388
- const filePath = path.resolve(repoRoot, opts.file ?? DEFAULT_FILE);
400
+ // #1722: only the DEFAULT store routes through the ledger resolver; an
401
+ // explicit --file stays exactly what the caller asked for.
402
+ const filePath = opts.file !== null ? path.resolve(repoRoot, opts.file) : resolveLedgerPath(DEFAULT_FILE, repoRoot);
389
403
  const archivePath = path.join(path.dirname(filePath), ARCHIVE_BASENAME);
390
404
  const decayRate = opts.decayRate ?? (await resolveDecayRate(repoRoot));
391
405
  const now = new Date();
@@ -429,6 +443,10 @@ async function main(argv) {
429
443
  // Joins the lock held below reentrantly; takes none on a dry run.
430
444
  res = await pruneLearnings({ filePath, archivePath, entries: built.next, now, dryRun: !opts.apply });
431
445
  } catch (err) {
446
+ if (err instanceof LearningsStoreRoleError) {
447
+ process.stderr.write(`apply-session-learnings: ${err.message}\n`);
448
+ return { code: 4 };
449
+ }
432
450
  process.stderr.write(`apply-session-learnings: prune/rewrite failed: ${err.message}\n`);
433
451
  return { code: 2 };
434
452
  }
@@ -443,6 +461,10 @@ async function main(argv) {
443
461
  process.stderr.write(`apply-session-learnings: ${err.message}\n`);
444
462
  return 2;
445
463
  }
464
+ if (err instanceof LearningsStoreRoleError) {
465
+ process.stderr.write(`apply-session-learnings: ${err.message}\n`);
466
+ return 4;
467
+ }
446
468
  throw err;
447
469
  }
448
470
  if ('code' in outcome) return outcome.code;
@@ -466,14 +488,20 @@ async function main(argv) {
466
488
  archived: res.archived,
467
489
  kept: res.kept,
468
490
  by_reason: byReason,
469
- written: opts.apply ? res.kept : 0,
491
+ // On an inbox-role host (#1516) the live store is not rewritten: only
492
+ // records with new ids reach learnings-inbox.jsonl, so `kept` is no count
493
+ // of written records there (io.mjs prints the real numbers on stderr).
494
+ written: opts.apply && !res.skipped ? res.kept : 0,
495
+ ...(res.skipped ? { skipped: res.skipped } : {}),
470
496
  };
471
497
 
472
498
  // The event's presence is documented as proof the write ran
473
499
  // (docs/events-schema.md). pruneLearnings() returns early WITHOUT writing
474
500
  // exactly when the store and the next generation are both empty — the only
475
501
  // path that reports `scanned: 0` — so no store + empty input emits nothing.
476
- if (opts.apply && res.scanned > 0) {
502
+ // No event on an inbox-role host (#1516): the store write it attests to did
503
+ // not happen there.
504
+ if (opts.apply && res.scanned > 0 && !res.skipped) {
477
505
  await emitSessionWriteApplied({
478
506
  repoRoot,
479
507
  filePath,
@@ -494,7 +522,7 @@ async function main(argv) {
494
522
  `${summary.dry_run ? '[dry-run] ' : ''}learnings: read ${summary.read}, confirmed ${summary.confirmed}, ` +
495
523
  `contradicted ${summary.contradicted}, appended ${summary.appended}, decayed ${summary.decayed} ` +
496
524
  `(rate ${decayRate}), pruned ${pruned}, consolidated ${summary.consolidated}, kept ${summary.kept}` +
497
- `${summary.dry_run ? ' — nothing written' : ` — wrote ${summary.written}`}\n`,
525
+ `${summary.dry_run ? ' — nothing written' : summary.skipped ? ` — ${summary.skipped}: live store not rewritten` : ` — wrote ${summary.written}`}\n`,
498
526
  );
499
527
  }
500
528
  return 0;
@@ -50,7 +50,7 @@ import { glabRun as defaultGlabRun } from './lib/vault-backfill/glab.mjs';
50
50
  import { archiveFileToVault, titleFromMarkdown } from './lib/vault-archive.mjs';
51
51
  import { defaultGlabRepo } from './lib/vcs-repo-spec.mjs';
52
52
  import { readLock } from './lib/session-lock.mjs';
53
- import { resolveStateMdPath } from './lib/state-md/frontmatter-mutators.mjs';
53
+ import { resolveStateMdWriteTarget, warnForeignStateMd } from './lib/state-md/frontmatter-mutators.mjs';
54
54
  import { parseStateMd } from './lib/state-md/yaml-parser.mjs';
55
55
  import { isMainModule } from './lib/is-main-module.mjs';
56
56
 
@@ -224,6 +224,24 @@ export function classifyOwnership({ repoRoot, rel, sessionStartedAt, gitRunFn })
224
224
  return committedMs >= startedMs ? 'foreign' : 'mine';
225
225
  }
226
226
 
227
+ /**
228
+ * Read THIS session's STATE.md frontmatter, or null. #1636: the write side's
229
+ * ownership rule — another platform's STATE.md not proven to be this process's
230
+ * (#1618) counts as absent, so it never lends its `started_at` / `issues:` to
231
+ * this run's `--apply` (both callers fail closed on null).
232
+ *
233
+ * @param {string} repoRoot
234
+ * @returns {Record<string, unknown>|null}
235
+ */
236
+ function readOwnStateFrontmatter(repoRoot) {
237
+ const { path: stateMdPath, rejected } = resolveStateMdWriteTarget(repoRoot);
238
+ if (rejected) {
239
+ warnForeignStateMd('archive-closed-prds', rejected, stateMdPath);
240
+ return null;
241
+ }
242
+ return parseStateMd(readFileSync(stateMdPath, 'utf8'))?.frontmatter ?? null;
243
+ }
244
+
227
245
  /**
228
246
  * Resolve THIS session's `started_at` (ISO-8601): the session lock first, the
229
247
  * STATE.md frontmatter as fallback.
@@ -239,8 +257,7 @@ export function readSessionStartedAt(repoRoot) {
239
257
  // fall through to STATE.md
240
258
  }
241
259
  try {
242
- const parsed = parseStateMd(readFileSync(resolveStateMdPath(repoRoot), 'utf8'));
243
- const startedAt = parsed?.frontmatter?.started_at;
260
+ const startedAt = readOwnStateFrontmatter(repoRoot)?.started_at;
244
261
  if (typeof startedAt === 'string' && !Number.isNaN(Date.parse(startedAt))) return startedAt;
245
262
  } catch {
246
263
  // no STATE.md, or unreadable → null (fail-closed at the call site)
@@ -255,8 +272,7 @@ export function readSessionStartedAt(repoRoot) {
255
272
  */
256
273
  function readSessionIssues(repoRoot) {
257
274
  try {
258
- const parsed = parseStateMd(readFileSync(resolveStateMdPath(repoRoot), 'utf8'));
259
- const issues = parsed?.frontmatter?.issues;
275
+ const issues = readOwnStateFrontmatter(repoRoot)?.issues;
260
276
  if (Array.isArray(issues)) return new Set(issues.map((n) => String(n)));
261
277
  } catch {
262
278
  // absent/unreadable → empty set