session-orchestrator 5.1.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (484) hide show
  1. package/.agents/skills/architecture/SKILL.md +3 -1
  2. package/.agents/skills/autopilot/SKILL.md +6 -1
  3. package/.agents/skills/autopilot/agents/openai.yaml +5 -0
  4. package/.agents/skills/bootstrap/SKILL.md +7 -1
  5. package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
  6. package/.agents/skills/brainstorm/SKILL.md +8 -1
  7. package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
  8. package/.agents/skills/claude-md-drift-check/SKILL.md +3 -1
  9. package/.agents/skills/close/SKILL.md +21 -0
  10. package/.agents/skills/close/agents/openai.yaml +5 -0
  11. package/.agents/skills/convergence-monitoring/SKILL.md +4 -2
  12. package/.agents/skills/debug/SKILL.md +7 -1
  13. package/.agents/skills/debug/agents/openai.yaml +5 -0
  14. package/.agents/skills/discovery/SKILL.md +7 -2
  15. package/.agents/skills/discovery/agents/openai.yaml +5 -0
  16. package/.agents/skills/dispatcher/SKILL.md +7 -1
  17. package/.agents/skills/dispatcher/agents/openai.yaml +5 -0
  18. package/.agents/skills/docs-orchestrator/SKILL.md +3 -1
  19. package/.agents/skills/ecosystem-health/SKILL.md +3 -1
  20. package/.agents/skills/eli5/SKILL.md +7 -1
  21. package/.agents/skills/eli5/agents/openai.yaml +5 -0
  22. package/.agents/skills/eval/SKILL.md +7 -2
  23. package/.agents/skills/eval/agents/openai.yaml +5 -0
  24. package/.agents/skills/evolve/SKILL.md +8 -3
  25. package/.agents/skills/evolve/agents/openai.yaml +5 -0
  26. package/.agents/skills/frontmatter-guard/SKILL.md +3 -1
  27. package/.agents/skills/gitlab-ops/SKILL.md +3 -1
  28. package/.agents/skills/gitlab-portfolio/SKILL.md +3 -1
  29. package/.agents/skills/go/SKILL.md +22 -0
  30. package/.agents/skills/go/agents/openai.yaml +5 -0
  31. package/.agents/skills/grill/SKILL.md +7 -1
  32. package/.agents/skills/grill/agents/openai.yaml +5 -0
  33. package/.agents/skills/harness-audit/SKILL.md +20 -0
  34. package/.agents/skills/harness-audit/agents/openai.yaml +5 -0
  35. package/.agents/skills/hook-development/SKILL.md +3 -1
  36. package/.agents/skills/mcp-builder/SKILL.md +3 -1
  37. package/.agents/skills/memory-cleanup/SKILL.md +6 -1
  38. package/.agents/skills/memory-cleanup/agents/openai.yaml +5 -0
  39. package/.agents/skills/mode-selector/SKILL.md +3 -1
  40. package/.agents/skills/npm-publish/SKILL.md +4 -2
  41. package/.agents/skills/peekaboo-driver/SKILL.md +3 -1
  42. package/.agents/skills/persona-panel/SKILL.md +6 -1
  43. package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
  44. package/.agents/skills/plan/SKILL.md +8 -2
  45. package/.agents/skills/plan/agents/openai.yaml +5 -0
  46. package/.agents/skills/playwright-driver/SKILL.md +3 -1
  47. package/.agents/skills/portfolio/SKILL.md +21 -0
  48. package/.agents/skills/portfolio/agents/openai.yaml +5 -0
  49. package/.agents/skills/quality-gates/SKILL.md +3 -1
  50. package/.agents/skills/reconcile/SKILL.md +6 -1
  51. package/.agents/skills/reconcile/agents/openai.yaml +5 -0
  52. package/.agents/skills/release/SKILL.md +22 -0
  53. package/.agents/skills/release/agents/openai.yaml +5 -0
  54. package/.agents/skills/remote-offload/SKILL.md +3 -1
  55. package/.agents/skills/repo-audit/SKILL.md +6 -1
  56. package/.agents/skills/repo-audit/agents/openai.yaml +5 -0
  57. package/.agents/skills/session/SKILL.md +21 -0
  58. package/.agents/skills/session/agents/openai.yaml +5 -0
  59. package/.agents/skills/session-end/SKILL.md +3 -1
  60. package/.agents/skills/session-plan/SKILL.md +3 -1
  61. package/.agents/skills/session-start/SKILL.md +3 -1
  62. package/.agents/skills/spinout/SKILL.md +6 -1
  63. package/.agents/skills/spinout/agents/openai.yaml +5 -0
  64. package/.agents/skills/sunset-review/SKILL.md +7 -1
  65. package/.agents/skills/sunset-review/agents/openai.yaml +5 -0
  66. package/.agents/skills/templates-ack/SKILL.md +21 -0
  67. package/.agents/skills/templates-ack/agents/openai.yaml +5 -0
  68. package/.agents/skills/test/SKILL.md +21 -0
  69. package/.agents/skills/test/agents/openai.yaml +5 -0
  70. package/.agents/skills/test-runner/SKILL.md +3 -1
  71. package/.agents/skills/tmux-layout/SKILL.md +3 -1
  72. package/.agents/skills/using-orchestrator/SKILL.md +3 -1
  73. package/.agents/skills/ux-grill/SKILL.md +7 -1
  74. package/.agents/skills/ux-grill/agents/openai.yaml +5 -0
  75. package/.agents/skills/vault-mirror/SKILL.md +3 -1
  76. package/.agents/skills/vault-sync/SKILL.md +3 -1
  77. package/.agents/skills/wave-executor/SKILL.md +3 -1
  78. package/.agents/skills/write-executable-plan/SKILL.md +3 -1
  79. package/.claude-plugin/marketplace.json +1 -1
  80. package/.claude-plugin/plugin.json +1 -1
  81. package/.codex-plugin/plugin.json +4 -4
  82. package/.codex-plugin/skills/autopilot/SKILL.md +5 -4
  83. package/.codex-plugin/skills/bootstrap/SKILL.md +8 -4
  84. package/.codex-plugin/skills/brainstorm/SKILL.md +11 -4
  85. package/.codex-plugin/skills/close/SKILL.md +3 -3
  86. package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -1
  87. package/.codex-plugin/skills/debug/SKILL.md +11 -4
  88. package/.codex-plugin/skills/discovery/SKILL.md +8 -4
  89. package/.codex-plugin/skills/dispatcher/SKILL.md +4 -4
  90. package/.codex-plugin/skills/eli5/SKILL.md +9 -4
  91. package/.codex-plugin/skills/eval/SKILL.md +9 -4
  92. package/.codex-plugin/skills/evolve/SKILL.md +9 -4
  93. package/.codex-plugin/skills/go/SKILL.md +3 -3
  94. package/.codex-plugin/skills/grill/SKILL.md +11 -4
  95. package/.codex-plugin/skills/harness-audit/SKILL.md +4 -3
  96. package/.codex-plugin/skills/memory-cleanup/SKILL.md +9 -4
  97. package/.codex-plugin/skills/npm-publish/SKILL.md +1 -1
  98. package/.codex-plugin/skills/persona-panel/SKILL.md +5 -5
  99. package/.codex-plugin/skills/plan/SKILL.md +8 -4
  100. package/.codex-plugin/skills/portfolio/SKILL.md +3 -3
  101. package/.codex-plugin/skills/reconcile/SKILL.md +9 -4
  102. package/.codex-plugin/skills/release/SKILL.md +3 -3
  103. package/.codex-plugin/skills/repo-audit/SKILL.md +6 -4
  104. package/.codex-plugin/skills/session/SKILL.md +1 -1
  105. package/.codex-plugin/skills/spinout/SKILL.md +4 -4
  106. package/.codex-plugin/skills/sunset-review/SKILL.md +5 -4
  107. package/.codex-plugin/skills/test/SKILL.md +3 -3
  108. package/.codex-plugin/skills/ux-grill/SKILL.md +11 -4
  109. package/.cursor/commands/autopilot.md +4 -4
  110. package/.cursor/commands/bootstrap.md +5 -4
  111. package/.cursor/commands/brainstorm.md +5 -4
  112. package/.cursor/commands/close.md +4 -3
  113. package/.cursor/commands/debug.md +4 -4
  114. package/.cursor/commands/discovery.md +4 -4
  115. package/.cursor/commands/dispatcher.md +4 -4
  116. package/.cursor/commands/eli5.md +4 -4
  117. package/.cursor/commands/eval.md +4 -4
  118. package/.cursor/commands/evolve.md +4 -4
  119. package/.cursor/commands/go.md +4 -3
  120. package/.cursor/commands/grill.md +4 -4
  121. package/.cursor/commands/harness-audit.md +3 -3
  122. package/.cursor/commands/memory-cleanup.md +4 -4
  123. package/.cursor/commands/persona-panel.md +4 -4
  124. package/.cursor/commands/plan.md +5 -4
  125. package/.cursor/commands/portfolio.md +3 -3
  126. package/.cursor/commands/reconcile.md +4 -4
  127. package/.cursor/commands/release.md +4 -3
  128. package/.cursor/commands/repo-audit.md +4 -4
  129. package/.cursor/commands/session.md +1 -1
  130. package/.cursor/commands/spinout.md +4 -4
  131. package/.cursor/commands/sunset-review.md +4 -4
  132. package/.cursor/commands/test.md +3 -3
  133. package/.cursor/commands/ux-grill.md +4 -4
  134. package/.cursor/rules/000-session-orchestrator.mdc +0 -2
  135. package/.cursor/rules/010-session-workflow.mdc +2 -2
  136. package/.cursor/rules/050-plan.mdc +1 -1
  137. package/.cursor/skills/bootstrap/SKILL.md +1 -0
  138. package/.cursor/skills/close/SKILL.md +13 -0
  139. package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
  140. package/.cursor/skills/debug/SKILL.md +0 -1
  141. package/.cursor/skills/discovery/SKILL.md +0 -1
  142. package/.cursor/skills/dispatcher/SKILL.md +0 -1
  143. package/.cursor/skills/eli5/SKILL.md +0 -1
  144. package/.cursor/skills/eval/SKILL.md +1 -1
  145. package/.cursor/skills/evolve/SKILL.md +0 -1
  146. package/.cursor/skills/go/SKILL.md +13 -0
  147. package/.cursor/skills/grill/SKILL.md +0 -1
  148. package/.cursor/skills/harness-audit/SKILL.md +12 -0
  149. package/.cursor/skills/npm-publish/SKILL.md +1 -0
  150. package/.cursor/skills/portfolio/SKILL.md +12 -0
  151. package/.cursor/skills/release/SKILL.md +13 -0
  152. package/.cursor/skills/repo-audit/SKILL.md +0 -1
  153. package/.cursor/skills/sunset-review/SKILL.md +0 -1
  154. package/.cursor/skills/test/SKILL.md +12 -0
  155. package/.cursor/skills/ux-grill/SKILL.md +0 -1
  156. package/.cursor-plugin/plugin.json +1 -1
  157. package/.orchestrator/policy/blocked-commands.json +13 -4
  158. package/AGENTS.md +3 -2
  159. package/CHANGELOG.md +197 -0
  160. package/README.md +11 -9
  161. package/SECURITY.md +12 -0
  162. package/agents/dialectic-deriver.md +13 -10
  163. package/agents/eval-judge.md +67 -45
  164. package/agents/skill-applied-judge.md +34 -19
  165. package/commands/session.md +17 -3
  166. package/docs/baseline.md +12 -6
  167. package/docs/ci-setup.md +53 -0
  168. package/docs/codex-setup.md +15 -3
  169. package/docs/components.md +13 -6
  170. package/docs/events-schema.md +59 -9
  171. package/docs/install.md +16 -0
  172. package/docs/persona-panel.md +1 -1
  173. package/docs/pi-setup.md +1 -1
  174. package/docs/rule-authoring.md +135 -14
  175. package/docs/scope-collision-guard.md +2 -0
  176. package/docs/session-config-reference.md +106 -11
  177. package/docs/session-config-template.md +31 -2
  178. package/docs/telemetry.md +2 -0
  179. package/hooks/_lib/hook-import-set.json +125 -8
  180. package/hooks/_lib/subagent-paths.mjs +15 -0
  181. package/hooks/_lib/subagent-transcript.mjs +582 -31
  182. package/hooks/_lib/vcs-create-matcher.mjs +217 -62
  183. package/hooks/config-protection.mjs +11 -3
  184. package/hooks/cwd-change-restore.mjs +11 -3
  185. package/hooks/enforce-commands.mjs +70 -23
  186. package/hooks/enforce-scope.mjs +143 -33
  187. package/hooks/hooks-codex.json +1 -1
  188. package/hooks/hooks.json +1 -1
  189. package/hooks/loop-guard.mjs +11 -3
  190. package/hooks/on-session-end.mjs +72 -25
  191. package/hooks/on-session-start.mjs +48 -11
  192. package/hooks/on-stop.mjs +211 -23
  193. package/hooks/operator-steer.mjs +11 -3
  194. package/hooks/post-bash-issue-budget-refund.mjs +18 -8
  195. package/hooks/post-bash-write-verify.mjs +6 -2
  196. package/hooks/post-edit-import-probe.mjs +17 -9
  197. package/hooks/post-edit-validate.mjs +13 -5
  198. package/hooks/post-subagent-discovery-validator.mjs +98 -13
  199. package/hooks/post-tool-batch-wave-signal.mjs +200 -38
  200. package/hooks/post-tool-failure-corrective-context.mjs +11 -5
  201. package/hooks/post-tooluse-frontend-slop.mjs +10 -4
  202. package/hooks/pre-auq-clarity.mjs +18 -2
  203. package/hooks/pre-bash-destructive-guard.mjs +80 -9
  204. package/hooks/pre-bash-issue-budget.mjs +119 -28
  205. package/hooks/pre-bash-memory-propose-audit.mjs +86 -54
  206. package/hooks/pre-bash-sessions-ledger-guard.mjs +391 -20
  207. package/hooks/pre-bash-staging-fence.mjs +335 -31
  208. package/hooks/pre-bash-templates-first.mjs +19 -14
  209. package/hooks/pre-task-scope-disjoint.mjs +385 -5
  210. package/hooks/skill-invocation-telemetry.mjs +2 -1
  211. package/hooks/subagent-telemetry.mjs +15 -19
  212. package/hooks/wave-scope-commit-guard.mjs +197 -100
  213. package/monitors/monitors.json +1 -1
  214. package/output-styles/wave-summary.md +1 -1
  215. package/package.json +2 -1
  216. package/pi/prompts/autopilot.md +3 -3
  217. package/pi/prompts/bootstrap.md +3 -3
  218. package/pi/prompts/brainstorm.md +3 -3
  219. package/pi/prompts/close.md +2 -2
  220. package/pi/prompts/debug.md +3 -3
  221. package/pi/prompts/discovery.md +3 -3
  222. package/pi/prompts/dispatcher.md +3 -3
  223. package/pi/prompts/eli5.md +3 -3
  224. package/pi/prompts/eval.md +3 -3
  225. package/pi/prompts/evolve.md +3 -3
  226. package/pi/prompts/go.md +2 -2
  227. package/pi/prompts/grill.md +3 -3
  228. package/pi/prompts/harness-audit.md +2 -3
  229. package/pi/prompts/memory-cleanup.md +3 -3
  230. package/pi/prompts/persona-panel.md +3 -3
  231. package/pi/prompts/plan.md +3 -3
  232. package/pi/prompts/portfolio.md +2 -2
  233. package/pi/prompts/reconcile.md +3 -3
  234. package/pi/prompts/release.md +3 -3
  235. package/pi/prompts/repo-audit.md +3 -4
  236. package/pi/prompts/session.md +2 -2
  237. package/pi/prompts/spinout.md +3 -3
  238. package/pi/prompts/sunset-review.md +3 -3
  239. package/pi/prompts/templates-ack.md +1 -1
  240. package/pi/prompts/test.md +3 -3
  241. package/pi/prompts/ux-grill.md +3 -3
  242. package/rules/README.md +1 -1
  243. package/rules/opt-in-domain/prompt-caching.md +1 -1
  244. package/rules/opt-in-stack/backend-data.md +1 -1
  245. package/rules/opt-in-stack/backend.md +3 -3
  246. package/rules/opt-in-stack/frontend.md +1 -1
  247. package/rules/opt-in-stack/security-web.md +3 -3
  248. package/rules/opt-in-stack/swift.md +1 -1
  249. package/scripts/archive-closed-prds.mjs +2 -2
  250. package/scripts/auq-audit.mjs +2 -3
  251. package/scripts/autopilot.mjs +23 -2
  252. package/scripts/backfill-abandoned-sessions.mjs +171 -15
  253. package/scripts/backfill-evidence-digest.mjs +2 -1
  254. package/scripts/backfill-learnings-from-vault.mjs +2 -2
  255. package/scripts/check-package-manager.mjs +2 -2
  256. package/scripts/check-sessions-integrity.mjs +300 -0
  257. package/scripts/ci/assert-vitest-green.mjs +2 -1
  258. package/scripts/dialectic-deriver.mjs +50 -13
  259. package/scripts/emit-session.mjs +77 -32
  260. package/scripts/eval-session.mjs +65 -3
  261. package/scripts/export-hw-learnings.mjs +2 -1
  262. package/scripts/express-path.mjs +1 -1
  263. package/scripts/gc-stale-worktrees.mjs +2 -1
  264. package/scripts/generate-agents-skills.mjs +102 -29
  265. package/scripts/generate-codex-skills.mjs +48 -4
  266. package/scripts/generate-cursor-adapter.mjs +220 -11
  267. package/scripts/generate-hook-import-set.mjs +12 -27
  268. package/scripts/generate-pi-prompts.mjs +183 -13
  269. package/scripts/github-protection-audit.mjs +2 -3
  270. package/scripts/lib/agent-frontmatter.mjs +23 -1
  271. package/scripts/lib/agent-status.mjs +2 -31
  272. package/scripts/lib/auq/clarity.mjs +10 -2
  273. package/scripts/lib/auq/parse.mjs +12 -31
  274. package/scripts/lib/auq/schema.mjs +56 -41
  275. package/scripts/lib/auto-dialectic.mjs +304 -15
  276. package/scripts/lib/autopilot/flags.mjs +12 -1
  277. package/scripts/lib/autopilot/kill-switches.mjs +6 -3
  278. package/scripts/lib/autopilot/loop.mjs +14 -1
  279. package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
  280. package/scripts/lib/ci-status-banner.mjs +376 -16
  281. package/scripts/lib/claude-md-budget-lint.mjs +2 -5
  282. package/scripts/lib/command-blocker.mjs +408 -33
  283. package/scripts/lib/config/dialectic.mjs +12 -3
  284. package/scripts/lib/config/drift-check.mjs +19 -0
  285. package/scripts/lib/config/gate.mjs +74 -0
  286. package/scripts/lib/config/reaper.mjs +162 -0
  287. package/scripts/lib/config.mjs +14 -0
  288. package/scripts/lib/convergence-monitor.mjs +76 -13
  289. package/scripts/lib/cursor-hook-bridge.mjs +2 -2
  290. package/scripts/lib/description-surface.mjs +2 -5
  291. package/scripts/lib/dispatcher/cli.mjs +2 -1
  292. package/scripts/lib/ecosystem-health.mjs +11 -0
  293. package/scripts/lib/ecosystem-wizard.mjs +2 -1
  294. package/scripts/lib/eval/engine.mjs +421 -53
  295. package/scripts/lib/eval/judge.mjs +463 -40
  296. package/scripts/lib/eval/schema.mjs +10 -1
  297. package/scripts/lib/events-rotation.mjs +221 -25
  298. package/scripts/lib/events-schema.mjs +114 -0
  299. package/scripts/lib/events.mjs +524 -5
  300. package/scripts/lib/fetch-baseline.mjs +3 -8
  301. package/scripts/lib/frontmatter-guard.mjs +21 -10
  302. package/scripts/lib/gates/gate-baseline.mjs +27 -2
  303. package/scripts/lib/gates/gate-full.mjs +28 -3
  304. package/scripts/lib/gates/gate-helpers.mjs +243 -21
  305. package/scripts/lib/gates/gate-incremental.mjs +28 -3
  306. package/scripts/lib/gates/gate-per-file.mjs +27 -2
  307. package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +2 -1
  308. package/scripts/lib/gitlab-portfolio/cli.mjs +2 -1
  309. package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
  310. package/scripts/lib/instruction-budget-guard.mjs +332 -50
  311. package/scripts/lib/io.mjs +42 -8
  312. package/scripts/lib/is-main-module.mjs +82 -0
  313. package/scripts/lib/issue-close-strip-labels.mjs +207 -49
  314. package/scripts/lib/js-mask.mjs +197 -0
  315. package/scripts/lib/learnings/evolve-telemetry.mjs +11 -7
  316. package/scripts/lib/locks/index.mjs +32 -25
  317. package/scripts/lib/maintenance-due-banner.mjs +122 -91
  318. package/scripts/lib/orphan-reaper.mjs +1588 -0
  319. package/scripts/lib/peer-cards/merger.mjs +48 -10
  320. package/scripts/lib/peer-cards/reader.mjs +78 -2
  321. package/scripts/lib/peer-discovery.mjs +2 -5
  322. package/scripts/lib/playwright-driver/runner.mjs +2 -1
  323. package/scripts/lib/process-group.mjs +899 -0
  324. package/scripts/lib/quality-gate.mjs +107 -28
  325. package/scripts/lib/reconcile/backlog.mjs +368 -0
  326. package/scripts/lib/reconcile/engine.mjs +55 -188
  327. package/scripts/lib/reconcile/rule-expiry-sweep.mjs +884 -0
  328. package/scripts/lib/reconcile/sanitize.mjs +69 -3
  329. package/scripts/lib/reconcile-nudge-banner.mjs +138 -45
  330. package/scripts/lib/resource-probe/parsers.mjs +31 -0
  331. package/scripts/lib/rule-loader.mjs +41 -12
  332. package/scripts/lib/rules-sync.mjs +2 -5
  333. package/scripts/lib/scope-echo.mjs +429 -7
  334. package/scripts/lib/scope-gate.mjs +605 -1
  335. package/scripts/lib/session-close-backfill.mjs +91 -12
  336. package/scripts/lib/session-id.mjs +9 -20
  337. package/scripts/lib/session-invocation.mjs +20 -0
  338. package/scripts/lib/session-schema/constants.mjs +30 -2
  339. package/scripts/lib/session-schema/normalizer.mjs +56 -4
  340. package/scripts/lib/session-schema.mjs +8 -3
  341. package/scripts/lib/session-start-probes.mjs +95 -10
  342. package/scripts/lib/sessions-canonical.mjs +23 -0
  343. package/scripts/lib/sessions-integrity-banner.mjs +7 -1
  344. package/scripts/lib/sessions-staleness-banner.mjs +193 -51
  345. package/scripts/lib/skill-evidence-window.mjs +891 -0
  346. package/scripts/lib/skill-evolution/candidate-intake.mjs +133 -12
  347. package/scripts/lib/skill-evolution/engine.mjs +18 -9
  348. package/scripts/lib/skill-judge.mjs +45 -3
  349. package/scripts/lib/state-md.mjs +84 -3
  350. package/scripts/lib/sunset/walker.mjs +31 -4
  351. package/scripts/lib/tail-window.mjs +56 -0
  352. package/scripts/lib/telemetry/schema.mjs +30 -0
  353. package/scripts/lib/telemetry/sync.mjs +61 -6
  354. package/scripts/lib/telemetry-flush-health-banner.mjs +4 -22
  355. package/scripts/lib/test-runner/issue-reconcile.mjs +48 -16
  356. package/scripts/lib/tests-src-ratio.mjs +2 -6
  357. package/scripts/lib/tmux-layout/telemetry-stats.mjs +74 -14
  358. package/scripts/lib/user-invocable-skills.mjs +205 -0
  359. package/scripts/lib/ux-grill/reconcile.mjs +48 -22
  360. package/scripts/lib/validate/check-agents-skills.mjs +26 -15
  361. package/scripts/lib/validate/check-banner-parity.mjs +2 -2
  362. package/scripts/lib/validate/check-cursor-adapter.mjs +3 -2
  363. package/scripts/lib/validate/check-dead-bridge.mjs +2 -2
  364. package/scripts/lib/validate/check-doc-cli-commands.mjs +2 -2
  365. package/scripts/lib/validate/check-entry-guard.mjs +329 -0
  366. package/scripts/lib/validate/check-guard-requires-parity.mjs +2 -2
  367. package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
  368. package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +2 -2
  369. package/scripts/lib/validate/check-learning-provenance.mjs +2 -2
  370. package/scripts/lib/validate/check-pi-prompts.mjs +1 -0
  371. package/scripts/lib/validate/check-rules.mjs +7 -5
  372. package/scripts/lib/validate/check-skill-links.mjs +35 -6
  373. package/scripts/lib/validate/check-skill-script-paths.mjs +241 -29
  374. package/scripts/lib/validate/check-test-git-config-target.mjs +26 -36
  375. package/scripts/lib/validate/check-unicode-safety.mjs +2 -2
  376. package/scripts/lib/validate/check-untracked-test-deps.mjs +9 -104
  377. package/scripts/lib/validate/check-unwired-features.mjs +220 -33
  378. package/scripts/lib/validate/check-validator-registration.mjs +36 -12
  379. package/scripts/lib/validate/check-vcs-repo-flag.mjs +2 -2
  380. package/scripts/lib/validate/confidential-names.mjs +10 -0
  381. package/scripts/lib/validate-vendored-rules.mjs +39 -12
  382. package/scripts/lib/vault-mirror/namespace.mjs +46 -8
  383. package/scripts/lib/vault-mirror/process.mjs +10 -3
  384. package/scripts/lib/vault-mirror/render-sessions.mjs +12 -2
  385. package/scripts/lib/vault-status/narrative-mirror.mjs +31 -7
  386. package/scripts/lib/vault-yaml.mjs +118 -0
  387. package/scripts/lib/wave-transcript-tail.mjs +2 -2
  388. package/scripts/lib/worktree/lifecycle.mjs +153 -1
  389. package/scripts/lock-reaper.mjs +2 -1
  390. package/scripts/materialize-wave-scope.mjs +87 -4
  391. package/scripts/migrate-sessions-jsonl.mjs +2 -1
  392. package/scripts/migrate-vault-paths.mjs +2 -3
  393. package/scripts/release-session-lock.mjs +305 -0
  394. package/scripts/release.mjs +109 -39
  395. package/scripts/relocate-vault-corpus.mjs +2 -3
  396. package/scripts/repair-invalid-sessions.mjs +2 -2
  397. package/scripts/resolve-session-invocation.mjs +59 -0
  398. package/scripts/run-quality-gate.mjs +156 -17
  399. package/scripts/session-shape.mjs +2 -2
  400. package/scripts/site-numbers.mjs +35 -11
  401. package/scripts/sweep-expired-rules.mjs +227 -0
  402. package/scripts/validate-plugin.mjs +21 -0
  403. package/scripts/validate-wave-scope.mjs +32 -105
  404. package/scripts/vault-consolidate.mjs +2 -2
  405. package/scripts/vault-mirror.mjs +11 -4
  406. package/scripts/wave-scope-binding.mjs +2 -3
  407. package/skills/_shared/bootstrap-gate.md +1 -1
  408. package/skills/_shared/monitor-patterns.md +1 -1
  409. package/skills/_shared/platform-tools.md +23 -11
  410. package/skills/_shared/research-evidence.md +53 -0
  411. package/skills/_shared/state-ownership.md +3 -0
  412. package/skills/autopilot/SKILL.md +80 -11
  413. package/skills/bootstrap/SKILL.md +51 -1
  414. package/skills/brainstorm/SKILL.md +16 -0
  415. package/skills/claude-md-drift-check/SKILL.md +1 -1
  416. package/skills/claude-md-drift-check/checker.mjs +49 -11
  417. package/{commands/close.md → skills/close/SKILL.md} +9 -3
  418. package/skills/convergence-monitoring/README.md +8 -1
  419. package/skills/convergence-monitoring/SIGNALS.md +50 -6
  420. package/skills/convergence-monitoring/SKILL.md +15 -6
  421. package/skills/debug/SKILL.md +10 -0
  422. package/skills/discovery/SKILL.md +24 -1
  423. package/skills/discovery/probes-session.md +2 -2
  424. package/skills/dispatcher/SKILL.md +38 -7
  425. package/skills/eli5/SKILL.md +11 -0
  426. package/skills/eval/SKILL.md +52 -23
  427. package/skills/eval/rubric-v1.md +1 -0
  428. package/skills/eval/rubric-v2.md +457 -0
  429. package/skills/evolve/SKILL.md +9 -2
  430. package/skills/evolve/references/evolve-dialectic-mode.md +46 -25
  431. package/skills/gitlab-ops/SKILL.md +3 -2
  432. package/{commands/go.md → skills/go/SKILL.md} +9 -1
  433. package/skills/grill/SKILL.md +19 -0
  434. package/{commands/harness-audit.md → skills/harness-audit/SKILL.md} +7 -2
  435. package/skills/hook-development/SKILL.md +46 -41
  436. package/skills/memory-cleanup/SKILL.md +7 -0
  437. package/skills/npm-publish/SKILL.md +2 -2
  438. package/skills/persona-panel/SKILL.md +56 -1
  439. package/skills/persona-panel/persona-format.md +1 -1
  440. package/skills/plan/SKILL.md +28 -1
  441. package/{commands/portfolio.md → skills/portfolio/SKILL.md} +8 -2
  442. package/skills/reconcile/SKILL.md +21 -0
  443. package/{commands/release.md → skills/release/SKILL.md} +16 -2
  444. package/skills/repo-audit/SKILL.md +7 -0
  445. package/skills/session-end/SKILL.md +13 -16
  446. package/skills/session-end/discovery-scan.md +1 -1
  447. package/skills/session-end/phase-3-6-tail.md +55 -9
  448. package/skills/session-end/plan-verification.md +2 -2
  449. package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
  450. package/skills/session-end/session-metrics-write.md +10 -0
  451. package/skills/session-plan/SKILL.md +18 -6
  452. package/skills/session-plan/references/session-plan-task-classification.md +2 -2
  453. package/skills/session-start/SKILL.md +5 -4
  454. package/skills/session-start/phase-8-5-express-path.md +6 -6
  455. package/skills/session-start/references/phase-1-5-session-continuity.md +1 -1
  456. package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +1 -1
  457. package/skills/session-start/references/phase-4-ssot-environment-check.md +6 -4
  458. package/skills/spinout/SKILL.md +12 -1
  459. package/skills/sunset-review/SKILL.md +13 -0
  460. package/{commands/test.md → skills/test/SKILL.md} +10 -4
  461. package/skills/ux-grill/SKILL.md +20 -2
  462. package/skills/wave-executor/SKILL.md +14 -7
  463. package/skills/wave-executor/circuit-breaker.md +2 -0
  464. package/skills/wave-executor/references/wave-executor-state-init.md +18 -4
  465. package/skills/wave-executor/references/wave-loop-dispatch.md +5 -2
  466. package/skills/wave-executor/references/wave-loop-review.md +17 -1
  467. package/commands/autopilot.md +0 -80
  468. package/commands/bootstrap.md +0 -56
  469. package/commands/brainstorm.md +0 -48
  470. package/commands/debug.md +0 -36
  471. package/commands/discovery.md +0 -32
  472. package/commands/dispatcher.md +0 -59
  473. package/commands/eli5.md +0 -33
  474. package/commands/eval.md +0 -28
  475. package/commands/evolve.md +0 -10
  476. package/commands/grill.md +0 -45
  477. package/commands/memory-cleanup.md +0 -26
  478. package/commands/persona-panel.md +0 -121
  479. package/commands/plan.md +0 -15
  480. package/commands/reconcile.md +0 -23
  481. package/commands/repo-audit.md +0 -24
  482. package/commands/spinout.md +0 -15
  483. package/commands/sunset-review.md +0 -27
  484. package/commands/ux-grill.md +0 -51
