session-orchestrator 3.23.0 → 4.0.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 (393) hide show
  1. package/.agents/skills/architecture/SKILL.md +18 -0
  2. package/.agents/skills/autopilot/SKILL.md +17 -0
  3. package/.agents/skills/bootstrap/SKILL.md +20 -0
  4. package/.agents/skills/brainstorm/SKILL.md +22 -0
  5. package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
  6. package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
  7. package/.agents/skills/debug/SKILL.md +22 -0
  8. package/.agents/skills/discovery/SKILL.md +20 -0
  9. package/.agents/skills/dispatcher/SKILL.md +15 -0
  10. package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
  11. package/.agents/skills/ecosystem-health/SKILL.md +20 -0
  12. package/.agents/skills/eli5/SKILL.md +20 -0
  13. package/.agents/skills/eval/SKILL.md +21 -0
  14. package/.agents/skills/evolve/SKILL.md +21 -0
  15. package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
  16. package/.agents/skills/gitlab-ops/SKILL.md +20 -0
  17. package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
  18. package/.agents/skills/grill/SKILL.md +22 -0
  19. package/.agents/skills/hook-development/SKILL.md +15 -0
  20. package/.agents/skills/mcp-builder/SKILL.md +15 -0
  21. package/.agents/skills/memory-cleanup/SKILL.md +21 -0
  22. package/.agents/skills/mode-selector/SKILL.md +17 -0
  23. package/.agents/skills/npm-publish/SKILL.md +16 -0
  24. package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
  25. package/.agents/skills/persona-panel/SKILL.md +17 -0
  26. package/.agents/skills/plan/SKILL.md +20 -0
  27. package/.agents/skills/playwright-driver/SKILL.md +20 -0
  28. package/.agents/skills/quality-gates/SKILL.md +20 -0
  29. package/.agents/skills/reconcile/SKILL.md +21 -0
  30. package/.agents/skills/remote-offload/SKILL.md +20 -0
  31. package/.agents/skills/repo-audit/SKILL.md +16 -0
  32. package/.agents/skills/session-end/SKILL.md +20 -0
  33. package/.agents/skills/session-plan/SKILL.md +20 -0
  34. package/.agents/skills/session-start/SKILL.md +20 -0
  35. package/.agents/skills/spinout/SKILL.md +16 -0
  36. package/.agents/skills/sunset-review/SKILL.md +16 -0
  37. package/.agents/skills/test-runner/SKILL.md +20 -0
  38. package/.agents/skills/tmux-layout/SKILL.md +21 -0
  39. package/.agents/skills/using-orchestrator/SKILL.md +17 -0
  40. package/.agents/skills/vault-mirror/SKILL.md +15 -0
  41. package/.agents/skills/vault-sync/SKILL.md +15 -0
  42. package/.agents/skills/wave-executor/SKILL.md +20 -0
  43. package/.agents/skills/write-executable-plan/SKILL.md +22 -0
  44. package/.claude-plugin/marketplace.json +1 -1
  45. package/.claude-plugin/plugin.json +1 -1
  46. package/.codex-plugin/plugin.json +1 -1
  47. package/.cursor/commands/autopilot.md +2 -2
  48. package/.cursor/commands/bootstrap.md +1 -1
  49. package/.cursor/commands/brainstorm.md +1 -1
  50. package/.cursor/commands/debug.md +1 -1
  51. package/.cursor/commands/discovery.md +1 -1
  52. package/.cursor/commands/dispatcher.md +2 -2
  53. package/.cursor/commands/eli5.md +2 -2
  54. package/.cursor/commands/eval.md +2 -2
  55. package/.cursor/commands/evolve.md +1 -1
  56. package/.cursor/commands/go.md +1 -1
  57. package/.cursor/commands/grill.md +2 -2
  58. package/.cursor/commands/memory-cleanup.md +2 -2
  59. package/.cursor/commands/persona-panel.md +1 -1
  60. package/.cursor/commands/plan.md +1 -1
  61. package/.cursor/commands/portfolio.md +1 -1
  62. package/.cursor/commands/reconcile.md +2 -2
  63. package/.cursor/commands/release.md +2 -2
  64. package/.cursor/commands/session.md +2 -2
  65. package/.cursor/commands/spinout.md +2 -2
  66. package/.cursor/commands/sunset-review.md +2 -2
  67. package/.cursor/commands/templates-ack.md +2 -2
  68. package/.cursor/commands/test.md +2 -2
  69. package/.cursor/skills/brainstorm/SKILL.md +1 -1
  70. package/.cursor/skills/eval/SKILL.md +1 -1
  71. package/.cursor/skills/quality-gates/SKILL.md +1 -1
  72. package/.cursor/skills/remote-offload/SKILL.md +13 -0
  73. package/.orchestrator/policy/blocked-commands.json +121 -0
  74. package/.orchestrator/policy/ecosystem.schema.json +66 -0
  75. package/.orchestrator/policy/quality-gates.example.json +16 -0
  76. package/.orchestrator/policy/quality-gates.schema.json +38 -0
  77. package/.orchestrator/policy/templates-policy.json +27 -0
  78. package/.orchestrator/policy/test-profiles.json +47 -0
  79. package/AGENTS.md +225 -0
  80. package/CHANGELOG.md +1401 -0
  81. package/NOTICE +11 -6
  82. package/README.md +127 -92
  83. package/agents/db-specialist.md +0 -1
  84. package/agents/eval-judge.md +1 -1
  85. package/agents/skill-applied-judge.md +1 -1
  86. package/assets/wave-lifecycle.svg +98 -0
  87. package/commands/release.md +6 -3
  88. package/commands/session.md +18 -3
  89. package/docs/README.md +4 -0
  90. package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
  91. package/docs/baseline.md +67 -0
  92. package/docs/ci-setup.md +249 -48
  93. package/docs/codex-setup.md +66 -22
  94. package/docs/components.md +37 -16
  95. package/docs/cursor-setup.md +6 -2
  96. package/docs/events-schema.md +51 -10
  97. package/docs/instruction-delivery.md +62 -0
  98. package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
  99. package/docs/migration-v4.md +341 -0
  100. package/docs/pi-setup.md +6 -1
  101. package/docs/plugin-architecture-v3.md +1 -1
  102. package/docs/rule-authoring.md +85 -19
  103. package/docs/scope-collision-guard.md +8 -8
  104. package/docs/session-config-reference.md +120 -61
  105. package/docs/session-config-template.md +40 -33
  106. package/docs/telemetry/telemetry-claims.md +11 -10
  107. package/docs/telemetry.md +187 -4
  108. package/docs/vault-docs-architecture.md +50 -11
  109. package/hooks/_lib/atomic-json.mjs +111 -0
  110. package/hooks/_lib/hook-import-set.json +1487 -0
  111. package/hooks/_lib/subagent-paths.mjs +143 -0
  112. package/hooks/_lib/subagent-transcript.mjs +562 -0
  113. package/hooks/config-protection.mjs +2 -2
  114. package/hooks/cwd-change-restore.mjs +11 -31
  115. package/hooks/enforce-commands.mjs +69 -0
  116. package/hooks/enforce-scope.mjs +35 -6
  117. package/hooks/hooks-codex.json +1 -1
  118. package/hooks/hooks-cursor.json +10 -0
  119. package/hooks/hooks-pi.json +5 -0
  120. package/hooks/hooks.json +6 -1
  121. package/hooks/loop-guard.mjs +3 -3
  122. package/hooks/on-session-end.mjs +280 -14
  123. package/hooks/on-session-start.mjs +153 -4
  124. package/hooks/on-stop.mjs +371 -17
  125. package/hooks/operator-steer.mjs +2 -2
  126. package/hooks/post-bash-write-verify.mjs +189 -4
  127. package/hooks/post-edit-import-probe.mjs +344 -0
  128. package/hooks/post-subagent-discovery-validator.mjs +278 -392
  129. package/hooks/post-tool-batch-wave-signal.mjs +272 -44
  130. package/hooks/post-tool-failure-corrective-context.mjs +11 -34
  131. package/hooks/post-tooluse-frontend-slop.mjs +3 -3
  132. package/hooks/pre-bash-destructive-guard.mjs +39 -13
  133. package/hooks/pre-bash-memory-propose-audit.mjs +13 -7
  134. package/hooks/skill-invocation-telemetry.mjs +17 -5
  135. package/hooks/subagent-telemetry.mjs +24 -30
  136. package/monitors/monitors.json +3 -3
  137. package/package.json +9 -1
  138. package/pi/prompts/session.md +2 -2
  139. package/plugin.json +27 -0
  140. package/scripts/autopilot.mjs +26 -12
  141. package/scripts/backfill-abandoned-sessions.mjs +130 -15
  142. package/scripts/backfill-learnings-from-vault.mjs +9 -3
  143. package/scripts/dialectic-deriver.mjs +73 -8
  144. package/scripts/emit-event.mjs +10 -2
  145. package/scripts/export-hw-learnings.mjs +113 -1
  146. package/scripts/generate-agents-skills.mjs +378 -0
  147. package/scripts/generate-cursor-adapter.mjs +45 -8
  148. package/scripts/generate-hook-import-set.mjs +249 -0
  149. package/scripts/lib/agent-status.mjs +13 -2
  150. package/scripts/lib/auq/parse.mjs +5 -29
  151. package/scripts/lib/auto-dialectic.mjs +68 -0
  152. package/scripts/lib/auto-dream.mjs +38 -36
  153. package/scripts/lib/autonomy/suitability.mjs +6 -0
  154. package/scripts/lib/autopilot/loop.mjs +2 -2
  155. package/scripts/lib/autopilot/worktree-pipeline.mjs +82 -6
  156. package/scripts/lib/build-live-signals.mjs +25 -22
  157. package/scripts/lib/ci-status-banner.mjs +220 -75
  158. package/scripts/lib/codex/plugin-contract.mjs +82 -6
  159. package/scripts/lib/cold-start-detector.mjs +23 -14
  160. package/scripts/lib/config/auto-dream.mjs +2 -1
  161. package/scripts/lib/config/block-header.mjs +63 -0
  162. package/scripts/lib/config/block-preprocess.mjs +177 -0
  163. package/scripts/lib/config/broken-window.mjs +2 -1
  164. package/scripts/lib/config/cold-start.mjs +2 -1
  165. package/scripts/lib/config/config-protection.mjs +22 -2
  166. package/scripts/lib/config/context-coverage.mjs +2 -1
  167. package/scripts/lib/config/cross-repo.mjs +2 -1
  168. package/scripts/lib/config/custom-phases.mjs +2 -1
  169. package/scripts/lib/config/dialectic.mjs +2 -1
  170. package/scripts/lib/config/discovery-validator.mjs +9 -3
  171. package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
  172. package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
  173. package/scripts/lib/config/docs-orchestrator.mjs +2 -1
  174. package/scripts/lib/config/docs-staleness.mjs +2 -1
  175. package/scripts/lib/config/drift-check.mjs +2 -1
  176. package/scripts/lib/config/eval.mjs +2 -1
  177. package/scripts/lib/config/events-rotation.mjs +2 -1
  178. package/scripts/lib/config/evolve.mjs +8 -2
  179. package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
  180. package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
  181. package/scripts/lib/config/handover-gate.mjs +2 -1
  182. package/scripts/lib/config/health-endpoints.mjs +388 -0
  183. package/scripts/lib/config/issue-budget.mjs +2 -1
  184. package/scripts/lib/config/loop-guard.mjs +2 -1
  185. package/scripts/lib/config/memory.mjs +2 -1
  186. package/scripts/lib/config/moc-staleness.mjs +2 -1
  187. package/scripts/lib/config/persona-gate-wave.mjs +2 -1
  188. package/scripts/lib/config/private-config-dir.mjs +67 -0
  189. package/scripts/lib/config/reconcile.mjs +2 -1
  190. package/scripts/lib/config/remote-hosts.mjs +234 -0
  191. package/scripts/lib/config/section-extractor.mjs +7 -1
  192. package/scripts/lib/config/skill-evolution.mjs +2 -1
  193. package/scripts/lib/config/slopcheck.mjs +2 -1
  194. package/scripts/lib/config/state-md-lock.mjs +2 -1
  195. package/scripts/lib/config/templates-first.mjs +2 -1
  196. package/scripts/lib/config/test.mjs +2 -1
  197. package/scripts/lib/config/vault-integration.mjs +7 -1
  198. package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
  199. package/scripts/lib/config/vault-staleness.mjs +2 -1
  200. package/scripts/lib/config/vault-sync.mjs +2 -1
  201. package/scripts/lib/config/verification-auto-fix.mjs +2 -1
  202. package/scripts/lib/config/wave-reviewers.mjs +2 -1
  203. package/scripts/lib/config/worktree-orphans.mjs +2 -1
  204. package/scripts/lib/config.mjs +31 -3
  205. package/scripts/lib/convergence-monitor.mjs +82 -16
  206. package/scripts/lib/dispatcher/enumerate.mjs +2 -17
  207. package/scripts/lib/dispatcher/rank.mjs +124 -48
  208. package/scripts/lib/ecosystem-health.mjs +16 -2
  209. package/scripts/lib/eval/engine.mjs +9 -1
  210. package/scripts/lib/eval/session-resolve.mjs +23 -4
  211. package/scripts/lib/events-schema.mjs +48 -0
  212. package/scripts/lib/events.mjs +256 -7
  213. package/scripts/lib/evolve/autonomy-verdict.mjs +9 -4
  214. package/scripts/lib/evolve/autopilot-effectiveness.mjs +18 -1
  215. package/scripts/lib/frontmatter-guard.mjs +131 -13
  216. package/scripts/lib/gates/gate-full.mjs +26 -0
  217. package/scripts/lib/gates/gate-helpers.mjs +76 -0
  218. package/scripts/lib/gitlab-portfolio/cli.mjs +3 -15
  219. package/scripts/lib/hardware-pattern-detector.mjs +18 -1
  220. package/scripts/lib/harness-audit/categories/category1.mjs +17 -6
  221. package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
  222. package/scripts/lib/host-identity.mjs +50 -11
  223. package/scripts/lib/instruction-budget-guard.mjs +171 -5
  224. package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
  225. package/scripts/lib/learnings/io.mjs +60 -6
  226. package/scripts/lib/memory-banner.mjs +20 -8
  227. package/scripts/lib/memory-proposals/store.mjs +30 -22
  228. package/scripts/lib/owner-config-banner.mjs +43 -6
  229. package/scripts/lib/owner-config-loader.mjs +21 -10
  230. package/scripts/lib/owner-interview.mjs +3 -3
  231. package/scripts/lib/owner-yaml.mjs +207 -14
  232. package/scripts/lib/peer-discovery.mjs +20 -2
  233. package/scripts/lib/platform.mjs +108 -15
  234. package/scripts/lib/plugin-update-banner.mjs +406 -0
  235. package/scripts/lib/project-hygiene.mjs +38 -2
  236. package/scripts/lib/qg-command-drift-banner.mjs +50 -12
  237. package/scripts/lib/quality-gate.mjs +133 -44
  238. package/scripts/lib/reconcile/emitter.mjs +68 -6
  239. package/scripts/lib/reconcile/engine.mjs +249 -9
  240. package/scripts/lib/reconcile/idempotency.mjs +37 -4
  241. package/scripts/lib/reconcile/writer.mjs +40 -18
  242. package/scripts/lib/scope-gate.mjs +36 -0
  243. package/scripts/lib/session-close-backfill.mjs +125 -18
  244. package/scripts/lib/session-discovery.mjs +57 -3
  245. package/scripts/lib/session-end/phase-skip.mjs +2 -2
  246. package/scripts/lib/session-id.mjs +12 -23
  247. package/scripts/lib/session-identity/own-session.mjs +187 -11
  248. package/scripts/lib/session-lock-shape.mjs +43 -0
  249. package/scripts/lib/session-lock.mjs +5 -10
  250. package/scripts/lib/session-registry.mjs +25 -9
  251. package/scripts/lib/session-schema/constants.mjs +36 -2
  252. package/scripts/lib/session-schema/validator.mjs +38 -4
  253. package/scripts/lib/session-start-probes.mjs +18 -1
  254. package/scripts/lib/session-transition.mjs +1 -1
  255. package/scripts/lib/sessions-canonical.mjs +446 -0
  256. package/scripts/lib/sessions-staleness-banner.mjs +18 -11
  257. package/scripts/lib/skill-health/join.mjs +17 -4
  258. package/scripts/lib/state-md.mjs +78 -0
  259. package/scripts/lib/sunset/walker.mjs +6 -0
  260. package/scripts/lib/telemetry/schema.mjs +255 -17
  261. package/scripts/lib/telemetry/sync.mjs +417 -24
  262. package/scripts/lib/tmux-layout/telemetry.mjs +14 -2
  263. package/scripts/lib/validate/check-agents-skills.mjs +327 -0
  264. package/scripts/lib/validate/check-agents.mjs +3 -3
  265. package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
  266. package/scripts/lib/validate/check-doc-cli-commands.mjs +9 -33
  267. package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +370 -0
  268. package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
  269. package/scripts/lib/validate/check-owner-leakage.mjs +281 -20
  270. package/scripts/lib/validate/check-skill-links.mjs +163 -0
  271. package/scripts/lib/validate/check-skill-script-paths.mjs +455 -0
  272. package/scripts/lib/validate/check-untracked-test-deps.mjs +10 -0
  273. package/scripts/lib/validate/check-unwired-features.mjs +0 -9
  274. package/scripts/lib/validate/check-validator-registration.mjs +254 -0
  275. package/scripts/lib/validate/check-vcs-repo-flag.mjs +6 -28
  276. package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
  277. package/scripts/lib/validate/markdown-fences.mjs +196 -0
  278. package/scripts/lib/vault-backfill/template.mjs +63 -6
  279. package/scripts/lib/vault-mirror/process.mjs +165 -42
  280. package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
  281. package/scripts/lib/vault-status/board-lock.mjs +185 -0
  282. package/scripts/lib/vault-status/board-writer.mjs +174 -135
  283. package/scripts/lib/vault-status/narrative-mirror.mjs +129 -37
  284. package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
  285. package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
  286. package/scripts/lib/wave-executor/remote-dispatch.mjs +502 -0
  287. package/scripts/lib/wave-resource-gate.mjs +133 -7
  288. package/scripts/lib/wave-sizing.mjs +4 -1
  289. package/scripts/lib/wave-transcript-tail.mjs +142 -8
  290. package/scripts/materialize-wave-scope.mjs +32 -9
  291. package/scripts/memory-propose.mjs +146 -8
  292. package/scripts/migrate-cold-start-seed.mjs +4 -1
  293. package/scripts/parse-config.mjs +60 -3
  294. package/scripts/promote-vault-strict.mjs +4 -15
  295. package/scripts/release.mjs +337 -29
  296. package/scripts/repair-invalid-sessions.mjs +3 -3
  297. package/scripts/run-quality-gate.mjs +128 -11
  298. package/scripts/site-numbers.mjs +36 -4
  299. package/scripts/sweep-expired-learnings.mjs +90 -0
  300. package/scripts/sync-vault-schema.mjs +3 -1
  301. package/scripts/telemetry.mjs +2 -2
  302. package/scripts/validate-plugin.mjs +187 -0
  303. package/scripts/validate-wave-scope.mjs +28 -8
  304. package/scripts/vault-consolidate.mjs +3 -11
  305. package/scripts/vault-integration-watcher.mjs +2 -4
  306. package/scripts/vault-mirror.mjs +111 -26
  307. package/scripts/wave-scope-binding.mjs +215 -0
  308. package/skills/_shared/instruction-file-resolution.md +10 -0
  309. package/skills/_shared/parallel-aware-auq.md +31 -2
  310. package/skills/_shared/parallel-aware-preamble.md +18 -4
  311. package/skills/_shared/platform-tools.md +1 -1
  312. package/skills/_shared/state-ownership.md +1 -1
  313. package/skills/architecture/SKILL.md +7 -5
  314. package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
  315. package/skills/autopilot/SKILL.md +4 -18
  316. package/skills/claude-md-drift-check/SKILL.md +5 -1
  317. package/skills/claude-md-drift-check/checker.mjs +62 -2
  318. package/skills/convergence-monitoring/SIGNALS.md +55 -0
  319. package/skills/discovery/probes/vault-staleness.mjs +37 -13
  320. package/skills/discovery/probes-arch.md +20 -18
  321. package/skills/dispatcher/SKILL.md +3 -2
  322. package/skills/ecosystem-health/SKILL.md +4 -1
  323. package/skills/ecosystem-health/wizard.md +5 -0
  324. package/skills/evolve/SKILL.md +87 -11
  325. package/skills/frontmatter-guard/SKILL.md +11 -5
  326. package/skills/npm-publish/SKILL.md +1 -1
  327. package/skills/reconcile/SKILL.md +38 -2
  328. package/skills/remote-offload/SKILL.md +89 -0
  329. package/skills/session-end/SKILL.md +18 -905
  330. package/skills/session-end/phase-3-6-tail.md +19 -9
  331. package/skills/session-end/plan-verification.md +221 -155
  332. package/skills/session-end/references/phase-2-quality-gate.md +93 -0
  333. package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
  334. package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
  335. package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
  336. package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
  337. package/skills/session-end/references/session-summary-template.md +62 -0
  338. package/skills/session-plan/SKILL.md +49 -0
  339. package/skills/session-start/SKILL.md +41 -900
  340. package/skills/session-start/phase-8-5-express-path.md +1 -1
  341. package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
  342. package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
  343. package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
  344. package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
  345. package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
  346. package/skills/session-start/references/phase-4-ssot-environment-check.md +155 -0
  347. package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
  348. package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
  349. package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
  350. package/skills/vault-sync/validator.mjs +21 -27
  351. package/skills/wave-executor/SKILL.md +16 -2
  352. package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
  353. package/skills/wave-executor/references/wave-loop-review.md +570 -0
  354. package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
  355. package/skills/wave-executor/wave-loop.md +14 -1271
  356. package/templates/_shared/journey-manifest.md +10 -6
  357. package/.cursor/commands/autopilot-multi.md +0 -14
  358. package/.cursor/commands/contract-version-bump.md +0 -14
  359. package/.cursor/commands/journey-audit.md +0 -14
  360. package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
  361. package/.cursor/skills/daily/SKILL.md +0 -12
  362. package/.cursor/skills/domain-model/SKILL.md +0 -13
  363. package/.cursor/skills/journey-audit/SKILL.md +0 -13
  364. package/.cursor/skills/skill-creator/SKILL.md +0 -13
  365. package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
  366. package/commands/autopilot-multi.md +0 -74
  367. package/commands/contract-version-bump.md +0 -28
  368. package/commands/journey-audit.md +0 -43
  369. package/pi/prompts/autopilot-multi.md +0 -12
  370. package/pi/prompts/contract-version-bump.md +0 -12
  371. package/pi/prompts/journey-audit.md +0 -12
  372. package/scripts/autopilot-multi.mjs +0 -885
  373. package/scripts/backfill-learnings-expires.mjs +0 -196
  374. package/scripts/backfill-learnings.mjs +0 -203
  375. package/scripts/fleet-instruction-scan.mjs +0 -141
  376. package/scripts/lib/autopilot/dep-graph.mjs +0 -417
  377. package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
  378. package/scripts/lib/webhook-url.mjs +0 -105
  379. package/scripts/lifecycle-sim-v6.mjs +0 -347
  380. package/scripts/migrate-learnings-jsonl.mjs +0 -189
  381. package/scripts/migrate-subagents-jsonl.mjs +0 -196
  382. package/scripts/upload-social-preview.mjs +0 -316
  383. package/skills/_shared/model-selection.md +0 -64
  384. package/skills/contract-version-bump/SKILL.md +0 -219
  385. package/skills/daily/SKILL.md +0 -222
  386. package/skills/daily/generate.sh +0 -92
  387. package/skills/daily/templates/daily.md.tpl +0 -36
  388. package/skills/journey-audit/SKILL.md +0 -269
  389. package/skills/skill-creator/SKILL.md +0 -168
  390. package/skills/ubiquitous-language/SKILL.md +0 -97
  391. package/skills/vault-sync/package-lock.json +0 -40
  392. /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
  393. /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
