session-orchestrator 5.1.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 (484) hide show
  1. package/.agents/skills/architecture/SKILL.md +3 -1
  2. package/.agents/skills/autopilot/SKILL.md +6 -1
  3. package/.agents/skills/autopilot/agents/openai.yaml +5 -0
  4. package/.agents/skills/bootstrap/SKILL.md +7 -1
  5. package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
  6. package/.agents/skills/brainstorm/SKILL.md +8 -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 +21 -0
  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 +7 -1
  13. package/.agents/skills/debug/agents/openai.yaml +5 -0
  14. package/.agents/skills/discovery/SKILL.md +7 -2
  15. package/.agents/skills/discovery/agents/openai.yaml +5 -0
  16. package/.agents/skills/dispatcher/SKILL.md +7 -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 +7 -1
  21. package/.agents/skills/eli5/agents/openai.yaml +5 -0
  22. package/.agents/skills/eval/SKILL.md +7 -2
  23. package/.agents/skills/eval/agents/openai.yaml +5 -0
  24. package/.agents/skills/evolve/SKILL.md +8 -3
  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 +22 -0
  30. package/.agents/skills/go/agents/openai.yaml +5 -0
  31. package/.agents/skills/grill/SKILL.md +7 -1
  32. package/.agents/skills/grill/agents/openai.yaml +5 -0
  33. package/.agents/skills/harness-audit/SKILL.md +20 -0
  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 +6 -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 +6 -1
  43. package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
  44. package/.agents/skills/plan/SKILL.md +8 -2
  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 +21 -0
  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 +6 -1
  51. package/.agents/skills/reconcile/agents/openai.yaml +5 -0
  52. package/.agents/skills/release/SKILL.md +22 -0
  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 +6 -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 +6 -1
  63. package/.agents/skills/spinout/agents/openai.yaml +5 -0
  64. package/.agents/skills/sunset-review/SKILL.md +7 -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 +21 -0
  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 +7 -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/autopilot/SKILL.md +5 -4
  83. package/.codex-plugin/skills/bootstrap/SKILL.md +8 -4
  84. package/.codex-plugin/skills/brainstorm/SKILL.md +11 -4
  85. package/.codex-plugin/skills/close/SKILL.md +3 -3
  86. package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -1
  87. package/.codex-plugin/skills/debug/SKILL.md +11 -4
  88. package/.codex-plugin/skills/discovery/SKILL.md +8 -4
  89. package/.codex-plugin/skills/dispatcher/SKILL.md +4 -4
  90. package/.codex-plugin/skills/eli5/SKILL.md +9 -4
  91. package/.codex-plugin/skills/eval/SKILL.md +9 -4
  92. package/.codex-plugin/skills/evolve/SKILL.md +9 -4
  93. package/.codex-plugin/skills/go/SKILL.md +3 -3
  94. package/.codex-plugin/skills/grill/SKILL.md +11 -4
  95. package/.codex-plugin/skills/harness-audit/SKILL.md +4 -3
  96. package/.codex-plugin/skills/memory-cleanup/SKILL.md +9 -4
  97. package/.codex-plugin/skills/npm-publish/SKILL.md +1 -1
  98. package/.codex-plugin/skills/persona-panel/SKILL.md +5 -5
  99. package/.codex-plugin/skills/plan/SKILL.md +8 -4
  100. package/.codex-plugin/skills/portfolio/SKILL.md +3 -3
  101. package/.codex-plugin/skills/reconcile/SKILL.md +9 -4
  102. package/.codex-plugin/skills/release/SKILL.md +3 -3
  103. package/.codex-plugin/skills/repo-audit/SKILL.md +6 -4
  104. package/.codex-plugin/skills/session/SKILL.md +1 -1
  105. package/.codex-plugin/skills/spinout/SKILL.md +4 -4
  106. package/.codex-plugin/skills/sunset-review/SKILL.md +5 -4
  107. package/.codex-plugin/skills/test/SKILL.md +3 -3
  108. package/.codex-plugin/skills/ux-grill/SKILL.md +11 -4
  109. package/.cursor/commands/autopilot.md +4 -4
  110. package/.cursor/commands/bootstrap.md +5 -4
  111. package/.cursor/commands/brainstorm.md +5 -4
  112. package/.cursor/commands/close.md +4 -3
  113. package/.cursor/commands/debug.md +4 -4
  114. package/.cursor/commands/discovery.md +4 -4
  115. package/.cursor/commands/dispatcher.md +4 -4
  116. package/.cursor/commands/eli5.md +4 -4
  117. package/.cursor/commands/eval.md +4 -4
  118. package/.cursor/commands/evolve.md +4 -4
  119. package/.cursor/commands/go.md +4 -3
  120. package/.cursor/commands/grill.md +4 -4
  121. package/.cursor/commands/harness-audit.md +3 -3
  122. package/.cursor/commands/memory-cleanup.md +4 -4
  123. package/.cursor/commands/persona-panel.md +4 -4
  124. package/.cursor/commands/plan.md +5 -4
  125. package/.cursor/commands/portfolio.md +3 -3
  126. package/.cursor/commands/reconcile.md +4 -4
  127. package/.cursor/commands/release.md +4 -3
  128. package/.cursor/commands/repo-audit.md +4 -4
  129. package/.cursor/commands/session.md +1 -1
  130. package/.cursor/commands/spinout.md +4 -4
  131. package/.cursor/commands/sunset-review.md +4 -4
  132. package/.cursor/commands/test.md +3 -3
  133. package/.cursor/commands/ux-grill.md +4 -4
  134. package/.cursor/rules/000-session-orchestrator.mdc +0 -2
  135. package/.cursor/rules/010-session-workflow.mdc +2 -2
  136. package/.cursor/rules/050-plan.mdc +1 -1
  137. package/.cursor/skills/bootstrap/SKILL.md +1 -0
  138. package/.cursor/skills/close/SKILL.md +13 -0
  139. package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
  140. package/.cursor/skills/debug/SKILL.md +0 -1
  141. package/.cursor/skills/discovery/SKILL.md +0 -1
  142. package/.cursor/skills/dispatcher/SKILL.md +0 -1
  143. package/.cursor/skills/eli5/SKILL.md +0 -1
  144. package/.cursor/skills/eval/SKILL.md +1 -1
  145. package/.cursor/skills/evolve/SKILL.md +0 -1
  146. package/.cursor/skills/go/SKILL.md +13 -0
  147. package/.cursor/skills/grill/SKILL.md +0 -1
  148. package/.cursor/skills/harness-audit/SKILL.md +12 -0
  149. package/.cursor/skills/npm-publish/SKILL.md +1 -0
  150. package/.cursor/skills/portfolio/SKILL.md +12 -0
  151. package/.cursor/skills/release/SKILL.md +13 -0
  152. package/.cursor/skills/repo-audit/SKILL.md +0 -1
  153. package/.cursor/skills/sunset-review/SKILL.md +0 -1
  154. package/.cursor/skills/test/SKILL.md +12 -0
  155. package/.cursor/skills/ux-grill/SKILL.md +0 -1
  156. package/.cursor-plugin/plugin.json +1 -1
  157. package/.orchestrator/policy/blocked-commands.json +13 -4
  158. package/AGENTS.md +3 -2
  159. package/CHANGELOG.md +197 -0
  160. package/README.md +11 -9
  161. package/SECURITY.md +12 -0
  162. package/agents/dialectic-deriver.md +13 -10
  163. package/agents/eval-judge.md +67 -45
  164. package/agents/skill-applied-judge.md +34 -19
  165. package/commands/session.md +17 -3
  166. package/docs/baseline.md +12 -6
  167. package/docs/ci-setup.md +53 -0
  168. package/docs/codex-setup.md +15 -3
  169. package/docs/components.md +13 -6
  170. package/docs/events-schema.md +59 -9
  171. package/docs/install.md +16 -0
  172. package/docs/persona-panel.md +1 -1
  173. package/docs/pi-setup.md +1 -1
  174. package/docs/rule-authoring.md +135 -14
  175. package/docs/scope-collision-guard.md +2 -0
  176. package/docs/session-config-reference.md +106 -11
  177. package/docs/session-config-template.md +31 -2
  178. package/docs/telemetry.md +2 -0
  179. package/hooks/_lib/hook-import-set.json +125 -8
  180. package/hooks/_lib/subagent-paths.mjs +15 -0
  181. package/hooks/_lib/subagent-transcript.mjs +582 -31
  182. package/hooks/_lib/vcs-create-matcher.mjs +217 -62
  183. package/hooks/config-protection.mjs +11 -3
  184. package/hooks/cwd-change-restore.mjs +11 -3
  185. package/hooks/enforce-commands.mjs +70 -23
  186. package/hooks/enforce-scope.mjs +143 -33
  187. package/hooks/hooks-codex.json +1 -1
  188. package/hooks/hooks.json +1 -1
  189. package/hooks/loop-guard.mjs +11 -3
  190. package/hooks/on-session-end.mjs +72 -25
  191. package/hooks/on-session-start.mjs +48 -11
  192. package/hooks/on-stop.mjs +211 -23
  193. package/hooks/operator-steer.mjs +11 -3
  194. package/hooks/post-bash-issue-budget-refund.mjs +18 -8
  195. package/hooks/post-bash-write-verify.mjs +6 -2
  196. package/hooks/post-edit-import-probe.mjs +17 -9
  197. package/hooks/post-edit-validate.mjs +13 -5
  198. package/hooks/post-subagent-discovery-validator.mjs +98 -13
  199. package/hooks/post-tool-batch-wave-signal.mjs +200 -38
  200. package/hooks/post-tool-failure-corrective-context.mjs +11 -5
  201. package/hooks/post-tooluse-frontend-slop.mjs +10 -4
  202. package/hooks/pre-auq-clarity.mjs +18 -2
  203. package/hooks/pre-bash-destructive-guard.mjs +80 -9
  204. package/hooks/pre-bash-issue-budget.mjs +119 -28
  205. package/hooks/pre-bash-memory-propose-audit.mjs +86 -54
  206. package/hooks/pre-bash-sessions-ledger-guard.mjs +391 -20
  207. package/hooks/pre-bash-staging-fence.mjs +335 -31
  208. package/hooks/pre-bash-templates-first.mjs +19 -14
  209. package/hooks/pre-task-scope-disjoint.mjs +385 -5
  210. package/hooks/skill-invocation-telemetry.mjs +2 -1
  211. package/hooks/subagent-telemetry.mjs +15 -19
  212. package/hooks/wave-scope-commit-guard.mjs +197 -100
  213. package/monitors/monitors.json +1 -1
  214. package/output-styles/wave-summary.md +1 -1
  215. package/package.json +2 -1
  216. package/pi/prompts/autopilot.md +3 -3
  217. package/pi/prompts/bootstrap.md +3 -3
  218. package/pi/prompts/brainstorm.md +3 -3
  219. package/pi/prompts/close.md +2 -2
  220. package/pi/prompts/debug.md +3 -3
  221. package/pi/prompts/discovery.md +3 -3
  222. package/pi/prompts/dispatcher.md +3 -3
  223. package/pi/prompts/eli5.md +3 -3
  224. package/pi/prompts/eval.md +3 -3
  225. package/pi/prompts/evolve.md +3 -3
  226. package/pi/prompts/go.md +2 -2
  227. package/pi/prompts/grill.md +3 -3
  228. package/pi/prompts/harness-audit.md +2 -3
  229. package/pi/prompts/memory-cleanup.md +3 -3
  230. package/pi/prompts/persona-panel.md +3 -3
  231. package/pi/prompts/plan.md +3 -3
  232. package/pi/prompts/portfolio.md +2 -2
  233. package/pi/prompts/reconcile.md +3 -3
  234. package/pi/prompts/release.md +3 -3
  235. package/pi/prompts/repo-audit.md +3 -4
  236. package/pi/prompts/session.md +2 -2
  237. package/pi/prompts/spinout.md +3 -3
  238. package/pi/prompts/sunset-review.md +3 -3
  239. package/pi/prompts/templates-ack.md +1 -1
  240. package/pi/prompts/test.md +3 -3
  241. package/pi/prompts/ux-grill.md +3 -3
  242. package/rules/README.md +1 -1
  243. package/rules/opt-in-domain/prompt-caching.md +1 -1
  244. package/rules/opt-in-stack/backend-data.md +1 -1
  245. package/rules/opt-in-stack/backend.md +3 -3
  246. package/rules/opt-in-stack/frontend.md +1 -1
  247. package/rules/opt-in-stack/security-web.md +3 -3
  248. package/rules/opt-in-stack/swift.md +1 -1
  249. package/scripts/archive-closed-prds.mjs +2 -2
  250. package/scripts/auq-audit.mjs +2 -3
  251. package/scripts/autopilot.mjs +23 -2
  252. package/scripts/backfill-abandoned-sessions.mjs +171 -15
  253. package/scripts/backfill-evidence-digest.mjs +2 -1
  254. package/scripts/backfill-learnings-from-vault.mjs +2 -2
  255. package/scripts/check-package-manager.mjs +2 -2
  256. package/scripts/check-sessions-integrity.mjs +300 -0
  257. package/scripts/ci/assert-vitest-green.mjs +2 -1
  258. package/scripts/dialectic-deriver.mjs +50 -13
  259. package/scripts/emit-session.mjs +77 -32
  260. package/scripts/eval-session.mjs +65 -3
  261. package/scripts/export-hw-learnings.mjs +2 -1
  262. package/scripts/express-path.mjs +1 -1
  263. package/scripts/gc-stale-worktrees.mjs +2 -1
  264. package/scripts/generate-agents-skills.mjs +102 -29
  265. package/scripts/generate-codex-skills.mjs +48 -4
  266. package/scripts/generate-cursor-adapter.mjs +220 -11
  267. package/scripts/generate-hook-import-set.mjs +12 -27
  268. package/scripts/generate-pi-prompts.mjs +183 -13
  269. package/scripts/github-protection-audit.mjs +2 -3
  270. package/scripts/lib/agent-frontmatter.mjs +23 -1
  271. package/scripts/lib/agent-status.mjs +2 -31
  272. package/scripts/lib/auq/clarity.mjs +10 -2
  273. package/scripts/lib/auq/parse.mjs +12 -31
  274. package/scripts/lib/auq/schema.mjs +56 -41
  275. package/scripts/lib/auto-dialectic.mjs +304 -15
  276. package/scripts/lib/autopilot/flags.mjs +12 -1
  277. package/scripts/lib/autopilot/kill-switches.mjs +6 -3
  278. package/scripts/lib/autopilot/loop.mjs +14 -1
  279. package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
  280. package/scripts/lib/ci-status-banner.mjs +376 -16
  281. package/scripts/lib/claude-md-budget-lint.mjs +2 -5
  282. package/scripts/lib/command-blocker.mjs +408 -33
  283. package/scripts/lib/config/dialectic.mjs +12 -3
  284. package/scripts/lib/config/drift-check.mjs +19 -0
  285. package/scripts/lib/config/gate.mjs +74 -0
  286. package/scripts/lib/config/reaper.mjs +162 -0
  287. package/scripts/lib/config.mjs +14 -0
  288. package/scripts/lib/convergence-monitor.mjs +76 -13
  289. package/scripts/lib/cursor-hook-bridge.mjs +2 -2
  290. package/scripts/lib/description-surface.mjs +2 -5
  291. package/scripts/lib/dispatcher/cli.mjs +2 -1
  292. package/scripts/lib/ecosystem-health.mjs +11 -0
  293. package/scripts/lib/ecosystem-wizard.mjs +2 -1
  294. package/scripts/lib/eval/engine.mjs +421 -53
  295. package/scripts/lib/eval/judge.mjs +463 -40
  296. package/scripts/lib/eval/schema.mjs +10 -1
  297. package/scripts/lib/events-rotation.mjs +221 -25
  298. package/scripts/lib/events-schema.mjs +114 -0
  299. package/scripts/lib/events.mjs +524 -5
  300. package/scripts/lib/fetch-baseline.mjs +3 -8
  301. package/scripts/lib/frontmatter-guard.mjs +21 -10
  302. package/scripts/lib/gates/gate-baseline.mjs +27 -2
  303. package/scripts/lib/gates/gate-full.mjs +28 -3
  304. package/scripts/lib/gates/gate-helpers.mjs +243 -21
  305. package/scripts/lib/gates/gate-incremental.mjs +28 -3
  306. package/scripts/lib/gates/gate-per-file.mjs +27 -2
  307. package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +2 -1
  308. package/scripts/lib/gitlab-portfolio/cli.mjs +2 -1
  309. package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
  310. package/scripts/lib/instruction-budget-guard.mjs +332 -50
  311. package/scripts/lib/io.mjs +42 -8
  312. package/scripts/lib/is-main-module.mjs +82 -0
  313. package/scripts/lib/issue-close-strip-labels.mjs +207 -49
  314. package/scripts/lib/js-mask.mjs +197 -0
  315. package/scripts/lib/learnings/evolve-telemetry.mjs +11 -7
  316. package/scripts/lib/locks/index.mjs +32 -25
  317. package/scripts/lib/maintenance-due-banner.mjs +122 -91
  318. package/scripts/lib/orphan-reaper.mjs +1588 -0
  319. package/scripts/lib/peer-cards/merger.mjs +48 -10
  320. package/scripts/lib/peer-cards/reader.mjs +78 -2
  321. package/scripts/lib/peer-discovery.mjs +2 -5
  322. package/scripts/lib/playwright-driver/runner.mjs +2 -1
  323. package/scripts/lib/process-group.mjs +899 -0
  324. package/scripts/lib/quality-gate.mjs +107 -28
  325. package/scripts/lib/reconcile/backlog.mjs +368 -0
  326. package/scripts/lib/reconcile/engine.mjs +55 -188
  327. package/scripts/lib/reconcile/rule-expiry-sweep.mjs +884 -0
  328. package/scripts/lib/reconcile/sanitize.mjs +69 -3
  329. package/scripts/lib/reconcile-nudge-banner.mjs +138 -45
  330. package/scripts/lib/resource-probe/parsers.mjs +31 -0
  331. package/scripts/lib/rule-loader.mjs +41 -12
  332. package/scripts/lib/rules-sync.mjs +2 -5
  333. package/scripts/lib/scope-echo.mjs +429 -7
  334. package/scripts/lib/scope-gate.mjs +605 -1
  335. package/scripts/lib/session-close-backfill.mjs +91 -12
  336. package/scripts/lib/session-id.mjs +9 -20
  337. package/scripts/lib/session-invocation.mjs +20 -0
  338. package/scripts/lib/session-schema/constants.mjs +30 -2
  339. package/scripts/lib/session-schema/normalizer.mjs +56 -4
  340. package/scripts/lib/session-schema.mjs +8 -3
  341. package/scripts/lib/session-start-probes.mjs +95 -10
  342. package/scripts/lib/sessions-canonical.mjs +23 -0
  343. package/scripts/lib/sessions-integrity-banner.mjs +7 -1
  344. package/scripts/lib/sessions-staleness-banner.mjs +193 -51
  345. package/scripts/lib/skill-evidence-window.mjs +891 -0
  346. package/scripts/lib/skill-evolution/candidate-intake.mjs +133 -12
  347. package/scripts/lib/skill-evolution/engine.mjs +18 -9
  348. package/scripts/lib/skill-judge.mjs +45 -3
  349. package/scripts/lib/state-md.mjs +84 -3
  350. package/scripts/lib/sunset/walker.mjs +31 -4
  351. package/scripts/lib/tail-window.mjs +56 -0
  352. package/scripts/lib/telemetry/schema.mjs +30 -0
  353. package/scripts/lib/telemetry/sync.mjs +61 -6
  354. package/scripts/lib/telemetry-flush-health-banner.mjs +4 -22
  355. package/scripts/lib/test-runner/issue-reconcile.mjs +48 -16
  356. package/scripts/lib/tests-src-ratio.mjs +2 -6
  357. package/scripts/lib/tmux-layout/telemetry-stats.mjs +74 -14
  358. package/scripts/lib/user-invocable-skills.mjs +205 -0
  359. package/scripts/lib/ux-grill/reconcile.mjs +48 -22
  360. package/scripts/lib/validate/check-agents-skills.mjs +26 -15
  361. package/scripts/lib/validate/check-banner-parity.mjs +2 -2
  362. package/scripts/lib/validate/check-cursor-adapter.mjs +3 -2
  363. package/scripts/lib/validate/check-dead-bridge.mjs +2 -2
  364. package/scripts/lib/validate/check-doc-cli-commands.mjs +2 -2
  365. package/scripts/lib/validate/check-entry-guard.mjs +329 -0
  366. package/scripts/lib/validate/check-guard-requires-parity.mjs +2 -2
  367. package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
  368. package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +2 -2
  369. package/scripts/lib/validate/check-learning-provenance.mjs +2 -2
  370. package/scripts/lib/validate/check-pi-prompts.mjs +1 -0
  371. package/scripts/lib/validate/check-rules.mjs +7 -5
  372. package/scripts/lib/validate/check-skill-links.mjs +35 -6
  373. package/scripts/lib/validate/check-skill-script-paths.mjs +241 -29
  374. package/scripts/lib/validate/check-test-git-config-target.mjs +26 -36
  375. package/scripts/lib/validate/check-unicode-safety.mjs +2 -2
  376. package/scripts/lib/validate/check-untracked-test-deps.mjs +9 -104
  377. package/scripts/lib/validate/check-unwired-features.mjs +220 -33
  378. package/scripts/lib/validate/check-validator-registration.mjs +36 -12
  379. package/scripts/lib/validate/check-vcs-repo-flag.mjs +2 -2
  380. package/scripts/lib/validate/confidential-names.mjs +10 -0
  381. package/scripts/lib/validate-vendored-rules.mjs +39 -12
  382. package/scripts/lib/vault-mirror/namespace.mjs +46 -8
  383. package/scripts/lib/vault-mirror/process.mjs +10 -3
  384. package/scripts/lib/vault-mirror/render-sessions.mjs +12 -2
  385. package/scripts/lib/vault-status/narrative-mirror.mjs +31 -7
  386. package/scripts/lib/vault-yaml.mjs +118 -0
  387. package/scripts/lib/wave-transcript-tail.mjs +2 -2
  388. package/scripts/lib/worktree/lifecycle.mjs +153 -1
  389. package/scripts/lock-reaper.mjs +2 -1
  390. package/scripts/materialize-wave-scope.mjs +87 -4
  391. package/scripts/migrate-sessions-jsonl.mjs +2 -1
  392. package/scripts/migrate-vault-paths.mjs +2 -3
  393. package/scripts/release-session-lock.mjs +305 -0
  394. package/scripts/release.mjs +109 -39
  395. package/scripts/relocate-vault-corpus.mjs +2 -3
  396. package/scripts/repair-invalid-sessions.mjs +2 -2
  397. package/scripts/resolve-session-invocation.mjs +59 -0
  398. package/scripts/run-quality-gate.mjs +156 -17
  399. package/scripts/session-shape.mjs +2 -2
  400. package/scripts/site-numbers.mjs +35 -11
  401. package/scripts/sweep-expired-rules.mjs +227 -0
  402. package/scripts/validate-plugin.mjs +21 -0
  403. package/scripts/validate-wave-scope.mjs +32 -105
  404. package/scripts/vault-consolidate.mjs +2 -2
  405. package/scripts/vault-mirror.mjs +11 -4
  406. package/scripts/wave-scope-binding.mjs +2 -3
  407. package/skills/_shared/bootstrap-gate.md +1 -1
  408. package/skills/_shared/monitor-patterns.md +1 -1
  409. package/skills/_shared/platform-tools.md +23 -11
  410. package/skills/_shared/research-evidence.md +53 -0
  411. package/skills/_shared/state-ownership.md +3 -0
  412. package/skills/autopilot/SKILL.md +80 -11
  413. package/skills/bootstrap/SKILL.md +51 -1
  414. package/skills/brainstorm/SKILL.md +16 -0
  415. package/skills/claude-md-drift-check/SKILL.md +1 -1
  416. package/skills/claude-md-drift-check/checker.mjs +49 -11
  417. package/{commands/close.md → skills/close/SKILL.md} +9 -3
  418. package/skills/convergence-monitoring/README.md +8 -1
  419. package/skills/convergence-monitoring/SIGNALS.md +50 -6
  420. package/skills/convergence-monitoring/SKILL.md +15 -6
  421. package/skills/debug/SKILL.md +10 -0
  422. package/skills/discovery/SKILL.md +24 -1
  423. package/skills/discovery/probes-session.md +2 -2
  424. package/skills/dispatcher/SKILL.md +38 -7
  425. package/skills/eli5/SKILL.md +11 -0
  426. package/skills/eval/SKILL.md +52 -23
  427. package/skills/eval/rubric-v1.md +1 -0
  428. package/skills/eval/rubric-v2.md +457 -0
  429. package/skills/evolve/SKILL.md +9 -2
  430. package/skills/evolve/references/evolve-dialectic-mode.md +46 -25
  431. package/skills/gitlab-ops/SKILL.md +3 -2
  432. package/{commands/go.md → skills/go/SKILL.md} +9 -1
  433. package/skills/grill/SKILL.md +19 -0
  434. package/{commands/harness-audit.md → skills/harness-audit/SKILL.md} +7 -2
  435. package/skills/hook-development/SKILL.md +46 -41
  436. package/skills/memory-cleanup/SKILL.md +7 -0
  437. package/skills/npm-publish/SKILL.md +2 -2
  438. package/skills/persona-panel/SKILL.md +56 -1
  439. package/skills/persona-panel/persona-format.md +1 -1
  440. package/skills/plan/SKILL.md +28 -1
  441. package/{commands/portfolio.md → skills/portfolio/SKILL.md} +8 -2
  442. package/skills/reconcile/SKILL.md +21 -0
  443. package/{commands/release.md → skills/release/SKILL.md} +16 -2
  444. package/skills/repo-audit/SKILL.md +7 -0
  445. package/skills/session-end/SKILL.md +13 -16
  446. package/skills/session-end/discovery-scan.md +1 -1
  447. package/skills/session-end/phase-3-6-tail.md +55 -9
  448. package/skills/session-end/plan-verification.md +2 -2
  449. package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
  450. package/skills/session-end/session-metrics-write.md +10 -0
  451. package/skills/session-plan/SKILL.md +18 -6
  452. package/skills/session-plan/references/session-plan-task-classification.md +2 -2
  453. package/skills/session-start/SKILL.md +5 -4
  454. package/skills/session-start/phase-8-5-express-path.md +6 -6
  455. package/skills/session-start/references/phase-1-5-session-continuity.md +1 -1
  456. package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +1 -1
  457. package/skills/session-start/references/phase-4-ssot-environment-check.md +6 -4
  458. package/skills/spinout/SKILL.md +12 -1
  459. package/skills/sunset-review/SKILL.md +13 -0
  460. package/{commands/test.md → skills/test/SKILL.md} +10 -4
  461. package/skills/ux-grill/SKILL.md +20 -2
  462. package/skills/wave-executor/SKILL.md +14 -7
  463. package/skills/wave-executor/circuit-breaker.md +2 -0
  464. package/skills/wave-executor/references/wave-executor-state-init.md +18 -4
  465. package/skills/wave-executor/references/wave-loop-dispatch.md +5 -2
  466. package/skills/wave-executor/references/wave-loop-review.md +17 -1
  467. package/commands/autopilot.md +0 -80
  468. package/commands/bootstrap.md +0 -56
  469. package/commands/brainstorm.md +0 -48
  470. package/commands/debug.md +0 -36
  471. package/commands/discovery.md +0 -32
  472. package/commands/dispatcher.md +0 -59
  473. package/commands/eli5.md +0 -33
  474. package/commands/eval.md +0 -28
  475. package/commands/evolve.md +0 -10
  476. package/commands/grill.md +0 -45
  477. package/commands/memory-cleanup.md +0 -26
  478. package/commands/persona-panel.md +0 -121
  479. package/commands/plan.md +0 -15
  480. package/commands/reconcile.md +0 -23
  481. package/commands/repo-audit.md +0 -24
  482. package/commands/spinout.md +0 -15
  483. package/commands/sunset-review.md +0 -27
  484. package/commands/ux-grill.md +0 -51