@@ -60,9 +60,9 @@
60
60
  // Order and duplicates are irrelevant (a glob list is matched
61
61
  // any-of), so the comparison is over the sorted unique set.
62
62
  // NOT flagged: `paths:` alone. It is the form the native
63
- // documentation prescribes and the form the primary downstream
64
- // consumer uses exclusively (projects-baseline: 26 rule files, all
65
- // `paths:`, 0 `globs:` — rule-loader.mjs module doc). The
63
+ // documentation prescribes, and the primary downstream consumer
64
+ // (projects-baseline) carries `paths:` on every scoped rule
65
+ // measured count: docs/baseline.md. The
66
66
  // `globs:`-is-canonical preference for VENDORED rules is a separate,
67
67
  // warn-level concern already owned by
68
68
  // scripts/lib/validate-vendored-rules.mjs (~:289, issue #742) and is
@@ -330,8 +330,10 @@ for (const name of mdFiles.sort()) {
330
330
  '("rules without a paths field are loaded unconditionally and apply to all files" — ' +
331
331
  'code.claude.com/docs/en/memory § Path-specific rules). `globs:` is the Cursor field name, so this rule ' +
332
332
  'is scoped for rule-loader.mjs and Cursor but loads ALWAYS-ON in Claude Code — a silent instruction-budget ' +
333
- 'failure (#1108). Add a paths: key carrying the same patterns as globs: (keep globs: it stays the ' +
334
- 'canonical form for vendored rules, validate-vendored-rules.mjs #742).',
333
+ 'failure (#1108). Add a paths: key carrying the same patterns as globs:. Under .claude/rules/ paths: is ' +
334
+ 'the canonical scope key (docs/rule-authoring.md) — keep globs: alongside it only if this rule is ALSO ' +
335
+ 'vendored out through rules/, where globs: is the vendored form (#742); otherwise paths: alone is enough ' +
336
+ 'and globs: can be dropped.',
335
337
  );
