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
@@ -38,26 +38,148 @@
38
38
  *
39
39
  * ── Exports ───────────────────────────────────────────────────────────────────
40
40
  *
41
- * OWNER_YAML_PATH — default file path on disk
41
+ * resolvePrivateConfigDir({env}?) — THE host-private config dir resolver (#1223)
42
+ * resolveOwnerYamlPath(env?) — owner.yaml path, resolved at CALL time (#1223)
42
43
  * validateOwnerSections(obj) — pure validation, no I/O; bucketed per section (#820)
43
44
  * validateOwnerConfig(obj) — pure validation, no I/O; thin wrapper over validateOwnerSections
44
45
  * loadOwnerConfig({path?}) — reads file; per-section tolerance for OPTIONAL
45
46
  * sections (paths/dispatcher/vaults/baselines) — #820
46
47
  * writeOwnerConfig(config, {path?}) — validates, writes YAML, creates dir
47
48
  * getDefaults() — returns sensible default config object
49
+ *
50
+ * ── Zero bare-specifier imports (GH#62/#63, GitLab #1230) ────────────────────
51
+ *
52
+ * `js-yaml` is resolved LAZILY inside {@link loadOwnerConfig} / {@link
53
+ * writeOwnerConfig} (see {@link getYaml}), never at module load. Importing this
54
+ * module therefore costs `node:fs` + `node:path` + `node:module` and nothing
55
+ * else, which is what keeps it safe on the hook import graph: before this
56
+ * change, `import yaml from 'js-yaml'` at the top of this file crashed FOUR
57
+ * hooks with `ERR_MODULE_NOT_FOUND` for anyone whose `node_modules` was absent
58
+ * (on-session-start, on-session-end, post-edit-validate,
59
+ * skill-invocation-telemetry — measured 2026-09-06 over a `hooks/` + `scripts/`
60
+ * copy with no `node_modules`, 4 of 27 hooks rc=1).
61
+ *
62
+ * When the package cannot be resolved both entry points DEGRADE rather than
63
+ * throw: they return their normal shape plus `reason: 'yaml-parser-missing'`
64
+ * (`source: 'defaults'` / `written: false`) and emit ONE rate-limited stderr
65
+ * WARN naming `npm install` as the fix.
48
66
  */
49
67
 
50
68
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
51
69
  import { join, dirname } from 'node:path';
52
- import { homedir } from 'node:os';
53
- import yaml from 'js-yaml';
70
+ import { createRequire } from 'node:module';
71
+ import { resolvePrivateConfigDir } from './config/private-config-dir.mjs';
54
72
 
55
73
  // ---------------------------------------------------------------------------
56
74
  // Constants
57
75
  // ---------------------------------------------------------------------------
58
76
 
59
- /** Default path for the owner persona config file. */
60
- export const OWNER_YAML_PATH = join(homedir(), '.config', 'session-orchestrator', 'owner.yaml');
77
+ /** Basename of the owner persona config file. */
78
+ const OWNER_YAML_FILE = 'owner.yaml';
79
+
80
+ // ---------------------------------------------------------------------------
81
+ // Lazy js-yaml resolution (GH#62/#63, GitLab #1230)
82
+ // ---------------------------------------------------------------------------
83
+
84
+ /**
85
+ * Memoised `js-yaml` module, or `false` once resolution has failed.
86
+ * `null` = not yet attempted.
87
+ * @type {null | false | { load: Function, dump: Function }}
88
+ */
89
+ let _yaml = null;
90
+
91
+ /** Guards the one-per-process stderr WARN below. */
92
+ let _yamlWarned = false;
93
+
94
+ /**
95
+ * Resolve `js-yaml` at CALL time instead of at import time (GH#62/#63).
96
+ *
97
+ * WHY `createRequire` and not `await import('js-yaml')`: every caller of
98
+ * {@link loadOwnerConfig} consumes it SYNCHRONOUSLY. Measured 2026-09-06 with
99
+ * `rg -n --glob '!tests/**' 'loadOwnerConfig\(' scripts hooks skills` (minus
100
+ * this file and the unrelated async homonym in `owner-config-loader.mjs`):
101
+ * 9 call sites — 8 in code, 1 in `skills/session-start/SKILL.md:1113` — and
102
+ * `rg 'await\s+loadOwnerConfig'` over the same scope returns ZERO. Eight read
103
+ * `loadOwnerConfig().config` (`hooks/on-session-start.mjs:639`,
104
+ * `hooks/skill-invocation-telemetry.mjs:155`, `scripts/telemetry.mjs:81,130`,
105
+ * `scripts/vault-mirror.mjs:444`, `scripts/lib/telemetry/sync.mjs:202,269`,
106
+ * and the SKILL.md snippet); the ninth destructures the same sync return
107
+ * (`scripts/lib/soul-resolve.mjs:123`). `writeOwnerConfig` is likewise sync at
108
+ * its single call site, `scripts/lib/owner-interview.mjs:228`.
109
+ *
110
+ * Making either loader async would be a breaking change to all of them; a lazy
111
+ * `require()` keeps the sync contract and only fails at CALL time — where the
112
+ * failure is catchable — instead of at module load, where it is not.
113
+ *
114
+ * Same shape as the `getPicomatch()` pattern in `scripts/lib/rule-loader.mjs`
115
+ * and `scripts/lib/validate-vendored-rules.mjs`.
116
+ *
117
+ * @returns {{ load: Function, dump: Function } | null} the module, or `null`
118
+ * when `node_modules` is absent / the package cannot be resolved.
119
+ */
120
+ function getYaml() {
121
+ if (_yaml !== null) return _yaml === false ? null : _yaml;
122
+ try {
123
+ _yaml = createRequire(import.meta.url)('js-yaml');
124
+ } catch {
125
+ _yaml = false;
126
+ }
127
+ return _yaml === false ? null : _yaml;
128
+ }
129
+
130
+ /**
131
+ * Emit the one-per-process actionable WARN for a missing `js-yaml`. Kept to a
132
+ * SINGLE line and rate-limited to once per process, mirroring the GH#63
133
+ * degradation contract `hooks/on-stop.mjs` already satisfies: a missing package
134
+ * must never be louder than the fix it asks for.
135
+ */
136
+ function warnYamlMissing() {
137
+ if (_yamlWarned) return;
138
+ _yamlWarned = true;
139
+ console.warn(
140
+ "WARN owner-yaml: 'js-yaml' is not installed — owner.yaml cannot be parsed or written; " +
141
+ "using defaults. Run 'npm install' in the plugin directory to restore it.",
142
+ );
143
+ }
144
+
145
+ /**
146
+ * Re-export of the single host-private-config-dir resolver (#1223).
147
+ *
148
+ * The implementation lives in the ZERO-IMPORT leaf
149
+ * `scripts/lib/config/private-config-dir.mjs`, NOT here: `host-identity.mjs`
150
+ * sits on `hooks/on-stop.mjs`'s import subgraph (via `session-lock.mjs`), and
151
+ * this module statically imports `js-yaml`. While the resolver lived here,
152
+ * importing it from `host-identity.mjs` dragged `js-yaml` onto THAT subgraph and
153
+ * broke the GH#63 "degrade gracefully without node_modules" contract
154
+ * (`ERR_MODULE_NOT_FOUND` in `tests/hooks/on-stop.test.mjs`).
155
+ *
156
+ * As of GitLab #1230 (2026-09-06) THIS module is no longer a bare-specifier node
157
+ * on that graph either: the `js-yaml` import it used to carry statically is now
158
+ * lazy (see {@link getYaml}), so the four hooks that reach this file —
159
+ * on-session-start, on-session-end, post-edit-validate,
160
+ * skill-invocation-telemetry — load without `node_modules` instead of dying with
161
+ * ERR_MODULE_NOT_FOUND. Keep it that way: any NEW static bare import added here
162
+ * re-breaks all four at once, in a file whose own unit tests would never notice
163
+ * (the repo always has `node_modules`).
164
+ *
165
+ * NOTE for `tests/husky/pre-commit-owner-leakage.test.mjs`: that test copies
166
+ * the CP11 import chain FILE BY FILE into a tmp repo, so the leaf below is on
167
+ * its copied-file list. Any FURTHER repo-local import added here needs the same
168
+ * treatment.
169
+ */
170
+ export { resolvePrivateConfigDir };
171
+
172
+ /**
173
+ * Resolve the owner.yaml path at CALL time via the single private-config-dir
174
+ * resolver {@link resolvePrivateConfigDir} (#1223). Honours
175
+ * `SO_CONFIG_HOME` > `XDG_CONFIG_HOME` > homedir, each trimmed.
176
+ *
177
+ * @param {Record<string, string|undefined>} [env] — env source (default `process.env`)
178
+ * @returns {string} absolute path to owner.yaml
179
+ */
180
+ export function resolveOwnerYamlPath(env = process.env) {
181
+ return join(resolvePrivateConfigDir({ env }), OWNER_YAML_FILE);
182
+ }
61
183
 
62
184
  const VALID_LANGUAGES = /** @type {const} */ (['de', 'en']);
63
185
  const VALID_TONE_STYLES = /** @type {const} */ (['direct', 'neutral', 'friendly']);
@@ -418,19 +540,43 @@ export function validateOwnerConfig(obj) {
418
540
  * surfaced only via `sectionWarnings` (never dropped, never counted towards
419
541
  * `'partial'`).
420
542
  *
543
+ * MERGE RULE for the whole-file discard (GitLab #1244). The discard returns
544
+ * `getDefaults()` — but a VALID optional object section is merged back on top
545
+ * of its default (`{...defaults[name], ...parsed[name]}`), key by key. `source`
546
+ * stays `'defaults'` and `errors` still carries every required-section error,
547
+ * so no existing caller's branch changes; what changes is that a host-local
548
+ * path the operator DID declare correctly is no longer thrown away because an
549
+ * unrelated section (e.g. `tone:`) is malformed. That silent loss was a
550
+ * fail-OPEN path for the owner-leakage scanner's CP11 rule: a partial
551
+ * owner.yaml carrying only `owner:` + `paths.confidential-names-file:` made the
552
+ * scanner report PASS while matching nothing. An INVALID optional section is
553
+ * still replaced by its default and is now reported via `droppedSections` on
554
+ * this branch too (previously only on the required-valid branch), so a consumer
555
+ * can tell "not configured" from "configured but unusable".
556
+ *
421
557
  * Defensive — never throws.
422
558
  *
559
+ * When `js-yaml` cannot be resolved (no `node_modules`), returns defaults with
560
+ * `source: 'defaults'`, `reason: 'yaml-parser-missing'` and one explanatory
561
+ * entry in `errors` — never throws, never crashes the importing hook (GH#62/#63).
562
+ * When the file exists but cannot be turned into an object at all (unreadable,
563
+ * YAML syntax error, non-mapping top level) the same shape is returned with
564
+ * `reason: 'unparseable'`: in that state NOTHING about the file's contents is
565
+ * knowable, which is the distinction CP11 needs in order to fail CLOSED rather
566
+ * than assume "nothing configured".
567
+ *
423
568
  * @param {{ path?: string }} [opts]
424
569
  * @returns {{
425
570
  * config: object,
426
571
  * source: 'file'|'defaults'|'partial',
427
572
  * errors: string[],
573
+ * reason?: 'yaml-parser-missing'|'unparseable',
428
574
  * droppedSections?: Array<{ section: string, errors: string[] }>,
429
575
  * sectionWarnings?: Array<{ section: string, errors: string[] }>,
430
576
  * }}
431
577
  */
432
578
  export function loadOwnerConfig(opts = {}) {
433
- const filePath = opts.path ?? OWNER_YAML_PATH;
579
+ const filePath = opts.path ?? resolveOwnerYamlPath();
434
580
 
435
581
  if (!existsSync(filePath)) {
436
582
  return { config: getDefaults(), source: 'defaults', errors: [] };
@@ -443,10 +589,24 @@ export function loadOwnerConfig(opts = {}) {
443
589
  return {
444
590
  config: getDefaults(),
445
591
  source: 'defaults',
592
+ reason: 'unparseable',
446
593
  errors: [`failed to read owner.yaml: ${err.message}`],
447
594
  };
448
595
  }
449
596
 
597
+ const yaml = getYaml();
598
+ if (!yaml) {
599
+ warnYamlMissing();
600
+ return {
601
+ config: getDefaults(),
602
+ source: 'defaults',
603
+ reason: 'yaml-parser-missing',
604
+ errors: [
605
+ "yaml-parser-missing: 'js-yaml' is not installed, so owner.yaml could not be parsed (run 'npm install' in the plugin directory)",
606
+ ],
607
+ };
608
+ }
609
+
450
610
  let parsed;
451
611
  try {
452
612
  parsed = yaml.load(raw);
@@ -454,6 +614,7 @@ export function loadOwnerConfig(opts = {}) {
454
614
  return {
455
615
  config: getDefaults(),
456
616
  source: 'defaults',
617
+ reason: 'unparseable',
457
618
  errors: [`YAML parse error: ${err.message}`],
458
619
  };
459
620
  }
@@ -462,20 +623,36 @@ export function loadOwnerConfig(opts = {}) {
462
623
  return {
463
624
  config: getDefaults(),
464
625
  source: 'defaults',
626
+ reason: 'unparseable',
465
627
  errors: ['owner.yaml must contain a YAML mapping at the top level'],
466
628
  };
467
629
  }
468
630
 
469
631
  const { sections, errors: allErrors } = validateOwnerSections(parsed);
470
632
 
471
- // Any REQUIRED section invalid → legacy whole-file-discard, unchanged (#820).
633
+ // Any REQUIRED section invalid → whole-file discard (#820), EXCEPT that a
634
+ // VALID optional object section is merged back onto its default (#1244 — see
635
+ // the MERGE RULE in the JSDoc above). `source` stays 'defaults' and `errors`
636
+ // is unchanged, so every existing caller branch is untouched; only the
637
+ // discarded-but-valid `paths:`/`dispatcher:` keys survive, which is what stops
638
+ // CP11 from going silently inert on a partial owner.yaml.
472
639
  const requiredInvalid = REQUIRED_SECTIONS.some((name) => !sections[name]?.valid);
473
640
  if (requiredInvalid) {
474
- return {
475
- config: getDefaults(),
476
- source: 'defaults',
477
- errors: allErrors,
478
- };
641
+ const discardConfig = getDefaults();
642
+ const discardDropped = [];
643
+ for (const name of OPTIONAL_OBJECT_SECTIONS) {
644
+ const sec = sections[name];
645
+ if (sec?.valid && isPlainObject(parsed[name])) {
646
+ discardConfig[name] = { ...discardConfig[name], ...parsed[name] };
647
+ } else if (sec && !sec.valid) {
648
+ // Present but malformed — already replaced by its default above. Report
649
+ // it so a consumer can distinguish "not configured" from "unusable".
650
+ discardDropped.push({ section: name, errors: sec.errors });
651
+ }
652
+ }
653
+ const discardResult = { config: discardConfig, source: 'defaults', errors: allErrors };
654
+ if (discardDropped.length > 0) discardResult.droppedSections = discardDropped;
655
+ return discardResult;
479
656
  }
480
657
 
481
658
  // All REQUIRED sections valid — tolerate malformed OPTIONAL sections instead
@@ -532,12 +709,16 @@ export function loadOwnerConfig(opts = {}) {
532
709
  *
533
710
  * Synchronous. Defensive — never throws.
534
711
  *
712
+ * When `js-yaml` cannot be resolved (no `node_modules`), returns
713
+ * `{ written: false, reason: 'yaml-parser-missing', errors: [...] }` — the file
714
+ * is left untouched rather than half-written (GH#62/#63).
715
+ *
535
716
  * @param {object} config
536
717
  * @param {{ path?: string }} [opts]
537
- * @returns {{ written: boolean, errors: string[] }}
718
+ * @returns {{ written: boolean, errors: string[], reason?: 'yaml-parser-missing' }}
538
719
  */
539
720
  export function writeOwnerConfig(config, opts = {}) {
540
- const filePath = opts.path ?? OWNER_YAML_PATH;
721
+ const filePath = opts.path ?? resolveOwnerYamlPath();
541
722
 
542
723
  const validation = validateOwnerConfig(config);
543
724
  if (!validation.valid) {
@@ -554,6 +735,18 @@ export function writeOwnerConfig(config, opts = {}) {
554
735
  };
555
736
  }
556
737
 
738
+ const yaml = getYaml();
739
+ if (!yaml) {
740
+ warnYamlMissing();
741
+ return {
742
+ written: false,
743
+ reason: 'yaml-parser-missing',
744
+ errors: [
745
+ "yaml-parser-missing: 'js-yaml' is not installed, so owner.yaml could not be written (run 'npm install' in the plugin directory)",
746
+ ],
747
+ };
748
+ }
749
+
557
750
  let yamlStr;
558
751
  try {
559
752
  yamlStr = yaml.dump(config, { lineWidth: 120, noRefs: true });
@@ -100,7 +100,16 @@ function _ageHoursFrom(startedAt, nowMs) {
100
100
  /**
101
101
  * Map one discoverActiveSessions entry into a provenance-tagged peer.
102
102
  *
103
- * @param {{worktreePath:string,sessionId:string,mode:string,startedAt:string,pid:number,host:string,branch:string}} s
103
+ * GH#67 pass-through: `registryOnly` / `lockSuperseded` / `lockOwnerId` are
104
+ * threaded through UNCHANGED when the upstream entry carries them (registry-
105
+ * sourced sessions only). Absent stays absent, so a lock-sourced peer object is
106
+ * byte-identical to the pre-GH#67 shape. `lockSuperseded: true` is a HINT that
107
+ * a LIVE lock at that repoRoot is owned by a different raw session_id — not a
108
+ * verdict that the peer is dead (the lock is advisory, #1085). Consumers
109
+ * deciding a worktree PROMOTION_OFFER downgrade such a peer to an advisory
110
+ * line; consumers counting or displaying peers keep it.
111
+ *
112
+ * @param {{worktreePath:string,sessionId:string,mode:string,startedAt:string,pid:number,host:string,branch:string,registryOnly?:boolean,lockSuperseded?:boolean,lockOwnerId?:string|null}} s
104
113
  * @param {number} nowMs
105
114
  * @returns {object}
106
115
  */
@@ -118,6 +127,10 @@ function _peerFromDiscovered(s, nowMs) {
118
127
  worktreePath: s.worktreePath,
119
128
  };
120
129
  if (ageHours !== undefined) peer.ageHours = ageHours;
130
+ // GH#67 — additive, absent-stays-absent.
131
+ if (s.registryOnly !== undefined) peer.registryOnly = s.registryOnly;
132
+ if (s.lockSuperseded !== undefined) peer.lockSuperseded = s.lockSuperseded;
133
+ if (s.lockOwnerId !== undefined) peer.lockOwnerId = s.lockOwnerId;
121
134
  return peer;
122
135
  }
123
136
 
@@ -189,7 +202,12 @@ function _discoveredSelfSessionId(mySessionId, repoRoot) {
189
202
  * per-source — only the fields the originating surface can supply are emitted
190
203
  * (no field is advertised that the implementation does not set):
191
204
  * - source 'discovered' (from discoverActiveSessions — lock + registry unified):
192
- * { source, sessionId, mode|null, host, pid, worktreePath, ageHours? }
205
+ * { source, sessionId, mode|null, host, pid, worktreePath, ageHours?,
206
+ * registryOnly?, lockSuperseded?, lockOwnerId? }
207
+ * The last three appear ONLY on registry-sourced entries (GH#67
208
+ * annotation, passed through verbatim — see `_peerFromDiscovered`).
209
+ * `lockSuperseded` is a HINT, never a liveness verdict: no peer is
210
+ * dropped on account of it, because the session lock is advisory.
193
211
  * - source 'state-md' (from checkPeerStateMd):
194
212
  * { source, sessionId, mode|null, currentWave, reason, ageHours? }
195
213
  */
@@ -2,10 +2,15 @@
2
2
  * platform.mjs — platform detection for session-orchestrator (Node.js port of platform.sh)
3
3
  * ESM-importable. Uses only Node built-ins. No external dependencies.
4
4
  *
5
- * Exports 10 constants + 5 named helper functions:
6
- * SO_PLATFORM, SO_PLUGIN_ROOT, SO_PROJECT_DIR, SO_STATE_DIR, SO_CONFIG_FILE,
7
- * SO_SHARED_DIR, SO_OS, SO_IS_WINDOWS, SO_IS_WSL, SO_PATH_SEP
8
- * detectPlatform, resolvePluginRoot, resolveProjectDir, resolveStateDir, resolveConfigFile
5
+ * Exports:
6
+ * - lazy memoized accessors (#1153 P5): getPlatform, getPluginRoot, getProjectDir,
7
+ * getStateDir, getConfigFile (+ test-only _resetPlatformCache)
8
+ * - plain constants (no filesystem work): SO_SHARED_DIR, SO_OS, SO_IS_WINDOWS,
9
+ * SO_IS_WSL, SO_PATH_SEP
10
+ * - pure resolvers: detectPlatform, resolvePluginRoot, resolveProjectDir,
11
+ * resolveStateDir, resolveConfigFile
12
+ *
13
+ * NOTHING in this module touches the filesystem at import time.
9
14
  */
10
15
 
11
16
  import { existsSync, statSync } from 'node:fs';
@@ -293,23 +298,111 @@ export function resolveConfigFile(platform) {
293
298
  }
294
299
 
295
300
  // ---------------------------------------------------------------------------
296
- // Auto-initialise — compute all exported constants at module load
301
+ // Lazy, memoized accessors (#1153 P5)
297
302
  // ---------------------------------------------------------------------------
303
+ //
304
+ // These five values used to be `export const … = detect…()` evaluated at MODULE
305
+ // LOAD, so every one of the ~31 static importers — including the hottest
306
+ // deny-capable hooks, which run on every single tool call — paid a filesystem
307
+ // walk-up (statSync/existsSync per ancestor directory, twice over for
308
+ // resolvePluginRoot) merely for importing this module, whether or not it ever
309
+ // read the value.
310
+ //
311
+ // They are now computed on FIRST USE and memoized. Call the getter at the point
312
+ // of use, never at a module's top level — a top-level `const X = getProjectDir()`
313
+ // re-creates the exact cost this change removes.
298
314
 
299
- /** @type {"claude"|"codex"|"cursor"|"pi"} */
300
- export const SO_PLATFORM = detectPlatform();
315
+ /**
316
+ * Memo slots. `undefined` is the miss sentinel — `''` is a legitimate
317
+ * resolvePluginRoot() result and must not re-trigger resolution.
318
+ * @type {{platform: ("claude"|"codex"|"cursor"|"pi")|undefined, pluginRoot: string|undefined, projectDir: string|undefined, stateDir: string|undefined, configFile: string|undefined}}
319
+ */
320
+ const _cache = {
321
+ platform: undefined,
322
+ pluginRoot: undefined,
323
+ projectDir: undefined,
324
+ stateDir: undefined,
325
+ configFile: undefined,
326
+ };
301
327
 
302
- /** Absolute path to the session-orchestrator plugin directory (empty string if unresolvable) */
303
- export const SO_PLUGIN_ROOT = resolvePluginRoot(SO_PLATFORM);
328
+ /**
329
+ * Host platform, detected once per process.
330
+ * @returns {"claude"|"codex"|"cursor"|"pi"}
331
+ */
332
+ export function getPlatform() {
333
+ if (_cache.platform === undefined) {
334
+ _cache.platform = detectPlatform();
335
+ }
336
+ return _cache.platform;
337
+ }
304
338
 
305
- /** Absolute path to the current project root */
306
- export const SO_PROJECT_DIR = resolveProjectDir(SO_PLATFORM);
339
+ /**
340
+ * Absolute path to the session-orchestrator plugin directory, resolved once.
341
+ * @returns {string} Absolute path, or empty string if unresolvable.
342
+ */
343
+ export function getPluginRoot() {
344
+ if (_cache.pluginRoot === undefined) {
345
+ _cache.pluginRoot = resolvePluginRoot(getPlatform());
346
+ }
347
+ return _cache.pluginRoot;
348
+ }
307
349
 
308
- /** Platform-native state directory name */
309
- export const SO_STATE_DIR = resolveStateDir(SO_PLATFORM);
350
+ /**
351
+ * Absolute path to the current project root, resolved once.
352
+ * @returns {string}
353
+ */
354
+ export function getProjectDir() {
355
+ if (_cache.projectDir === undefined) {
356
+ _cache.projectDir = resolveProjectDir(getPlatform());
357
+ }
358
+ return _cache.projectDir;
359
+ }
310
360
 
311
- /** Platform config file name */
312
- export const SO_CONFIG_FILE = resolveConfigFile(SO_PLATFORM);
361
+ /**
362
+ * Platform-native state directory name (".claude" | ".codex" | ".cursor" | ".pi").
363
+ * @returns {string}
364
+ */
365
+ export function getStateDir() {
366
+ if (_cache.stateDir === undefined) {
367
+ _cache.stateDir = resolveStateDir(getPlatform());
368
+ }
369
+ return _cache.stateDir;
370
+ }
371
+
372
+ /**
373
+ * Platform config file name ("CLAUDE.md" | "AGENTS.md").
374
+ * @returns {string}
375
+ */
376
+ export function getConfigFile() {
377
+ if (_cache.configFile === undefined) {
378
+ _cache.configFile = resolveConfigFile(getPlatform());
379
+ }
380
+ return _cache.configFile;
381
+ }
382
+
383
+ /**
384
+ * Test-only: drop every memoized value so the next getter re-detects.
385
+ * Production code must never call this — the values are process-stable.
386
+ * @returns {void}
387
+ */
388
+ export function _resetPlatformCache() {
389
+ _cache.platform = undefined;
390
+ _cache.pluginRoot = undefined;
391
+ _cache.projectDir = undefined;
392
+ _cache.stateDir = undefined;
393
+ _cache.configFile = undefined;
394
+ }
395
+
396
+ // The deprecated `export let SO_PLATFORM / SO_PLUGIN_ROOT / SO_PROJECT_DIR /
397
+ // SO_STATE_DIR / SO_CONFIG_FILE` live bindings (#1153 P5) are GONE. They were
398
+ // `undefined` until the matching getter had run in the process, which made
399
+ // every bare read a silent wrong answer. Every consumer now calls the getter
400
+ // (getPlatform(), getProjectDir(), …). A re-introduction is caught by the
401
+ // named-export assertion in tests/lib/platform.test.mjs — do not add them back.
402
+
403
+ // ---------------------------------------------------------------------------
404
+ // Plain constants — no filesystem work, safe to evaluate at load
405
+ // ---------------------------------------------------------------------------
313
406
 
314
407
  /** Shared orchestrator directory name — always ".orchestrator" */
315
408
  export const SO_SHARED_DIR = '.orchestrator';