@@ -0,0 +1,249 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * generate-hook-import-set.mjs — build the committed allowlist of modules that
4
+ * are transitively reachable from a hook entry file (GitLab #1224).
5
+ *
6
+ * WHY: a hook entry file imports a helper module on EVERY tool call. A helper
7
+ * saved in a broken intermediate state (2026-09-04: a call to a function that
8
+ * did not exist yet) turns every Bash/Edit/Write call into "Internal hook error
9
+ * — request blocked", host-wide, for every session sharing the working copy.
10
+ * `hooks/post-edit-import-probe.mjs` checks a file right after it is edited —
11
+ * but only when the file is in THIS set, so the probe stays cheap.
12
+ *
13
+ * The set is a COMMITTED artefact (`hooks/_lib/hook-import-set.json`) rather
14
+ * than a runtime crawl: the probe runs inside a 5 s PostToolUse budget and must
15
+ * not walk 142 modules per edit. `--check` re-generates and diffs, which is the
16
+ * drift guard that keeps the committed copy honest.
17
+ *
18
+ * Usage:
19
+ * node scripts/generate-hook-import-set.mjs [--plugin-root <dir>] [--out <file>] [--check]
20
+ *
21
+ * Exit codes: 0 = written / in sync; 1 = drift (with --check) or usage error.
22
+ */
23
+
24
+ import { readFileSync, writeFileSync, existsSync, statSync, realpathSync } from 'node:fs';
25
+ import path from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+ import { execFileSync } from 'node:child_process';
28
+
29
+ /** The four hook manifests whose entry files seed the crawl. */
30
+ export const HOOK_MANIFESTS = [
31
+ 'hooks.json',
32
+ 'hooks-codex.json',
33
+ 'hooks-cursor.json',
34
+ 'hooks-pi.json',
35
+ ];
36
+
37
+ /** Module extensions the crawl resolves an extension-less specifier against. */
38
+ const RESOLVE_EXTS = ['.mjs', '.js', '.cjs'];
39
+
40
+ /**
41
+ * Collect every `<something>.mjs` path mentioned in a hook manifest's command
42
+ * strings. The manifests differ per platform ($CLAUDE_PLUGIN_ROOT /
43
+ * $PI_PLUGIN_ROOT / ${PLUGIN_ROOT}), so match on the hooks/-relative tail
44
+ * rather than on any one variable spelling.
45
+ *
46
+ * @param {string} json - raw manifest contents
47
+ * @returns {string[]} plugin-root-relative entry paths (e.g. "hooks/enforce-scope.mjs")
48
+ */
49
+ export function extractEntryFiles(json) {
50
+ const out = new Set();
51
+ for (const m of json.matchAll(/hooks\/((?:[\w.-]+\/)*[\w.-]+\.mjs)/g)) {
52
+ const rel = `hooks/${m[1]}`;
53
+ // run-node.sh is the launcher, never an entry module; .../hooks/_lib/* is
54
+ // reached through imports, not through a manifest command.
55
+ out.add(rel);
56
+ }
57
+ return [...out].sort();
58
+ }
59
+
60
+ /**
61
+ * Extract relative module specifiers (static import/export-from and dynamic
62
+ * `import()`) from a module's source.
63
+ *
64
+ * @param {string} src
65
+ * @returns {string[]} relative specifiers, e.g. ["./_lib/profile-gate.mjs"]
66
+ */
67
+ export function extractRelativeSpecifiers(src) {
68
+ const out = new Set();
69
+ const patterns = [
70
+ /(?:^|[\s;}])(?:import|export)\s[^;'"]*?\sfrom\s*['"](\.[^'"]+)['"]/g,
71
+ /(?:^|[\s;}])import\s*['"](\.[^'"]+)['"]/g,
72
+ /\bimport\s*\(\s*['"](\.[^'"]+)['"]\s*\)/g,
73
+ ];
74
+ for (const re of patterns) {
75
+ for (const m of src.matchAll(re)) out.add(m[1]);
76
+ }
77
+ return [...out];
78
+ }
79
+
80
+ /**
81
+ * Resolve a relative specifier against the importing file.
82
+ *
83
+ * @param {string} fromFile - absolute path of the importing module
84
+ * @param {string} spec - relative specifier
85
+ * @returns {string|null} absolute path of an existing file, or null
86
+ */
87
+ function resolveSpecifier(fromFile, spec) {
88
+ const base = path.resolve(path.dirname(fromFile), spec);
89
+ const candidates = [base, ...RESOLVE_EXTS.map((e) => base + e),
90
+ ...RESOLVE_EXTS.map((e) => path.join(base, `index${e}`))];
91
+ for (const c of candidates) {
92
+ try {
93
+ if (existsSync(c) && statSync(c).isFile()) return c;
94
+ } catch { /* unreadable → not a resolution */ }
95
+ }
96
+ return null;
97
+ }
98
+
99
+ /**
100
+ * Crawl every hook entry file transitively and return the reachable module set.
101
+ *
102
+ * @param {string} pluginRoot
103
+ * @returns {{file: string, reachable_from: string[]}[]} sorted by `file`
104
+ */
105
+ export function buildImportSet(pluginRoot) {
106
+ /** @type {Map<string, Set<string>>} rel path → set of hook entry basenames */
107
+ const reach = new Map();
108
+
109
+ const entries = new Set();
110
+ for (const manifest of HOOK_MANIFESTS) {
111
+ const abs = path.join(pluginRoot, 'hooks', manifest);
112
+ if (!existsSync(abs)) continue;
113
+ for (const rel of extractEntryFiles(readFileSync(abs, 'utf8'))) {
114
+ if (existsSync(path.join(pluginRoot, rel))) entries.add(rel);
115
+ }
116
+ }
117
+
118
+ for (const entryRel of [...entries].sort()) {
119
+ const entryName = path.basename(entryRel);
120
+ const stack = [path.join(pluginRoot, entryRel)];
121
+ const seen = new Set();
122
+ while (stack.length > 0) {
123
+ const abs = stack.pop();
124
+ if (seen.has(abs)) continue;
125
+ seen.add(abs);
126
+
127
+ const rel = path.relative(pluginRoot, abs).split(path.sep).join('/');
128
+ if (!reach.has(rel)) reach.set(rel, new Set());
129
+ reach.get(rel).add(entryName);
130
+
131
+ let src;
132
+ try { src = readFileSync(abs, 'utf8'); } catch { continue; }
133
+ for (const spec of extractRelativeSpecifiers(src)) {
134
+ const next = resolveSpecifier(abs, spec);
135
+ if (next) stack.push(next);
136
+ }
137
+ }
138
+ }
139
+
140
+ return [...reach.entries()]
141
+ .sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0))
142
+ .map(([file, from]) => ({ file, reachable_from: [...from].sort() }));
143
+ }
144
+
145
+ /**
146
+ * Current HEAD sha of the plugin root, or "unknown" outside a git checkout.
147
+ *
148
+ * @param {string} pluginRoot
149
+ * @returns {string}
150
+ */
151
+ function headSha(pluginRoot) {
152
+ try {
153
+ return execFileSync('git', ['rev-parse', 'HEAD'], {
154
+ cwd: pluginRoot,
155
+ encoding: 'utf8',
156
+ stdio: ['ignore', 'pipe', 'ignore'],
157
+ }).trim();
158
+ } catch {
159
+ return 'unknown';
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Build the full artefact payload.
165
+ *
166
+ * @param {string} pluginRoot
167
+ * @returns {{generated_at: string, head: string, entries: {file: string, reachable_from: string[]}[]}}
168
+ */
169
+ export function buildArtifact(pluginRoot) {
170
+ return {
171
+ generated_at: new Date().toISOString(),
172
+ head: headSha(pluginRoot),
173
+ entries: buildImportSet(pluginRoot),
174
+ };
175
+ }
176
+
177
+ // ---------------------------------------------------------------------------
178
+ // CLI
179
+ // ---------------------------------------------------------------------------
180
+
181
+ /**
182
+ * @param {string[]} argv
183
+ * @returns {number} process exit code
184
+ */
185
+ export function main(argv) {
186
+ const args = argv.slice(2);
187
+ const flag = (name) => {
188
+ const i = args.indexOf(name);
189
+ return i >= 0 && i + 1 < args.length ? args[i + 1] : null;
190
+ };
191
+ const pluginRoot = path.resolve(flag('--plugin-root') ?? path.resolve(import.meta.dirname, '..'));
192
+ const out = path.resolve(flag('--out') ?? path.join(pluginRoot, 'hooks', '_lib', 'hook-import-set.json'));
193
+ const check = args.includes('--check');
194
+
195
+ const fresh = buildArtifact(pluginRoot);
196
+
197
+ if (check) {
198
+ if (!existsSync(out)) {
199
+ process.stderr.write(`✗ hook-import-set: ${path.relative(pluginRoot, out)} is missing — run generate-hook-import-set.mjs\n`);
200
+ return 1;
201
+ }
202
+ let committed;
203
+ try { committed = JSON.parse(readFileSync(out, 'utf8')); } catch {
204
+ process.stderr.write('✗ hook-import-set: committed file is not valid JSON\n');
205
+ return 1;
206
+ }
207
+ // Only `entries` is compared — generated_at/head are provenance, not content.
208
+ const a = JSON.stringify(committed.entries ?? null);
209
+ const b = JSON.stringify(fresh.entries);
210
+ if (a !== b) {
211
+ process.stderr.write('✗ hook-import-set: committed set differs from a fresh crawl — run `node scripts/generate-hook-import-set.mjs`\n');
212
+ return 1;
213
+ }
214
+ process.stdout.write(`✓ hook-import-set: ${fresh.entries.length} modules, in sync\n`);
215
+ return 0;
216
+ }
217
+
218
+ writeFileSync(out, JSON.stringify(fresh, null, 2) + '\n', 'utf8');
219
+ process.stdout.write(`✓ hook-import-set: wrote ${fresh.entries.length} modules to ${path.relative(pluginRoot, out)}\n`);
220
+ return 0;
221
+ }
222
+
223
+ /**
224
+ * True when this module was launched as the process entry script.
225
+ *
226
+ * A string compare of `import.meta.url` against `file://${process.argv[1]}` is
227
+ * fragile in exactly the invocations CI and husky use: any symlinked or
228
+ * realpath-differing path (measured: `/tmp` → `/private/tmp` on macOS) makes
229
+ * the compare false, so the whole CLI body becomes a silent no-op that still
230
+ * exits 0 — a fail-OPEN drift gate. A space or `#` in the path breaks the
231
+ * hand-built URL the same way. Same shape as `hooks/post-bash-write-verify.mjs`.
232
+ *
233
+ * @returns {boolean}
234
+ */
235
+ function invokedAsScript() {
236
+ const entry = process.argv[1];
237
+ if (!entry) return false;
238
+ const self = fileURLToPath(import.meta.url);
239
+ try {
240
+ return realpathSync(entry) === realpathSync(self);
241
+ } catch {
242
+ // argv[1] unresolvable (deleted/renamed mid-run) — best-effort raw compare.
243
+ return entry === self;
244
+ }
245
+ }
246
+
247
+ if (invokedAsScript()) {
248
+ process.exit(main(process.argv));
249
+ }
@@ -166,7 +166,11 @@ function releaseLock(lockFile, _myBody) {
166
166
  }
167
167
 
168
168
  /**
169
- * Read the current LWW map from disk. Returns {} on miss or parse error.
169
+ * Read the current LWW map from disk. Returns {} on a missing file (ENOENT)
170
+ * silently; an unreadable/unparseable one (EACCES/EISDIR/malformed JSON/…)
171
+ * also returns {} but with a stderr WARN (#1210 — ENOENT and other failures
172
+ * are different facts, same split as `sessions-canonical.mjs`
173
+ * `readCanonicalSessions`).
170
174
  * @param {string} currentFile
171
175
  * @returns {Record<string, object>}
172
176
  */
@@ -176,7 +180,14 @@ function readCurrentMap(currentFile) {
176
180
  const obj = JSON.parse(raw);
177
181
  if (obj && typeof obj === 'object' && !Array.isArray(obj)) return obj;
178
182
  return {};
179
- } catch {
183
+ } catch (err) {
184
+ if (!err || err.code !== 'ENOENT') {
185
+ process.stderr.write(
186
+ `⚠ readCurrentMap: cannot read ${currentFile} ` +
187
+ `(${err?.code ?? '?'}: ${err?.message ?? String(err)}) — ` +
188
+ 'treating as EMPTY, counts below are floors\n',
189
+ );
190
+ }
180
191
  return {};
181
192
  }
182
193
  }
@@ -82,6 +82,7 @@ import {
82
82
  RECOMMENDED_MARKERS,
83
83
  isRecommendedOption,
84
84
  } from './schema.mjs';
85
+ import { scanFenceBlocks } from '../validate/markdown-fences.mjs';
85
86
 
86
87
  // ---------------------------------------------------------------------------
87
88
  // Korpus-Abgrenzung
@@ -330,42 +331,17 @@ function lineOfOffset(starts, offset) {
330
331
  }
331
332
 
332
333
  /**
333
- * Zaun-Anker. **Zeilenanfangs-verankert** — ohne den Anker schließt
334
+ * Zerlegt eine Markdown-Datei in Code-Zaun-Blöcke. **Zeilenanfangs- UND
335
+ * -ende-verankert** (`{ wholeLine: true }`, gemeinsamer Zaun-Tracker
336
+ * `../validate/markdown-fences.mjs`, #1181) — ohne den Anker schließt
334
337
  * `skills/discovery/SKILL.md:373` seinen eigenen Block mitten im Fragetext
335
338
  * (Falle 1).
336
- */
337
- export const FENCE_LINE_PATTERN = /^[ \t]*(`{3,}|~{3,})[ \t]*([^\s`~]*)[ \t]*$/u;
338
-
339
- /**
340
- * Zerlegt eine Markdown-Datei in Code-Zaun-Blöcke.
341
339
  *
342
340
  * @param {string} content
343
341
  * @returns {Array<{openLine: number, closeLine: number, lang: string, bodyLines: string[], bodyStartLine: number}>}
344
342
  */
