session-orchestrator 3.24.0 → 4.0.1

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 (435) 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 +3 -2
  47. package/.codex-plugin/skills/architecture/SKILL.md +20 -0
  48. package/.codex-plugin/skills/autopilot/SKILL.md +21 -0
  49. package/.codex-plugin/skills/autopilot/agents/openai.yaml +5 -0
  50. package/.codex-plugin/skills/bootstrap/SKILL.md +22 -0
  51. package/.codex-plugin/skills/bootstrap/agents/openai.yaml +5 -0
  52. package/.codex-plugin/skills/brainstorm/SKILL.md +22 -0
  53. package/.codex-plugin/skills/brainstorm/agents/openai.yaml +5 -0
  54. package/.codex-plugin/skills/claude-md-drift-check/SKILL.md +17 -0
  55. package/.codex-plugin/skills/close/SKILL.md +21 -0
  56. package/.codex-plugin/skills/close/agents/openai.yaml +5 -0
  57. package/.codex-plugin/skills/convergence-monitoring/SKILL.md +24 -0
  58. package/.codex-plugin/skills/debug/SKILL.md +21 -0
  59. package/.codex-plugin/skills/debug/agents/openai.yaml +5 -0
  60. package/.codex-plugin/skills/discovery/SKILL.md +21 -0
  61. package/.codex-plugin/skills/discovery/agents/openai.yaml +5 -0
  62. package/.codex-plugin/skills/dispatcher/SKILL.md +21 -0
  63. package/.codex-plugin/skills/dispatcher/agents/openai.yaml +5 -0
  64. package/.codex-plugin/skills/docs-orchestrator/SKILL.md +20 -0
  65. package/.codex-plugin/skills/ecosystem-health/SKILL.md +22 -0
  66. package/.codex-plugin/skills/eli5/SKILL.md +21 -0
  67. package/.codex-plugin/skills/eli5/agents/openai.yaml +5 -0
  68. package/.codex-plugin/skills/eval/SKILL.md +21 -0
  69. package/.codex-plugin/skills/eval/agents/openai.yaml +5 -0
  70. package/.codex-plugin/skills/evolve/SKILL.md +21 -0
  71. package/.codex-plugin/skills/evolve/agents/openai.yaml +5 -0
  72. package/.codex-plugin/skills/frontmatter-guard/SKILL.md +17 -0
  73. package/.codex-plugin/skills/gitlab-ops/SKILL.md +22 -0
  74. package/.codex-plugin/skills/gitlab-portfolio/SKILL.md +17 -0
  75. package/.codex-plugin/skills/go/SKILL.md +22 -0
  76. package/.codex-plugin/skills/go/agents/openai.yaml +5 -0
  77. package/.codex-plugin/skills/grill/SKILL.md +21 -0
  78. package/.codex-plugin/skills/grill/agents/openai.yaml +5 -0
  79. package/.codex-plugin/skills/harness-audit/SKILL.md +19 -0
  80. package/.codex-plugin/skills/harness-audit/agents/openai.yaml +5 -0
  81. package/.codex-plugin/skills/hook-development/SKILL.md +17 -0
  82. package/.codex-plugin/skills/mcp-builder/SKILL.md +17 -0
  83. package/.codex-plugin/skills/memory-cleanup/SKILL.md +21 -0
  84. package/.codex-plugin/skills/memory-cleanup/agents/openai.yaml +5 -0
  85. package/.codex-plugin/skills/mode-selector/SKILL.md +19 -0
  86. package/.codex-plugin/skills/npm-publish/SKILL.md +18 -0
  87. package/.codex-plugin/skills/peekaboo-driver/SKILL.md +20 -0
  88. package/.codex-plugin/skills/persona-panel/SKILL.md +22 -0
  89. package/.codex-plugin/skills/persona-panel/agents/openai.yaml +5 -0
  90. package/.codex-plugin/skills/plan/SKILL.md +22 -0
  91. package/.codex-plugin/skills/plan/agents/openai.yaml +5 -0
  92. package/.codex-plugin/skills/playwright-driver/SKILL.md +22 -0
  93. package/.codex-plugin/skills/portfolio/SKILL.md +21 -0
  94. package/.codex-plugin/skills/portfolio/agents/openai.yaml +5 -0
  95. package/.codex-plugin/skills/quality-gates/SKILL.md +22 -0
  96. package/.codex-plugin/skills/reconcile/SKILL.md +21 -0
  97. package/.codex-plugin/skills/reconcile/agents/openai.yaml +5 -0
  98. package/.codex-plugin/skills/release/SKILL.md +22 -0
  99. package/.codex-plugin/skills/release/agents/openai.yaml +5 -0
  100. package/.codex-plugin/skills/remote-offload/SKILL.md +22 -0
  101. package/.codex-plugin/skills/repo-audit/SKILL.md +19 -0
  102. package/.codex-plugin/skills/repo-audit/agents/openai.yaml +5 -0
  103. package/.codex-plugin/skills/session/SKILL.md +21 -0
  104. package/.codex-plugin/skills/session/agents/openai.yaml +5 -0
  105. package/.codex-plugin/skills/session-end/SKILL.md +22 -0
  106. package/.codex-plugin/skills/session-plan/SKILL.md +22 -0
  107. package/.codex-plugin/skills/session-start/SKILL.md +22 -0
  108. package/.codex-plugin/skills/spinout/SKILL.md +21 -0
  109. package/.codex-plugin/skills/spinout/agents/openai.yaml +5 -0
  110. package/.codex-plugin/skills/sunset-review/SKILL.md +21 -0
  111. package/.codex-plugin/skills/sunset-review/agents/openai.yaml +5 -0
  112. package/.codex-plugin/skills/templates-ack/SKILL.md +21 -0
  113. package/.codex-plugin/skills/templates-ack/agents/openai.yaml +5 -0
  114. package/.codex-plugin/skills/test/SKILL.md +21 -0
  115. package/.codex-plugin/skills/test/agents/openai.yaml +5 -0
  116. package/.codex-plugin/skills/test-runner/SKILL.md +22 -0
  117. package/.codex-plugin/skills/tmux-layout/SKILL.md +23 -0
  118. package/.codex-plugin/skills/using-orchestrator/SKILL.md +19 -0
  119. package/.codex-plugin/skills/vault-mirror/SKILL.md +17 -0
  120. package/.codex-plugin/skills/vault-sync/SKILL.md +17 -0
  121. package/.codex-plugin/skills/wave-executor/SKILL.md +22 -0
  122. package/.codex-plugin/skills/write-executable-plan/SKILL.md +24 -0
  123. package/.cursor/commands/autopilot.md +2 -2
  124. package/.cursor/commands/bootstrap.md +1 -1
  125. package/.cursor/commands/brainstorm.md +1 -1
  126. package/.cursor/commands/debug.md +1 -1
  127. package/.cursor/commands/discovery.md +1 -1
  128. package/.cursor/commands/dispatcher.md +2 -2
  129. package/.cursor/commands/eli5.md +2 -2
  130. package/.cursor/commands/eval.md +2 -2
  131. package/.cursor/commands/evolve.md +1 -1
  132. package/.cursor/commands/go.md +1 -1
  133. package/.cursor/commands/grill.md +2 -2
  134. package/.cursor/commands/memory-cleanup.md +2 -2
  135. package/.cursor/commands/persona-panel.md +1 -1
  136. package/.cursor/commands/plan.md +1 -1
  137. package/.cursor/commands/portfolio.md +1 -1
  138. package/.cursor/commands/reconcile.md +2 -2
  139. package/.cursor/commands/release.md +2 -2
  140. package/.cursor/commands/session.md +2 -2
  141. package/.cursor/commands/spinout.md +2 -2
  142. package/.cursor/commands/sunset-review.md +2 -2
  143. package/.cursor/commands/templates-ack.md +2 -2
  144. package/.cursor/commands/test.md +2 -2
  145. package/.cursor/skills/brainstorm/SKILL.md +1 -1
  146. package/.cursor/skills/eval/SKILL.md +1 -1
  147. package/.cursor/skills/quality-gates/SKILL.md +1 -1
  148. package/.cursor/skills/remote-offload/SKILL.md +1 -1
  149. package/.cursor-plugin/plugin.json +30 -0
  150. package/.orchestrator/policy/blocked-commands.json +121 -0
  151. package/.orchestrator/policy/ecosystem.schema.json +66 -0
  152. package/.orchestrator/policy/quality-gates.example.json +16 -0
  153. package/.orchestrator/policy/quality-gates.schema.json +38 -0
  154. package/.orchestrator/policy/templates-policy.json +27 -0
  155. package/.orchestrator/policy/test-profiles.json +47 -0
  156. package/AGENTS.md +225 -0
  157. package/CHANGELOG.md +1314 -2
  158. package/NOTICE +11 -6
  159. package/README.md +135 -94
  160. package/agents/eval-judge.md +1 -1
  161. package/agents/skill-applied-judge.md +1 -1
  162. package/assets/wave-lifecycle.svg +98 -0
  163. package/commands/release.md +6 -3
  164. package/commands/session.md +18 -3
  165. package/docs/README.md +4 -0
  166. package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
  167. package/docs/baseline.md +67 -0
  168. package/docs/ci-setup.md +108 -62
  169. package/docs/codex-setup.md +107 -29
  170. package/docs/components.md +38 -16
  171. package/docs/cursor-setup.md +6 -2
  172. package/docs/events-schema.md +9 -6
  173. package/docs/instruction-delivery.md +69 -0
  174. package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
  175. package/docs/migration-v4.md +365 -0
  176. package/docs/pi-setup.md +6 -1
  177. package/docs/plugin-architecture-v3.md +1 -1
  178. package/docs/rule-authoring.md +85 -19
  179. package/docs/scope-collision-guard.md +5 -5
  180. package/docs/session-config-reference.md +57 -56
  181. package/docs/session-config-template.md +6 -29
  182. package/docs/telemetry.md +157 -3
  183. package/docs/vault-docs-architecture.md +50 -11
  184. package/hooks/_lib/hook-import-set.json +1488 -0
  185. package/hooks/_lib/subagent-transcript.mjs +562 -0
  186. package/hooks/config-protection.mjs +2 -2
  187. package/hooks/cwd-change-restore.mjs +2 -2
  188. package/hooks/enforce-commands.mjs +69 -0
  189. package/hooks/hooks-codex.json +1 -1
  190. package/hooks/hooks-cursor.json +10 -0
  191. package/hooks/hooks-pi.json +5 -0
  192. package/hooks/hooks.json +6 -1
  193. package/hooks/loop-guard.mjs +3 -3
  194. package/hooks/on-session-end.mjs +2 -2
  195. package/hooks/on-session-start.mjs +103 -2
  196. package/hooks/on-stop.mjs +60 -14
  197. package/hooks/operator-steer.mjs +2 -2
  198. package/hooks/post-bash-write-verify.mjs +85 -0
  199. package/hooks/post-edit-import-probe.mjs +344 -0
  200. package/hooks/post-subagent-discovery-validator.mjs +187 -431
  201. package/hooks/post-tool-batch-wave-signal.mjs +118 -4
  202. package/hooks/post-tool-failure-corrective-context.mjs +2 -2
  203. package/hooks/post-tooluse-frontend-slop.mjs +3 -3
  204. package/hooks/pre-bash-destructive-guard.mjs +39 -13
  205. package/hooks/skill-invocation-telemetry.mjs +17 -5
  206. package/hooks/subagent-telemetry.mjs +13 -4
  207. package/monitors/monitors.json +3 -3
  208. package/package.json +9 -1
  209. package/pi/prompts/session.md +2 -2
  210. package/scripts/backfill-abandoned-sessions.mjs +50 -4
  211. package/scripts/backfill-learnings-from-vault.mjs +9 -3
  212. package/scripts/dialectic-deriver.mjs +73 -8
  213. package/scripts/export-hw-learnings.mjs +113 -1
  214. package/scripts/generate-agents-skills.mjs +378 -0
  215. package/scripts/generate-codex-skills.mjs +246 -0
  216. package/scripts/generate-cursor-adapter.mjs +45 -8
  217. package/scripts/generate-hook-import-set.mjs +292 -0
  218. package/scripts/lib/agent-status.mjs +13 -2
  219. package/scripts/lib/auto-dream.mjs +38 -36
  220. package/scripts/lib/autonomy/suitability.mjs +6 -0
  221. package/scripts/lib/autopilot/loop.mjs +2 -2
  222. package/scripts/lib/ci-status-banner.mjs +220 -75
  223. package/scripts/lib/codex/plugin-contract.mjs +88 -6
  224. package/scripts/lib/config/auto-dream.mjs +2 -1
  225. package/scripts/lib/config/block-header.mjs +8 -0
  226. package/scripts/lib/config/block-preprocess.mjs +177 -0
  227. package/scripts/lib/config/broken-window.mjs +2 -1
  228. package/scripts/lib/config/cold-start.mjs +2 -1
  229. package/scripts/lib/config/config-protection.mjs +22 -2
  230. package/scripts/lib/config/context-coverage.mjs +2 -1
  231. package/scripts/lib/config/cross-repo.mjs +2 -1
  232. package/scripts/lib/config/custom-phases.mjs +2 -1
  233. package/scripts/lib/config/dialectic.mjs +2 -1
  234. package/scripts/lib/config/discovery-validator.mjs +2 -1
  235. package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
  236. package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
  237. package/scripts/lib/config/docs-orchestrator.mjs +2 -1
  238. package/scripts/lib/config/docs-staleness.mjs +2 -1
  239. package/scripts/lib/config/drift-check.mjs +2 -1
  240. package/scripts/lib/config/eval.mjs +2 -1
  241. package/scripts/lib/config/events-rotation.mjs +2 -1
  242. package/scripts/lib/config/evolve.mjs +8 -2
  243. package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
  244. package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
  245. package/scripts/lib/config/handover-gate.mjs +2 -1
  246. package/scripts/lib/config/health-endpoints.mjs +7 -2
  247. package/scripts/lib/config/host-paths.mjs +20 -4
  248. package/scripts/lib/config/issue-budget.mjs +2 -1
  249. package/scripts/lib/config/loop-guard.mjs +2 -1
  250. package/scripts/lib/config/memory.mjs +2 -1
  251. package/scripts/lib/config/moc-staleness.mjs +2 -1
  252. package/scripts/lib/config/persona-gate-wave.mjs +2 -1
  253. package/scripts/lib/config/private-config-dir.mjs +67 -0
  254. package/scripts/lib/config/reconcile.mjs +2 -1
  255. package/scripts/lib/config/remote-hosts.mjs +2 -1
  256. package/scripts/lib/config/section-extractor.mjs +7 -1
  257. package/scripts/lib/config/skill-evolution.mjs +2 -1
  258. package/scripts/lib/config/slopcheck.mjs +2 -1
  259. package/scripts/lib/config/state-md-lock.mjs +2 -1
  260. package/scripts/lib/config/templates-first.mjs +2 -1
  261. package/scripts/lib/config/test.mjs +2 -1
  262. package/scripts/lib/config/vault-integration.mjs +7 -1
  263. package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
  264. package/scripts/lib/config/vault-staleness.mjs +2 -1
  265. package/scripts/lib/config/vault-sync.mjs +2 -1
  266. package/scripts/lib/config/verification-auto-fix.mjs +2 -1
  267. package/scripts/lib/config/wave-reviewers.mjs +2 -1
  268. package/scripts/lib/config/worktree-orphans.mjs +2 -1
  269. package/scripts/lib/convergence-monitor.mjs +82 -16
  270. package/scripts/lib/dispatcher/rank.mjs +124 -48
  271. package/scripts/lib/ecosystem-health.mjs +16 -2
  272. package/scripts/lib/eval/engine.mjs +9 -1
  273. package/scripts/lib/eval/session-resolve.mjs +23 -4
  274. package/scripts/lib/events.mjs +22 -6
  275. package/scripts/lib/frontmatter-guard.mjs +131 -13
  276. package/scripts/lib/gates/gate-full.mjs +30 -0
  277. package/scripts/lib/gates/gate-helpers.mjs +76 -0
  278. package/scripts/lib/hardware-pattern-detector.mjs +18 -1
  279. package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
  280. package/scripts/lib/host-identity.mjs +50 -11
  281. package/scripts/lib/instruction-budget-guard.mjs +171 -5
  282. package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
  283. package/scripts/lib/learnings/io.mjs +60 -6
  284. package/scripts/lib/memory-proposals/store.mjs +30 -22
  285. package/scripts/lib/owner-config-banner.mjs +41 -6
  286. package/scripts/lib/owner-config-loader.mjs +21 -10
  287. package/scripts/lib/owner-interview.mjs +3 -3
  288. package/scripts/lib/owner-yaml.mjs +215 -15
  289. package/scripts/lib/platform.mjs +108 -15
  290. package/scripts/lib/plugin-update-banner.mjs +414 -0
  291. package/scripts/lib/project-hygiene.mjs +38 -2
  292. package/scripts/lib/qg-command-drift-banner.mjs +50 -12
  293. package/scripts/lib/quality-gate.mjs +133 -44
  294. package/scripts/lib/reconcile/emitter.mjs +68 -6
  295. package/scripts/lib/reconcile/engine.mjs +51 -11
  296. package/scripts/lib/reconcile/idempotency.mjs +37 -4
  297. package/scripts/lib/reconcile/writer.mjs +40 -18
  298. package/scripts/lib/session-close-backfill.mjs +67 -9
  299. package/scripts/lib/session-id.mjs +12 -23
  300. package/scripts/lib/session-identity/own-session.mjs +125 -10
  301. package/scripts/lib/session-lock-shape.mjs +43 -0
  302. package/scripts/lib/session-lock.mjs +5 -10
  303. package/scripts/lib/session-registry.mjs +25 -9
  304. package/scripts/lib/session-schema/constants.mjs +64 -3
  305. package/scripts/lib/session-schema/validator.mjs +38 -4
  306. package/scripts/lib/session-start-probes.mjs +30 -1
  307. package/scripts/lib/sessions-staleness-banner.mjs +18 -11
  308. package/scripts/lib/skill-health/join.mjs +17 -4
  309. package/scripts/lib/state-md.mjs +78 -0
  310. package/scripts/lib/sunset/walker.mjs +6 -0
  311. package/scripts/lib/telemetry/schema.mjs +202 -9
  312. package/scripts/lib/telemetry/sync.mjs +368 -12
  313. package/scripts/lib/telemetry-flush-health-banner.mjs +211 -0
  314. package/scripts/lib/validate/check-agents-skills.mjs +327 -0
  315. package/scripts/lib/validate/check-agents.mjs +3 -3
  316. package/scripts/lib/validate/check-codex-skills.mjs +191 -0
  317. package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
  318. package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
  319. package/scripts/lib/validate/check-owner-leakage.mjs +319 -22
  320. package/scripts/lib/validate/check-skill-links.mjs +193 -0
  321. package/scripts/lib/validate/check-skill-script-paths.mjs +47 -28
  322. package/scripts/lib/validate/check-test-git-config-target.mjs +192 -12
  323. package/scripts/lib/validate/check-unwired-features.mjs +163 -15
  324. package/scripts/lib/validate/check-validator-registration.mjs +10 -4
  325. package/scripts/lib/validate/confidential-names.mjs +95 -30
  326. package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
  327. package/scripts/lib/validate/repo-files.mjs +48 -14
  328. package/scripts/lib/vault-backfill/template.mjs +63 -6
  329. package/scripts/lib/vault-mirror/process.mjs +165 -42
  330. package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
  331. package/scripts/lib/vault-status/narrative-mirror.mjs +127 -18
  332. package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
  333. package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
  334. package/scripts/lib/wave-executor/remote-dispatch.mjs +5 -7
  335. package/scripts/lib/wave-resource-gate.mjs +8 -2
  336. package/scripts/lib/wave-sizing.mjs +4 -1
  337. package/scripts/lib/wave-transcript-tail.mjs +118 -4
  338. package/scripts/materialize-wave-scope.mjs +12 -5
  339. package/scripts/memory-propose.mjs +19 -5
  340. package/scripts/migrate-cold-start-seed.mjs +4 -1
  341. package/scripts/parse-config.mjs +60 -3
  342. package/scripts/release.mjs +430 -31
  343. package/scripts/repair-invalid-sessions.mjs +3 -3
  344. package/scripts/run-quality-gate.mjs +128 -11
  345. package/scripts/site-numbers.mjs +344 -8
  346. package/scripts/sweep-expired-learnings.mjs +90 -0
  347. package/scripts/sync-vault-schema.mjs +3 -1
  348. package/scripts/telemetry.mjs +2 -2
  349. package/scripts/validate-plugin.mjs +164 -0
  350. package/scripts/validate-wave-scope.mjs +28 -8
  351. package/scripts/wave-scope-binding.mjs +215 -0
  352. package/skills/_shared/instruction-file-resolution.md +10 -0
  353. package/skills/_shared/parallel-aware-preamble.md +1 -0
  354. package/skills/_shared/platform-tools.md +1 -1
  355. package/skills/_shared/state-ownership.md +1 -1
  356. package/skills/architecture/SKILL.md +7 -5
  357. package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
  358. package/skills/autopilot/SKILL.md +4 -18
  359. package/skills/claude-md-drift-check/SKILL.md +5 -1
  360. package/skills/claude-md-drift-check/checker.mjs +62 -2
  361. package/skills/convergence-monitoring/SIGNALS.md +55 -0
  362. package/skills/discovery/probes/vault-staleness.mjs +37 -13
  363. package/skills/discovery/probes-arch.md +20 -18
  364. package/skills/dispatcher/SKILL.md +3 -2
  365. package/skills/evolve/SKILL.md +65 -26
  366. package/skills/frontmatter-guard/SKILL.md +11 -5
  367. package/skills/npm-publish/SKILL.md +1 -1
  368. package/skills/reconcile/SKILL.md +33 -0
  369. package/skills/remote-offload/SKILL.md +1 -1
  370. package/skills/session-end/SKILL.md +18 -905
  371. package/skills/session-end/phase-3-6-tail.md +10 -3
  372. package/skills/session-end/plan-verification.md +221 -155
  373. package/skills/session-end/references/phase-2-quality-gate.md +93 -0
  374. package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
  375. package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
  376. package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
  377. package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
  378. package/skills/session-end/references/session-summary-template.md +62 -0
  379. package/skills/session-plan/SKILL.md +49 -0
  380. package/skills/session-start/SKILL.md +22 -904
  381. package/skills/session-start/phase-8-5-express-path.md +1 -1
  382. package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
  383. package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
  384. package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
  385. package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
  386. package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
  387. package/skills/session-start/references/phase-4-ssot-environment-check.md +160 -0
  388. package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
  389. package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
  390. package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
  391. package/skills/vault-sync/SKILL.md +10 -0
  392. package/skills/vault-sync/validator.mjs +21 -27
  393. package/skills/wave-executor/SKILL.md +15 -1
  394. package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
  395. package/skills/wave-executor/references/wave-loop-review.md +570 -0
  396. package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
  397. package/skills/wave-executor/wave-loop.md +14 -1309
  398. package/templates/_shared/journey-manifest.md +10 -6
  399. package/.cursor/commands/autopilot-multi.md +0 -14
  400. package/.cursor/commands/contract-version-bump.md +0 -14
  401. package/.cursor/commands/journey-audit.md +0 -14
  402. package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
  403. package/.cursor/skills/daily/SKILL.md +0 -12
  404. package/.cursor/skills/domain-model/SKILL.md +0 -13
  405. package/.cursor/skills/journey-audit/SKILL.md +0 -13
  406. package/.cursor/skills/skill-creator/SKILL.md +0 -13
  407. package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
  408. package/commands/autopilot-multi.md +0 -74
  409. package/commands/contract-version-bump.md +0 -28
  410. package/commands/journey-audit.md +0 -43
  411. package/pi/prompts/autopilot-multi.md +0 -12
  412. package/pi/prompts/contract-version-bump.md +0 -12
  413. package/pi/prompts/journey-audit.md +0 -12
  414. package/scripts/autopilot-multi.mjs +0 -885
  415. package/scripts/backfill-learnings-expires.mjs +0 -196
  416. package/scripts/backfill-learnings.mjs +0 -203
  417. package/scripts/fleet-instruction-scan.mjs +0 -141
  418. package/scripts/lib/autopilot/dep-graph.mjs +0 -417
  419. package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
  420. package/scripts/lib/webhook-url.mjs +0 -105
  421. package/scripts/lifecycle-sim-v6.mjs +0 -347
  422. package/scripts/migrate-learnings-jsonl.mjs +0 -189
  423. package/scripts/migrate-subagents-jsonl.mjs +0 -196
  424. package/scripts/upload-social-preview.mjs +0 -316
  425. package/skills/_shared/model-selection.md +0 -64
  426. package/skills/contract-version-bump/SKILL.md +0 -219
  427. package/skills/daily/SKILL.md +0 -222
  428. package/skills/daily/generate.sh +0 -92
  429. package/skills/daily/templates/daily.md.tpl +0 -36
  430. package/skills/journey-audit/SKILL.md +0 -270
  431. package/skills/skill-creator/SKILL.md +0 -168
  432. package/skills/ubiquitous-language/SKILL.md +0 -97
  433. package/skills/vault-sync/package-lock.json +0 -40
  434. /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
  435. /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
