session-orchestrator 5.2.0 → 5.3.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 (295) hide show
  1. package/.agents/skills/architecture/SKILL.md +3 -1
  2. package/.agents/skills/autopilot/SKILL.md +5 -1
  3. package/.agents/skills/autopilot/agents/openai.yaml +5 -0
  4. package/.agents/skills/bootstrap/SKILL.md +5 -1
  5. package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
  6. package/.agents/skills/brainstorm/SKILL.md +5 -1
  7. package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
  8. package/.agents/skills/claude-md-drift-check/SKILL.md +3 -1
  9. package/.agents/skills/close/SKILL.md +5 -1
  10. package/.agents/skills/close/agents/openai.yaml +5 -0
  11. package/.agents/skills/convergence-monitoring/SKILL.md +4 -2
  12. package/.agents/skills/debug/SKILL.md +5 -1
  13. package/.agents/skills/debug/agents/openai.yaml +5 -0
  14. package/.agents/skills/discovery/SKILL.md +5 -1
  15. package/.agents/skills/discovery/agents/openai.yaml +5 -0
  16. package/.agents/skills/dispatcher/SKILL.md +5 -1
  17. package/.agents/skills/dispatcher/agents/openai.yaml +5 -0
  18. package/.agents/skills/docs-orchestrator/SKILL.md +3 -1
  19. package/.agents/skills/ecosystem-health/SKILL.md +3 -1
  20. package/.agents/skills/eli5/SKILL.md +5 -1
  21. package/.agents/skills/eli5/agents/openai.yaml +5 -0
  22. package/.agents/skills/eval/SKILL.md +6 -2
  23. package/.agents/skills/eval/agents/openai.yaml +5 -0
  24. package/.agents/skills/evolve/SKILL.md +6 -2
  25. package/.agents/skills/evolve/agents/openai.yaml +5 -0
  26. package/.agents/skills/frontmatter-guard/SKILL.md +3 -1
  27. package/.agents/skills/gitlab-ops/SKILL.md +3 -1
  28. package/.agents/skills/gitlab-portfolio/SKILL.md +3 -1
  29. package/.agents/skills/go/SKILL.md +5 -1
  30. package/.agents/skills/go/agents/openai.yaml +5 -0
  31. package/.agents/skills/grill/SKILL.md +5 -1
  32. package/.agents/skills/grill/agents/openai.yaml +5 -0
  33. package/.agents/skills/harness-audit/SKILL.md +5 -1
  34. package/.agents/skills/harness-audit/agents/openai.yaml +5 -0
  35. package/.agents/skills/hook-development/SKILL.md +3 -1
  36. package/.agents/skills/mcp-builder/SKILL.md +3 -1
  37. package/.agents/skills/memory-cleanup/SKILL.md +5 -1
  38. package/.agents/skills/memory-cleanup/agents/openai.yaml +5 -0
  39. package/.agents/skills/mode-selector/SKILL.md +3 -1
  40. package/.agents/skills/npm-publish/SKILL.md +4 -2
  41. package/.agents/skills/peekaboo-driver/SKILL.md +3 -1
  42. package/.agents/skills/persona-panel/SKILL.md +5 -1
  43. package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
  44. package/.agents/skills/plan/SKILL.md +5 -1
  45. package/.agents/skills/plan/agents/openai.yaml +5 -0
  46. package/.agents/skills/playwright-driver/SKILL.md +3 -1
  47. package/.agents/skills/portfolio/SKILL.md +5 -1
  48. package/.agents/skills/portfolio/agents/openai.yaml +5 -0
  49. package/.agents/skills/quality-gates/SKILL.md +3 -1
  50. package/.agents/skills/reconcile/SKILL.md +5 -1
  51. package/.agents/skills/reconcile/agents/openai.yaml +5 -0
  52. package/.agents/skills/release/SKILL.md +5 -1
  53. package/.agents/skills/release/agents/openai.yaml +5 -0
  54. package/.agents/skills/remote-offload/SKILL.md +3 -1
  55. package/.agents/skills/repo-audit/SKILL.md +5 -1
  56. package/.agents/skills/repo-audit/agents/openai.yaml +5 -0
  57. package/.agents/skills/session/SKILL.md +21 -0
  58. package/.agents/skills/session/agents/openai.yaml +5 -0
  59. package/.agents/skills/session-end/SKILL.md +3 -1
  60. package/.agents/skills/session-plan/SKILL.md +3 -1
  61. package/.agents/skills/session-start/SKILL.md +3 -1
  62. package/.agents/skills/spinout/SKILL.md +5 -1
  63. package/.agents/skills/spinout/agents/openai.yaml +5 -0
  64. package/.agents/skills/sunset-review/SKILL.md +5 -1
  65. package/.agents/skills/sunset-review/agents/openai.yaml +5 -0
  66. package/.agents/skills/templates-ack/SKILL.md +21 -0
  67. package/.agents/skills/templates-ack/agents/openai.yaml +5 -0
  68. package/.agents/skills/test/SKILL.md +5 -1
  69. package/.agents/skills/test/agents/openai.yaml +5 -0
  70. package/.agents/skills/test-runner/SKILL.md +3 -1
  71. package/.agents/skills/tmux-layout/SKILL.md +3 -1
  72. package/.agents/skills/using-orchestrator/SKILL.md +3 -1
  73. package/.agents/skills/ux-grill/SKILL.md +5 -1
  74. package/.agents/skills/ux-grill/agents/openai.yaml +5 -0
  75. package/.agents/skills/vault-mirror/SKILL.md +3 -1
  76. package/.agents/skills/vault-sync/SKILL.md +3 -1
  77. package/.agents/skills/wave-executor/SKILL.md +3 -1
  78. package/.agents/skills/write-executable-plan/SKILL.md +3 -1
  79. package/.claude-plugin/marketplace.json +1 -1
  80. package/.claude-plugin/plugin.json +1 -1
  81. package/.codex-plugin/plugin.json +4 -4
  82. package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -3
  83. package/.codex-plugin/skills/eval/SKILL.md +1 -1
  84. package/.codex-plugin/skills/evolve/SKILL.md +1 -1
  85. package/.codex-plugin/skills/npm-publish/SKILL.md +1 -3
  86. package/.codex-plugin/skills/session/SKILL.md +1 -1
  87. package/.cursor/commands/eval.md +1 -1
  88. package/.cursor/commands/session.md +1 -1
  89. package/.cursor/rules/000-session-orchestrator.mdc +0 -2
  90. package/.cursor/rules/050-plan.mdc +1 -1
  91. package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
  92. package/.cursor/skills/eval/SKILL.md +1 -1
  93. package/.cursor/skills/npm-publish/SKILL.md +1 -0
  94. package/.cursor-plugin/plugin.json +1 -1
  95. package/.orchestrator/policy/blocked-commands.json +12 -3
  96. package/AGENTS.md +3 -2
  97. package/CHANGELOG.md +136 -0
  98. package/README.md +9 -9
  99. package/SECURITY.md +12 -0
  100. package/agents/dialectic-deriver.md +13 -10
  101. package/agents/eval-judge.md +67 -45
  102. package/agents/skill-applied-judge.md +34 -19
  103. package/commands/session.md +7 -3
  104. package/docs/baseline.md +12 -6
  105. package/docs/codex-setup.md +14 -2
  106. package/docs/components.md +7 -5
  107. package/docs/events-schema.md +56 -9
  108. package/docs/rule-authoring.md +58 -6
  109. package/docs/session-config-reference.md +100 -7
  110. package/docs/session-config-template.md +31 -2
  111. package/docs/telemetry.md +2 -0
  112. package/hooks/_lib/hook-import-set.json +85 -8
  113. package/hooks/_lib/subagent-transcript.mjs +582 -31
  114. package/hooks/config-protection.mjs +11 -3
  115. package/hooks/cwd-change-restore.mjs +11 -3
  116. package/hooks/enforce-commands.mjs +70 -23
  117. package/hooks/enforce-scope.mjs +143 -33
  118. package/hooks/hooks-codex.json +1 -1
  119. package/hooks/hooks.json +1 -1
  120. package/hooks/loop-guard.mjs +11 -3
  121. package/hooks/on-session-end.mjs +58 -23
  122. package/hooks/on-session-start.mjs +48 -11
  123. package/hooks/on-stop.mjs +168 -22
  124. package/hooks/operator-steer.mjs +11 -3
  125. package/hooks/post-bash-issue-budget-refund.mjs +18 -8
  126. package/hooks/post-bash-write-verify.mjs +3 -2
  127. package/hooks/post-edit-import-probe.mjs +17 -9
  128. package/hooks/post-edit-validate.mjs +13 -5
  129. package/hooks/post-subagent-discovery-validator.mjs +98 -13
  130. package/hooks/post-tool-batch-wave-signal.mjs +200 -38
  131. package/hooks/post-tool-failure-corrective-context.mjs +11 -5
  132. package/hooks/post-tooluse-frontend-slop.mjs +10 -4
  133. package/hooks/pre-auq-clarity.mjs +15 -2
  134. package/hooks/pre-bash-destructive-guard.mjs +80 -9
  135. package/hooks/pre-bash-issue-budget.mjs +16 -11
  136. package/hooks/pre-bash-memory-propose-audit.mjs +86 -54
  137. package/hooks/pre-bash-sessions-ledger-guard.mjs +391 -20
  138. package/hooks/pre-bash-staging-fence.mjs +335 -31
  139. package/hooks/pre-bash-templates-first.mjs +19 -14
  140. package/hooks/pre-task-scope-disjoint.mjs +233 -2
  141. package/hooks/subagent-telemetry.mjs +15 -19
  142. package/hooks/wave-scope-commit-guard.mjs +197 -100
  143. package/monitors/monitors.json +1 -1
  144. package/output-styles/wave-summary.md +1 -1
  145. package/package.json +1 -1
  146. package/pi/prompts/eval.md +1 -1
  147. package/pi/prompts/session.md +1 -1
  148. package/rules/README.md +1 -1
  149. package/rules/opt-in-domain/prompt-caching.md +1 -1
  150. package/rules/opt-in-stack/backend-data.md +1 -1
  151. package/rules/opt-in-stack/backend.md +3 -3
  152. package/rules/opt-in-stack/frontend.md +1 -1
  153. package/rules/opt-in-stack/security-web.md +3 -3
  154. package/rules/opt-in-stack/swift.md +1 -1
  155. package/scripts/autopilot.mjs +23 -2
  156. package/scripts/backfill-abandoned-sessions.mjs +117 -15
  157. package/scripts/check-sessions-integrity.mjs +300 -0
  158. package/scripts/dialectic-deriver.mjs +50 -13
  159. package/scripts/emit-session.mjs +75 -29
  160. package/scripts/eval-session.mjs +65 -3
  161. package/scripts/generate-agents-skills.mjs +102 -29
  162. package/scripts/generate-cursor-adapter.mjs +61 -16
  163. package/scripts/lib/agent-status.mjs +2 -31
  164. package/scripts/lib/auq/clarity.mjs +10 -2
  165. package/scripts/lib/auq/parse.mjs +12 -31
  166. package/scripts/lib/auq/schema.mjs +56 -41
  167. package/scripts/lib/auto-dialectic.mjs +304 -15
  168. package/scripts/lib/autopilot/flags.mjs +12 -1
  169. package/scripts/lib/autopilot/kill-switches.mjs +6 -3
  170. package/scripts/lib/autopilot/loop.mjs +14 -1
  171. package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
  172. package/scripts/lib/ci-status-banner.mjs +376 -16
  173. package/scripts/lib/command-blocker.mjs +275 -28
  174. package/scripts/lib/config/dialectic.mjs +12 -3
  175. package/scripts/lib/config/gate.mjs +74 -0
  176. package/scripts/lib/config/reaper.mjs +162 -0
  177. package/scripts/lib/config.mjs +14 -0
  178. package/scripts/lib/convergence-monitor.mjs +74 -11
  179. package/scripts/lib/ecosystem-health.mjs +11 -0
  180. package/scripts/lib/eval/engine.mjs +421 -53
  181. package/scripts/lib/eval/judge.mjs +463 -40
  182. package/scripts/lib/eval/schema.mjs +10 -1
  183. package/scripts/lib/events-rotation.mjs +221 -25
  184. package/scripts/lib/events-schema.mjs +114 -0
  185. package/scripts/lib/events.mjs +524 -5
  186. package/scripts/lib/frontmatter-guard.mjs +21 -10
  187. package/scripts/lib/gates/gate-baseline.mjs +27 -2
  188. package/scripts/lib/gates/gate-full.mjs +28 -3
  189. package/scripts/lib/gates/gate-helpers.mjs +243 -21
  190. package/scripts/lib/gates/gate-incremental.mjs +28 -3
  191. package/scripts/lib/gates/gate-per-file.mjs +27 -2
  192. package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
  193. package/scripts/lib/instruction-budget-guard.mjs +146 -4
  194. package/scripts/lib/io.mjs +42 -8
  195. package/scripts/lib/issue-close-strip-labels.mjs +207 -49
  196. package/scripts/lib/js-mask.mjs +197 -0
  197. package/scripts/lib/learnings/evolve-telemetry.mjs +11 -7
  198. package/scripts/lib/maintenance-due-banner.mjs +53 -88
  199. package/scripts/lib/orphan-reaper.mjs +1588 -0
  200. package/scripts/lib/peer-cards/merger.mjs +48 -10
  201. package/scripts/lib/peer-cards/reader.mjs +78 -2
  202. package/scripts/lib/process-group.mjs +899 -0
  203. package/scripts/lib/quality-gate.mjs +107 -28
  204. package/scripts/lib/reconcile/backlog.mjs +368 -0
  205. package/scripts/lib/reconcile/engine.mjs +55 -188
  206. package/scripts/lib/reconcile/rule-expiry-sweep.mjs +302 -60
  207. package/scripts/lib/reconcile/sanitize.mjs +69 -3
  208. package/scripts/lib/reconcile-nudge-banner.mjs +138 -45
  209. package/scripts/lib/resource-probe/parsers.mjs +31 -0
  210. package/scripts/lib/rule-loader.mjs +41 -12
  211. package/scripts/lib/scope-echo.mjs +39 -2
  212. package/scripts/lib/scope-gate.mjs +605 -1
  213. package/scripts/lib/session-close-backfill.mjs +33 -6
  214. package/scripts/lib/session-id.mjs +9 -20
  215. package/scripts/lib/session-invocation.mjs +20 -0
  216. package/scripts/lib/session-schema/constants.mjs +30 -2
  217. package/scripts/lib/session-schema/normalizer.mjs +56 -4
  218. package/scripts/lib/session-schema.mjs +8 -3
  219. package/scripts/lib/session-start-probes.mjs +95 -10
  220. package/scripts/lib/sessions-canonical.mjs +23 -0
  221. package/scripts/lib/sessions-integrity-banner.mjs +7 -1
  222. package/scripts/lib/sessions-staleness-banner.mjs +193 -51
  223. package/scripts/lib/skill-evidence-window.mjs +891 -0
  224. package/scripts/lib/skill-evolution/candidate-intake.mjs +133 -12
  225. package/scripts/lib/skill-evolution/engine.mjs +18 -9
  226. package/scripts/lib/skill-judge.mjs +45 -3
  227. package/scripts/lib/tail-window.mjs +56 -0
  228. package/scripts/lib/telemetry/schema.mjs +30 -0
  229. package/scripts/lib/telemetry/sync.mjs +61 -6
  230. package/scripts/lib/telemetry-flush-health-banner.mjs +4 -22
  231. package/scripts/lib/test-runner/issue-reconcile.mjs +48 -16
  232. package/scripts/lib/tmux-layout/telemetry-stats.mjs +72 -13
  233. package/scripts/lib/user-invocable-skills.mjs +23 -3
  234. package/scripts/lib/ux-grill/reconcile.mjs +48 -22
  235. package/scripts/lib/validate/check-agents-skills.mjs +26 -15
  236. package/scripts/lib/validate/check-cursor-adapter.mjs +1 -0
  237. package/scripts/lib/validate/check-entry-guard.mjs +13 -50
  238. package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
  239. package/scripts/lib/validate/check-pi-prompts.mjs +1 -0
  240. package/scripts/lib/validate/check-rules.mjs +7 -5
  241. package/scripts/lib/validate/check-skill-links.mjs +9 -1
  242. package/scripts/lib/validate/check-skill-script-paths.mjs +239 -27
  243. package/scripts/lib/validate/check-test-git-config-target.mjs +24 -34
  244. package/scripts/lib/validate/check-untracked-test-deps.mjs +7 -102
  245. package/scripts/lib/validate/check-unwired-features.mjs +130 -27
  246. package/scripts/lib/validate/check-validator-registration.mjs +34 -10
  247. package/scripts/lib/validate/confidential-names.mjs +10 -0
  248. package/scripts/lib/validate-vendored-rules.mjs +4 -3
  249. package/scripts/lib/vault-mirror/namespace.mjs +46 -8
  250. package/scripts/lib/vault-mirror/process.mjs +10 -3
  251. package/scripts/lib/vault-mirror/render-sessions.mjs +12 -2
  252. package/scripts/lib/vault-status/narrative-mirror.mjs +31 -7
  253. package/scripts/lib/vault-yaml.mjs +118 -0
  254. package/scripts/lib/worktree/lifecycle.mjs +153 -1
  255. package/scripts/release-session-lock.mjs +305 -0
  256. package/scripts/release.mjs +30 -5
  257. package/scripts/resolve-session-invocation.mjs +59 -0
  258. package/scripts/run-quality-gate.mjs +156 -17
  259. package/scripts/sweep-expired-rules.mjs +14 -3
  260. package/scripts/validate-plugin.mjs +12 -0
  261. package/scripts/validate-wave-scope.mjs +32 -105
  262. package/scripts/vault-mirror.mjs +9 -1
  263. package/skills/_shared/platform-tools.md +23 -11
  264. package/skills/autopilot/SKILL.md +22 -7
  265. package/skills/claude-md-drift-check/SKILL.md +1 -1
  266. package/skills/convergence-monitoring/README.md +8 -1
  267. package/skills/convergence-monitoring/SIGNALS.md +50 -6
  268. package/skills/convergence-monitoring/SKILL.md +15 -6
  269. package/skills/eval/SKILL.md +39 -24
  270. package/skills/eval/rubric-v1.md +1 -0
  271. package/skills/eval/rubric-v2.md +457 -0
  272. package/skills/evolve/SKILL.md +1 -1
  273. package/skills/evolve/references/evolve-dialectic-mode.md +42 -25
  274. package/skills/gitlab-ops/SKILL.md +3 -2
  275. package/skills/npm-publish/SKILL.md +1 -1
  276. package/skills/reconcile/SKILL.md +11 -0
  277. package/skills/session-end/SKILL.md +13 -16
  278. package/skills/session-end/discovery-scan.md +1 -1
  279. package/skills/session-end/phase-3-6-tail.md +55 -9
  280. package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
  281. package/skills/session-end/session-metrics-write.md +10 -0
  282. package/skills/session-plan/SKILL.md +17 -5
  283. package/skills/session-plan/references/session-plan-task-classification.md +2 -2
  284. package/skills/session-start/references/phase-4-ssot-environment-check.md +2 -1
  285. package/skills/ux-grill/SKILL.md +1 -1
  286. package/skills/wave-executor/SKILL.md +8 -4
  287. package/skills/wave-executor/circuit-breaker.md +2 -0
  288. package/skills/wave-executor/references/wave-executor-state-init.md +5 -3
  289. package/skills/wave-executor/references/wave-loop-dispatch.md +2 -1
  290. package/.codex-plugin/skills/convergence-monitoring/agents/openai.yaml +0 -5
  291. package/.codex-plugin/skills/npm-publish/agents/openai.yaml +0 -5
  292. package/.cursor/commands/convergence-monitoring.md +0 -13
  293. package/.cursor/commands/npm-publish.md +0 -13
  294. package/pi/prompts/convergence-monitoring.md +0 -11
  295. package/pi/prompts/npm-publish.md +0 -11