345
343
  export function fencesOf(content) {
346
- const lines = content.split('\n');
347
- const fences = [];
348
- let open = null;
349
- for (let i = 0; i < lines.length; i++) {
350
- const m = FENCE_LINE_PATTERN.exec(lines[i]);
351
- if (!m) continue;
352
- if (open === null) {
353
- open = { marker: m[1][0], len: m[1].length, lang: m[2] || '', startIdx: i };
354
- continue;
355
- }
356
- // Ein schließender Zaun muss dasselbe Zeichen und mindestens dieselbe
357
- // Länge haben — sonst beendet ein ```js-Zaun einen ````-Block.
358
- if (m[1][0] !== open.marker || m[1].length < open.len || m[2]) continue;
359
- fences.push({
360
- openLine: open.startIdx + 1,
361
- closeLine: i + 1,
362
- lang: open.lang,
363
- bodyLines: lines.slice(open.startIdx + 1, i),
364
- bodyStartLine: open.startIdx + 2,
365
- });
366
- open = null;
367
- }
368
- return fences;
344
+ return scanFenceBlocks(content, { wholeLine: true });
369
345
  }
370
346
 
371
347
  // ---------------------------------------------------------------------------
@@ -23,6 +23,7 @@ import { randomUUID } from 'node:crypto';
23
23
  import path from 'node:path';