@@ -23,6 +23,7 @@
23
23
  * Usage:
24
24
  * node scripts/sweep-expired-learnings.mjs [--prune] [--dry-run|--apply] [--json]
25
25
  * [--grace-days N] [--entries PATH] [--file PATH] [--archive PATH]
26
+ * [--appended N] [--boosted M] [--duration-ms D] [--skipped a,b] [--repo-root PATH]
26
27
  *
27
28
  * Flags:
28
29
  * --prune Decision-driven prune+consolidate+rewrite instead of the
@@ -40,6 +41,23 @@
40
41
  * store.
41
42
  * --file PATH Learnings store (default: .orchestrator/metrics/learnings.jsonl)
42
43
  * --archive PATH Archive sidecar (default: .orchestrator/metrics/learnings-archive.jsonl)
44
+ * --appended N PRUNE ONLY (#1206). New learnings written this `/evolve`
45
+ * run (Step 3.5(4)); folded into the mechanical
46
+ * `orchestrator.evolve.completed` emit alongside this
47
+ * call's own `pruned` count. Default 0.
48
+ * --boosted M PRUNE ONLY (#1206). Existing learnings reinforced this
49
+ * run (Step 3.5(2)). Default 0.
50
+ * --duration-ms D PRUNE ONLY (#1206). Elapsed ms since the run's telemetry
51
+ * start marker. Default 0.
52
+ * --skipped a,b PRUNE ONLY (#1206). Comma-separated list of optional
53
+ * steps that ran but were themselves skipped this run
54
+ * (HR-105 — e.g. `skill-evolution-off`). Default none.
55
+ * --repo-root PATH PRUNE ONLY (#1206). Repo root the
56
+ * `orchestrator.evolve.completed` record is pinned to.
57
+ * No default and NO process.cwd() fallback (#1119) — when
58
+ * omitted, no event is emitted at all (stderr WARN, exit
59
+ * code unaffected). Emission is best-effort and never
60
+ * changes this command's exit code or stdout contract.
43
61
  *
44
62
  * Exit codes:
45
63
  * 0 Success (including no-op when nothing is archive-eligible)
@@ -51,6 +69,7 @@
51
69
  import { existsSync } from 'node:fs';
52
70
  import { sweepExpiredLearnings, pruneLearnings } from './lib/learnings/expiry-sweep.mjs';
53
71
  import { readLearnings } from './lib/learnings/io.mjs';
72
+ import { emitEvolveCompleted } from './lib/learnings/evolve-telemetry.mjs';
54
73
 
55
74
  const DEFAULT_FILE = '.orchestrator/metrics/learnings.jsonl';
56
75
  const DEFAULT_ARCHIVE = '.orchestrator/metrics/learnings-archive.jsonl';
@@ -97,6 +116,20 @@ function parseArgs(argv) {
97
116
  entries: null,
98
117
  file: DEFAULT_FILE,
99
118
  archive: DEFAULT_ARCHIVE,
119
+ appended: 0,
120
+ boosted: 0,
121
+ durationMs: 0,
122
+ skipped: [],
123
+ // #1119 — NO process.cwd() fallback. Most CLI callers (every test in this
124
+ // file except the SKILL.md-extraction fixture, which sets its own tmp
125
+ // `cwd`) invoke this script from the repo root without `--repo-root`; a
126
+ // cwd fallback would silently append synthetic `orchestrator.evolve.
127
+ // completed` records to the operator's REAL fleet ledger on every
128
+ // `--prune --apply` run in `npm test`. `emitEvolveCompleted()` already
129
+ // refuses to emit (stderr WARN, no throw) when repoRoot is absent — same
130
+ // fail-closed contract as `emitReconcileCompleted` / `express-path.mjs`.
131
+ repoRoot: null,
132
+ telemetryExplicit: false,
100
133
  };
101
134
  for (let i = 0; i < argv.length; i++) {
102
135
  const a = argv[i];
@@ -122,6 +155,37 @@ function parseArgs(argv) {
122
155
  args.file = argv[++i];
123
156
  } else if (a === '--archive') {
124
157
  args.archive = argv[++i];
158
+ } else if (a === '--appended') {
159
+ const raw = argv[++i];
160
+ const v = Number(raw);
161
+ if (!Number.isInteger(v) || v < 0) {
162
+ usageError(`--appended requires a non-negative integer, got: ${raw}`);
163
+ }
164
+ args.appended = v;
165
+ args.telemetryExplicit = true;
166
+ } else if (a === '--boosted') {
167
+ const raw = argv[++i];
168
+ const v = Number(raw);
169
+ if (!Number.isInteger(v) || v < 0) {
170
+ usageError(`--boosted requires a non-negative integer, got: ${raw}`);
171
+ }
172
+ args.boosted = v;
173
+ args.telemetryExplicit = true;
174
+ } else if (a === '--duration-ms') {
175
+ const raw = argv[++i];
176
+ const v = Number(raw);
177
+ if (!Number.isFinite(v) || v < 0) {
178
+ usageError(`--duration-ms requires a non-negative number, got: ${raw}`);
179
+ }
180
+ args.durationMs = v;
181
+ args.telemetryExplicit = true;
182
+ } else if (a === '--skipped') {
183
+ const raw = argv[++i];
184
+ args.skipped = typeof raw === 'string' ? raw.split(',').filter((s) => s.length > 0) : [];
185
+ args.telemetryExplicit = true;
186
+ } else if (a === '--repo-root') {
187
+ args.repoRoot = argv[++i];
188
+ args.telemetryExplicit = true;
125
189
  } else if (a === '--help' || a === '-h') {
126
190
  printHelp();
127
191
  process.exit(0);
@@ -140,6 +204,12 @@ function parseArgs(argv) {
140
204
  if (!args.prune && args.entries !== null) {
141
205
  usageError('--entries is only valid with --prune');
142
206
  }
207
+ if (!args.prune && args.telemetryExplicit) {
208
+ usageError(
209
+ '--appended/--boosted/--duration-ms/--skipped/--repo-root are only valid with --prune ' +
210
+ '(the sweep path never emits orchestrator.evolve.completed)',
211
+ );
212
+ }
143
213
  return args;
144
214
  }
145
215
 
@@ -265,6 +335,26 @@ async function runPrune(args) {
265
335
  ...result,
266
336
  };
267
337
 
338
+ // #1206 — the ONE mechanical call site for `orchestrator.evolve.completed`:
339
+ // this `--prune --apply` invocation IS `/evolve analyze`'s Step 3.5(5) store
340
+ // write, so folding the emit in here (instead of a separate
341
+ // `emit-event.mjs` call in skill prose) means the event can no longer be
342
+ // forgotten independently of the write it reports on. Gated on `!dryRun` —
343
+ // a `--prune --dry-run` preview (the docs' own recommended pre-check for a
344
+ // hand-assembled `--entries` sidecar) never wrote anything, so it must not
345
+ // report a completed run either. Best-effort — never changes this
346
+ // command's exit code or stdout contract.
347
+ if (!args.dryRun) {
348
+ await emitEvolveCompleted({
349
+ repoRoot: args.repoRoot,
350
+ appended: args.appended,
351
+ boosted: args.boosted,
352
+ pruned: result.archived,
353
+ durationMs: args.durationMs,
354
+ skipped: args.skipped,
355
+ });
356
+ }
357
+
268
358
  if (args.json) {
269
359
  process.stdout.write(JSON.stringify(summary) + '\n');
270
360
  } else {
@@ -141,7 +141,9 @@ function stripCommentLine(line) {
141
141
  return null;
142
142
  }
143
143
  // Single-line doc comment
144
- if (trimmed.startsWith('// ') && !trimmed.startsWith('// ──')) {
144
+ // A bare `//` (blank line inside a canonical comment run) is a comment too —
145
+ // without this the generated block carried a stray `//` (measured 2026-09-03).
146
+ if (trimmed === '//' || (trimmed.startsWith('// ') && !trimmed.startsWith('// ──'))) {
145
147
  // Keep structural separator comments, drop narrative ones
146
148
  return null;
147
149
  }
@@ -33,7 +33,7 @@ import { queueStats } from './lib/telemetry/queue.mjs';
33
33
  import { flush, buildBatch } from './lib/telemetry/sync.mjs';
34
34
  import { loadOwnerConfig } from './lib/owner-yaml.mjs';
35
35
  import { readPluginVersionFromPackageJson } from './lib/bootstrap-lock-freshness.mjs';
36
- import { SO_PLUGIN_ROOT } from './lib/platform.mjs';
36
+ import { getPluginRoot } from './lib/platform.mjs';
37
37
 
38
38
  const EXIT_OK = 0;
39
39
  const EXIT_USER = 1;
@@ -69,7 +69,7 @@ ENV KILL-SWITCHES
69
69
 
70
70
  /** Plugin version for `--version` — single-sourced via readPluginVersionFromPackageJson (null → 'unknown'). */
71
71
  function readPkgVersion() {
72
- return readPluginVersionFromPackageJson(SO_PLUGIN_ROOT) ?? 'unknown';
72
+ return readPluginVersionFromPackageJson(getPluginRoot()) ?? 'unknown';
73
73
  }
74
74
 
75
75
  // ---------------------------------------------------------------------------
@@ -94,6 +94,42 @@ function runCheck(script) {
94
94
  return result.status ?? 1;
95
95
  }
96
96
 
97
+ /**
98
+ * Advisory second pass for `check-skill-script-paths.mjs` (#1208): run it a
99
+ * second time with `--strict-sh --json` so the flag has a run surface inside
100
+ * validate-plugin, without promoting `.sh` findings to blocking here — the
101
+ * BLOCKING `.mjs`-only call above stays exactly as it is. Same non-blocking
102
+ * shape as the `check-unwired-features.mjs` / `check-learning-provenance.mjs`
103
+ * / `check-vcs-repo-flag.mjs` calls further down (exit code deliberately
104
+ * ignored; `checkFailed` is never set from this call). `runCheck()` cannot be
105
+ * reused here because it always spawns `[script, PLUGIN_ROOT]` with no room
106
+ * for extra flags, so this is a small dedicated spawn instead.
107
+ *
108
+ * @returns {void}
109
+ */
110
+ function runStrictShAdvisory() {
111
+ const result = spawnSync(
112
+ 'node',
113
+ [path.join(VALIDATE_DIR, 'check-skill-script-paths.mjs'), PLUGIN_ROOT, '--strict-sh', '--json'],
114
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
115
+ );
116
+ let parsed;
117
+ try {
118
+ parsed = JSON.parse(result.stdout || '');
119
+ } catch {
120
+ process.stdout.write(
121
+ ' WARN: check-skill-script-paths --strict-sh: could not parse JSON output (advisory)\n',
122
+ );
123
+ return;
124
+ }
125
+ const shDead = (parsed.findings || []).filter(
126
+ (f) => f.kind === 'missing-path' && f.path.endsWith('.sh'),
127
+ ).length;
128
+ process.stdout.write(
129
+ ` check-skill-script-paths --strict-sh: ${shDead} dead .sh citation(s) (advisory)\n`,
130
+ );
131
+ }
132
+
97
133
  /**
98
134
  * Run the surface-count drift family (issue #663) from the claude-md-drift-check
99
135
  * checker over the doc surfaces that carry artifact counts (README.md +
@@ -175,6 +211,105 @@ function runDriftCheck() {
175
211
  return 0;
176
212
  }
177
213
 
214
+ /**
215
+ * BLOCKING (#1224): the committed hook-reachable allowlist
216
+ * (`hooks/_lib/hook-import-set.json`) must match a fresh crawl of the four hook
217
+ * manifests. The allowlist is what gates `hooks/post-edit-import-probe.mjs`, so
218
+ * a stale set silently NARROWS that probe — a newly hook-reachable module would
219
+ * be edited without ever being checked, which is precisely the 2026-09-04
220
+ * host-wide-block incident class the probe exists to catch. A rotting allowlist
221
+ * is therefore a real defect, not a cosmetic drift, hence FAIL rather than WARN.
222
+ *
223
+ * `scripts/generate-hook-import-set.mjs --check` owns the comparison (it exits 1
224
+ * on drift); this adapter only translates it into validate-plugin's vocabulary.
225
+ * `runCheck()` cannot be reused — it spawns from `scripts/lib/validate/` and
226
+ * passes PLUGIN_ROOT positionally, while the generator takes `--plugin-root`.
227
+ *
228
+ * @returns {number} 0 when in sync, 1 on drift or a generator error.
229
+ */
230
+ function runHookImportSetCheck() {
231
+ console.log('--- Check: hook-import-set drift (#1224) ---');
232
+ const script = path.join(SCRIPT_DIR, 'generate-hook-import-set.mjs');
233
+ const result = spawnSync(
234
+ process.execPath,
235
+ [script, '--plugin-root', PLUGIN_ROOT, '--check'],
236
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
237
+ );
238
+ const combined = ((result.stdout ?? '') + (result.stderr ?? '')).trim();
239
+
240
+ // A silent exit 0 is NOT a pass. The generator's CLI entry is gated on
241
+ // `import.meta.url === file://${process.argv[1]}`, which does not hold when
242
+ // the spawned path traverses a symlink (measured: `/tmp` → `/private/tmp` on
243
+ // macOS makes the whole CLI body a no-op that still exits 0). Demanding the
244
+ // in-sync marker turns that failure mode from a silent pass into a finding.
245
+ const inSync = combined.match(/(\d+) modules, in sync/);
246
+ if (result.status === 0 && inSync) {
247
+ console.log(` PASS: hook-import-set: in sync (${inSync[1]} entries)`);
248
+ totalPass += 1;
249
+ return 0;
250
+ }
251
+ if (result.status === 0) {
252
+ console.log(' FAIL: hook-import-set: generator exited 0 without reporting a comparison '
253
+ + `(no in-sync marker in output: ${JSON.stringify(combined.slice(0, 120))})`);
254
+ totalFail += 1;
255
+ return 1;
256
+ }
257
+ const detail = combined.split('\n').filter(Boolean).pop()
258
+ ?? `generator exited ${result.status}`;
259
+ console.log(` FAIL: hook-import-set: ${detail.replace(/^✗\s*hook-import-set:\s*/, '')}`);
260
+ console.log(' Remedy: node scripts/generate-hook-import-set.mjs && stage hooks/_lib/hook-import-set.json');
261
+ totalFail += 1;
262
+ return 1;
263
+ }
264
+
265
+ /**
266
+ * BLOCKING: the cross-harness portable surface (`AGENTS.md` +
267
+ * `.agents/skills/`) must match a fresh generation from `CLAUDE.md` +
268
+ * `skills/`. 7 of 8 surveyed harnesses read `AGENTS.md` and never `CLAUDE.md`,
269
+ * so a stale or absent copy means those harnesses silently see NO Session
270
+ * Config — a failure that is invisible from inside Claude Code, which is
271
+ * exactly why it needs a mechanical gate rather than a convention.
272
+ *
273
+ * `scripts/generate-agents-skills.mjs --check` owns the comparison (exit 1 on
274
+ * drift); this adapter only translates it. `runCheck()` cannot be reused — it
275
+ * spawns from `scripts/lib/validate/` and passes PLUGIN_ROOT positionally,
276
+ * while the generator takes `--plugin-root`. The in-sync marker is demanded
277
+ * for the same reason `runHookImportSetCheck()` demands one: a spawn whose
278
+ * path traverses a symlink can make the generator's CLI body a no-op that
279
+ * still exits 0.
280
+ *
281
+ * @returns {number} 0 when in sync, 1 on drift or a generator error.
282
+ */
283
+ function runAgentsSurfaceCheck() {
284
+ console.log('--- Check: cross-harness portable surface (AGENTS.md + .agents/skills/) ---');
285
+ const script = path.join(SCRIPT_DIR, 'generate-agents-skills.mjs');
286
+ const result = spawnSync(
287
+ process.execPath,
288
+ [script, '--plugin-root', PLUGIN_ROOT, '--check'],
289
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
290
+ );
291
+ const combined = ((result.stdout ?? '') + (result.stderr ?? '')).trim();
292
+
293
+ const inSync = combined.match(/agents-surface: (\d+) artefact\(s\), in sync/);
294
+ if (result.status === 0 && inSync) {
295
+ console.log(` PASS: agents-surface: in sync (${inSync[1]} artefacts)`);
296
+ totalPass += 1;
297
+ return 0;
298
+ }
299
+ if (result.status === 0) {
300
+ console.log(' FAIL: agents-surface: generator exited 0 without reporting a comparison '
301
+ + `(no in-sync marker in output: ${JSON.stringify(combined.slice(0, 120))})`);
302
+ totalFail += 1;
303
+ return 1;
304
+ }
305
+ for (const line of combined.split('\n').filter((l) => l.startsWith('✗'))) {
306
+ console.log(` FAIL: ${line.replace(/^✗\s*/, '')}`);
307
+ totalFail += 1;
308
+ }
309
+ console.log(' Remedy: node scripts/generate-agents-skills.mjs');
310
+ return 1;
311
+ }
312
+
178
313
  // ---------------------------------------------------------------------------
179
314
  // Run all checks — same order as validate-plugin.sh
180
315
  // plugin.json checks are prerequisite; abort early if they fail.
@@ -220,6 +355,9 @@ if (runDriftCheck() !== 0) checkFailed = 1;
220
355
  process.stdout.write('\n');
221
356
  if (runCheck('check-hooks-symmetry.mjs') !== 0) checkFailed = 1;
222
357
 
358
+ process.stdout.write('\n');
359
+ if (runHookImportSetCheck() !== 0) checkFailed = 1;
360
+
223
361
  process.stdout.write('\n');
224
362
  if (runCheck('check-guard-requires-parity.mjs') !== 0) checkFailed = 1;
225
363
 
@@ -238,6 +376,9 @@ if (runCheck('check-path-utils-canary.mjs') !== 0) checkFailed = 1;
238
376
  process.stdout.write('\n');
239
377
  if (runCheck('check-codex-plugin.mjs') !== 0) checkFailed = 1;
240
378
 
379
+ process.stdout.write('\n');
380
+ if (runCheck('check-codex-skills.mjs') !== 0) checkFailed = 1;
381
+
241
382
  process.stdout.write('\n');
242
383
  if (runCheck('check-pi-package.mjs') !== 0) checkFailed = 1;
243
384
 
@@ -247,6 +388,18 @@ if (runCheck('check-pi-prompts.mjs') !== 0) checkFailed = 1;
247
388
  process.stdout.write('\n');
248
389
  if (runCheck('check-cursor-adapter.mjs') !== 0) checkFailed = 1;
249
390
 
391
+ // BLOCKING: the vendor-neutral portable surface — root AGENTS.md ↔ CLAUDE.md
392
+ // alias, root plugin.json (agent-plugins.org 1.0.0) manifest-version parity
393
+ // against package.json, and the .agents/skills/ mirror against the
394
+ // agentskills.io field list. The generator's own --check runs first (below);
395
+ // this checker is the INDEPENDENT oracle over the same artefacts, so a
396
+ // generator bug cannot vouch for its own output.
397
+ process.stdout.write('\n');
398
+ if (runCheck('check-agents-skills.mjs') !== 0) checkFailed = 1;
399
+
400
+ process.stdout.write('\n');
401
+ if (runAgentsSurfaceCheck() !== 0) checkFailed = 1;
402
+
250
403
  process.stdout.write('\n');
251
404
  if (runCheck('check-session-plan-routing.mjs') !== 0) checkFailed = 1;
252
405
 
@@ -327,6 +480,17 @@ runCheck('check-doc-cli-commands.mjs');
327
480
  // citation's own line or the line immediately above.
328
481
  process.stdout.write('\n');
329
482
  if (runCheck('check-skill-script-paths.mjs') !== 0) checkFailed = 1;
483
+ runStrictShAdvisory();
484
+
485
+ // BLOCKING (#1157 follow-up): a relative markdown link under skills/, commands/,
486
+ // agents/ or .claude/rules/ that does not resolve from the LINKING file's own
487
+ // directory. The check above judges cited SCRIPT paths; this one judges links
488
+ // between the instruction files themselves — the defect class a `references/`
489
+ // split creates, where the moved text is byte-identical and every `./sibling.md`
490
+ // in it now points one directory too deep. Three such links shipped broken on
491
+ // 2026-09-06 and no gate saw them; the worst silently detached the six
492
+ // session-end tail phases from their dispatcher.
493
+ if (runCheck('check-skill-links.mjs') !== 0) checkFailed = 1;
330
494
 
331
495
  // WARN-only: a state-mutating `git` call in tests/ that names no target resolves
332
496
  // its destination from the ambient cwd (or an inherited GIT_DIR) — the 2026-08-19
@@ -73,6 +73,7 @@ import path from 'node:path';
73
73
  import { readFileSync, existsSync, statSync } from 'node:fs';
74
74
  import { execFileSync } from 'node:child_process';
75
75
  import { warn } from './lib/common.mjs';
76
+ import { MANIFEST_SESSION_KEYS } from './lib/session-identity/own-session.mjs';
76
77
  import {
77
78
  assertFileScopeSubset,
78
79
  assertTestSiblingCoverage,
@@ -289,10 +290,13 @@ function validateOptionalSessionId(obj, key, errors) {
289
290
  }
290
291
 
291
292
  /**
292
- * Validate the OPTIONAL session binding (#1123): `session` (the raw
293
- * `session_id` of the session that WROTE this manifest) and its human-readable
294
- * twin `semantic_session`. Both come from one `sessionAttribution(repoRoot)`
295
- * call — see `skills/wave-executor/wave-loop.md` § Scope Manifest.
293
+ * Validate the OPTIONAL session binding (#1123): `session_id` (the raw session
294
+ * id of the session that WROTE this manifest) and its human-readable twin
295
+ * `semantic_session_id`. Both come from one `sessionAttribution(repoRoot)`
296
+ * call — see `skills/wave-executor/wave-loop.md` § Scope Manifest. The
297
+ * pre-#1153 spellings `session` / `semantic_session` are still ACCEPTED here
298
+ * (read side only, until the next minor release) — key names come from
299
+ * `MANIFEST_SESSION_KEYS` so the writer and every reader share one list.
296
300
  *
297
301
  * Deliberately NOT part of {@link validateRequired}, and that is a compatibility
298
302
  * constraint rather than a preference: `wave-scope.json` is a shared
@@ -306,9 +310,25 @@ function validateOptionalSessionId(obj, key, errors) {
306
310
  * @param {string[]} errors
307
311
  * @param {string[]} warnings
308
312
  */
309
- function validateSession(obj, errors, warnings) {
310
- const present = validateOptionalSessionId(obj, 'session', errors);
311
- validateOptionalSessionId(obj, 'semantic_session', errors);
313
+ function validateSessionBinding(obj, errors, warnings) {
314
+ let present = false;
315
+ MANIFEST_SESSION_KEYS.current.forEach((key, i) => {
316
+ const legacyKey = MANIFEST_SESSION_KEYS.legacy[i];
317
+ const hasCurrent = validateOptionalSessionId(obj, key, errors);
318
+ const hasLegacy = validateOptionalSessionId(obj, legacyKey, errors);
319
+ // Both spellings of the SAME slot, disagreeing, is the one case the reader
320
+ // cannot resolve honestly: it silently prefers `key` and drops the other
321
+ // id, so a manifest that names two different sessions would classify as
322
+ // `own` for one of them. Name it here rather than let the preference decide.
323
+ if (hasCurrent && hasLegacy && obj[key] !== obj[legacyKey]) {
324
+ errors.push(
325
+ `${key} and legacy ${legacyKey} are both present with DIFFERENT values — ` +
326
+ 'a manifest binds to exactly one session; drop the legacy key (accepted on ' +
327
+ 'the read side only, until the next minor release, #1153)',
328
+ );
329
+ }
330
+ if (i === 0) present = hasCurrent || hasLegacy;
331
+ });
312
332
  if (!present) {
313
333
  warnings.push(
314
334
  'no session field — manifest is not session-bound (legacy, #1123), so every session sharing this ' +
@@ -749,7 +769,7 @@ function validate(
749
769
  const warnings = [];
750
770
 
751
771
  validateRequired(obj, errors);
752
- validateSession(obj, errors, warnings);
772
+ validateSessionBinding(obj, errors, warnings);
753
773
  validateAllowedPaths(obj, errors, warnings);
754
774
  validateBlockedCommands(obj, errors);
755
775
  validateGates(obj, errors);
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * wave-scope-binding.mjs — print the session binding for a `wave-scope.json`
4
+ * manifest, and record the fail-closed case as an event (#1153 P4).
5
+ *
6
+ * Usage:
7
+ * node scripts/wave-scope-binding.mjs [--merge] [--wave N] [--role R] [--repo-root DIR]
8
+ *
9
+ * Prints ONE JSON object on stdout:
10
+ *
11
+ * {"session_id":"<raw session_id>","semantic_session_id":"<semantic id>"}
12
+ *
13
+ * Those key names are the canonical ones since #1153 P2 — the same spelling
14
+ * `.orchestrator/session.lock` and `current-session.json` already use. Readers
15
+ * additionally accept the pre-#1153 `session` / `semantic_session` spellings
16
+ * until the next minor release (`MANIFEST_SESSION_KEYS` in
17
+ * `scripts/lib/session-identity/own-session.mjs`); this writer never emits them.
18
+ *
19
+ * Keys whose value is unavailable are OMITTED, never written as `""` — an empty
20
+ * id is present-but-equal-to-nobody, which every reader classifies as FOREIGN
21
+ * (the one disposition that skips enforcement entirely), and
22
+ * `validate-wave-scope.mjs` rejects it outright.
23
+ *
24
+ * WHY A COMMAND AND NOT PROSE: this binding used to be an inline
25
+ * `node --input-type=module -e` block in `skills/wave-executor/wave-loop.md`,
26
+ * retyped by the coordinator once per wave. An unbound manifest (`{}`) is the
27
+ * fail-closed direction and therefore SILENT — indistinguishable from a
28
+ * coordinator that skipped the step. `orchestrator.scope.unbound_manifest` is
29
+ * what makes the silent case countable; 0 hits repo-wide before this file.
30
+ *
31
+ * The binding itself is NOT recomputed here. It is exactly one
32
+ * `attributionForRecord()` call, which reads `.orchestrator/session.lock` and
33
+ * confirms the raw `session_id` against this process's own identity before
34
+ * returning anything — under a peer-owned lock it returns `{}` rather than the
35
+ * peer's ids. Duplicating that logic would be the one-fact-two-copies class this
36
+ * repo keeps paying for.
37
+ */
38
+
39
+ import { parseArgs } from 'node:util';
40
+ import { resolve as resolvePath } from 'node:path';
41
+ import { fileURLToPath } from 'node:url';
42
+ import { attributionForRecord, emitEvent } from './lib/events.mjs';
43
+ import { MANIFEST_SESSION_KEYS } from './lib/session-identity/own-session.mjs';
44
+
45
+ const HELP = `Usage: node scripts/wave-scope-binding.mjs [--merge] [--wave N] [--role R] [--repo-root DIR]
46
+
47
+ Print the session-binding keys for a wave-scope.json manifest as one JSON object.
48
+ Empty values are OMITTED; an unbound binding prints {} and emits exactly one
49
+ orchestrator.scope.unbound_manifest event.
50
+
51
+ With --merge, read the DRAFT manifest as one JSON object on stdin and print that
52
+ same manifest with the binding keys merged in (or with them omitted, plus the
53
+ unbound event, when the binding is {}). Every other field is passed through.
54
+
55
+ Options:
56
+ --merge Read the draft manifest on stdin, print it bound.
57
+ --wave <n> Wave number, recorded in the unbound event payload.
58
+ --role <role> Wave role, recorded in the unbound event payload.
59
+ --repo-root <dir> Repo root to resolve the lock and the events log against
60
+ (default: process.cwd()).
61
+ -h, --help Show this help and exit 0.
62
+
63
+ Output:
64
+ stdout — exactly one JSON object. Diagnostics go to stderr.
65
+ `;
66
+
67
+ /**
68
+ * Resolve the manifest binding for `repoRoot`.
69
+ *
70
+ * @param {string} repoRoot
71
+ * @returns {{ session_id?: string, semantic_session_id?: string }} binding with
72
+ * empty values omitted
73
+ */
74
+ export function resolveBinding(repoRoot) {
75
+ const attribution = attributionForRecord(repoRoot) ?? {};
76
+ /** @type {{ session_id?: string, semantic_session_id?: string }} */
77
+ const out = {};
78
+ const session = typeof attribution.session_id === 'string' ? attribution.session_id.trim() : '';
79
+ const semantic = typeof attribution.semantic_session_id === 'string'
80
+ ? attribution.semantic_session_id.trim()
81
+ : '';
82
+ if (session) out.session_id = session;
83
+ if (semantic) out.semantic_session_id = semantic;
84
+ return out;
85
+ }
86
+
87
+ async function main(argv) {
88
+ let parsed;
89
+ try {
90
+ parsed = parseArgs({
91
+ args: argv,
92
+ options: {
93
+ wave: { type: 'string' },
94
+ role: { type: 'string' },
95
+ 'repo-root': { type: 'string' },
96
+ merge: { type: 'boolean' },
97
+ help: { type: 'boolean', short: 'h' },
98
+ },
99
+ allowPositionals: false,
100
+ });
101
+ } catch (error) {
102
+ process.stderr.write(`wave-scope-binding: ${error instanceof Error ? error.message : String(error)}\n`);
103
+ process.stderr.write(HELP);
104
+ process.exitCode = 2;
105
+ return;
106
+ }
107
+
108
+ if (parsed.values.help) {
109
+ process.stdout.write(HELP);
110
+ return;
111
+ }
112
+
113
+ const repoRoot = parsed.values['repo-root'] ?? process.cwd();
114
+ const binding = resolveBinding(repoRoot);
115
+
116
+ if (Object.keys(binding).length === 0) {
117
+ // The fail-closed case, and the whole reason this is a command: an unbound
118
+ // manifest enforces against EVERY session in the checkout, and until now it
119
+ // left no trace at all. Best-effort — the binding is still printed if the
120
+ // append fails, because a broken events log must not stall a wave.
121
+ try {
122
+ await emitEvent(
123
+ 'orchestrator.scope.unbound_manifest',
124
+ {
125
+ // Numeric when it parses as one, so the field matches every other
126
+ // `wave` in the stream; `null` (never `undefined`) when absent, since
127
+ // an undefined `wave` lets emitEvent's correlation envelope fill it
128
+ // from the live manifest — the very artefact this event says is
129
+ // unbound.
130
+ wave: Number.isFinite(Number(parsed.values.wave)) && parsed.values.wave !== undefined
131
+ ? Number(parsed.values.wave)
132
+ : (parsed.values.wave ?? null),
133
+ role: parsed.values.role ?? null,
134
+ reason: 'no-confirmed-session-attribution',
135
+ },
136
+ { repoRoot },
137
+ );
138
+ } catch (error) {
139
+ process.stderr.write(
140
+ `wave-scope-binding: could not record unbound_manifest event: ${error instanceof Error ? error.message : String(error)}\n`,
141
+ );
142
+ }
143
+ }
144
+
145
+ if (!parsed.values.merge) {
146
+ process.stdout.write(`${JSON.stringify(binding)}\n`);
147
+ return;
148
+ }
149
+
150
+ // --merge: the coordinator pipes the draft manifest in and gets the SAME
151
+ // manifest back with the binding merged, instead of hand-copying two keys
152
+ // out of the printed object into the JSON it is about to write (#1207).
153
+ let draftRaw;
154
+ try {
155
+ draftRaw = await readStdin();
156
+ } catch (error) {
157
+ process.stderr.write(
158
+ `wave-scope-binding: could not read the draft manifest from stdin: ${error instanceof Error ? error.message : String(error)}\n`,
159
+ );
160
+ process.exitCode = 1;
161
+ return;
162
+ }
163
+ let draft;
164
+ try {
165
+ draft = JSON.parse(draftRaw);
166
+ } catch {
167
+ process.stderr.write('wave-scope-binding: --merge expects ONE JSON object on stdin\n');
168
+ process.exitCode = 1;
169
+ return;
170
+ }
171
+ if (!draft || typeof draft !== 'object' || Array.isArray(draft)) {
172
+ process.stderr.write('wave-scope-binding: --merge expects ONE JSON object on stdin\n');
173
+ process.exitCode = 1;
174
+ return;
175
+ }
176
+
177
+ // Any binding key already in the draft is dropped first: under an unbound or
178
+ // peer-owned lock the merged manifest must name NOBODY, and a stale key
179
+ // surviving the merge would name somebody. Omitted, never `""` — see above.
180
+ // BOTH spellings are dropped (close-review 2026-09-04, HIGH): a legacy
181
+ // `session`/`semantic_session` pair surviving the merge is still READ by every
182
+ // consumer and would name a foreign session — `foreign` = gates stand down.
183
+ const merged = { ...draft };
184
+ for (const key of [...MANIFEST_SESSION_KEYS.current, ...MANIFEST_SESSION_KEYS.legacy]) delete merged[key];
185
+ // Explicit per-key copy, never Object.assign: `merged` derives from a
186
+ // JSON.parse'd draft, and a `__proto__` payload key would reach the
187
+ // Object.prototype setter through [[Set]] semantics (CWE-1321, semgrep
188
+ // prototype-pollution-object-assign — CI-red on ce6a28aa).
189
+ for (const key of MANIFEST_SESSION_KEYS.current) {
190
+ if (typeof binding[key] === 'string' && binding[key]) merged[key] = binding[key];
191
+ }
192
+ process.stdout.write(`${JSON.stringify(merged)}\n`);
193
+ }
194
+
195
+ /**
196
+ * Read all of stdin as UTF-8. Resolves to `''` when stdin is closed/empty.
197
+ * @returns {Promise<string>}
198
+ */
199
+ function readStdin() {
200
+ return new Promise((resolve, reject) => {
201
+ let buf = '';
202
+ process.stdin.setEncoding('utf8');
203
+ process.stdin.on('data', (chunk) => {
204
+ buf += chunk;
205
+ });
206
+ process.stdin.on('end', () => resolve(buf));
207
+ process.stdin.on('error', reject);
208
+ });
209
+ }
210
+
211
+ // `import.meta.main` is not available on every supported Node — compare argv[1]
212
+ // instead, so importing this module from a test never runs the CLI.
213
+ if (process.argv[1] && fileURLToPath(import.meta.url) === resolvePath(process.argv[1])) {
214
+ await main(process.argv.slice(2));
215
+ }
@@ -28,6 +28,16 @@ Apply these steps in order against the repo root. Stop at the first hit. Never m
28
28
  - Never read both files. Never concatenate, diff, or cross-validate them — the SSOT is whichever the rule selects.
29
29
  - The resolved kind (`claude` | `agents`) is part of the contract. Consumers that report paths in JSON output (e.g., `skills/claude-md-drift-check/checker.mjs`) must surface the resolved path so users on either platform can audit the result.
30
30
 
31
+ ## Interaction with the root `AGENTS.md` this repo now ships
32
+
33
+ Since the cross-harness portable surface landed, this repo carries BOTH files at its root. That does not weaken the rule above — it is what makes the rule safe to hold while still serving foreign readers:
34
+
35
+ - **Our own readers still never read both.** `resolveInstructionFile()` picks exactly one (`CLAUDE.md` wins ties), and every consumer listed below goes through it. Nothing merges, diffs, or cross-validates the two as sources.
36
+ - **The root `AGENTS.md` exists for FOREIGN readers**, not for ours. 7 of 8 surveyed harnesses (Codex CLI, Cursor, Copilot CLI, OpenCode, Amp, Kiro, …) read `AGENTS.md`; only Claude Code reads `CLAUDE.md`, and only Copilot CLI reads both. Without a root `AGENTS.md` this repo's `## Session Config` was unreachable from six of them.
37
+ - **It is byte-identical by construction, and generated.** `scripts/generate-agents-skills.mjs` copies `CLAUDE.md` verbatim; `--check` (wired into `scripts/validate-plugin.mjs`) fails CI on any divergence. **Never edit `AGENTS.md`** — edit `CLAUDE.md` and regenerate. A consumer repo may instead symlink it; both shapes are accepted by the drift gate.
38
+ - **Why a copy and not a symlink here:** `package.json` `files[]` does not publish `CLAUDE.md`, so a symlink would be DANGLING in the npm tarball; and `core.symlinks` defaults to false on Windows without Developer Mode, where git materialises the link as a 10-byte regular file containing the literal text `CLAUDE.md` — a pointer with no Session Config, which is precisely the failure this file guards against.
39
+ - **The invariant is "the two cannot disagree", not "exactly one file exists."** `claude-md-drift-check` Check 7 (`vault-dir-parity`) enforces it: alias-by-construction → parity satisfied; two independent files that diverge → error. Check 9's probe 2a uses the same predicate so a defect in `CLAUDE.md` is never reported twice.
40
+
31
41
  ## Reference implementations
32
42
 
33
43
  ### Bash one-liner (matches `skills/_shared/bootstrap-gate.md` style)