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
@@ -2,6 +2,7 @@
2
2
 
3
3
  > Single source of truth for the parallel-session-detection preamble.
4
4
  > Referenced by: autopilot, session-start, session-plan, wave-executor, session-end (5 orchestrator entry-points).
5
+ > Design history: the 2026-05-26 parallel-aware-sessions spec is superseded on lock liveness (heartbeat, not PID) — see `docs/README.md` § Superseded design notes.
5
6
 
6
7
  ## Purpose
7
8
 
@@ -70,7 +70,7 @@ Lifecycle: `spawn_agent` (launch, one per wave task) → `list_agents` (enumerat
70
70
  - **session-reviewer** — quality review; spawn as a separate agent rather than reviewing in the main session
71
71
 
72
72
  **On Cursor IDE:**
73
- No native Agent() tool or typed agent roles inside the Composer session — wave tasks run sequentially there, and `agents-per-wave` is ignored. This is a statement about the IDE session only: coordinator-side foreign dispatch to Cursor models via the headless `cursor-agent` CLI does exist (`scripts/lib/wave-executor/foreign-dispatch.mjs`, #1150 — detached worktree, filesystem-measured result, `orchestrator.foreign_dispatch.completed` telemetry, mandatory Claude review, `NEVER_FOREIGN_ROLES` lock). Measured 2026-08-25 on `cursor-agent 2026.08.11-e8db854`. Slash commands live in `.cursor/commands/`. There is no Skill tool — Read `skills/<name>/SKILL.md` instead.
73
+ No native Agent() tool or typed agent roles inside the Composer session — wave tasks run sequentially there, and `agents-per-wave` is ignored. This is a statement about the IDE session only: coordinator-side foreign dispatch to Cursor models via the headless `cursor-agent` CLI does exist (`scripts/lib/wave-executor/foreign-dispatch.mjs`, #1150 — detached worktree, filesystem-measured result, `orchestrator.foreign_dispatch.completed` telemetry, mandatory Claude review, `NEVER_FOREIGN_ROLES` lock in `scripts/lib/wave-executor/dispatch-common.mjs`). Measured 2026-08-25 on `cursor-agent 2026.08.11-e8db854`. Slash commands live in `.cursor/commands/`. There is no Skill tool — Read `skills/<name>/SKILL.md` instead.
74
74
 
75
75
  **On Pi:**
76
76
  No Session Orchestrator Pi subagent dispatcher exists in v1. Execute wave tasks sequentially in the active Pi session. Treat `agents-per-wave` as advisory until the SDK-based dispatcher lands.
@@ -147,7 +147,7 @@ This is the wave-plan-time analog of PSA-007 (subagents never race the shared gi
147
147
 
148
148
  The rule above deconflicts writers *inside one wave*. The same working copy is also shared across SESSIONS, and `<state-dir>/wave-scope.json` is the one control artefact that constrains writes rather than describing them. It lives in the working copy, not in the session — so before #1123 a manifest written by session A governed session B's every Edit. Measured 2026-08-22 (#1082): a Discovery wave's `allowedPaths: []` — prescribed for every Discovery wave — denied all writes of an unrelated parallel session, with a deny reason that could only tell it to fix a wave plan it does not own.
149
149
 
150
- **The manifest is SESSION-BOUND since #1123.** The coordinator that writes it names itself in two optional fields, `session` (raw `session_id`) and `semantic_session`, both from ONE `sessionAttribution(repoRoot)` call (`scripts/lib/events.mjs`) — see `skills/wave-executor/wave-loop.md` § Scope Manifest 1. `hooks/enforce-scope.mjs` Gate 3b classifies the manifest with `readProcessLocalSessionIds()` + `classifyManifestSession()` (`scripts/lib/session-identity/own-session.mjs`) — process-local tiers only (hook payload, `CLAUDE_CODE_SESSION_ID`), never the repo-global `session.lock`, which is shared by every session in the checkout and made a peer's manifest read `own` (#1194):
150
+ **The manifest is SESSION-BOUND since #1123.** The coordinator that writes it names itself in two optional fields, `session_id` (the raw harness session id) and `semantic_session_id` (renamed from `session` / `semantic_session` in #1153 P2 to match `session.lock` and `current-session.json`; the legacy pair is still READ until the next minor release), both from ONE `sessionAttribution(repoRoot)` call (`scripts/lib/events.mjs`) — see `skills/wave-executor/wave-loop.md` § Scope Manifest 1. `hooks/enforce-scope.mjs` Gate 3b classifies the manifest with `readProcessLocalSessionIds()` + `classifyManifestSession()` (`scripts/lib/session-identity/own-session.mjs`) — process-local tiers only (hook payload, `CLAUDE_CODE_SESSION_ID`), never the repo-global `session.lock`, which is shared by every session in the checkout and made a peer's manifest read `own` (#1194):
151
151
 
152
152
  - **`foreign`** (ids present, none of them ours) → the gate ALLOWS the write and emits one `orchestrator.scope.foreign_session_ignored` event. A foreign manifest is somebody else's wave plan; it never had authority here, and the event keeps the skip counted rather than silent.
153
153
  - **`own`** → enforce, unchanged.
@@ -30,7 +30,9 @@ Key principles (see [LANGUAGE.md](LANGUAGE.md) for the full list):
30
30
  - **The interface is the test surface.**
31
31
  - **One adapter = hypothetical seam. Two adapters = real seam.**
32
32
 
33
- This skill is _informed_ by the project's domain model — `CONTEXT.md` and any `docs/adr/`. The domain language gives names to good seams; ADRs record decisions the skill should not re-litigate. See [CONTEXT-FORMAT.md](../domain-model/CONTEXT-FORMAT.md) and [ADR-FORMAT.md](../domain-model/ADR-FORMAT.md).
33
+ This skill is _informed_ by the project's domain model — `CONTEXT.md` and any `docs/adr/`. The domain language gives names to good seams; ADRs record decisions the skill should not re-litigate. See [references/CONTEXT-FORMAT.md](./references/CONTEXT-FORMAT.md) and [references/ADR-FORMAT.md](./references/ADR-FORMAT.md).
34
+
35
+ **When to load [references/domain-model.md](./references/domain-model.md):** before this skill's first pass on a repo with no `CONTEXT.md` yet, or one too thin to name the seams below — run its grilling interview to establish the domain vocabulary first. Skip it once `CONTEXT.md` already reflects the domain.
34
36
 
35
37
  ## Process
36
38
 
@@ -45,7 +47,7 @@ If any of these files don't exist, proceed silently — don't flag their absence
45
47
 
46
48
  **Optional pre-pass — export inventory injection (mapper-supported projects only):**
47
49
 
48
- Before dispatching the Explore subagent, call `extractSemanticSlices(filePath, { type: 'exports' })` from `scripts/lib/language-mappers/index.mjs` on known entry-point files (e.g., `index.ts`, `src/index.ts`, main export barrel). If the mapper returns a non-empty result, format the export list as structured context and inject it into the Explore subagent prompt. This gives the subagent an immediate map of the codebase's public surface without requiring it to grep manually.
50
+ Before dispatching the Explore subagent, read each known entry-point file (e.g. `index.ts`, `src/index.ts`, the main export barrel) and pass its contents to `extractSemanticSlices(filePath, content)` from `scripts/lib/language-mappers/index.mjs`. The signature is `(filePath, content, options?)` — the second argument is the raw file text, **not** an options object, and there is no slice-kind filter parameter. It resolves to an array of `SemanticSlice` records (`{ kind, name, exported, … }`); keep the ones with `exported === true`, format them as structured context, and inject that into the Explore subagent prompt. This gives the subagent an immediate map of the codebase's public surface without requiring it to grep manually.
49
51
 
50
52
  ```
51
53
  ## Export inventory (auto-generated)
@@ -53,7 +55,7 @@ Before dispatching the Explore subagent, call `extractSemanticSlices(filePath, {
53
55
  - src/lib/bar.ts: exports `processBar`
54
56
  ```
55
57
 
56
- This pre-pass is OPTIONAL — only activate when the entry-point file is mapper-supported (TypeScript/JavaScript). For unsupported file types, `extractSemanticSlices` returns an empty array and the pre-pass is silently skipped. Never block Explore dispatch on a mapper failure.
58
+ This pre-pass is OPTIONAL — only activate when the entry-point file is mapper-supported (`.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`, `.md`, `.mdx`, `.swift`, `.py` — the `EXT_TO_LANG` map in `index.mjs` is the source of truth). For any other extension `extractSemanticSlices` **throws** rather than returning an empty array, so wrap the call in a try/catch and skip the pre-pass silently on failure. Never block Explore dispatch on a mapper error.
57
59
 
58
60
  Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction:
59
61
 
@@ -86,7 +88,7 @@ Once the user picks a candidate, drop into a grilling conversation. Walk the des
86
88
 
87
89
  Side effects happen inline as decisions crystallize:
88
90
 
89
- - **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `/domain-model` (see [CONTEXT-FORMAT.md](../domain-model/CONTEXT-FORMAT.md)). Create the file lazily if it doesn't exist.
91
+ - **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as the domain-model grilling reference (see [references/CONTEXT-FORMAT.md](./references/CONTEXT-FORMAT.md)). Create the file lazily if it doesn't exist.
90
92
  - **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there.
91
- - **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See [ADR-FORMAT.md](../domain-model/ADR-FORMAT.md).
93
+ - **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See [references/ADR-FORMAT.md](./references/ADR-FORMAT.md).
92
94
  - **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md).
@@ -1,12 +1,12 @@
1
- ---
2
- name: domain-model
3
- description: Use when the user wants to stress-test a plan against the existing domain model and documented decisions. Grilling session that interviews the user one question at a time, sharpens fuzzy terminology inline, updates CONTEXT.md lazily, and offers ADRs sparingly under a 3-criteria gate. Reads docs/adr/ and CONTEXT.md if present.
4
- model: inherit
5
- disable-model-invocation: true
6
- derived-from: mattpocock/skills@90ea8ee
7
- license: MIT
8
- upstream-url: https://github.com/mattpocock/skills/tree/main/domain-model
9
- ---
1
+ # Domain-Model Grilling (reference)
2
+
3
+ > Reference material for the `architecture` skill **not a skill of its own.**
4
+ > Merged here in v4.0.0 from the former `skills/domain-model/` (retired: zero
5
+ > telemetry, zero fleet invocations, reference-only usage — see
6
+ > `docs/audits/2026-09-06-360-audit.md` § 5A). Invoke it by reading this file
7
+ > from `/architecture`, not via a slash command.
8
+ >
9
+ > Adapted from [`mattpocock/skills@90ea8ee`](https://github.com/mattpocock/skills/tree/main/domain-model), MIT — Copyright (c) 2026 Matt Pocock. See the repo-root `NOTICE`.
10
10
 
11
11
  Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.
12
12
 
@@ -377,24 +377,10 @@ identically by readers per the v1 schema additive convention).
377
377
 
378
378
  ## Configuration
379
379
 
380
- The `autopilot` block in Session Config (`CLAUDE.md` / `AGENTS.md`) accepts the following fields. All fields are optional; omitting a field applies the documented default.
381
-
382
- ```yaml
383
- autopilot:
384
- bg-isolation: worktree # worktree | none (default: worktree) — see #431
385
- ```
386
-
387
- ### bg-isolation
388
-
389
- **Type:** `worktree` | `none` — **Default:** `worktree`
390
-
391
- Controls whether `autopilot --multi-story` creates a per-story git worktree before spawning sub-sessions.
392
-
393
- `worktree` (default): Each story pipeline receives its own isolated git worktree via `EnterWorktree`. Parallel writes are safe because every agent edits a private working copy. Cost: disk space proportional to the number of concurrent stories plus the latency of worktree creation at story-start.
394
-
395
- `none` (opt-in): No worktrees are created. Sub-sessions spawn directly in the main working tree. Useful for monorepos where worktree creation is impractical due to large `node_modules`, sparse-checkout setups, or build caches that must be shared. **Requires file-scope discipline:** when `max-stories > 1`, every story must edit a disjoint set of files. If two stories touch the same file simultaneously, edits will collide silently. To enforce acknowledgement of this discipline, `autopilot-multi` requires `--deconflict-paths=<glob>` whenever `bg-isolation: none` AND `max-stories > 1`; omitting the flag is a hard error (exit 1). See `.claude/rules/parallel-sessions.md` PSA-001/002/003.
396
-
397
- **Operator-awareness note:** CC 2.1.133 silently flipped `worktree.baseRef` default from `head` to `origin/<default>`, breaking users who relied on unpushed commits being included in their worktree base. The same class of upstream change can affect `bg-isolation` semantics in a future CC release. Treat CC changelog entries related to worktree or `--bg` session behaviour as requiring a re-read of this section before upgrading.
380
+ Single-story `/autopilot` takes no Session Config block. Multi-story
381
+ (`autopilot.bg-isolation`, `--multi-story`, `--deconflict-paths`) was removed in
382
+ 4.0.0 — see `docs/migration-v4.md` and `docs/session-config-reference.md` §
383
+ "Autopilot Multi-Story (#431) — removed".
398
384
 
399
385
  ## References
400
386
 
@@ -39,7 +39,7 @@ PHASE 1 IMPLEMENTED (2026-04-19). Session-end opt-in quality gate. Upstream of `
39
39
  | 4 | `session-file-existence` | `50-sessions/YYYY-MM-DD-*.md` references anywhere in scope | `existsSync(vault/50-sessions/<file>)` |
40
40
  | 5 | `command-count` | "N commands" / "N /commands" claims in prose | compare to `ls commands/*.md \| wc -l`; skipped if no `commands/` dir |
41
41
  | 6 | `session-config-parity` | Top-level keys under `## Session Config` in `CLAUDE.md` / `AGENTS.md` | diff against `docs/session-config-template.md`; a missing MANDATORY (minimal-baseline) key is an error, a missing OPT-IN-baseline-only key is a warning |
42
- | 7 | `vault-dir-parity` | `vault-integration.vault-dir` in BOTH `CLAUDE.md` AND `AGENTS.md` | reuse `_parseVaultIntegration`; flag when the two files disagree |
42
+ | 7 | `vault-dir-parity` | `vault-integration.vault-dir` in BOTH `CLAUDE.md` AND `AGENTS.md` | reuse `_parseVaultIntegration`; flag when the two files disagree. Short-circuits to PASS when the two are the SAME file by construction (symlink / same inode / byte-identical generated copy) |
43
43
  | 8 | `generated-rule-staleness` *(WARN only)* | `.claude/rules/*.md` with `auto-generated: true` frontmatter | extract `learning-key`; WARN when the key is absent from `.orchestrator/metrics/learnings.jsonl` or its learning's `expires_at` is in the past; skipped silently when no auto-generated rules exist |
44
44
  | 9 | `rule-scoping` | `.claude/rules/*.md` frontmatter + `## See Also` footers + `.claude/rules/<name>.md` citations in `CLAUDE.md`/`AGENTS.md` | five probes: `paths:` frontmatter (error), cited-but-missing rule citations (error), zero-match `globs:` patterns (warn), foreign PascalCase glob tokens (warn), unreadable rule files (warn — surfaced instead of silently skipped); skipped silently when `.claude/rules/` is absent |
45
45
  | 10 | `docs-parity` | `docs/components.md` count-claims, Session Config key parity (`docs/session-config-template.md` vs `docs/session-config-reference.md`), `.claude/metrics/` stale-path references in `docs/*.md` / `docs/examples/*.md` | three sub-checks (a/b/c) — see below; skipped silently when `docs/components.md` is absent |
@@ -61,6 +61,10 @@ A local CLAUDE.md / AGENTS.md that omits any of these now surfaces a `session-co
61
61
 
62
62
  Check 7 (issue #600) is the **only** check that intentionally reads BOTH instruction files rather than the single alias-resolved one. The alias rule (CLAUDE.md wins ties, AGENTS.md is the Codex alias) means `resolveInstructionFile()` picks exactly one — so a repo carrying both files can silently let `AGENTS.md` drift out of sync with `CLAUDE.md`. A sibling project ran for weeks with a correct `vault-integration.vault-dir` in `CLAUDE.md` and a dead path in `AGENTS.md`. Check 7 reads `vault-integration.vault-dir` from each file (reusing the `_parseVaultIntegration` parser from `scripts/lib/config/vault-integration.mjs` — no hand-rolled YAML) and flags a `vault-dir-parity` error when the two values diverge (the error is attributed to `AGENTS.md`, the secondary alias, and names both values). The check skips gracefully when only one instruction file is present (nothing to compare), when neither file declares a `vault-integration:` block, or when explicitly disabled via `--skip-vault-dir-parity`. Two files that both omit `vault-dir` (both unset) agree and pass.
63
63
 
64
+ **Alias-by-construction short-circuit.** Since this repo's root `AGENTS.md` became a GENERATED byte-identical copy of `CLAUDE.md` (`scripts/generate-agents-skills.mjs`), Check 7 no longer skips here — it RUNS on every invocation. Two files that cannot disagree have nothing to diff, so the check reports parity as satisfied without deriving one: `instructionFilesAreAliased()` recognises a symlink resolving to the same path, the same inode/device (hardlink or already-resolved symlink), and byte-identical content. Branch order is load-bearing: the "neither file has a `vault-integration:` block" skip is evaluated FIRST, because an absent subject is a different (and more informative) outcome than "the files cannot disagree". The error class the check exists for is untouched — two INDEPENDENT files that declare different `vault-dir` values still produce the `vault-dir-parity` error attributed to `AGENTS.md`.
65
+
66
+ The same predicate guards Check 9's probe 2a, which iterates `['CLAUDE.md', 'AGENTS.md']`: with an aliased `AGENTS.md` it scans `CLAUDE.md` only, because otherwise every dangling `.claude/rules/*.md` citation would be reported TWICE — one defect, two errors, and in `--mode strict` a doubled blocking surface against a file the operator is not allowed to edit.
67
+
64
68
  Check 9 (`rule-scoping`) validates `.claude/rules/*.md` frontmatter against the `scripts/lib/rule-loader.mjs` contract, catching the class of defect where a rule silently drifts out of the activation pipeline the loader actually implements. Four probes: **(1) paths-presence** — a top-level `paths:` frontmatter key is not a key `rule-loader.mjs` recognises (it only reads `globs:`), so a rule with `paths:` silently loads ALWAYS-ON regardless of intended file scope; flagged as an error. **(2) cited-but-missing** — `(a)` `.claude/rules/<name>.md` citations inside `CLAUDE.md`/`AGENTS.md` that don't resolve to a file on disk, and `(b)` bare `<name>.md` tokens in a rule's own `## See Also` footer that don't exist as sibling rule files (tokens carrying a path separator, e.g. `../../skills/_shared/state-ownership.md`, are cross-directory references and explicitly out of scope); both flagged as errors. **(3) zero-match-globs** — a `globs:` pattern matching zero files in `git ls-files` (falls back to a manual directory walk when git is unavailable); flagged as a WARNING, not an error, because library/exemplar repos legitimately carry dead stack rules (this repo alone carries ~37 by design — Swift/Next.js/Supabase rules with no matching files in a pure-Node-ESM codebase). **(4) foreign-glob** — a glob pattern containing a PascalCase product-like token (regex `[A-Z][a-z]+[A-Z]`, e.g. `WalkAITalkieTests`) — a likely copy-paste leftover from another project's rule scope; flagged as a WARNING. Glob matching reuses the same picomatch-with-inline-fallback resolution `scripts/lib/rule-loader.mjs` uses (`parseGlobsFrontmatter` is imported directly; the picomatch resolution itself is duplicated locally since `rule-loader.mjs` does not export a public matcher function). The check is skipped silently (no `checks_run` entry, no `checks_skipped` entry) when `.claude/rules/` is absent, or explicitly via `--skip-rule-scoping`.
65
69
 
66
70
  Check 10 (`docs-parity`, issue #780) validates the public docs surface against live repository state via three sub-checks, all reported under the single `docs-parity` check id: **(a) count-claims** — `docs/components.md`'s own heading counts ("## Skills (N user-facing)", "## Commands (N)", "## Agents (N typed sub-agents)", "## Hook event types (N)") are compared against the SAME actual on-disk derivation the surface-count family (Check 5) uses (`countSkills`/the commands-dir listing/`countAgents`/`readHookCounts`) — NOT Check 5's `claimRe` regexes, which are tuned for CLAUDE.md/README prose phrasing and verifiably do not match `components.md`'s own heading convention; a dedicated set of regexes tailored to the doc's actual authored structure is used instead. **(b) config-block-parity** — top-level Session Config keys documented in `docs/session-config-template.md` (opt-in baseline, via the Check 6 'last'-occurrence extractor) are compared against `docs/session-config-reference.md`; a key is considered documented in the reference when it appears in a `yaml` fence (any indent), a `##`/`###`/`####` heading, or the first cell of a markdown table row (`` | `key-name` | ... ``) — the reference's dominant documentation shape is one table row per field, so a naive fence-only extraction under-counts what it actually documents. Missing keys surface as `docs-parity` errors attributed to `docs/session-config-reference.md`. **(c) metrics-path-liveness** — any `.claude/metrics/` path reference (stale; canonical is `.orchestrator/metrics/`) found in root `docs/*.md` or `docs/examples/*.md` surfaces as an error at its exact file/line. The whole check is skipped silently (no `checks_run` entry, no `checks_skipped` entry) when `docs/components.md` is absent, or explicitly via `--skip-docs-parity`.
@@ -28,7 +28,7 @@
28
28
  * rule-loader.mjs uses); otherwise pure Node stdlib.
29
29
  */
30
30
 
31
- import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
31
+ import { readFileSync, readdirSync, existsSync, statSync, lstatSync, realpathSync } from 'node:fs';
32
32
  import { join, relative, resolve } from 'node:path';
33
33
  import { execFileSync } from 'node:child_process';
34
34
  import { createRequire } from 'node:module';
@@ -294,6 +294,45 @@ function readVaultIntegration(filePath) {
294
294
  return { present, vaultDir: parsed['vault-dir'] };
295
295
  }
296
296
 
297
+ /**
298
+ * Are the two instruction files the SAME file by construction?
299
+ *
300
+ * Since the cross-harness portable surface landed, this repo's root `AGENTS.md`
301
+ * is GENERATED from `CLAUDE.md` (`scripts/generate-agents-skills.mjs`) as a
302
+ * byte-identical copy, and a consumer repo may instead symlink it. Three shapes
303
+ * therefore count as aliased: a symlink resolving to the same path, the same
304
+ * inode/device (symlink already resolved, or a hardlink), and byte-identical
305
+ * content.
306
+ *
307
+ * This matters because Check 7 exists to catch TWO INDEPENDENT files that
308
+ * disagree. Files that cannot disagree have nothing to diff — running the
309
+ * comparison on them would turn a check that skipped for years into a source of
310
+ * spurious findings the moment AGENTS.md appears. Check 9's probe 2a uses the
311
+ * same predicate to avoid reporting every CLAUDE.md defect twice.
312
+ *
313
+ * @param {string} pathA
314
+ * @param {string} pathB
315
+ * @returns {boolean}
316
+ */
317
+ function instructionFilesAreAliased(pathA, pathB) {
318
+ if (!existsSync(pathA) || !existsSync(pathB)) return false;
319
+ try {
320
+ if (lstatSync(pathA).isSymbolicLink() || lstatSync(pathB).isSymbolicLink()) {
321
+ if (realpathSync(pathA) === realpathSync(pathB)) return true;
322
+ }
323
+ const a = statSync(pathA);
324
+ const b = statSync(pathB);
325
+ if (a.ino !== 0 && a.ino === b.ino && a.dev === b.dev) return true;
326
+ } catch {
327
+ // stat/realpath failure (race, permissions) — fall back to bytes
328
+ }
329
+ try {
330
+ return readFileSync(pathA).equals(readFileSync(pathB));
331
+ } catch {
332
+ return false;
333
+ }
334
+ }
335
+
297
336
  function classifySection(heading) {
298
337
  if (!heading) return null;
299
338
  if (BACKWARD_HEADING_RE.test(heading)) return 'backward';
@@ -881,7 +920,19 @@ function main() {
881
920
  const claudeVi = readVaultIntegration(claudePath);
882
921
  const agentsVi = readVaultIntegration(agentsPath);
883
922
  if (!claudeVi.present && !agentsVi.present) {
923
+ // Nothing to compare — the SUBJECT of the check is absent from both
924
+ // files. This branch precedes the alias short-circuit on purpose: an
925
+ // absent vault-integration block is a different (and more informative)
926
+ // outcome than "the files cannot disagree".
884
927
  checksSkipped.push('vault-dir-parity: neither file has a vault-integration: block');
928
+ } else if (instructionFilesAreAliased(claudePath, agentsPath)) {
929
+ // Identical by construction (generated copy, symlink, or hardlink) —
930
+ // parity is SATISFIED, so the check RAN and found nothing. Reporting
931
+ // this as a skip would be wrong too: the invariant Check 7 guards is
932
+ // actively held here, it is simply held mechanically rather than by
933
+ // agreement, and re-deriving a diff between one file and itself can
934
+ // only manufacture noise.
935
+ vaultDirParityRan = true;
885
936
  } else {
886
937
  vaultDirParityRan = true;
887
938
  const claudeDir = claudeVi.vaultDir;
@@ -1182,7 +1233,16 @@ function main() {
1182
1233
  }
1183
1234
 
1184
1235
  // --- Probe 2a: cited-but-missing (CLAUDE.md / AGENTS.md citations) → errors[] ---
1185
- for (const instrName of ['CLAUDE.md', 'AGENTS.md']) {
1236
+ // AGENTS.md is scanned as a SECOND file only when it is genuinely
1237
+ // independent. When it is an alias of CLAUDE.md (this repo generates it
1238
+ // byte-identically), scanning both would report every dangling citation
1239
+ // TWICE — one defect, two errors, and in `--mode strict` a doubled exit
1240
+ // surface for a file the operator cannot edit.
1241
+ const aliasedInstruction = instructionFilesAreAliased(
1242
+ join(vaultDir, 'CLAUDE.md'), join(vaultDir, 'AGENTS.md'),
1243
+ );
1244
+ const instrNames = aliasedInstruction ? ['CLAUDE.md'] : ['CLAUDE.md', 'AGENTS.md'];
1245
+ for (const instrName of instrNames) {
1186
1246
  const filePath = join(vaultDir, instrName);
1187
1247
  if (!existsSync(filePath) || !statSync(filePath).isFile()) continue;
1188
1248
  const fcontent = readFileSync(filePath, 'utf8');
@@ -32,6 +32,34 @@ PEAK_DIFF_SIZE=$(jq -s 'map(.diff_size) | max' <<< "$WAVE_HISTORY_JSON")
32
32
  SD_VALUE=$(echo "scale=3; $WAVE_DIFF_SIZE / $PEAK_DIFF_SIZE" | bc)
33
33
  ```
34
34
 
35
+ ### Live monitor input (`scripts/lib/convergence-monitor.mjs`)
36
+
37
+ The `shrinking_diff` NDJSON signal compares `files_changed` across two
38
+ consecutive `orchestrator.wave.completed` records. Until 2026-09-05 **no
39
+ producer wrote that key** — 144 such records in
40
+ `.orchestrator/metrics/events.jsonl`, none carrying a measurement key, so the
41
+ signal was structurally dead. The emitter is now
42
+ `hooks/post-tool-batch-wave-signal.mjs` (#980): at an N→N+1 wave transition its
43
+ `orchestrator.wave.completed` for wave N carries
44
+
45
+ - `files_changed` — the DEDUPED count of `git diff --name-only <wave_start_sha>`
46
+ ∪ `git ls-files --others --exclude-standard`, and
47
+ - `files_changed_source: 'worktree-vs-wave-start-sha'` — how it was measured.
48
+
49
+ `wave_start_sha` is the `git rev-parse HEAD` the same hook persists into
50
+ `.orchestrator/current-session.json` when wave N is opened. The comparison is
51
+ against the WORKING TREE, not `<sha>..HEAD`, because the coordinator commits at
52
+ session close rather than per wave.
53
+
54
+ **Both keys are optional, and absent is not zero.** Any git failure, a 1.5 s
55
+ timeout, or a missing `wave_start_sha` omits them; the monitor reads the absence
56
+ as `null` and `shrinking_diff` simply does not fire. Two records carry no
57
+ measurement by construction: the final wave's completion from
58
+ `hooks/on-session-end.mjs` (no wave-open transition ran there, so it has no
59
+ start sha) and any completion emitted from an explicit injected `wave_signal`.
60
+ The git/jq recipe above is the SKILL-level computation and is independent of the
61
+ monitor.
62
+
35
63
  ### Thresholds
36
64
 
37
65
  | SD value | Interpretation |
@@ -99,6 +127,22 @@ PR_PRIOR=$(echo "$PASS_RATES" | jq '.[length - 2] // 0')
99
127
  If `quality.incremental` events are absent (test runner not configured), set
100
128
  `PR_confidence = 0.0` and trend = `plateau` (neutral; do not block on missing data).
101
129
 
130
+ ### Live monitor input (`scripts/lib/convergence-monitor.mjs`)
131
+
132
+ The `pass_rate_plateau` NDJSON signal reads the **quality-gate envelope**:
133
+ `orchestrator.quality_gate.passed` / `.failed` records carrying BOTH
134
+ `wave_number` and a well-formed `counts` object. It folds `counts.passed` into
135
+ the wave's pass count (and `counts.failed` alongside it), then fires when two
136
+ consecutive waves report the identical pass count. Measured 2026-09-05: 33 such
137
+ records in `.orchestrator/metrics/events.jsonl`; the flat `test.passed` key the
138
+ reader also accepts has **0** producers and is kept only as an alias for a future
139
+ direct emitter.
140
+
141
+ The `wave_number` + `counts` pair is a type-AND-shape gate, not a prefix
142
+ widening: a session-level gate run (no `wave_number`) must stay invisible to the
143
+ monitor, because admitting it would instantiate a `WaveSummary` per gate run and
144
+ burn the once-per-wave emit keys before the real wave record arrives (#966).
145
+
102
146
  ### Thresholds
103
147
 
104
148
  | PR value | Interpretation |
@@ -160,6 +204,17 @@ VEL_LINES=$(git diff --shortstat "$WAVE_START_REF" "$WAVE_END_REF" \
160
204
  VEL_LINES_PRIOR=<read from events.jsonl for wave N-1>
161
205
  ```
162
206
 
207
+ ### Live monitor input (`scripts/lib/convergence-monitor.mjs`)
208
+
209
+ The `velocity_drop` NDJSON signal counts **one `orchestrator.agent.stopped`
210
+ record per dispatched agent** (wave read from `wave` or `wave_number`) and fires
211
+ when the later wave stopped fewer agents than the earlier one. Measured
212
+ 2026-09-05 over `.orchestrator/metrics/events.jsonl`: 11,754
213
+ `orchestrator.agent.stopped` records versus **0** for the `agent.dispatched` type
214
+ the reader also accepts — `agent.stopped` is the only per-agent record this repo
215
+ actually emits with a wave number. The git-based `VEL_COMMITS` / `VEL_LINES`
216
+ recipe above is the SKILL-level computation and is independent of the monitor.
217
+
163
218
  ### Thresholds
164
219
 
165
220
  | Condition | Interpretation |
@@ -10,12 +10,21 @@
10
10
  * Output: {findings[], metrics, duration_ms, [skipped_reason]}. Never throws.
11
11
  * Also appends one JSONL summary record to .orchestrator/metrics/vault-staleness.jsonl.
12
12
  *
13
- * Current limitation: we compare lastSync age against probe run time only —
14
- * not against the upstream repo's most-recent commit. Mapping vault slugs to
15
- * local repo paths is not reliably available from the probe's inputs. A future
16
- * iteration should resolve each slug to a repo path and run
17
- * `git log -1 --format=%aI` to obtain the true lastCommit timestamp, then
18
- * compute |lastCommit - lastSync| instead.
13
+ * Denominator (GitLab #1238): staleness is `lastCommit - lastSync` how far the
14
+ * upstream repo advanced PAST the last sync — not `now - lastSync`. A mirror of a
15
+ * repo nobody has committed to in three weeks is CURRENT, not three weeks stale.
16
+ * `lastCommit` needs no slug→repo-path resolution: the vault sync writer already
17
+ * stamps it into the same `_overview.md` frontmatter it writes `lastSync` into,
18
+ * so both sides of the comparison come from one read of one file.
19
+ *
20
+ * Measured against the live vault before the fix (2026-09-05): 33 of 48 overviews
21
+ * reported "stale", 26 of them >7d, with a demonstrably healthy sync chain —
22
+ * because the clock, not the repo, was the denominator.
23
+ *
24
+ * Fallback: an overview WITHOUT `lastCommit` carries no repo-activity signal at
25
+ * all, so the wall-clock comparison is the only thing left. It is retained for
26
+ * that case only, marked `basis: 'probe-runtime'` in the evidence and carried at
27
+ * lower confidence, so a consumer can tell a measured delta from a guessed one.
19
28
  */
20
29
 
21
30
  import { existsSync, readFileSync, readdirSync, mkdirSync, appendFileSync } from 'node:fs';
@@ -173,6 +182,7 @@ export async function runProbe(projectRoot, config) {
173
182
  const slug = fm.slug || entry.name;
174
183
  const tier = fm.tier || undefined;
175
184
  const lastSync = fm.lastSync || undefined;
185
+ const lastCommit = fm.lastCommit || undefined;
176
186
 
177
187
  if (!lastSync) {
178
188
  metrics.stale_count++;
@@ -202,21 +212,33 @@ export async function runProbe(projectRoot, config) {
202
212
  continue;
203
213
  }
204
214
 
205
- const delta = now - lastSyncMs;
215
+ // #1238 the denominator. `lastCommit` is the repo's own newest activity,
216
+ // so `lastCommit - lastSync` measures what the mirror actually MISSED.
217
+ // A negative or zero delta means the sync ran at or after the newest
218
+ // commit: current, whatever the wall clock says.
219
+ const lastCommitMs = lastCommit ? Date.parse(lastCommit) : NaN;
220
+ const haveCommitBasis = !isNaN(lastCommitMs);
221
+ const basis = haveCommitBasis ? 'lastCommit' : 'probe-runtime';
222
+ const delta = haveCommitBasis ? lastCommitMs - lastSyncMs : now - lastSyncMs;
223
+
206
224
  if (delta > HOURS_24) {
207
225
  const severity = delta > HOURS_168 ? 'medium' : 'low';
208
226
  const dh = deltaHours(delta);
209
227
  metrics.stale_count++;
210
228
  findings.push({
211
229
  severity,
212
- confidence: 0.9,
230
+ // A repo-anchored delta is a measurement; a clock-anchored one is the
231
+ // best available guess about a repo this probe cannot see.
232
+ confidence: haveCommitBasis ? 0.9 : 0.6,
213
233
  file_path: overviewPath,
214
234
  title: `[vault-staleness] ${slug}: ${formatDelta(delta)} since last sync`,
215
- description:
216
- `lastSync is ${formatDelta(delta)} old (threshold: 24h). ` +
217
- `Note: this compares lastSync age against probe run time, not upstream lastCommit — ` +
218
- `a future iteration will add repo-path resolution for a more precise delta.`,
219
- evidence: { slug, tier, lastSync, delta_hours: dh },
235
+ description: haveCommitBasis
236
+ ? `The repo advanced ${formatDelta(delta)} past the last vault sync ` +
237
+ `(lastCommit ${lastCommit} vs lastSync ${lastSync}, threshold: 24h).`
238
+ : `lastSync is ${formatDelta(delta)} old (threshold: 24h). ` +
239
+ `No lastCommit in the frontmatter, so this compares against probe run time — ` +
240
+ `an idle repo reads as stale here. Add lastCommit to make the delta repo-anchored.`,
241
+ evidence: { slug, tier, lastSync, lastCommit, basis, delta_hours: dh },
220
242
  });
221
243
  }
222
244
  }
@@ -237,6 +259,8 @@ export async function runProbe(projectRoot, config) {
237
259
  slug: f.evidence.slug,
238
260
  severity: f.severity,
239
261
  last_sync: f.evidence.lastSync,
262
+ last_commit: f.evidence.lastCommit,
263
+ basis: f.evidence.basis,
240
264
  delta_hours: f.evidence.delta_hours,
241
265
  flag: 'stale-yes',
242
266
  })),
@@ -8,23 +8,16 @@
8
8
 
9
9
  **Detection Method:**
10
10
 
11
- **Preferred (language-mapper-supported projects):**
12
-
13
- Call `extractSemanticSlices(filePath, { type: 'imports' })` from `scripts/lib/language-mappers/index.mjs` for each source file to obtain a typed list of import edges. The mapper handles TypeScript, JavaScript, and Markdown files natively; unsupported file types return an empty array (graceful degradation). Feed the resulting `{source_file -> [imported_file]}` adjacency map directly into the cycle-detection loop below.
14
-
15
- ```js
16
- // Pseudocodewire once per probe run
17
- import { extractSemanticSlices } from '$PLUGIN_ROOT/scripts/lib/language-mappers/index.mjs';
18
-
19
- for (const filePath of sourceFiles) {
20
- const imports = await extractSemanticSlices(filePath, { type: 'imports' });
21
- for (const { resolved } of imports) {
22
- adjacency.get(filePath).push(resolved);
23
- }
24
- }
25
- ```
26
-
27
- **Fallback (when language-mapper returns empty or for unsupported file types):**
11
+ > **The language-mapper does NOT produce import edges.** An earlier revision of this
12
+ > file described a "preferred" path calling
13
+ > `extractSemanticSlices(filePath, { type: 'imports' })`. Neither half of that call
14
+ > exists: the real signature is
15
+ > `extractSemanticSlices(filePath, content, options)` — the second parameter is the
16
+ > file's CONTENT, not an options object and `'imports'` is not a slice kind.
17
+ > `SLICE_KINDS` (`scripts/lib/language-mappers/index.mjs`) is
18
+ > `function | class | interface | type | export | section`; there is no import kind
19
+ > and no `resolved` field, so no adjacency map can be built from it. The grep/madge
20
+ > method below is the only detection method this probe has.
28
21
 
29
22
  ```bash
30
23
  # Build import graph from source files
@@ -39,7 +32,7 @@ Grep pattern: (import\s+.*from\s+["']([^"']+)["']|require\s*\(\s*["']([^"']+)["'
39
32
  npx madge --circular --extensions ts,tsx,js,jsx src/ 2>/dev/null
40
33
  ```
41
34
 
42
- Algorithm (cycle detection — same for both paths above):
35
+ Algorithm (cycle detection):
43
36
  1. Build `{source_file -> [imported_file]}` adjacency map
44
37
  2. Resolve relative imports to absolute paths
45
38
  3. For each file, BFS through imports with depth limit of 10
@@ -56,6 +49,15 @@ Files Involved:
56
49
 
57
50
  **Default Severity:** High.
58
51
 
52
+ **Related committed artefact (this repo only, and NOT a substitute):**
53
+ `hooks/_lib/hook-import-set.json`, regenerated by
54
+ `node scripts/generate-hook-import-set.mjs` (flags: `--plugin-root`, `--out`,
55
+ `--check`). It records `{file, reachable_from[]}` — which modules are transitively
56
+ reachable from a HOOK entry file — so it is a reachability allowlist scoped to
57
+ `hooks/`, not an edge list over the whole tree, and it cannot detect cycles. Use it
58
+ to answer "does an edit to this module reach a hook?", never as the adjacency map
59
+ above.
60
+
59
61
  ---
60
62
 
61
63
  ### Probe: complexity-hotspots
@@ -65,7 +65,7 @@ Compute the suitability verdict for **R** via the pure four-gate engine `compute
65
65
  | `autonomy` | `resolveDispatcherAutonomy({ committed, env, ownerConfig })` from `scripts/lib/config/dispatcher-autonomy.mjs` | The effective dial. Defaults to `'off'` when unset (fail-closed). |
66
66
  | `confidenceFloor` | the `confidence-floor` from the parsed `dispatcher-autonomy:` block (default 0.5) | Same source object as `autonomy`. |
67
67
  | `confidence` | mode-selector `selectMode(signals).confidence` (0..1 float) for the recommended session-type | The same mode-selector the Phase-2 heuristic and autopilot use. |
68
- | `ci` | `checkCiStatus({ repoRoot: R })` → `{ status }` \| `null` | **CRITICAL (NICE-b):** Phase-1 `rank.mjs` exposes only the BARE status string (`readiness.ciStatus`). The engine's G3 gate expects an OBJECT `{ status }` — wrap it as `{ status: ciStatus }` when you HAVE a status; on a CI-fetch FAILURE pass `ci = null` (checkCiStatus already returns `null` on failure — pass it straight through). **Do NOT synthesize `{ status: undefined }`** (or `{}`): that present-but-unusable object hits the engine's MALFORMED branch (`'CI signal malformed — treated as absent'`) instead of the clean ABSENT branch (`'CI signal absent'`). Both pass G3 + warn, but `null` is the honest "no signal" — reserve the malformed branch for a genuinely unexpected shape. A bare string ALSO hits the malformed branch — always wrap or null. |
68
+ | `ci` | `checkCiStatus({ repoRoot: R })` → `{ status }` \| `null` | **CRITICAL (NICE-b):** Phase-1 `rank.mjs` exposes only the BARE status string (`readiness.ciStatus`). The engine's G3 gate expects an OBJECT `{ status }` — wrap it as `{ status: ciStatus }` when you HAVE a status; on a genuine ABSENCE (no CI configured) pass `ci = null`. **Since #1031 `checkCiStatus` no longer returns `null` on failure**an unreadable state comes back as `{severity:'warn', ok:false, degraded:<reason>}`, and Phase-1 `rank.mjs` surfaces exactly that as `readiness.ciDegraded` with `readiness.ciStatus === 'unknown'`. Wrap that state as `{ status: 'unknown' }` (the engine warns `'CI signal unknown — treated as absent'` and G3 still passes) rather than flattening it to `null` — `null` claims a measured absence the probe never established. **Do NOT synthesize `{ status: undefined }`** (or `{}`): that present-but-unusable object hits the engine's MALFORMED branch (`'CI signal malformed — treated as absent'`) instead of the clean ABSENT branch (`'CI signal absent'`). Both pass G3 + warn, but `null` is the honest "no signal" — reserve the malformed branch for a genuinely unexpected shape. A bare string ALSO hits the malformed branch — always wrap or null. |
69
69
  | `resourceVerdict` | the host resource verdict string (`'green'\|'warn'\|'degraded'\|'critical'`) from `rank.mjs` (`readiness.resourceVerdict`) or a fresh `evaluate(probe(), thresholds).verdict` | Host-level — already fetched once in Phase 1. **NICE-b:** on a genuine probe FAILURE (no signal), prefer `resourceVerdict = null` over synthesizing `'green'`. `null` = "no signal" ⇒ G4 passes + warns (`'resource signal absent'`) — honest. Synthesizing `'green'` fabricates a positive signal the host never reported and can let an autonomous launch proceed against an unknown host state. Pass the real verdict string when you have one; `null` when you do not. |
70
70
  | `recentRuns` | `readRecentAutopilotRuns({ repoRoot: R })` from `scripts/lib/autopilot/recent-runs.mjs` | NEW reader. Reads `<R>/.orchestrator/metrics/autopilot.jsonl`, returns the most-recent records (newest-last), never throws (`[]` on missing/unreadable). Pass the TRUE count — the engine's G2 gate omits-with-warn below 5 runs and otherwise checks `fired/N < 0.2`. |
71
71
 
@@ -167,7 +167,8 @@ Data → stdout, warnings/errors → stderr (never mixed). Exit codes follow `.c
167
167
  - **Running this from a subagent** — coordinator-only (AUQ is unavailable in subagents).
168
168
  - **Fail-OPEN verdict gate (#682)** — keying the autonomous launch on `verdict.suitable` alone (ignoring `autonomy === 'autonomous-gated'`) auto-launches in `advisory`/`off` mode. ALWAYS gate on BOTH.
169
169
  - **Feeding the bare CI string into the engine** — `rank.mjs` exposes `ciStatus` as a bare string; `computeSuitabilityVerdict` wants `{ status }`. Wrap it (`{ status: ciStatus }`) or pass `null` — a bare string silently hits the malformed-absent branch. A red CI fed as a bare string would PASS G3 (masked as malformed) instead of forcing the FORCED-fail branch (NICE-c).
170
- - **Synthesizing an absent signal instead of passing `null` (NICE-b)** — on a CI-fetch or resource-probe failure, pass `ci = null` / `resourceVerdict = null` (honest "no signal" gate passes + warns). Do NOT synthesize `{ status: undefined }` (hits the malformed branch) and do NOT fabricate `'green'` / `{ status: 'green' }` (invents a positive signal the host never reported and can green-light an autonomous launch against an unknown state).
170
+ - **Flattening a DEGRADED CI reading to `null` (#1031)** — `readiness.ciDegraded` set (⇒ `readiness.ciStatus === 'unknown'`) means the state could NOT be read, which is weaker than absence, not equal to it. Pass `{ status: 'unknown' }`; `null` asserts a measured absence nothing established.
171
+ - **Synthesizing an absent signal instead of passing `null` (NICE-b)** — on a resource-probe failure, pass `resourceVerdict = null` (honest "no signal" ⇒ gate passes + warns). Do NOT synthesize `{ status: undefined }` (hits the malformed branch) and do NOT fabricate `'green'` / `{ status: 'green' }` (invents a positive signal the host never reported and can green-light an autonomous launch against an unknown state).
171
172
  - **Pre-truncating `recentRuns` below 5** — passing fewer than the true on-disk count when ≥ 5 runs exist falsely triggers the engine's <5-run omission branch and skips the kill-switch gate. Pass the TRUE count; never call `readRecentAutopilotRuns` with `limit < 5` on the launch-gate read (the reader honours a small `limit` literally and will not clamp it upward).
172
173
  - **Auto-launching against a non-green verdict** — a CI-red / resource-critical / low-confidence verdict ALWAYS falls to inform + ask, even under `autonomous-gated`. Never proceed straight to claim on a non-suitable verdict.
173
174