24
24
 
25
25
  import { filterRealSessions } from './session-schema.mjs';
26
+ import { emitEvent, sessionAttribution } from './events.mjs';
26
27
 
27
28
  // ---------------------------------------------------------------------------
28
29
  // Constants
@@ -245,6 +246,73 @@ export async function shouldDispatchAutoDialectic({
245
246
  };
246
247
  }
247
248
 
249
+ // ---------------------------------------------------------------------------
250
+ // Decision + mechanical telemetry (#1200 part c)
251
+ // ---------------------------------------------------------------------------
252
+
253
+ /**
254
+ * `shouldDispatchAutoDialectic()` plus a MECHANICAL `orchestrator.dialectic.nudge_decided`
255
+ * record, so the nudge decision is observable without depending on the
256
+ * session-end skill prose actually reaching the emit step (#1200: 0 records of
257
+ * this class across 164k fleet events despite the nudge firing every close).
258
+ *
259
+ * Contract-preserving wrapper: calls `shouldDispatchAutoDialectic()` unchanged
260
+ * and returns its decision object verbatim — the emit is a side effect bolted
261
+ * on, never a change to the decision logic or its return shape.
262
+ *
263
+ * Emits on ALL FOUR return paths (kill-switch, no-new-input,
264
+ * cadence-threshold-met, under-threshold) — best-effort, try/catch-wrapped,
265
+ * because `emitEvent()` throws `EventValidationError` on a malformed record
266
+ * and a telemetry failure must never change what the caller decides to do
267
+ * (same posture as `scripts/lib/reconcile/engine.mjs`'s `emitReconcileCompleted`
268
+ * wrapper).
269
+ *
270
+ * @param {object} args
271
+ * @param {string} args.repoRoot
272
+ * @param {number} [args.cadence=DEFAULT_CADENCE] `dialectic.cadence` from config.
273
+ * @param {object} [args.signals] Pre-computed signals (skips disk reads) — forwarded verbatim.
274
+ * @param {Function|null} [args.emitFn=emitEvent] DI hook for testing / disabling
275
+ * emission; defaults to `emitEvent` from `./events.mjs`. Any error it throws
276
+ * is swallowed — it never changes the returned decision.
277
+ * @param {boolean} [args.record=true] When `false`, no event is emitted at all —
278
+ * for read-only PROBE callers (e.g. the session-end Phase 3.6.x tail-skip
279
+ * aggregator, `scripts/lib/session-end/phase-skip.mjs`, whose documented
280
+ * contract is side-effect-free) where the decision is computed for internal
281
+ * branching only and must never itself be recorded as a nudge decision.
282
+ * @returns {Promise<{trigger:boolean, reason:string, signals:object}>}
283
+ */
284
+ export async function decideAndRecordAutoDialectic({
285
+ repoRoot,
286
+ cadence = DEFAULT_CADENCE,
287
+ signals,
288
+ emitFn = emitEvent,
289
+ record = true,
290
+ } = {}) {
291
+ const decision = await shouldDispatchAutoDialectic({ repoRoot, cadence, signals });
292
+
293
+ if (record && typeof emitFn === 'function') {
294
+ try {
295
+ await emitFn(
296
+ 'orchestrator.dialectic.nudge_decided',
297
+ {
298
+ ...sessionAttribution(repoRoot),
299
+ decided: decision.trigger,
300
+ reason: decision.reason,
301
+ cadence,
302
+ sessions_since: decision.signals?.sessionsSinceLast,
303
+ learnings_since: decision.signals?.learningsSinceLast,
304
+ },
305
+ { repoRoot },
306
+ );
307
+ } catch {
308
+ // best-effort — a telemetry failure must never block or change the
309
+ // decision the caller already has in hand.
310
+ }
311
+ }
312
+
313
+ return decision;
314
+ }
315
+
248
316
  // ---------------------------------------------------------------------------