@@ -2,7 +2,7 @@
2
2
  {
3
3
  "name": "ecosystem-health",
4
4
  "command": "node \"${CLAUDE_PLUGIN_ROOT}\"/scripts/lib/ecosystem-health.mjs --watch",
5
- "description": "Ecosystem repo health watcher (CI status, branch drift, stale baselines across vault-registered projects). Emits one NDJSON line per detected state change. Hung on session-start because the previous trigger (on-skill-invoke:ecosystem-health) was self-referential: it could only start when the ecosystem-health skill ran, and that skill has 0 recorded invocations fleet-wide, so the watcher never started once.",
5
+ "description": "Local state-file watcher: every 900 s it stats and reads ONE file, .orchestrator/metrics/ecosystem-health.jsonl under the plugin root, and emits one NDJSON line per change (appeared / disappeared / changed). No network, no CI or branch probing, no repo enumeration, no Session Config read. Nothing writes that file yet (0 writers, measured 2026-09-18), so every run emits `no-state-yet` and idles until SIGTERM — ceiling and revisit trigger at DEFAULT_INTERVAL_S in scripts/lib/ecosystem-health.mjs. Hung on session-start because the previous trigger (on-skill-invoke:ecosystem-health) was self-referential: it could only start when the ecosystem-health skill ran, and that skill has 0 recorded invocations fleet-wide, so the watcher never started once.",
6
6
  "when": "on-skill-invoke:session-start"
7
7
  },