@@ -30,29 +30,43 @@
30
30
  * newest-across-all, not genuine-first" for why the
31
31
  * two kinds are maxed rather than ranked.
32
32
  * - `cutoff` = the CURRENT session's `session.lock`
33
- * `started_at` (via `readLock()`); when no lock is
34
- * readable, `cutoff = now` (all events count).
33
+ * `started_at` (via `readLock()`), pulled back to
34
+ * that lock id's FIRST `lock.acquired` event
35
+ * (`ownGenesisMs()`, a compact re-acquire rewrites
36
+ * `started_at`); when no lock is readable,
37
+ * `cutoff = now` (all events count).
35
38
  * - `lastForeignEventAt`= the NEWEST `events.jsonl` line whose `timestamp`
36
39
  * is STRICTLY BEFORE `cutoff` — this structurally
37
40
  * excludes the current session's own events without
38
41
  * needing a `session_id` filter (most event lines
39
42
  * don't carry one; see the mission-log sample in
40
- * `.orchestrator/metrics/events.jsonl`).
43
+ * `.orchestrator/metrics/events.jsonl`). A previous
44
+ * session's own teardown events are skipped
45
+ * (`CLOSING_DIAGNOSTIC_EVENTS`): they postdate its
46
+ * ledger record by construction.
41
47
  * - `deltaHours` = (lastForeignEventAt − lastLedgerAt) / 1h, only