249
317
  // last-run — atomic write
250
318
  // ---------------------------------------------------------------------------
@@ -25,6 +25,7 @@ import { existsSync, statSync } from 'node:fs';
25
25
  import { randomUUID } from 'node:crypto';
26
26
  import path from 'node:path';
27
27
 
28
+ import { readCanonicalSessions } from './sessions-canonical.mjs';
28
29
  import { filterRealSessions } from './session-schema.mjs';
29
30
 
30
31
  // ---------------------------------------------------------------------------
@@ -61,49 +62,50 @@ export async function readDreamSignals({ repoRoot, memoryDir }) {
61
62
  }
62
63
 
63
64
  // 2. Read sessions.jsonl entries to compute lastCleanupAt + sessionsSinceCleanup
65
+ //
66
+ // #1209: migrated from a hand-rolled parse loop to `readCanonicalSessions`
67
+ // — the raw file is APPEND-ONLY (a physical session can carry more than one
68
+ // LINE: crash-recovery re-appends, #1068 stub/supersede pairs), and the
69
+ // hand-rolled loop counted every line. `sessionsSinceCleanup` is a CADENCE
70
+ // count ("how many sessions since the last cleanup"), so counting a
71
+ // duplicated line twice inflated it — this migration LOWERS the count,
72
+ // which is the fix, not a side effect. `canonicalizeSessions` also drops
73
+ // lines with no `session_id` (they cannot be deduplicated, so they were
74
+ // never a countable "session" to begin with); the existing fixtures below
75
+ // were given synthetic `session_id` values so their asserted counts still
76
+ // reflect the same intended scenario post-migration.
64
77
  let lastCleanupAt = null;