336
338
  } else if (!parity.ok && parity.kind === 'divergent') {
337
339
  fail(
@@ -20,7 +20,7 @@
20
20
  * citations, not intra-surface markdown links. Different predicate, different blind spot.
21
21
  *
22
22
  * WHAT IT CHECKS
23
- * For every `.md` under the scanned surfaces, every inline link `[text](target)` whose target is
23
+ * For every `.md`/`.mdc` under the scanned surfaces, every inline link `[text](target)` whose target is
24
24
  * relative (not http(s):, not mailto:, not `#anchor`, not an absolute path) must exist on disk,
25
25
  * resolved against the LINKING FILE's own directory. A `#fragment` suffix is stripped before the
26
26
  * existence check — anchors are out of scope (no heading index here); the path half is not.
@@ -42,6 +42,7 @@
42
42
 
43
43
  import { readFileSync, readdirSync, statSync } from 'node:fs';
44
44
  import { join, dirname, resolve, relative, sep } from 'node:path';
45
+ import { isMainModule } from '../is-main-module.mjs';
45
46
 
46
47
  /**
47
48
  * Surfaces whose markdown is instruction, i.e. read and acted on.
@@ -52,8 +53,36 @@ import { join, dirname, resolve, relative, sep } from 'node:path';
52
53
  * `-/issues/N` renderer links now carved out by `SKIP_TARGET`, two of them genuinely dangling
53
54
  * links to PRDs that had been archived to the private Meta-Vault. Both classes were resolved
54
55
  * before the widening, so the widening itself lands at 0 findings.
56
+ *
57
+ * `.cursor/rules` joined in the 3ebf0e9d fix-pass, with its yield stated honestly: that surface
58
+ * ships in the npm tarball and is symlinked into consumer repos by `scripts/cursor-install.mjs`,
59
+ * and it carried two citations of `commands/{go,close}.md` after those files were folded into
60
+ * their skills. Measured 2026-09-17: `.cursor/rules/*.mdc` contain ZERO `[text](target)` links
61
+ * (`grep -rnoE '\[[^]]*\]\([^)]+\)' .cursor/rules/` → no matches), so the widening adds 9 files
62
+ * and 0 checked links today and would NOT by itself have caught that defect — those citations are
63
+ * INLINE-CODE (`` `commands/go.md` ``), which this checker deliberately skips (see DELIBERATE
64
+ * NON-CHECKS). It is a forward guard for the day a real link is authored there, not the catcher
65
+ * for the backticked-citation class.
66
+ *
67
+ * That last clause read "and that class has no gate in this repo" until #1384 P3, when it stopped
68
+ * being true: `check-skill-script-paths.mjs` — which DOES judge complete inline-code spans against
69
+ * `MARKDOWN_CITATION_RE` — took the same two roots and `.mdc` into its own `SCAN_DIRS`, and is
70
+ * blocking via `scripts/validate-plugin.mjs`. Measured 2026-09-18: its corpus went 295 → 329 files
71
+ * and it reported the `commands/{go,close}.md` class of defect (4 blocking findings on first run).
72
+ * So the division of labour is: LINKS here, backticked CITATIONS there — neither surface is
73
+ * ungated.
74
+ */
75
+ export const SCAN_DIRS = Object.freeze(['skills', 'commands', 'agents', '.claude/rules', 'docs', '.cursor/rules']);
76
+
77
+ /**
78
+ * File extensions this checker treats as instruction markdown.
79
+ *
80
+ * `.mdc` is Cursor's own rule extension and is the ONLY reason `.cursor/rules`
81
+ * is scannable at all: every file there is `<nnn>-<name>.mdc`, so a `.md`-only
82
+ * filter would have added the directory to {@link SCAN_DIRS} and enumerated
83
+ * zero files — a widening that looks live and checks nothing.
55
84
  */
56
- export const SCAN_DIRS = Object.freeze(['skills', 'commands', 'agents', '.claude/rules', 'docs']);
85
+ export const MD_EXTENSIONS = Object.freeze(['.md', '.mdc']);
57
86
 
58
87
  /** Path segments that end the walk: vendored or machine-owned trees, never instruction. */
59
88
  export const PRUNE_DIRS = new Set(['node_modules', '.git', 'coverage', 'dist', '.pnpm']);
@@ -108,8 +137,8 @@ function fencedLineNumbers(text) {
108
137
  * and the check would report clean on the tree that carries the defect. This repo has the incident
109
138
  * on record (`.claude/rules/measurement-discipline.md` § "A `git grep` drift sweep cannot see
110
139
  * untracked files": a release sweep passed, then failed after the commit, from the same working
111
- * tree with no edit in between). The cost of the filesystem walk is that a gitignored `.md` under
112
- * these four directories would also be checked — there are none, and one would be a finding worth
140
+ * tree with no edit in between). The cost of the filesystem walk is that a gitignored markdown file
141
+ * under these directories would also be checked — there are none, and one would be a finding worth
113
142
  * seeing anyway.
114
143
  *
115
144
  * @param {string} repoRoot
@@ -125,7 +154,7 @@ export function listMarkdown(repoRoot, { dirs = SCAN_DIRS } = {}) {
125
154
  let entries;
126
155
  try { entries = readdirSync(abs, { recursive: true, withFileTypes: true }); } catch { continue; }
127
156
  for (const e of entries) {
128
- if (!e.isFile() || !e.name.endsWith('.md')) continue;
157
+ if (!e.isFile() || !MD_EXTENSIONS.some((ext) => e.name.endsWith(ext))) continue;
129
158
  // parentPath is absolute; make the record repo-relative with POSIX separators.
130
159
  const rel = relative(repoRoot, join(e.parentPath ?? abs, e.name));
131
160
  const posix = sep === '/' ? rel : rel.split(sep).join('/');
@@ -190,4 +219,4 @@ function main() {
190
219
  process.stdout.write(` PASS: ${checked} relative link(s) in ${files} markdown file(s) resolve\n`);
191
220
  }
192
221
 
193
- if (import.meta.url === `file://${process.argv[1]}`) main();
222
+ if (isMainModule(import.meta.url)) main();
@@ -11,6 +11,16 @@
11
11
  * #1241 adds blocking repo-rooted `.md` paths quoted as complete inline-code
12
12
  * spans, with the same annotations and fence handling. Scan roots stay fixed.
13
13
  *
14
+ * ## The runtime-artefact carve-out needs BOTH halves
15
+ *
16
+ * A missing citation is excused as a runtime artefact only when it sits under
17
+ * a harness STATE root ({@link RUNTIME_STATE_PREFIXES}) *and* git ignores it.
18
+ * The git half alone is not the claim it looks like: `.gitignore` also
19
+ * declines to track real local documents (`docs/specs/`, `docs/_private/`) and
20
+ * build output (`coverage/`, `node_modules/`), so "git ignores it" would have
21
+ * silenced a typo in a `docs/specs/…` citation forever — in the very check
22
+ * whose subject is "ships in the tarball, target missing".
23
+ *
14
24
  * ## Why
15
25
  *
16
26
  * Prose is not executed. A skill body that tells the coordinator to run
@@ -98,14 +108,60 @@
98
108
  * @module scripts/lib/validate/check-skill-script-paths
99
109
  */
100
110
 
111
+ import { execFileSync } from 'node:child_process';
101
112
  import { existsSync, readFileSync } from 'node:fs';
102
113
  import path from 'node:path';
103
- import { pathToFileURL } from 'node:url';
104
114
  import { enumerateRepoFiles } from './enumerate-repo-files.mjs';
105
115
  import { forEachLine } from './markdown-fences.mjs';
116
+ import { isMainModule } from '../is-main-module.mjs';
106
117
 
107
- /** Documentation roots whose prose is treated as a claim about the repo. */
108
- export const SCAN_DIRS = Object.freeze(['skills', 'commands', 'agents', 'docs']);
118
+ /**
119
+ * Documentation roots whose prose is treated as a claim about the repo.
120
+ *
121
+ * `.claude/rules` and `.cursor/rules` joined in #1384 P3. Both are instruction
122
+ * corpora loaded by every session, and both cite repo paths in backticks with
123
+ * exactly the grammar {@link MARKDOWN_CITATION_RE} already judges — but until
124
+ * this widening neither had a gate. The real incident:
125
+ * `.cursor/rules/010-session-workflow.mdc` kept citing `commands/go.md` and
126
+ * `commands/close.md` after `3ebf0e9d` folded those files into their skills,
127
+ * and that surface SHIPS (npm tarball, symlinked into consumer repos by
128
+ * `scripts/cursor-install.mjs`). Same widening, mirrored in
129
+ * `check-skill-links.mjs` § SCAN_DIRS.
130
+ *
131
+ * `rules` and `output-styles` joined in #1384 P3 for the same reason one step
132
+ * further out: both are in `package.json` `files[]`, so both SHIP to every
133
+ * consumer, and neither had a gate. The census that motivated it (whole-repo
134
+ * probe, 2026-09-18 @ 20a4cbff) found 6 dangling citations across them —
135
+ * `output-styles/wave-summary.md` cited `hooks/on-stop.sh` for a file that has
136
+ * been `.mjs` for two migrations, and `rules/opt-in-stack/*` cited
137
+ * consumer-side example paths with no marker.
138
+ *
139
+ * `CHANGELOG.md` stays OUT, and root files are not a scan root at all: a
140
+ * changelog cites the paths a release TOUCHED, so a path deleted afterwards is
141
+ * still a correct historical statement — 88 such hits, all correct by
142
+ * construction. Annotating them would be noise on every future entry.
143
+ */
144
+ export const SCAN_DIRS = Object.freeze([
145
+ 'skills',
146
+ 'commands',
147
+ 'agents',
148
+ 'docs',
149
+ 'rules',
150
+ 'output-styles',
151
+ '.claude/rules',
152
+ '.cursor/rules',
153
+ ]);
154
+
155
+ /**
156
+ * Extensions treated as instruction markdown.
157
+ *
158
+ * `.mdc` is Cursor's own rule extension and is the ONLY reason `.cursor/rules`
159
+ * is scannable at all: every file there is `<nnn>-<name>.mdc`, so a `.md`-only
160
+ * filter would have added the directory to {@link SCAN_DIRS} and enumerated
161
+ * zero files — a widening that looks live and checks nothing. Same constant,
162
+ * same reason, as `check-skill-links.mjs` § MD_EXTENSIONS.
163
+ */
164
+ export const MD_EXTENSIONS = Object.freeze(['.md', '.mdc']);
109
165
 
110
166
  /**
111
167
  * A cited script path. One regex, one alternation, reused for every
@@ -114,8 +170,35 @@ export const SCAN_DIRS = Object.freeze(['skills', 'commands', 'agents', 'docs'])
114
170
  * `hooks/**.sh`. `hooks/**.mjs` is deliberately NOT part of this grammar —
115
171
  * the `.mjs` half of the citation surface stays exactly `scripts/`, matching
116
172
  * every existing annotation and fence-skip test unchanged.
173
+ *
174
+ * Ceiling (BV-004, #1390 P2): the match is UNANCHORED on the left, so a path
175
+ * NESTED under another directory is read from its `scripts/` / `hooks/`
176
+ * segment onward. `templates/shared/hooks/check-csp-single-source.sh` is judged
177
+ * as `hooks/check-csp-single-source.sh` — a path nobody cited. That cuts both
178
+ * ways: a finding names the truncated tail (fail-closed noise), and a nested
179
+ * path whose tail HAPPENS to exist at the repo root passes although the cited
180
+ * file is missing (fail-open).
181
+ *
182
+ * A lookbehind cannot tell the two readings apart, because the character
183
+ * before the tail is `/` in both: `templates/shared/hooks/…` is a real
184
+ * subdirectory, while `$PLUGIN_ROOT/scripts/…`, `${CLAUDE_PLUGIN_ROOT}/…`,
185
+ * `../../scripts/…` and `…/blob/main/hooks/…` all resolve to the plugin root,
186
+ * so their tail IS the right repo-rooted path. Only the prefix token's meaning
187
+ * differs, and the regex cannot evaluate a variable. Measured 2026-09-19 @
188
+ * 8f6ac022 (prose lines of every SCAN_DIRS file, fences skipped — `forEachLine`
189
+ * + this regex, the character before each hit): 1505 hits in 351 files, 42
190
+ * preceded by `/` — 16 variable-rooted, 21 relative, 5 literal, and of those
191
+ * five exactly ONE is a genuine nested directory (the CSP hook above, in
192
+ * `rules/opt-in-stack/security-web.md`). A `(?<![\w./}-])` guard would drop 37
193
+ * correctly-judged citations to fix one.
194
+ *
195
+ * Escape hatch until then: the nested citation carries
196
+ * `<!-- path-check: example -->` (or `planned #<iid>`), which the one live
197
+ * occurrence already does. Revisit when a second real nested-path citation
198
+ * appears that the annotation cannot reasonably cover — then the grammar needs
199
+ * a list of known plugin-root prefixes, not a lookbehind.
117
200
  */
118
- const CITATION_RE = /scripts\/[a-zA-Z0-9_/-]*\.(?:mjs|sh)|hooks\/[a-zA-Z0-9_/-]*\.sh/g;
201
+ const CITATION_RE =/scripts\/[a-zA-Z0-9_/-]*\.(?:mjs|sh)|hooks\/[a-zA-Z0-9_/-]*\.sh/g;
119
202
 
120
203
  /**
121
204
  * A complete inline-code span, including spans delimited by multiple backticks.
@@ -166,6 +249,107 @@ export function isPlaceholderCitation(citedPath) {
166
249
  return PLACEHOLDER_FRAGMENTS.some((fragment) => lower.includes(fragment));
167
250
  }
168
251
 
252
+ /**
253
+ * The per-harness STATE roots. A missing citation may be excused as a runtime
254
+ * artefact ONLY from inside one of these — and only when git also ignores it
255
+ * (see {@link isRuntimeArtifact}).
256
+ *
257
+ * Each is a harness's own session-state directory: `.orchestrator/` (this
258
+ * plugin's ledgers, locks and metrics), plus the four harness mirrors that
259
+ * carry a per-session `STATE.md` / `wave-scope.json` / `filescopes/` —
260
+ * `.claude/`, `.codex/`, `.cursor/` (all three gitignored file-by-file in this
261
+ * repo's `.gitignore`) and `.pi/` (`skills/_shared/platform-tools.md` § Pi;
262
+ * absent from this checkout, listed so the Pi harness is covered wherever the
263
+ * plugin is installed).
264
+ *
265
+ * Ceiling (BV-004): exactly these five prefixes, and ONLY in conjunction with
266
+ * the git test. `.gitignore` here also declines to track real, hand-authored
267
+ * documents that simply do not ship (`docs/specs/`, `docs/_private/`) and
268
+ * whole build outputs (`coverage/`, `node_modules/`) — a typo in a
269
+ * `docs/specs/…` citation is a defect this check exists to catch, so those
270
+ * must keep REPORTING. Revisit when a harness adds a sixth state root, or
271
+ * when a state root stops being a dot-directory at the repo root.
272
+ */
273
+ export const RUNTIME_STATE_PREFIXES = Object.freeze([
274
+ '.orchestrator/',
275
+ '.claude/',
276
+ '.codex/',
277
+ '.cursor/',
278
+ '.pi/',
279
+ ]);
280
+
281
+ /**
282
+ * Does `citedPath` live under a harness state root?
283
+ *
284
+ * Necessary but NOT sufficient for the runtime-artefact carve-out — the git
285
+ * test in {@link gitIgnoredPaths} is the second half. `.claude/rules/x.md` is
286
+ * under a state root and TRACKED, so it still reports.
287
+ *
288
+ * @param {string} citedPath repo-relative POSIX path
289
+ * @returns {boolean}
290
+ */
291
+ export function isRuntimeStatePath(citedPath) {
292
+ return RUNTIME_STATE_PREFIXES.some((prefix) => citedPath.startsWith(prefix));
293
+ }
294
+
295
+ /**
296
+ * Which of `candidates` git IGNORES — one half of the runtime-artefact test
297
+ * (the other is {@link isRuntimeStatePath}).
298
+ *
299
+ * The rules corpora widened into by #1384 P3 cite paths that never exist in a
300
+ * checkout and are not supposed to: `.cursor/STATE.md`, `.orchestrator/…`
301
+ * ledgers, per-session state files. Rather than a hand-kept name list (which
302
+ * would rot the moment a new ledger is added, and which nothing measures), the
303
+ * test is MECHANICAL and matches the repo's own definition: a path the
304
+ * repository declines to track, which does not exist, is by construction
305
+ * written at runtime — so there is nothing for a citation gate to catch.
306
+ *
307
+ * That predicate alone is too wide, which is why the prefix half exists: "git
308
+ * ignores it" and "it is written at runtime" are not the same claim. Measured
309
+ * 2026-09-18, `gitIgnoredPaths` matched `docs/specs/foo.md`,
310
+ * `docs/_private/x.md`, `coverage/report.md` and `node_modules/x/y.mjs` just
311
+ * as readily as `.orchestrator/metrics/events.jsonl` — real local documents
312
+ * whose dead citations this check must still report.
313
+ *
314
+ * ONE `git check-ignore --stdin` for the whole run, never one per path: the
315
+ * corpus carries hundreds of citations and a per-path spawn would dominate the
316
+ * check's runtime.
317
+ *
318
+ * Fails OPEN toward REPORTING (the safe direction here): a missing `git`, a
319
+ * non-git root, or exit 128 yields an empty set, so every absent citation is
320
+ * still reported and no real defect is suppressed by a tooling failure. Exit 1
321
+ * means "none of them are ignored" and is NOT an error.
322
+ *
323
+ * @param {string} repoRoot absolute repo root
324
+ * @param {string[]} candidates repo-relative POSIX paths (already known absent)
325
+ * @returns {Set<string>} the subset git ignores
326
+ */
327
+ export function gitIgnoredPaths(repoRoot, candidates) {
328
+ const unique = [...new Set(candidates)].filter((p) => p.length > 0 && !p.includes('\n'));
329
+ if (unique.length === 0) return new Set();
330
+ let stdout;
331
+ try {
332
+ stdout = execFileSync('git', ['check-ignore', '--stdin'], {
333
+ cwd: repoRoot,
334
+ input: `${unique.join('\n')}\n`,
335
+ encoding: 'utf8',
336
+ stdio: ['pipe', 'pipe', 'ignore'],
337
+ maxBuffer: 8 * 1024 * 1024,
338
+ });
339
+ } catch (error) {
340
+ // Exit 1 = nothing matched, and git still wrote (empty) stdout. Anything
341
+ // else (128, ENOENT, non-git root) leaves `stdout` undefined → empty set.
342
+ if (error?.status !== 1) return new Set();
343
+ stdout = typeof error.stdout === 'string' ? error.stdout : '';
344
+ }
345
+ return new Set(
346
+ String(stdout)
347
+ .split('\n')
348
+ .map((line) => line.trim())
349
+ .filter(Boolean),
350
+ );
351
+ }
352
+
169
353
  /** The annotation marker, in any of its three classes. */
170
354
  const ANNOTATION_RE = /<!--\s*path-check:\s*([^>]*?)\s*-->/;
171
355
 
@@ -274,10 +458,20 @@ export function scanSkillScriptPaths({ pluginRoot, dirs = SCAN_DIRS, strictSh =
274
458
  existing: 0,
275
459
  annotated: 0,
276
460
  placeholders: 0,
461
+ runtimeArtifacts: 0,
277
462
  findings: 0,
278
463
  warnings: 0,
279
464
  };
280
465
 
466
+ /**
467
+ * Citations that are absent AND unannotated — judged only after the corpus
468
+ * loop, when the single `git check-ignore` call can separate a dead
469
+ * reference from a path written at runtime.
470
+ *
471
+ * @type {{file: string, citation: {line: number, path: string}}[]}
472
+ */
473
+ const pending = [];
474
+
281
475
  /** @type {string[]} */
282
476
  let files;
283
477
  try {
@@ -290,7 +484,7 @@ export function scanSkillScriptPaths({ pluginRoot, dirs = SCAN_DIRS, strictSh =
290
484
  // honours `.gitignore`, so the #1143 exposure a bare `readdirSync` walk
291
485
  // would reintroduce (a worktree under `.claude/worktrees/`, gitignored
292
486
  // `docs/specs/*.md`) stays closed — see that module's header.
293
- files = enumerateRepoFiles({ repoRoot: pluginRoot, dirs, exts: ['.md'] });
487
+ files = enumerateRepoFiles({ repoRoot: pluginRoot, dirs, exts: MD_EXTENSIONS });
294
488
  } catch (error) {
295
489
  findings.push({
296
490
  kind: 'tool-error',
@@ -389,30 +583,48 @@ export function scanSkillScriptPaths({ pluginRoot, dirs = SCAN_DIRS, strictSh =
389
583
  }
390
584
  if (marker && !marker.ok) continue; // already reported as bad-annotation
391
585
 
392
- // `.mjs` and `.md` are blocking.
393
- // `.sh` is advisory (`warn`) unless the caller opted into `strictSh`.
394
- const isSh = path.extname(citation.path) === '.sh';
395
- const severity = isSh && !strictSh ? 'warn' : 'fail';
396
- if (severity === 'warn') summary.warnings += 1;
397
- findings.push({
398
- kind: 'missing-path',
399
- file: relative,
400
- line: citation.line,
401
- path: citation.path,
402
- annotation: null,
403
- message:
404
- (isSh
405
- ? severity === 'warn'
406
- ? `\`${citation.path}\` does not exist (advisory .sh citations do not block ` +
407
- 'validate-plugin until re-run with --strict-sh; see #1187) — '
408
- : `\`${citation.path}\` does not exist (--strict-sh) — `
409
- : `\`${citation.path}\` does not exist — `) +
410
- 'create it, fix the path, or annotate the citation with ' +
411
- '`<!-- path-check: planned #<iid> | historical | example -->` on this line or the ' +
412
- 'line directly above',
413
- severity,
414
- });
586
+ // Deferred, not reported yet: the runtime-artefact test below needs ONE
587
+ // `git check-ignore` for the whole corpus, not one per citation.
588
+ pending.push({ file: relative, citation });
589
+ }
590
+ }
591
+
592
+ // BOTH halves, never one: under a harness state root AND untracked by git.
593
+ // Only state-root candidates are even offered to git, so the subprocess
594
+ // shrinks to the paths that could possibly qualify.
595
+ const ignored = gitIgnoredPaths(
596
+ pluginRoot,
597
+ pending.map((p) => p.citation.path).filter(isRuntimeStatePath),
598
+ );
599
+
600
+ for (const { file: relative, citation } of pending) {
601
+ if (isRuntimeStatePath(citation.path) && ignored.has(citation.path)) {
602
+ summary.runtimeArtifacts += 1;
603
+ continue;
415
604
  }
605
+ // `.mjs` and `.md` are blocking.
606
+ // `.sh` is advisory (`warn`) unless the caller opted into `strictSh`.
607
+ const isSh = path.extname(citation.path) === '.sh';
608
+ const severity = isSh && !strictSh ? 'warn' : 'fail';
609
+ if (severity === 'warn') summary.warnings += 1;
610
+ findings.push({
611
+ kind: 'missing-path',
612
+ file: relative,
613
+ line: citation.line,
614
+ path: citation.path,
615
+ annotation: null,
616
+ message:
617
+ (isSh
618
+ ? severity === 'warn'
619
+ ? `\`${citation.path}\` does not exist (advisory — .sh citations do not block ` +
620
+ 'validate-plugin until re-run with --strict-sh; see #1187) — '
621
+ : `\`${citation.path}\` does not exist (--strict-sh) — `
622
+ : `\`${citation.path}\` does not exist — `) +
623
+ 'create it, fix the path, or annotate the citation with ' +
624
+ '`<!-- path-check: planned #<iid> | historical | example -->` on this line or the ' +
625
+ 'line directly above',
626
+ severity,
627
+ });
416
628
  }
417
629
 
418
630
  findings.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
@@ -461,7 +673,7 @@ export function runCheckSkillScriptPaths(pluginRoot, { strictSh = false } = {})
461
673
  return inspection.ok ? 0 : 1;
462
674
  }
463
675
 
464
- const isMain = import.meta.url === pathToFileURL(process.argv[1] || '').href;
676
+ const isMain =isMainModule(import.meta.url);
465
677
  if (isMain) {
466
678
  const strictSh = process.argv.includes('--strict-sh');
467
679
  const args = process.argv.slice(2).filter((arg) => arg !== '--json' && arg !== '--strict-sh');
@@ -110,7 +110,8 @@
110
110
 
111
111
  import { readFileSync, readdirSync, statSync } from 'node:fs';
112
112
  import path from 'node:path';
113
- import { pathToFileURL } from 'node:url';
113
+ import { isMainModule } from '../is-main-module.mjs';
114
+ import { maskSource } from '../js-mask.mjs';
114
115
 
115
116
  /** Only this directory is scanned — a mutation in `scripts/` is production intent. */
116
117
  const SCAN_DIR = 'tests';
@@ -487,43 +488,24 @@ export function insideStringLiteral(body, index) {
487
488
  * "no cwd" measured as a cwd. `inCommentLine` cannot serve here: it answers a
488
489
  * question about a whole LINE, while this one runs inside a call expression.
489
490
  *
491
+ * Delegates to the shared lexer {@link maskSource} in `keepLiterals` mode
492
+ * (#1388). The hand-rolled walker this replaced had no REGEX-LITERAL branch,
493
+ * so a call tail carrying one — measured 2026-09-18:
494
+ * `wrapperHasCwd(", undefined, { env: /\/*$/.source, cwd: dir }")` — read the
495
+ * regex's `/*` as a block-comment opener, swallowed the rest of the tail and
496
+ * returned `false`: a false alarm from this check.
497
+ *
498
+ * One behavioural difference the callers must honour: `maskSource` blanks a
499
+ * comment to SPACES (length-preserving) where this function used to collapse
500
+ * it to a single space. {@link wrapperHasCwd} judges its second positional
501
+ * textually against `second !== ''`, so that difference was checked rather
502
+ * than assumed — see the measurement recorded at that test.
503
+ *
490
504
  * @param {string} text
491
- * @returns {string} same length semantics, comments blanked to one space
505
+ * @returns {string} same length, comment bytes blanked to spaces
492
506
  */
493
507
  export function stripComments(text) {
494
- let out = '';
495
- /** @type {string|null} */
496
- let quote = null;
497
- for (let i = 0; i < text.length; i += 1) {
498
- const ch = text[i];
499
- if (quote !== null) {
500
- out += ch;
501
- if (ch === '\\') {
502
- out += text[i + 1] ?? '';
503
- i += 1;
504
- } else if (ch === quote) quote = null;
505
- continue;
506
- }
507
- if (ch === "'" || ch === '"' || ch === '`') {
508
- quote = ch;
509
- out += ch;
510
- continue;
511
- }
512
- if (ch === '/' && text[i + 1] === '*') {
513
- const end = text.indexOf('*/', i + 2);
514
- i = end === -1 ? text.length : end + 1;
515
- out += ' ';
516
- continue;
517
- }
518
- if (ch === '/' && text[i + 1] === '/') {
519
- const end = text.indexOf('\n', i);
520
- i = end === -1 ? text.length : end - 1;
521
- out += ' ';
522
- continue;
523
- }
524
- out += ch;
525
- }
526
- return out;
508
+ return maskSource(text, { keepLiterals: true });
527
509
  }
528
510
 
529
511
  /**
@@ -549,6 +531,14 @@ export function wrapperHasCwd(tail) {
549
531
  const clean = stripComments(tail);
550
532
  const m = /^\s*,\s*/.exec(clean);
551
533
  if (m) {
534
+ // NO `.trim()` here, and that is measured rather than assumed (#1388): the
535
+ // shared lexer blanks a comment to SPACES where the local stripper used to
536
+ // delete it, so a comment-only tail LOOKS like it would arrive as truthy
537
+ // whitespace. It cannot — the `\s*` above is greedy and consumes every
538
+ // blanked byte, leaving `second === ''`. Measured 2026-09-18 over
539
+ // `, /* no cwd */`, `, /* a */ /* b */`, `,\n // note\n`,
540
+ // `, /* x */ undefined`, `, undefined /* y */`: all `false`, `, /* c */ dir`
541
+ // → `true`. A `.trim()` would be dead code; the case is pinned by a test.
552
542
  const second = clean.slice(m[0].length);
553
543
  if (second !== '' && !/^(?:undefined|null)\b/.test(second) && !/^,/.test(second)) return true;
554
544
  }
@@ -810,7 +800,7 @@ export function runCheckTestGitConfigTarget(pluginRoot) {
810
800
  return 0;
811
801
  }
812
802
 
813
- const isMain = import.meta.url === pathToFileURL(process.argv[1] || '').href;
803
+ const isMain =isMainModule(import.meta.url);
814
804
  if (isMain) {
815
805
  const argv = process.argv.slice(2);
816
806
  const flags = new Set(argv.filter((a) => a.startsWith('--')));
@@ -35,7 +35,7 @@
35
35
  import { readFileSync, readdirSync, statSync, existsSync, writeFileSync } from 'node:fs';
36
36
  import { join, extname, relative, sep } from 'node:path';
37
37
  import { execFileSync } from 'node:child_process';
38
- import { pathToFileURL } from 'node:url';
38
+ import { isMainModule } from '../is-main-module.mjs';
39
39
 
40
40
  // ---------------------------------------------------------------------------
41
41
  // File enumeration — git ls-files primary, recursive fs walk fallback.
@@ -535,7 +535,7 @@ export function runCheckUnicodeSafety(pluginRoot, opts = {}) {
535
535
 
536
536
  // CLI entry — only when executed directly, never on import (keeps the exports
537
537
  // safe to import from tests without triggering process.exit).
538
- const isMain = import.meta.url === pathToFileURL(process.argv[1] || '').href;
538
+ const isMain =isMainModule(import.meta.url);
539
539
  if (isMain) {
540
540
  const args = process.argv.slice(2);
541
541
  const flags = new Set(args.filter((a) => a.startsWith('--')));