8
8
  {
@@ -32,7 +32,7 @@ Only include if the plan changed:
32
32
 
33
33
  - One table row per agent — no nested details
34
34
  - Status is exactly one of: `done`, `partial`, `failed`
35
- - File lists use short relative paths (e.g., `hooks/on-stop.sh`)
35
+ - File lists use short relative paths (e.g., `hooks/on-stop.mjs`)
36
36
  - Quality gates are pass/fail — no ambiguity
37
37
  - Adaptations only when deviating from plan — omit if on track
38
38
  - Keep under 20 lines total
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "session-orchestrator",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "description": "A repeatable Plan, Go, Close workflow for AI coding sessions: /session reads your repo and agrees the scope, /go runs the work in waves with a quality gate between each, /close verifies and commits. Runs on Claude Code, Codex CLI, Cursor and Pi.",
5
5
  "type": "module",
6
6
  "homepage": "https://session-orchestrator.com",
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Use this skill to run an honest session-process evaluation (Standard v1, aiat-llm-eval/1.0) — score the last completed orchestrator session against the pre-registered rubric-v1 dimensions, run /eval, evaluate this session, produce an eval report, or re-verify a stored eval run for reproducibility. Deterministic-first with an optional advisory LLM judge; never produces a global score."
2
+ description: "Use this skill to run an honest session-process evaluation (Standard v1, aiat-llm-eval/1.0) — score the last completed orchestrator session against the pre-registered rubric-v2 dimensions, run /eval, evaluate this session, produce an eval report, or re-verify a stored eval run for reproducibility. Deterministic-first with an optional advisory LLM judge; never produces a global score."
3
3
  argument-hint: "[--session <id>] [--no-write] [--verify <run-id>]"
4
4
  ---
5
5
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: "Start a development session (housekeeping, feature, deep; ultradeep = deep + profile)"
3
- argument-hint: "[housekeeping|feature|deep|ultradeep]"
3
+ argument-hint: "[housekeeping|feature|deep|ultradeep] [task context]"
4
4
  ---
5
5
 
6
6
  # /session
package/rules/README.md CHANGED
@@ -9,7 +9,7 @@ It is **not** the always-on rule set of *this* repository — those live in
9
9
  "rules" but play opposite roles: `rules/` ships rules **out** to other repos,
10
10
  `.claude/rules/` is what this repo runs on day-to-day.
11
11
 
12
- Every file here carries a
12
+ Every rule file here carries a
13
13
  `<!-- source: session-orchestrator plugin (canonical: rules/<path>) -->` header.
14
14
  On sync, plugin-sourced files are overwritten in the consumer while its
15
15
  locally-authored rules are preserved (copy-on-write). The canonical manifest of
@@ -267,4 +267,4 @@ The four PoCs filed against this rule. Each adopts in <30 LOC and is independent
267
267
 
268
268
  ## See Also
269
269
 
270
- backend.md · testing.md · development.md
270
+ backend.md
@@ -185,4 +185,4 @@ async function updateUser(id: string, data: Partial<User>): Promise<void> {
185
185
  - Batch operations: use `.upsert()` or `.insert()` with arrays instead of individual inserts in loops.
186
186
 
187
187
  ## See Also
188
- development.md · security.md · security-web.md · testing.md · frontend.md · backend.md · swift.md · mvp-scope.md · cli-design.md · parallel-sessions.md
188
+ security-web.md · frontend.md · backend.md · swift.md · parallel-sessions.md
@@ -165,7 +165,7 @@ Split api and worker into separate processes when any of these apply: queue-base
165
165
  - Named events (`event: <name>\n`) instead of untyped `data:` payloads, so client code reads `source.addEventListener('<name>', ...)` rather than switching on payload shape.
166
166
  - Naming: lowercase, dot-scoped, noun-verb. Examples: `draft.ready`, `classification.done`, `job.failed`, `stream.heartbeat`, `stream.end`.
167
167
  - Scope events by domain, not by endpoint. Two endpoints emitting `job.failed` is fine if the payload shape is identical.
168
- - Document the event catalog in the service's `CLAUDE.md` or `docs/api.md` — consumers need to know what to listen for without reading source.
168
+ - Document the event catalog in the service's `CLAUDE.md` or `docs/api.md` — consumers need to know what to listen for without reading source. <!-- path-check: example -->
169
169
 
170
170
  ### Client reconnect pattern
171
171
  - `EventSource` reconnects automatically on network drop. Use the `Last-Event-ID` header to resume from the last received ID — set `id: <number>\n` on each server event for this to work.
@@ -285,7 +285,7 @@ Uses the canonical envelope from the [Canonical API Response Envelope](#canonica
285
285
  - **Reusable helper:** candidate for `@your-org/http-client` or a dedicated `@your-org/ringbuffer` package if demand grows past 2 consumer repos.
286
286
 
287
287
  ## Feature Flags
288
- - Use environment-variable-backed typed flags as default (see `docs/feature-flags.md`).
288
+ - Use environment-variable-backed typed flags as default (see `docs/feature-flags.md`). <!-- path-check: example -->
289
289
  - Naming convention: `FF_<FEATURE_NAME>` in `.env`.
290
290
  - Every flag must have an expiry date. Remove within 30 days of full rollout.
291
291
 
@@ -387,4 +387,4 @@ export async function askAI(prompt: string, options?: { model?: string; maxToken
387
387
  - Checking budget after the LLM call (check before, enforce before spending)
388
388
 
389
389
  ## See Also
390
- development.md · security.md · security-web.md · testing.md · frontend.md · backend-data.md · swift.md · mvp-scope.md · cli-design.md · parallel-sessions.md
390
+ security-web.md · frontend.md · backend-data.md · swift.md · parallel-sessions.md
@@ -95,4 +95,4 @@ tier: wave-only
95
95
  - No arbitrary z-index (`z-index: 999 / 9999`). Build a semantic z-index scale (dropdown → sticky → modal → toast → tooltip); never magic numbers.
96
96
 
97
97
  ## See Also
98
- development.md · security.md · security-web.md · testing.md · backend.md · backend-data.md · mvp-scope.md · parallel-sessions.md
98
+ security-web.md · backend.md · backend-data.md · parallel-sessions.md
@@ -67,7 +67,7 @@ CSRF, rate limiting, CSP, and transport security. Core security rules are in `se
67
67
 
68
68
  #### SEC-011: CSP single-source enforcement
69
69
  - Advisory rules are silent when they regress. Wire the single-source rule into a mechanical check so duplicates fail fast.
70
- - Canonical hook: `templates/shared/hooks/check-csp-single-source.sh`. Greps all TS/JS under the given paths for `Content-Security-Policy`, filters to `proxy.ts` / `middleware.ts` / `next.config.*`, fails if more than one file matches.
70
+ - Canonical hook: `templates/shared/hooks/check-csp-single-source.sh`. Greps all TS/JS under the given paths for `Content-Security-Policy`, filters to `proxy.ts` / `middleware.ts` / `next.config.*`, fails if more than one file matches. <!-- path-check: example -->
71
71
  - **Wire into pre-push (Husky):**
72
72
  ```json
73
73
  // package.json
@@ -77,7 +77,7 @@ CSRF, rate limiting, CSP, and transport security. Core security rules are in `se
77
77
  # .husky/pre-push
78
78
  pnpm run hook:check-csp
79
79
  ```
80
- - **Wire into CI security stage:** copy the `check-csp-single-source` job from `templates/shared/hooks/README.md` into `.gitlab-ci.yml`. Runs on every merge request.
80
+ - **Wire into CI security stage:** copy the `check-csp-single-source` job from `templates/shared/hooks/README.md` into `.gitlab-ci.yml`. Runs on every merge request. <!-- path-check: example -->
81
81
  - Exit codes: `0` pass, `1` duplicate CSP source (SEC-011 violation), `2` tool error.
82
82
  - Why: browsers receiving two `Content-Security-Policy` headers take the intersection — nonces and `strict-dynamic` break silently. Humans miss this in review; a grep doesn't.
83
83
 
@@ -191,4 +191,4 @@ securityMiddleware(app);
191
191
  - **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.
192
192
 
193
193
  ## See Also
194
- development.md · security.md · testing.md · frontend.md · backend.md · backend-data.md · swift.md · mvp-scope.md · cli-design.md · parallel-sessions.md
194
+ frontend.md · backend.md · backend-data.md · swift.md · parallel-sessions.md
@@ -62,4 +62,4 @@ tier: wave-only
62
62
  - Validate all server-sent data with Codable schemas. Never `force-unwrap` optional JSON fields.
63
63
 
64
64
  ## See Also
65
- development.md · security.md · testing.md · frontend.md · backend.md · mvp-scope.md · parallel-sessions.md
65
+ frontend.md · backend.md · parallel-sessions.md
@@ -14,6 +14,7 @@
14
14
  * --max-sessions=N Max iterations (1..50, default 5).
15
15
  * --max-hours=H Max wall-clock hours (0.5..24.0, default 4.0).
16
16
  * --confidence-threshold=0.X Mode confidence gate (0.0..1.0, default 0.85).
17
+ * --max-tokens=N Cumulative output-token budget (0..10000000, default 500000; 0 disables).
17
18
  * --dry-run Emit a single record without spawning sessions.
18
19
  * --verbose Pipe child process stdio (instead of inherit).
19
20
  *
@@ -35,6 +36,7 @@
35
36
  import { readFileSync, existsSync } from 'node:fs';
36
37
  import { spawnSync, spawn } from 'node:child_process';
37
38
  import { resolve } from 'node:path';
39
+ import { fileURLToPath } from 'node:url';
38
40
 
39
41
  import {
40
42
  runLoop,
@@ -55,6 +57,14 @@ import { readCanonicalSessions } from './lib/sessions-canonical.mjs';
55
57
 
56
58
  const argv = process.argv.slice(2);
57
59
 
60
+ /**
61
+ * Plugin root, resolved from THIS file's own location (`<root>/scripts/autopilot.mjs`
62
+ * → `<root>`) and never from an env var. The driver is spawned unattended from an
63
+ * arbitrary cwd, so `--plugin-dir` must name the plugin that owns this very file;
64
+ * an env-var read would point wherever the operator's shell last happened to point.
65
+ */
66
+ const PLUGIN_ROOT = resolve(fileURLToPath(new URL('.', import.meta.url)), '..');
67
+
58
68
  const hasHeadless = argv.includes('--headless');
59
69
  const hasVerbose = argv.includes('--verbose');
60
70
 
@@ -69,7 +79,7 @@ const flagsForParse = argv.filter(
69
79
  (a) => a !== '--headless' && a !== '--verbose'
70
80
  );
71
81
 
72
- const { maxSessions, maxHours, confidenceThreshold, dryRun } = parseFlags(flagsForParse);
82
+ const { maxSessions, maxHours, confidenceThreshold, maxTokens, dryRun } = parseFlags(flagsForParse);
73
83
 
74
84
  // ---------------------------------------------------------------------------
75
85
  // Session Config (parsed once, shared by thresholds + decay readers)
@@ -269,9 +279,14 @@ async function sessionRunner({ mode, autopilotRunId }) {
269
279
  ? ['ignore', 'pipe', 'pipe']
270
280
  : ['ignore', 'inherit', 'inherit'];
271
281
 
282
+ // `session` is a RESERVED terminal-only built-in name under `claude -p`: the
283
+ // bare `/session <mode>` form answers "/session isn't available in this
284
+ // environment." and the child exits having done nothing (measured 2026-09-16,
285
+ // claude 2.1.273 — `commands/session.md` § Headless). Only the namespaced form
286
+ // resolves, and only when the plugin that defines it is on `--plugin-dir`.
272
287
  const child = spawn(
273
288
  'claude',
274
- ['-p', `/session ${mode}`],
289
+ ['-p', `/session-orchestrator:session ${mode}`, '--plugin-dir', PLUGIN_ROOT],
275
290
  {
276
291
  env: { ...process.env, AUTOPILOT_RUN_ID: autopilotRunId },
277
292
  stdio: childStdio,
@@ -372,12 +387,18 @@ async function main() {
372
387
  maxSessions,
373
388
  maxHours,
374
389
  confidenceThreshold,
390
+ maxTokens,
375
391
  dryRun,
376
392
  modeSelector,
377
393
  sessionRunner,
378
394
  resourceEvaluator,
379
395
  peerCounter,
380
396
  abortSignal: controller.signal,
397
+ // STALL_TIMEOUT progress marker. `autopilot.jsonl` is written ONCE per
398
+ // invocation (after the loop), so its mtime belongs to the PREVIOUS run and
399
+ // fired the kill-switch after iteration 1; the session.lock heartbeat is
400
+ // refreshed while a session is alive. See stall-sampler.mjs § PRECEDENCE.
401
+ sessionLockPath: resolve('.orchestrator/session.lock'),
381
402
  runId: autopilotRunId,
382
403
  branch: branch ?? undefined,
383
404
  });
@@ -2,7 +2,8 @@
2
2
  /**
3
3
  * backfill-abandoned-sessions.mjs — one-time historical migration CLI (#724 C1).
4
4
  *
5
- * Scans `.orchestrator/metrics/events.jsonl` for every distinct
5
+ * Scans `.orchestrator/metrics/events.jsonl` and every rotated archive
6
+ * beside it (#1414) — for every distinct
6
7
  * `orchestrator.session.started` UUID that has no counterpart in
7
8
  * `.orchestrator/metrics/sessions.jsonl`, bridges each to its semantic id via
8
9
  * `orchestrator.session.lock.acquired` where available, and synthesizes a
@@ -41,7 +42,7 @@ import path from 'node:path';
41
42
  import { parseArgs } from 'node:util';
42
43
 
43
44
  import { backfillAbandonedSession, isUuid } from './lib/session-close-backfill.mjs';
44
- import { emitEvent } from './lib/events.mjs';
45
+ import { SCAN_CHUNK_BYTES, emitEvent, listEventSourcesNewestFirst } from './lib/events.mjs';
45
46
  import { getProjectDir } from './lib/platform.mjs';
46
47
  import { isMainModule } from './lib/is-main-module.mjs';
47
48
  import { isLockLive, readLock } from './lib/session-lock.mjs';
@@ -125,7 +126,7 @@ function readJsonl(filePath) {
125
126
  * @returns {Array<{ sessionId: string, semanticSessionId: string|null }>}
126
127
  */
127
128
  export function planSessions({ repoRoot }) {
128
- const events = readJsonl(path.join(repoRoot, '.orchestrator', 'metrics', 'events.jsonl'));
129
+ const eventsPath = path.join(repoRoot, '.orchestrator', 'metrics', 'events.jsonl');
129
130
 
130
131
  // Two independent UUID -> semantic bridges (#1167). `lock.acquired` is the
131
132
  // original one, but a session that LOST the lock-acquire race never emits it;
@@ -137,29 +138,130 @@ export function planSessions({ repoRoot }) {
137
138
  // lock.acquired keeps precedence — it is the older, mode-carrying attestation.
138
139
  const semanticFromLock = new Map();
139
140
  const semanticFromEnded = new Map();
140
- for (const ev of events) {
141
- if (typeof ev.session_id !== 'string' || typeof ev.semantic_session_id !== 'string') continue;
142
- if (ev.event === LOCK_ACQUIRED) semanticFromLock.set(ev.session_id, ev.semantic_session_id);
143
- else if (ev.event === SESSION_ENDED) semanticFromEnded.set(ev.session_id, ev.semantic_session_id);
141
+ const seen = new Set();
142
+ /** Distinct `session.started` ids in FIRST-SEEN (chronological) order. */
143
+ const startedIds = [];
144
+
145
+ // #1414 — the whole history, OLDEST SOURCE FIRST, streamed line by line. The
146
+ // previous `readJsonl` of the active file alone missed every session whose
147
+ // `session.started` had been rotated into `_archive/`; reading the same
148
+ // sources through `readEventsWithRotations` instead would have loaded up to
149
+ // ~60 MB into memory on a path that runs at SESSION START
150
+ // (`hooks/on-session-start.mjs` → `backfillOnSessionStart`). Reversing the
151
+ // newest-first source list keeps first-seen order chronological, exactly as
152
+ // the single-file read produced it.
153
+ for (const source of [...listEventSourcesNewestFirst({ filePath: eventsPath })].reverse()) {
154
+ forEachJsonlRecord(source.path, (ev) => {
155
+ if (!ev || typeof ev !== 'object') return;
156
+ if (typeof ev.session_id === 'string' && typeof ev.semantic_session_id === 'string') {
157
+ if (ev.event === LOCK_ACQUIRED) semanticFromLock.set(ev.session_id, ev.semantic_session_id);
158
+ else if (ev.event === SESSION_ENDED) {
159
+ semanticFromEnded.set(ev.session_id, ev.semantic_session_id);
160
+ }
161
+ }
162
+ if (ev.event !== SESSION_STARTED || typeof ev.session_id !== 'string') return;
163
+ if (seen.has(ev.session_id)) return;
164
+ seen.add(ev.session_id);
165
+ startedIds.push(ev.session_id);
166
+ });
144
167
  }
168
+
169
+ // Resolved AFTER the pass, never during it: a `lock.acquired` / `session.ended`
170
+ // bridge may sit later in the stream (or in a later source) than the
171
+ // `session.started` it names, and deciding mid-stream would mint a synthetic
172
+ // id for a session whose semantic id was two lines further on.
145
173
  const semanticByUuid = new Map([...semanticFromEnded, ...semanticFromLock]);
146
174
 
147
175
  const runningIds = readRunningSessionIds(repoRoot);
148
176
 
149
- const seen = new Set();
150
177
  const plan = [];
151
- for (const ev of events) {
152
- if (ev.event !== SESSION_STARTED || typeof ev.session_id !== 'string') continue;
153
- if (seen.has(ev.session_id)) continue;
154
- seen.add(ev.session_id);
155
- const semanticSessionId = semanticByUuid.get(ev.session_id) ?? null;
156
- if (runningIds.has(ev.session_id)
178
+ for (const sessionId of startedIds) {
179
+ const semanticSessionId = semanticByUuid.get(sessionId) ?? null;
180
+ if (runningIds.has(sessionId)
157
181
  || (semanticSessionId !== null && runningIds.has(semanticSessionId))) continue;
158
- plan.push({ sessionId: ev.session_id, semanticSessionId });
182
+ plan.push({ sessionId, semanticSessionId });
159
183
  }
160
184
  return plan;
161
185
  }
162
186
 
187
+ /**
188
+ * Stream one JSONL file, handing every PARSED record to `onRecord` in file
189
+ * order — without ever holding the file in memory (#1414).
190
+ *
191
+ * MISSING (ENOENT) → silent no-op; UNREADABLE → a stderr WARN, same split as
192
+ * {@link readJsonl} (#1188: a missing ledger is the fresh-repo case, an
193
+ * unreadable one used to read as "no records" and made every count below
194
+ * silently wrong). Malformed lines are skipped, as in the full-read path.
195
+ *
196
+ * A line split across a chunk boundary is CARRIED as a BUFFER, so neither a
197
+ * record nor a multibyte character is ever cut in half — the 0x0A byte cannot
198
+ * occur inside a UTF-8 continuation sequence.
199
+ *
200
+ * @param {string} filePath
201
+ * @param {(record: object) => void} onRecord
202
+ */
203
+ function forEachJsonlRecord(filePath, onRecord) {
204
+ const emit = (buf) => {
205
+ const text = buf.toString('utf8').trim();
206
+ if (!text) return;
207
+ try {
208
+ onRecord(JSON.parse(text));
209
+ } catch {
210
+ /* skip malformed */
211
+ }
212
+ };
213
+
214
+ let fd;
215
+ try {
216
+ fd = fs.openSync(filePath, 'r');
217
+ } catch (err) {
218
+ if (!err || err.code !== 'ENOENT') {
219
+ process.stderr.write(
220
+ `⚠ backfill-abandoned-sessions: cannot read ${filePath} ` +
221
+ `(${err?.code ?? '?'}: ${err?.message ?? String(err)}) — ` +
222
+ 'treating as EMPTY, counts below are floors\n',
223
+ );
224
+ }
225
+ return;
226
+ }
227
+ try {
228
+ const size = fs.fstatSync(fd).size;
229
+ const buf = Buffer.alloc(SCAN_CHUNK_BYTES);
230
+ let pos = 0;
231
+ let carry = Buffer.alloc(0);
232
+ while (pos < size) {
233
+ const n = fs.readSync(fd, buf, 0, SCAN_CHUNK_BYTES, pos);
234
+ if (n <= 0) break;
235
+ pos += n;
236
+ const block =
237
+ carry.length > 0
238
+ ? Buffer.concat([carry, buf.subarray(0, n)])
239
+ : Buffer.from(buf.subarray(0, n));
240
+ let start = 0;
241
+ let idx = block.indexOf(0x0a, start);
242
+ while (idx !== -1) {
243
+ emit(block.subarray(start, idx));
244
+ start = idx + 1;
245
+ idx = block.indexOf(0x0a, start);
246
+ }
247
+ carry = block.subarray(start);
248
+ }
249
+ if (carry.length > 0) emit(carry);
250
+ } catch (err) {
251
+ process.stderr.write(
252
+ `⚠ backfill-abandoned-sessions: cannot read ${filePath} ` +
253
+ `(${err?.code ?? '?'}: ${err?.message ?? String(err)}) — ` +
254
+ 'treating as EMPTY, counts below are floors\n',
255
+ );
256
+ } finally {
257
+ try {
258
+ fs.closeSync(fd);
259
+ } catch {
260
+ /* best-effort */
261
+ }
262
+ }
263
+ }
264
+
163
265
  /**
164
266
  * The id(s) naming the session THIS process is, when that session holds the
165
267
  * live lock — else the empty set (#1376). Never throws: an unreadable lock,