65
78
  let sessionsSinceCleanup = 0;
66
79
 
67
- if (existsSync(sessionsFilePath)) {
68
- const raw = await readFile(sessionsFilePath, 'utf8');
69
- const lines = raw.split('\n').filter((l) => l.length > 0);
70
- const entries = [];
71
- for (const line of lines) {
72
- try {
73
- entries.push(JSON.parse(line));
74
- } catch {
75
- // Malformed line — skip silently, this is a best-effort signal reader.
80
+ const entries = readCanonicalSessions({ filePath: sessionsFilePath });
81
+
82
+ // Abandoned-session filter (#834): sessions.jsonl carries phantom
83
+ // `status: 'abandoned'` stubs (session-close-backfill records for
84
+ // sessions that ended without a real close). They are legitimate DATA
85
+ // but not legitimate SIGNAL — a burst of abandoned stubs must not fire
86
+ // /memory-cleanup off zero real work. filterRealSessions() is the
87
+ // shared, tested implementation (scripts/lib/session-schema/filters.mjs).
88
+ const realEntries = filterRealSessions(entries);
89
+
90
+ // Find the most recent memory_cleanup_at timestamp across all REAL entries.
91
+ for (const entry of realEntries) {
92
+ const ts = entry.memory_cleanup_at;
93
+ if (typeof ts === 'string' && ts.length > 0) {
94
+ if (lastCleanupAt === null || ts > lastCleanupAt) {
95
+ lastCleanupAt = ts;
76
96
  }
77
97
  }
98
+ }
78
99
 
79
- // Abandoned-session filter (#834): sessions.jsonl carries phantom
80
- // `status: 'abandoned'` stubs (session-close-backfill records for
81
- // sessions that ended without a real close). They are legitimate DATA
82
- // but not legitimate SIGNAL — a burst of abandoned stubs must not fire
83
- // /memory-cleanup off zero real work. filterRealSessions() is the
84
- // shared, tested implementation (scripts/lib/session-schema/filters.mjs).
85
- const realEntries = filterRealSessions(entries);
86
-
87
- // Find the most recent memory_cleanup_at timestamp across all REAL entries.
100
+ // Count REAL entries newer than the last cleanup (or total REAL entries
101
+ // when no cleanup ever ran).
102
+ if (lastCleanupAt === null) {
103
+ sessionsSinceCleanup = realEntries.length;
104
+ } else {
88
105
  for (const entry of realEntries) {
89
- const ts = entry.memory_cleanup_at;
90
- if (typeof ts === 'string' && ts.length > 0) {
91
- if (lastCleanupAt === null || ts > lastCleanupAt) {
92
- lastCleanupAt = ts;
93
- }
94
- }
95
- }
96
-
97
- // Count REAL entries newer than the last cleanup (or total REAL entries
98
- // when no cleanup ever ran).
99
- if (lastCleanupAt === null) {
100
- sessionsSinceCleanup = realEntries.length;
101
- } else {
102
- for (const entry of realEntries) {
103
- const startedAt = entry.started_at;
104
- if (typeof startedAt === 'string' && startedAt > lastCleanupAt) {
105
- sessionsSinceCleanup += 1;
106
- }
106
+ const startedAt = entry.started_at;
107
+ if (typeof startedAt === 'string' && startedAt > lastCleanupAt) {
108
+ sessionsSinceCleanup += 1;
107
109
  }
108
110
  }
109
111
  }
@@ -158,6 +158,12 @@ export function computeSuitabilityVerdict(deps = {}) {
158
158
  const ciNotRed = ciStatus !== 'red'; // null status (absent/malformed) passes
159
159
  if (ciAbsent) warnings.push('CI signal absent');
160
160
  else if (ciMalformed) warnings.push('CI signal malformed — treated as absent');
161
+ // `{status:'unknown'}` is a WELL-FORMED reading that establishes nothing —
162
+ // since #1031 it is what a degraded probe produces, and it hits neither
163
+ // branch above. Without this line it passed G3 with NO warning at all, i.e.
164
+ // more quietly than the plain-absent case it is strictly weaker than. The
165
+ // gate stays non-blocking (only a literal 'red' fails); only the silence goes.
166
+ else if (ciStatus === 'unknown') warnings.push('CI signal unknown — treated as absent');
161
167
  const ciSegment = ciAbsent ? 'absent' : ciMalformed ? 'malformed' : `${ciStatus}`;
162
168
 
163
169
  // --- G4 — resource gate (green/warn/degraded/null pass; only 'critical' fails)
@@ -20,7 +20,7 @@ import {
20
20
  finalizeState,
21
21
  } from './telemetry.mjs';
22
22
  import { durableCommit } from './durable-telemetry.mjs';
23
- import { SO_STATE_DIR } from '../platform.mjs';
23
+ import { getStateDir } from '../platform.mjs';
24
24
 
25
25
  // Internal clamp — mirrors flags.mjs internal; not re-exported there.
26
26
  function clampNumber(value, { min, max, fallback }) {
@@ -311,7 +311,7 @@ export async function runLoop(opts = {}) {
311
311
  files: [
312
312
  jsonlPath.replace(process.cwd() + '/', ''),
313
313
  '.orchestrator/metrics/sessions.jsonl',
314
- `${SO_STATE_DIR}/STATE.md`,
314
+ `${getStateDir()}/STATE.md`,
315
315
  ],
316
316
  enabled: false,
317
317
  });