42
48
  * meaningful when > 0 (foreign activity happened
43
49
  * AFTER the last ledger entry).
44
50
  *
45
- * Backfill-stub self-erasure fix — the anchor axis: a backfill-produced
46
- * `sessions.jsonl` record (`_backfill_source` / `status: 'abandoned'`, see
47
- * `session-close-backfill.mjs` `synthesizeRecord()`) sets `completed_at =
48
- * max(started_at, lastTerminalMs ?? nowMs)`. When the abandoned session
49
- * never emitted a STOPPED/ENDED event the COMMON case, since that is
50
- * *why* it is "abandoned" `completed_at` silently becomes the BACKFILL
51
- * RUN's own wall-clock instant, not a measurement of when the session
52
- * actually ended. Anchoring `lastLedgerEntry()` on that value means a
53
- * backfill run can retroactively erase a multi-day staleness gap just by
54
- * writing a stub today (observed: a 92.5h gap to the last GENUINE record
55
- * collapsed to 0.6h the moment a backfill stub landed).
51
+ * Backfill-stub self-erasure fix — the anchor axis: a backfill STUB
52
+ * (`status: 'abandoned'`, see `session-close-backfill.mjs` `synthesizeRecord()`)
53
+ * is a reconstruction, not a measured close. Before #914 R1 its `completed_at`
54
+ * could be the BACKFILL RUN's own wall-clock; since #914 R1 it is
55
+ * events-derived (terminal event, else last life-sign, else `started_at`) and
56
+ * flagged in `_backfill_incomplete_fields` whenever it is an estimate. A stub's
57
+ * `completed_at` is still refused as an anchor: an abandoned session proves
58
+ * the ledger alive only at its start. Anchoring on it once let a backfill run
59
+ * retroactively erase a multi-day staleness gap just by writing a stub today
60
+ * (observed: a 92.5h gap to the last GENUINE record collapsed to 0.6h).
61
+ *
62
+ * A `state-md-completed` backfill (status `completed`, #429/#1068) is NOT a
63
+ * stub: its status is the session's own truth claim, and its timestamps are
64
+ * events-derived like any other. Treating every `_backfill_source` as a stub
65
+ * kept the probe warning after a stub had been superseded, and recommended a
66
+ * backfill run with nothing left to do (live 2026-09-18). Independently of the
67
+ * kind, a timestamp named in a record's `_backfill_incomplete_fields` was not
68
+ * measured (e.g. `started_at` fell back to the backfill run's `now`) and is
69
+ * never an anchor candidate.
56
70
  *
57
71
  * Two axes were available to fix this: (a) skip stub records when scanning
58
72
  * for the ledger anchor, keeping `completed_at` as the anchor field; or (b)
@@ -63,10 +77,9 @@
63
77
  * (a session's own mid-session events would newly count as "after" the
64
78
  * anchor), reintroducing false positives on the opposite side. (a) is
65
79
  * chosen: a stub's `completed_at` is NEVER an anchor candidate. Stub
66
- * recognition uses EITHER marker (OR, not AND) deliberately — both are set by
67
- * the same producer today, but requiring both would silently stop matching the
68
- * day a future backfill variant drops one of them while keeping the other; OR
69
- * degrades gracefully (still catches it), AND does not.
80
+ * recognition: `status: 'abandoned'` alone suffices, and so does a
81
+ * `_backfill_source` on any record whose status is not `completed` OR, not
82
+ * AND, so a future backfill variant that drops one marker is still caught.
70
83
  *
71
84
  * #1125 — newest-across-ALL records, not genuine-first: excluding the stub's
72
85
  * `completed_at` (above) is correct; excluding the whole STUB from the anchor
@@ -146,6 +159,64 @@ export const WARN_THRESHOLD_HOURS = DEFAULT_TTL_HOURS * 2;
146
159
  /** alert threshold: a full day behind is a strong close-through signal. */
147
160
  export const ALERT_THRESHOLD_HOURS = 24;
148
161
 
162
+ /**
163
+ * A session's own CLOSING diagnostics — events emitted at teardown, i.e. AFTER
164
+ * that session's `/close` has already written its ledger record. They measure
165
+ * when the process exited, not when work happened, so they are never "foreign
166
+ * activity": counting them turned every terminal left open after `/close` into
167
+ * a close-through warning (live 2026-09-19: a 9.6h warn whose only post-ledger
168
+ * events were one SessionEnd's `wave.final_refused`, `session.ended`,
169
+ * `session.backfill_completed` and `telemetry.flush`, while
170
+ * `backfill-abandoned-sessions.mjs --dry-run` found 0 sessions to backfill).
171
+ * Map value = the `emitted_by` an event must carry to be
172
+ * skipped; `null` = skipped from every emitter.
173
+ *
174
+ * Deliberately NOT listed, and still counted: `orchestrator.turn.stopped`, its
175
+ * deprecated alias `orchestrator.session.stopped`, `orchestrator.agent.stopped`
176
+ * and every other event. A process killed by OOM or `kill -9` never runs its
177
+ * SessionEnd hook but has left those behind — that is the close-through gap
178
+ * this probe exists to catch. Skipping teardown events loses nothing there:
179
+ * a session that exits WITHOUT `/close` still leaves its turn/agent events,
180
+ * and only the idle tail between its last turn and the exit goes uncounted.
181
+ */
182
+ const CLOSING_DIAGNOSTIC_EVENTS = new Map([
183
+ // Only emitter: hooks/on-session-end.mjs — the SessionEnd breadcrumb itself.
184
+ ['orchestrator.session.ended', null],
185
+ // Only emitter: hooks/on-session-end.mjs — the outcome of the exit-time telemetry flush.
186
+ ['orchestrator.telemetry.flush', null],
187
+ // hooks/on-session-end.mjs + scripts/backfill-abandoned-sessions.mjs — a backfill run
188
+ // is only ever ABOUT an already-dead session, and writes that session's ledger stub itself.
189
+ ['orchestrator.session.backfill_completed', null],
190
+ // Lock-teardown breadcrumbs. Every emitter is a session teardown: hooks/on-session-end.mjs,
191
+ // scripts/lib/session-transition.mjs (#1069 process boundary), and
192
+ // scripts/lib/autopilot/worktree-pipeline.mjs (release_failed/released only).
193
+ ['orchestrator.session.lock.released', null],
194
+ ['orchestrator.session.lock.release_failed', null],
195
+ // Only emitter: hooks/on-session-end.mjs — reading the lock during teardown.
196
+ ['orchestrator.session.lock.read_anomaly', null],
197
+ // Wave lifecycle: hooks/post-tool-batch-wave-signal.mjs emits wave.completed MID-session
198
+ // (real activity, still counted); only the SessionEnd final-wave stamp is a closing
199
+ // diagnostic. final_refused has on-session-end as its sole emitter today, keyed the
200
+ // same way so a future mid-session emitter stays counted by default.
201
+ ['orchestrator.wave.completed', 'on-session-end'],
202
+ ['orchestrator.wave.final_refused', 'on-session-end'],
203
+ ]);
204
+
205
+ /**
206
+ * True when an events.jsonl record is one of the session's own closing
207
+ * diagnostics (`CLOSING_DIAGNOSTIC_EVENTS`). An unknown or missing `event`
208
+ * counts as activity — the failure direction stays "warn", never "silence".
209
+ * Caller guarantees `record` is already a non-null object.
210
+ *
211
+ * @param {object} record
212
+ * @returns {boolean}
213
+ */
214
+ function isClosingDiagnostic(record) {
215
+ if (typeof record.event !== 'string' || !CLOSING_DIAGNOSTIC_EVENTS.has(record.event)) return false;
216
+ const requiredEmitter = CLOSING_DIAGNOSTIC_EVENTS.get(record.event);
217
+ return requiredEmitter === null || record.emitted_by === requiredEmitter;
218
+ }
219
+
149
220
  /**
150
221
  * Read a JSONL file's non-empty lines. Returns `null` when the file is
151
222
  * absent or unreadable, `[]` when it exists but has no non-empty lines.
@@ -166,26 +237,44 @@ function readJsonlLines(filePath) {
166
237
  }
167
238
 
168
239
  /**
169
- * True when a `sessions.jsonl` record's `completed_at` was SYNTHESIZED by
170
- * the backfill engine (`scripts/lib/session-close-backfill.mjs`
171
- * `synthesizeRecord()`) rather than measured at real session-close time —
172
- * see the module-header "Backfill-stub self-erasure fix" section above for
173
- * the full reasoning behind the OR (not AND) combination of the two markers.
174
- *
175
- * Reuses `isRealSession()` from `./session-schema/filters.mjs` its own doc
176
- * names `status: 'abandoned'` "the canonical marker" for exactly this phantom
177
- * class, so this is the SAME predicate every other real/phantom-aware
178
- * consumer in this repo already relies on, not a hand-rolled duplicate of it.
179
- * `_backfill_source` is layered on top as the second, independent signal.
180
- * Caller guarantees `record` is already a non-null object (see
181
- * `lastLedgerEntry()`'s guard above the call site).
240
+ * True when a `sessions.jsonl` record is a backfill STUB — a reconstruction
241
+ * whose `completed_at` is never an anchor candidate (module header,
242
+ * "Backfill-stub self-erasure fix").
243
+ *
244
+ * `isRealSession()` (`./session-schema/filters.mjs`) catches `status:
245
+ * 'abandoned'`, the canonical phantom marker. A `_backfill_source` counts only
246
+ * when the status is not `completed`: the `state-md-completed` supersede record
247
+ * is the session's own completion claim. Same exclusion as
248
+ * `isSupersedableStub()` in `session-close-backfill.mjs` (which keeps
249
+ * `completed` out of its stub statuses); not imported from there because that
250
+ * predicate demands BOTH markers before a record may be OVERWRITTEN, where
251
+ * this one accepts EITHER a record only has to be doubtful to be
252
+ * disqualified as a time anchor. The two therefore disagree by design on
253
+ * `{_backfill_source, status: 'interrupted'}`, and the names say which
254
+ * question each answers.
255
+ * Caller guarantees `record` is already a non-null object.
182
256
  *
183
257
  * @param {object} record
184
258
  * @returns {boolean}
185
259
  */
186
- function isBackfillStub(record) {
260
+ function isNonAnchorStub(record) {
187
261
  if (!isRealSession(record)) return true;
188
- return typeof record._backfill_source === 'string' && record._backfill_source.length > 0;
262
+ return typeof record._backfill_source === 'string' && record._backfill_source.length > 0
263
+ && record.status !== 'completed';
264
+ }
265
+
266
+ /**
267
+ * Anchor candidate for one timestamp field of a record — `null` when the
268
+ * backfill engine listed that field in `_backfill_incomplete_fields` (it was
269
+ * not measured, e.g. a `started_at` that fell back to the backfill run's now).
270
+ *
271
+ * @param {object} record
272
+ * @param {'started_at'|'completed_at'} field
273
+ * @returns {{iso: string, ms: number}|null}
274
+ */
275
+ function fieldCandidate(record, field) {
276
+ if (Array.isArray(record._backfill_incomplete_fields) && record._backfill_incomplete_fields.includes(field)) return null;
277
+ return tsCandidate(record[field]);
189
278
  }
190
279
 
191
280
  /**
@@ -225,15 +314,16 @@ function keepNewer(current, candidate) {
225
314
  *
226
315
  * Which timestamp each record kind contributes, and why (full reasoning in the
227
316
  * module header, "#1125 — newest-across-ALL records"):
228
- * - GENUINE (`!isBackfillStub()`) → `completed_at`, written by the real
317
+ * - GENUINE (`!isNonAnchorStub()`) → `completed_at`, written by the real
229
318
  * session-end path; falling back to `started_at` when `completed_at` is
230
319
  * absent or unparseable, so a truncated/in-flight record still contributes
231
320
  * the coverage it does prove. Taking the newer of the two can only move the
232
321
  * anchor forward, never backward, so it does not inflate `deltaHours`.
233
- * - BACKFILL STUB → `started_at` ONLY. Its `completed_at` may be the backfill
234
- * RUN's own wall-clock rather than a measurement of when the session ended
235
- * (see `synthesizeRecord()`), and anchoring on that would let a backfill run
236
- * retroactively erase a real multi-day gap.
322
+ * - BACKFILL STUB → `started_at` ONLY. An abandoned session proves the
323
+ * ledger alive at its start, not at a reconstructed end; anchoring on the
324
+ * stub's `completed_at` would let a backfill run erase a real gap.
325
+ * - Either kind: a field listed in `_backfill_incomplete_fields` was not
326
+ * measured and contributes nothing (`fieldCandidate()`).
237
327
  *
238
328
  * The two kinds are MAXED, not ranked: an older genuine record must not outrank
239
329
  * a newer stub (that priority order was the #1125 false-alert defect). When the
@@ -258,13 +348,13 @@ function lastLedgerEntry(records) {
258
348
  for (const record of records) {
259
349
  if (!record || typeof record !== 'object') continue;
260
350
 
261
- if (isBackfillStub(record)) {
262
- newestStub = keepNewer(newestStub, tsCandidate(record.started_at));
351
+ if (isNonAnchorStub(record)) {
352
+ newestStub = keepNewer(newestStub, fieldCandidate(record, 'started_at'));
263
353
  continue;
264
354
  }
265
355
 
266
- let own = tsCandidate(record.completed_at);
267
- const started = tsCandidate(record.started_at);
356
+ let own = fieldCandidate(record, 'completed_at');
357
+ const started = fieldCandidate(record, 'started_at');
268
358
  // `completed_at` is the normal anchor and wins ties; `started_at` only
269
359
  // takes over when it is genuinely newer or `completed_at` is unusable.
270
360
  if (started !== null && (own === null || started.ms > own.ms)) own = started;
@@ -284,7 +374,23 @@ function lastLedgerEntry(records) {
284
374
  * current session's own events (all >= cutoff, since cutoff is this
285
375
  * session's lock `started_at`) are structurally excluded without needing a
286
376
  * `session_id` filter. Malformed lines and lines with a missing/invalid
287
- * `timestamp` are skipped.
377
+ * `timestamp` are skipped, and so is a previous session's own teardown output
378
+ * (`isClosingDiagnostic()`), which postdates that session's ledger record by
379
+ * construction.
380
+ *
381
+ * Known ceiling (BV-004) — post-close TURNS still count: a session that keeps
382
+ * talking after its `/close` wrote the ledger record emits
383
+ * `orchestrator.turn.stopped` (and the deprecated `orchestrator.session.stopped`)
384
+ * at the end of every further turn, so a post-close conversation spanning more
385
+ * than `WARN_THRESHOLD_HOURS` warns although that session IS recorded. Kept on
386
+ * purpose: those same events are the only trace an OOM-/`kill -9`-ended session
387
+ * leaves. Size (W1 census 2026-09-19 over this repo's events.jsonl(.1), not
388
+ * re-measured here): 4 of 10 resolvable firings were this class. Revisit trigger: the HR-105 firing-rate
389
+ * audit shows this class alone above ~10% of sessions that ran the probe
390
+ * (HR-101) — then skip a stop event whose `semantic_session_id` matches a
391
+ * GENUINE ledger record (that session is recorded, so its post-close turns are
392
+ * not a close-through gap), which needs `newestForeignEvent` to receive the
393
+ * canonical records.
288
394
  *
289
395
  * @param {string[]} lines
290
396
  * @param {number} cutoffMs
@@ -303,6 +409,7 @@ function newestForeignEvent(lines, cutoffMs) {
303
409
  const ms = Date.parse(record.timestamp);
304
410
  if (!Number.isFinite(ms)) continue;
305
411
  if (ms >= cutoffMs) continue; // not "foreign" — belongs to (or postdates) the current session
412
+ if (isClosingDiagnostic(record)) continue; // a previous session's teardown, not activity
306
413
  if (best === null || ms > best.ms) best = { iso: record.timestamp, ms };
307
414
  }
308
415
  return best;
@@ -325,18 +432,53 @@ function newestForeignEvent(lines, cutoffMs) {
325
432
  *
326
433
  * @param {string} repoRoot
327
434
  * @param {number} nowMs
328
- * @returns {number}
435
+ * @returns {{cutoffMs: number, ownSessionId: string|null}}
329
436
  */
330
- function resolveCutoffMs(repoRoot, nowMs) {
437
+ function resolveCutoff(repoRoot, nowMs) {
331
438
  let lock;
332
439
  try {
333
440
  lock = readLock({ repoRoot });
334
441
  } catch {
335
- return nowMs;
442
+ return { cutoffMs: nowMs, ownSessionId: null };
443
+ }
444
+ if (!lock) return { cutoffMs: nowMs, ownSessionId: null };
445
+ const ownSessionId = typeof lock.session_id === 'string' && lock.session_id !== '' ? lock.session_id : null;
446
+ const parsed = typeof lock.started_at === 'string' ? Date.parse(lock.started_at) : NaN;
447
+ return { cutoffMs: Number.isFinite(parsed) ? parsed : nowMs, ownSessionId };
448
+ }
449
+
450
+ /**
451
+ * Pull the cutoff back to the FIRST `orchestrator.session.lock.acquired` event
452
+ * carrying the current lock's raw `session_id`. A re-acquire under the same id
453
+ * (the SessionStart hook on a context compact) rewrites `started_at`, which
454
+ * moved the cutoff past the session's own earlier events — and not all of them
455
+ * carry a `session_id` to filter on (`secret_masker.applied` does not).
456
+ * Measured 2026-09-19: a 2h15m session warned "11.9h behind" about itself on
457
+ * compact. Raw id only, never the semantic label (two sessions can mint the
458
+ * same label, #1066). Linear scan over events.jsonl — the same pass
459
+ * `newestForeignEvent()` already makes.
460
+ *
461
+ * @param {string[]} lines
462
+ * @param {string|null} ownSessionId
463
+ * @param {number} cutoffMs
464
+ * @returns {number}
465
+ */
466
+ function ownGenesisMs(lines, ownSessionId, cutoffMs) {
467
+ if (ownSessionId === null) return cutoffMs;
468
+ let genesis = cutoffMs;
469
+ for (const line of lines) {
470
+ if (!line.includes(ownSessionId)) continue;
471
+ let record;
472
+ try {
473
+ record = JSON.parse(line);
474
+ } catch {
475
+ continue;
476
+ }
477
+ if (record?.event !== 'orchestrator.session.lock.acquired' || record.session_id !== ownSessionId) continue;
478
+ const ms = Date.parse(record.timestamp);
479
+ if (Number.isFinite(ms) && ms < genesis) genesis = ms;
336
480
  }
337
- if (!lock || typeof lock.started_at !== 'string') return nowMs;
338
- const parsed = Date.parse(lock.started_at);
339
- return Number.isFinite(parsed) ? parsed : nowMs;
481
+ return genesis;
340
482
  }
341
483
 
342
484
  /**
@@ -386,9 +528,9 @@ export function checkSessionsStaleness({ repoRoot, now = Date.now() } = {}) {
386
528
  const eventLines = readJsonlLines(path.join(repoRoot, EVENTS_PATH));
387
529
  if (eventLines === null || eventLines.length === 0) return null;
388
530
 
389
- const cutoffMs = resolveCutoffMs(repoRoot, nowMs);
531
+ const { cutoffMs, ownSessionId } = resolveCutoff(repoRoot, nowMs);
390
532
 
391
- const foreign = newestForeignEvent(eventLines, cutoffMs);
533
+ const foreign = newestForeignEvent(eventLines, ownGenesisMs(eventLines, ownSessionId, cutoffMs));
392
534
  if (foreign === null) return null;
393
535
 
394
536
  const deltaMs = foreign.ms - ledger.ms;