session-orchestrator 3.21.0 → 3.23.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 (345) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.cursor/commands/autopilot-multi.md +14 -0
  5. package/.cursor/commands/autopilot.md +14 -0
  6. package/.cursor/commands/bootstrap.md +14 -0
  7. package/.cursor/commands/brainstorm.md +14 -0
  8. package/.cursor/commands/close.md +13 -0
  9. package/.cursor/commands/contract-version-bump.md +14 -0
  10. package/.cursor/commands/debug.md +14 -0
  11. package/.cursor/commands/discovery.md +14 -0
  12. package/.cursor/commands/dispatcher.md +14 -0
  13. package/.cursor/commands/eli5.md +14 -0
  14. package/.cursor/commands/eval.md +14 -0
  15. package/.cursor/commands/evolve.md +14 -0
  16. package/.cursor/commands/go.md +14 -0
  17. package/.cursor/commands/grill.md +14 -0
  18. package/.cursor/commands/harness-audit.md +13 -0
  19. package/.cursor/commands/journey-audit.md +14 -0
  20. package/.cursor/commands/memory-cleanup.md +14 -0
  21. package/.cursor/commands/persona-panel.md +14 -0
  22. package/.cursor/commands/plan.md +14 -0
  23. package/.cursor/commands/portfolio.md +14 -0
  24. package/.cursor/commands/reconcile.md +14 -0
  25. package/.cursor/commands/release.md +14 -0
  26. package/.cursor/commands/repo-audit.md +13 -0
  27. package/.cursor/commands/session.md +14 -0
  28. package/.cursor/commands/spinout.md +14 -0
  29. package/.cursor/commands/sunset-review.md +14 -0
  30. package/.cursor/commands/templates-ack.md +14 -0
  31. package/.cursor/commands/test.md +14 -0
  32. package/.cursor/hooks.json +60 -0
  33. package/.cursor/rules/000-session-orchestrator.mdc +11 -2
  34. package/.cursor/rules/010-session-workflow.mdc +9 -1
  35. package/.cursor/rules/020-quality-gates.mdc +1 -1
  36. package/.cursor/rules/030-wave-execution.mdc +1 -1
  37. package/.cursor/rules/040-discovery.mdc +6 -6
  38. package/.cursor/rules/050-plan.mdc +10 -10
  39. package/.cursor/rules/070-gitlab-ops.mdc +73 -57
  40. package/.cursor/rules/080-ecosystem-health.mdc +7 -7
  41. package/.cursor/skills/architecture/SKILL.md +13 -0
  42. package/.cursor/skills/autopilot/SKILL.md +12 -0
  43. package/.cursor/skills/bootstrap/SKILL.md +12 -0
  44. package/.cursor/skills/brainstorm/SKILL.md +13 -0
  45. package/.cursor/skills/claude-md-drift-check/SKILL.md +13 -0
  46. package/.cursor/skills/contract-version-bump/SKILL.md +12 -0
  47. package/.cursor/skills/convergence-monitoring/SKILL.md +12 -0
  48. package/.cursor/skills/daily/SKILL.md +12 -0
  49. package/.cursor/skills/debug/SKILL.md +13 -0
  50. package/.cursor/skills/discovery/SKILL.md +13 -0
  51. package/.cursor/skills/dispatcher/SKILL.md +13 -0
  52. package/.cursor/skills/docs-orchestrator/SKILL.md +13 -0
  53. package/.cursor/skills/domain-model/SKILL.md +13 -0
  54. package/.cursor/skills/ecosystem-health/SKILL.md +13 -0
  55. package/.cursor/skills/eli5/SKILL.md +13 -0
  56. package/.cursor/skills/eval/SKILL.md +12 -0
  57. package/.cursor/skills/evolve/SKILL.md +13 -0
  58. package/.cursor/skills/frontmatter-guard/SKILL.md +13 -0
  59. package/.cursor/skills/gitlab-ops/SKILL.md +13 -0
  60. package/.cursor/skills/gitlab-portfolio/SKILL.md +13 -0
  61. package/.cursor/skills/grill/SKILL.md +13 -0
  62. package/.cursor/skills/hook-development/SKILL.md +13 -0
  63. package/.cursor/skills/journey-audit/SKILL.md +13 -0
  64. package/.cursor/skills/mcp-builder/SKILL.md +13 -0
  65. package/.cursor/skills/memory-cleanup/SKILL.md +12 -0
  66. package/.cursor/skills/mode-selector/SKILL.md +13 -0
  67. package/.cursor/skills/npm-publish/SKILL.md +12 -0
  68. package/.cursor/skills/peekaboo-driver/SKILL.md +13 -0
  69. package/.cursor/skills/persona-panel/SKILL.md +12 -0
  70. package/.cursor/skills/plan/SKILL.md +13 -0
  71. package/.cursor/skills/playwright-driver/SKILL.md +13 -0
  72. package/.cursor/skills/quality-gates/SKILL.md +13 -0
  73. package/.cursor/skills/reconcile/SKILL.md +12 -0
  74. package/.cursor/skills/repo-audit/SKILL.md +13 -0
  75. package/.cursor/skills/session-end/SKILL.md +13 -0
  76. package/.cursor/skills/session-plan/SKILL.md +13 -0
  77. package/.cursor/skills/session-start/SKILL.md +13 -0
  78. package/.cursor/skills/skill-creator/SKILL.md +13 -0
  79. package/.cursor/skills/spinout/SKILL.md +12 -0
  80. package/.cursor/skills/sunset-review/SKILL.md +13 -0
  81. package/.cursor/skills/test-runner/SKILL.md +13 -0
  82. package/.cursor/skills/tmux-layout/SKILL.md +13 -0
  83. package/.cursor/skills/ubiquitous-language/SKILL.md +13 -0
  84. package/.cursor/skills/using-orchestrator/SKILL.md +13 -0
  85. package/.cursor/skills/vault-mirror/SKILL.md +13 -0
  86. package/.cursor/skills/vault-sync/SKILL.md +13 -0
  87. package/.cursor/skills/wave-executor/SKILL.md +13 -0
  88. package/.cursor/skills/write-executable-plan/SKILL.md +13 -0
  89. package/.mcp.json +4 -1
  90. package/CHANGELOG.md +269 -0
  91. package/README.md +18 -15
  92. package/agents/AGENTS.md +23 -4
  93. package/agents/code-implementer.md +2 -1
  94. package/agents/db-specialist.md +2 -1
  95. package/agents/docs-writer.md +3 -1
  96. package/agents/eval-judge.md +1 -1
  97. package/agents/memory-proposal-collector.md +6 -4
  98. package/agents/session-reviewer.md +7 -1
  99. package/agents/test-writer.md +2 -1
  100. package/agents/ui-developer.md +2 -1
  101. package/commands/bootstrap.md +2 -2
  102. package/commands/close.md +3 -1
  103. package/commands/eli5.md +33 -0
  104. package/commands/go.md +1 -1
  105. package/commands/journey-audit.md +43 -0
  106. package/commands/release.md +5 -3
  107. package/commands/test.md +2 -2
  108. package/docs/USER-GUIDE.md +2 -2
  109. package/docs/ci-setup.md +14 -0
  110. package/docs/codex-setup.md +64 -0
  111. package/docs/components.md +7 -6
  112. package/docs/cursor-setup.md +26 -47
  113. package/docs/events-schema.md +76 -4
  114. package/docs/github-mirror-protection.md +197 -0
  115. package/docs/pi-setup.md +2 -0
  116. package/docs/rule-authoring.md +3 -1
  117. package/docs/scope-collision-guard.md +52 -5
  118. package/docs/session-config-reference.md +57 -12
  119. package/docs/session-config-template.md +4 -3
  120. package/docs/telemetry.md +22 -0
  121. package/hooks/_lib/lock-bootstrap.mjs +26 -16
  122. package/hooks/_lib/vcs-create-matcher.mjs +397 -38
  123. package/hooks/enforce-scope.mjs +64 -0
  124. package/hooks/hooks-codex.json +1 -1
  125. package/hooks/hooks-cursor.json +201 -20
  126. package/hooks/hooks-pi.json +1 -1
  127. package/hooks/hooks.json +12 -2
  128. package/hooks/on-session-end.mjs +237 -104
  129. package/hooks/on-session-start.mjs +404 -110
  130. package/hooks/on-stop.mjs +48 -9
  131. package/hooks/post-subagent-discovery-validator.mjs +34 -3
  132. package/hooks/post-tool-batch-wave-signal.mjs +11 -2
  133. package/hooks/pre-auq-clarity.mjs +787 -0
  134. package/hooks/pre-bash-issue-budget.mjs +133 -21
  135. package/hooks/pre-bash-sessions-ledger-guard.mjs +159 -0
  136. package/hooks/pre-bash-staging-fence.mjs +4 -0
  137. package/hooks/pre-task-scope-disjoint.mjs +368 -35
  138. package/hooks/skill-invocation-telemetry.mjs +21 -10
  139. package/monitors/monitors.json +6 -0
  140. package/package.json +3 -1
  141. package/pi/prompts/eli5.md +12 -0
  142. package/pi/prompts/journey-audit.md +12 -0
  143. package/rules/_index.md +9 -1
  144. package/rules/always-on/ask-via-tool.md +62 -0
  145. package/rules/always-on/bash-harness-pitfalls.md +168 -0
  146. package/rules/always-on/build-value.md +47 -0
  147. package/rules/always-on/cross-session-messaging.md +59 -0
  148. package/rules/always-on/loop-and-monitor.md +221 -0
  149. package/rules/always-on/parallel-sessions.md +142 -12
  150. package/rules/always-on/receiving-review.md +108 -0
  151. package/rules/always-on/test-value.md +40 -0
  152. package/rules/always-on/verification-before-completion.md +77 -0
  153. package/scripts/archive-closed-prds.mjs +258 -18
  154. package/scripts/auq-audit.mjs +825 -0
  155. package/scripts/autopilot.mjs +12 -8
  156. package/scripts/backfill-evidence-digest.mjs +376 -0
  157. package/scripts/cursor-install.mjs +89 -48
  158. package/scripts/export-hw-learnings.mjs +143 -2
  159. package/scripts/express-path.mjs +299 -0
  160. package/scripts/generate-cursor-adapter.mjs +253 -0
  161. package/scripts/github-protection-audit.mjs +358 -0
  162. package/scripts/lib/auq/clarity.mjs +1314 -0
  163. package/scripts/lib/auq/parse.mjs +1006 -0
  164. package/scripts/lib/auq/schema.mjs +1457 -0
  165. package/scripts/lib/autopilot/worktree-pipeline.mjs +240 -16
  166. package/scripts/lib/build-live-signals.mjs +24 -5
  167. package/scripts/lib/ci-status-banner.mjs +221 -68
  168. package/scripts/lib/command-blocker.mjs +70 -0
  169. package/scripts/lib/config/dispatcher-autonomy-capture.mjs +32 -9
  170. package/scripts/lib/config/reconcile.mjs +79 -4
  171. package/scripts/lib/config/section-extractor.mjs +235 -36
  172. package/scripts/lib/config/vault-integration.mjs +12 -1
  173. package/scripts/lib/config-schema.mjs +9 -1
  174. package/scripts/lib/config.mjs +57 -6
  175. package/scripts/lib/convergence-monitor.mjs +13 -2
  176. package/scripts/lib/cursor-hook-bridge.mjs +443 -0
  177. package/scripts/lib/dispatcher/cli.mjs +2 -2
  178. package/scripts/lib/dispatcher/rank.mjs +4 -7
  179. package/scripts/lib/express-path.mjs +327 -0
  180. package/scripts/lib/file-lock.mjs +22 -4
  181. package/scripts/lib/gates/gate-full.mjs +84 -11
  182. package/scripts/lib/gates/gate-helpers.mjs +93 -21
  183. package/scripts/lib/git-config-drift.mjs +134 -5
  184. package/scripts/lib/host-identity.mjs +247 -2
  185. package/scripts/lib/instruction-budget-guard.mjs +31 -1
  186. package/scripts/lib/io.mjs +239 -0
  187. package/scripts/lib/issue-budget.mjs +284 -31
  188. package/scripts/lib/learnings/io.mjs +55 -10
  189. package/scripts/lib/learnings/schema.mjs +95 -28
  190. package/scripts/lib/lock-reaper.mjs +7 -1
  191. package/scripts/lib/locks/staging-fence-lock.mjs +5 -1
  192. package/scripts/lib/locks/state-md-lock.mjs +8 -1
  193. package/scripts/lib/memory-banner.mjs +5 -2
  194. package/scripts/lib/memory-paths.mjs +15 -6
  195. package/scripts/lib/mode-selector/scoring.mjs +53 -6
  196. package/scripts/lib/owner-interview.mjs +78 -32
  197. package/scripts/lib/peer-discovery.mjs +73 -22
  198. package/scripts/lib/platform.mjs +72 -9
  199. package/scripts/lib/plugin-root.mjs +143 -19
  200. package/scripts/lib/project-hygiene.mjs +104 -4
  201. package/scripts/lib/quality-gate.mjs +271 -13
  202. package/scripts/lib/reconcile/emitter.mjs +87 -19
  203. package/scripts/lib/reconcile/engine.mjs +281 -13
  204. package/scripts/lib/reconcile/idempotency.mjs +102 -1
  205. package/scripts/lib/reconcile/renderer.mjs +165 -7
  206. package/scripts/lib/reconcile/sanitize.mjs +40 -17
  207. package/scripts/lib/reconcile/writer.mjs +415 -84
  208. package/scripts/lib/resource-probe/evaluate.mjs +330 -149
  209. package/scripts/lib/resource-probe/probe-platform.mjs +35 -0
  210. package/scripts/lib/resource-probe.mjs +18 -2
  211. package/scripts/lib/rule-loader.mjs +37 -2
  212. package/scripts/lib/rules-sync.mjs +51 -8
  213. package/scripts/lib/scope-gate.mjs +90 -0
  214. package/scripts/lib/session-close-backfill.mjs +369 -28
  215. package/scripts/lib/session-discovery.mjs +13 -3
  216. package/scripts/lib/session-end/phase-skip.mjs +37 -4
  217. package/scripts/lib/session-end/worktree-cleanup.mjs +154 -7
  218. package/scripts/lib/session-id.mjs +30 -14
  219. package/scripts/lib/session-identity/own-session.mjs +159 -0
  220. package/scripts/lib/session-lock.mjs +85 -30
  221. package/scripts/lib/session-schema/normalizer.mjs +70 -3
  222. package/scripts/lib/session-schema/validator.mjs +40 -0
  223. package/scripts/lib/session-start-probes.mjs +608 -0
  224. package/scripts/lib/session-transition.mjs +277 -0
  225. package/scripts/lib/sessions-staleness-banner.mjs +124 -57
  226. package/scripts/lib/spiral-carryover.mjs +111 -9
  227. package/scripts/lib/state-md/frontmatter-mutators.mjs +41 -8
  228. package/scripts/lib/state-md/mission-status.mjs +467 -72
  229. package/scripts/lib/state-md/yaml-parser.mjs +145 -16
  230. package/scripts/lib/state-md.mjs +12 -2
  231. package/scripts/lib/telemetry/sync.mjs +46 -8
  232. package/scripts/lib/validate/check-agents.mjs +66 -0
  233. package/scripts/lib/validate/check-auq-clarity.mjs +274 -0
  234. package/scripts/lib/validate/check-cursor-adapter.mjs +102 -0
  235. package/scripts/lib/validate/check-dead-bridge.mjs +24 -2
  236. package/scripts/lib/validate/check-doc-cli-commands.mjs +16 -32
  237. package/scripts/lib/validate/check-hooks-symmetry.mjs +59 -63
  238. package/scripts/lib/validate/check-playwright-mcp-canary.mjs +13 -22
  239. package/scripts/lib/validate/check-plugin-monitors.mjs +10 -4
  240. package/scripts/lib/validate/check-rules.mjs +153 -9
  241. package/scripts/lib/validate/check-test-value-bans.mjs +165 -17
  242. package/scripts/lib/validate/check-unwired-features.mjs +340 -32
  243. package/scripts/lib/validate/repo-files.mjs +275 -0
  244. package/scripts/lib/validate-vendored-rules.mjs +229 -7
  245. package/scripts/lib/vault-backfill/glab.mjs +91 -58
  246. package/scripts/lib/vault-backfill/manifest.mjs +28 -8
  247. package/scripts/lib/vault-mirror/process.mjs +99 -43
  248. package/scripts/lib/vault-mirror/telemetry.mjs +210 -0
  249. package/scripts/lib/vault-staleness-banner.mjs +76 -6
  250. package/scripts/lib/vault-status/board-writer.mjs +211 -10
  251. package/scripts/lib/vault-status/narrative-mirror.mjs +188 -8
  252. package/scripts/lib/vcs-repo-spec.mjs +182 -13
  253. package/scripts/lib/wave-executor/foreign-dispatch.mjs +832 -0
  254. package/scripts/lib/wave-resource-gate.mjs +67 -73
  255. package/scripts/lib/wave-transcript-tail.mjs +869 -0
  256. package/scripts/materialize-wave-scope.mjs +478 -0
  257. package/scripts/mcp-server.sh +11 -2
  258. package/scripts/parse-config.mjs +65 -0
  259. package/scripts/release.mjs +443 -122
  260. package/scripts/run-quality-gate.mjs +14 -0
  261. package/scripts/token-audit.sh +9 -2
  262. package/scripts/validate-plugin.mjs +6 -0
  263. package/scripts/validate-wave-scope.mjs +73 -1
  264. package/scripts/vault-backfill.mjs +32 -5
  265. package/scripts/vault-mirror.mjs +203 -34
  266. package/skills/_shared/monitor-patterns.md +31 -5
  267. package/skills/_shared/parallel-aware-auq.md +31 -25
  268. package/skills/_shared/parallel-aware-preamble.md +35 -4
  269. package/skills/_shared/platform-tools.md +11 -5
  270. package/skills/_shared/state-ownership.md +61 -8
  271. package/skills/autopilot/SKILL.md +5 -1
  272. package/skills/bootstrap/SKILL.md +5 -4
  273. package/skills/bootstrap/_shared-template.md +18 -10
  274. package/skills/bootstrap/deep-template.md +10 -6
  275. package/skills/bootstrap/fast-template.md +15 -8
  276. package/skills/bootstrap/standard-template.md +10 -6
  277. package/skills/brainstorm/SKILL.md +18 -18
  278. package/skills/brainstorm/soul.md +12 -0
  279. package/skills/claude-md-drift-check/checker.mjs +39 -11
  280. package/skills/discovery/SKILL.md +28 -24
  281. package/skills/dispatcher/SKILL.md +1 -1
  282. package/skills/eli5/SKILL.md +43 -0
  283. package/skills/evolve/SKILL.md +8 -9
  284. package/skills/gitlab-ops/SKILL.md +30 -26
  285. package/skills/grill/SKILL.md +6 -6
  286. package/skills/grill/soul.md +16 -0
  287. package/skills/journey-audit/SKILL.md +269 -0
  288. package/skills/memory-cleanup/SKILL.md +2 -2
  289. package/skills/npm-publish/SKILL.md +4 -4
  290. package/skills/peekaboo-driver/SKILL.md +18 -6
  291. package/skills/persona-panel/SKILL.md +1 -1
  292. package/skills/plan/SKILL.md +18 -16
  293. package/skills/plan/mode-feature.md +1 -1
  294. package/skills/plan/mode-new.md +35 -23
  295. package/skills/plan/soul.md +12 -0
  296. package/skills/reconcile/SKILL.md +44 -4
  297. package/skills/session-end/SKILL.md +70 -24
  298. package/skills/session-end/metrics-collection.md +7 -4
  299. package/skills/session-end/phase-3-6-tail.md +48 -5
  300. package/skills/session-end/phase-3-7a-recommendations.md +16 -2
  301. package/skills/session-plan/SKILL.md +6 -1
  302. package/skills/session-plan/wave-template.md +1 -0
  303. package/skills/session-start/SKILL.md +96 -48
  304. package/skills/session-start/phase-2-5-docs-planning.md +8 -8
  305. package/skills/session-start/phase-4-5-resource-health.md +82 -19
  306. package/skills/session-start/phase-7-5-mode-selector.md +15 -3
  307. package/skills/session-start/phase-8-5-express-path.md +77 -12
  308. package/skills/session-start/soul.md +110 -0
  309. package/skills/test-runner/SKILL.md +2 -2
  310. package/skills/using-orchestrator/SKILL.md +1 -1
  311. package/skills/vault-sync/validator.mjs +31 -0
  312. package/skills/wave-executor/SKILL.md +4 -2
  313. package/skills/wave-executor/circuit-breaker.md +34 -9
  314. package/skills/wave-executor/wave-loop.md +129 -24
  315. package/skills/write-executable-plan/SKILL.md +6 -6
  316. package/templates/_shared/journey-manifest.md +110 -0
  317. package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +0 -8
  318. package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +0 -8
  319. package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
  320. package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +0 -8
  321. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
  322. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +0 -8
  323. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +0 -8
  324. package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +0 -8
  325. package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +0 -10
  326. package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +0 -8
  327. package/skills/vault-sync/tests/fixtures/clean-vault/README.md +0 -3
  328. package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +0 -11
  329. package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
  330. package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +0 -9
  331. package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +0 -8
  332. package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
  333. package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
  334. package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +0 -7
  335. package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +0 -9
  336. package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
  337. package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +0 -11
  338. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +0 -3
  339. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +0 -3
  340. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
  341. package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +0 -11
  342. package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
  343. package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +0 -11
  344. package/skills/vault-sync/tests/schema-drift.test.mjs +0 -133
  345. package/templates/_shared/rules/parallel-sessions.md +0 -77
@@ -97,9 +97,13 @@ Skip the deviation entry on `proceed`, even when `concurrentSessions` warns —
97
97
 
98
98
  When `worker-pool.enabled: true` in Session Config, dispatch via `runWavePool()` from `scripts/lib/wave-executor/pool.mjs` with `maxParallel = worker-pool.max-parallel || agents-per-wave` — the bounded cursor is the opt-in alternative that supersedes manual batching. Else fall back to the small-batch Agent() dispatch described below (3–4 calls per message, cumulative up to the wave's `agents-per-wave` cap).
99
99
 
100
+ **Worker-pool and background dispatch compose — neither replaces the other.** The pool is the opt-in for bounded-concurrency *pull* (how many agents may be in flight at once); background dispatch is the default *transport* (whether the coordinator's turn returns before an agent finishes). Pool workers may themselves background-dispatch. Adopting either removes no mechanism from the other, and neither adds a second mechanism to maintain.
101
+
100
102
  **Worker-pool timing note:** when `worker-pool.enabled: true`, per-agent start and end times are recorded individually in subagents.jsonl as workers pull from the cursor at different moments. Wave-level timings (for progress updates and metrics) are computed as first-worker-start to last-worker-finish, not as a uniform fan-out timestamp.
101
103
 
102
- Use the **Agent tool** to dispatch this wave's agents in **SMALL BATCHES of 3–4 Agent() calls per message** (cumulative up to the wave's `agents-per-wave` cap). Large single-message fan-outs (>4 Agent() calls in one message) are **FORBIDDEN** — fleet evidence (conf 1.0, 5 sessions) shows they drop Agent() calls SILENTLY (the coordinator receives fewer tool-results than it dispatched, with no error), whereas serial / small-batch dispatch held 13/13 and 8/8. Dispatch the first batch, wait for its tool-results, then dispatch the next batch, until the wave's planned agents are all started. See `docs/specs/2026-07-02-fleet-mining-followup-grill.md` (C4) for the policy rationale. The `worker-pool.enabled: true` path (above) is the mechanised opt-in alternative to manual batching.
104
+ Use the **Agent tool** to dispatch this wave's agents. **`run_in_background: true` is ALLOWED and RECOMMENDED for wave dispatch** measured 2026-08-22 (v2.1.239): under blocking dispatch the coordinator was 143 s incapable of acting between an agent's mid-run escalation and its own next turn escalation latency equals the batch's remaining runtime. Background dispatch returns turns to the coordinator between agent completions; a running agent received a queued message mid-run and answered ~9 min before its final report.
105
+
106
+ Dispatch in **SMALL BATCHES of 3–4 Agent() calls per message** (cumulative up to the wave's `agents-per-wave` cap). Large single-message fan-outs (>4 Agent() calls in one message) remain **FORBIDDEN** — fleet evidence (conf 1.0, 5 sessions) shows they drop Agent() calls SILENTLY (the coordinator receives fewer results than it dispatched, with no error), whereas serial / small-batch dispatch held 13/13 and 8/8. That evidence PREDATES backgrounding and is untouched by it; what backgrounding changes is the *cost* of batching, which is now near zero — a background batch returns its launch acks immediately, so batches no longer serialize the wave. Dispatch a batch, let its acks return, then dispatch the next, until every planned agent is started; then run **Started-Set Verification** below. See `docs/specs/2026-07-02-fleet-mining-followup-grill.md` (C4) for the batching-policy rationale. The `worker-pool.enabled: true` path (above) is the mechanised opt-in alternative to manual batching.
103
107
 
104
108
  Read each wave's dispatch metadata from the session plan header (e.g., `(4 agents, parallel, isolation: worktree)`). When the plan specifies `isolation`, use it verbatim. When the plan does not specify, resolve the effective value via `resolveIsolation({ agentCount, sessionType, collisionRisk, configIsolation })` from `scripts/lib/wave-sizing.mjs` — the graduated default (#194) replaces the previous session-type-only switch. Pass the resolved value to each Agent() tool call per `circuit-breaker.md` (omit the parameter when resolved to `none`).
105
109
 
@@ -109,15 +113,26 @@ Before dispatching, verify the wave's agent count does not exceed `$CONFIG.agent
109
113
 
110
114
  #### Contract-Lock Serialization (Pattern A, #730/H1)
111
115
 
112
- When the session plan marks a wave task `contract-lock: true` (session-plan Step 3.5 step 6), dispatch that single agent ALONE as the first batch and WAIT for its tool-result before dispatching the disjoint fan-out batches. The lock agent freezes the shared contract (interfaces/schemas/shared types/constants) so the N follow-on agents build against a fixed surface instead of racing to invent it. Never place the contract-lock agent in the same batch as the impl agents — its output is an input to theirs. The contract file MUST NOT appear in any follow-on agent's allowedPaths (read-only reference). If the lock agent reports STATUS: partial/failed, PAUSE the fan-out and surface the choice via AskUserQuestion (proceed with partial contract / re-dispatch lock / abort wave).
116
+ When the session plan marks a wave task `contract-lock: true` (session-plan Step 3.5 step 6), dispatch that single agent ALONE as the first batch and WAIT for its **task-notification** (`<status>completed</status>`) before dispatching the disjoint fan-out batches — NOT for its tool-result, which under background dispatch is the immediate launch ack and would release the fan-out against an unfrozen contract (§ Started-Set Verification). The lock agent freezes the shared contract (interfaces/schemas/shared types/constants) so the N follow-on agents build against a fixed surface instead of racing to invent it. Never place the contract-lock agent in the same batch as the impl agents — its output is an input to theirs. The contract file MUST NOT appear in any follow-on agent's allowedPaths (read-only reference). If the lock agent reports STATUS: partial/failed, PAUSE the fan-out and surface the choice via AskUserQuestion (proceed with partial contract / re-dispatch lock / abort wave).
117
+
118
+ #### Started-Set Verification (fail-loud — #724, #1115)
119
+
120
+ Once all batches for the wave have been dispatched, verify the **started set** against the planned agent list (the agents named in the session plan for this wave). This closes the silent-drop failure class that motivated the small-batch default above (a large fan-out drops calls with no error).
121
+
122
+ **Never count launch acks.** Under background dispatch every Agent() call returns an immediate `Async agent launched successfully` ack. Counting acks against the plan would report full success with zero work done — a fail-silent hole inside the fail-loud check. Two distinct signals, each with its own source:
113
123
 
114
- #### Dispatch Verification (fail-loud#724)
124
+ - **STARTED** the agent's `agent-<id>.meta.json` sidecar exists under `~/.claude/projects/<encoded-repo-path>/<session-uuid>/subagents/`. The harness writes it at spawn, carrying `toolUseId` + `description` (measured 2026-08-25). Same substrate the tailer reads see the header of `scripts/lib/wave-transcript-tail.mjs`. The `subagents/` directory does not exist until the first spawn of the session.
125
+ - **COMPLETED** — the agent's task-notification carrying its `<tool-use-id>` and `<status>completed</status>`. NEVER the launch ack. `hooks/pre-task-scope-disjoint.mjs` (`ASYNC_LAUNCH_ACK` constant + `buildTranscriptIndex()`) already implements exactly this ack-vs-completion distinction across both the sync and async record shapes — **cite it as the reference implementation; do not restate its logic here and do not fork a second one.**
115
126
 
116
- After each batch's Agent() tool-results return, and once all batches for the wave have been dispatched, **count the Agent tool-results received for this wave against the planned agent list** (the agents named in the session plan for this wave). This closes the silent-drop failure class that motivated the small-batch default above (a large fan-out drops calls with no error).
127
+ Three distinguishable states, each with its own action:
117
128
 
118
- - If every planned agent produced a tool-result → proceed to `### 2. Review Agent Outputs`.
119
- - If any planned agent produced **NO** tool-result (silent drop) → **re-dispatch ONLY the missing agents in a fresh batch** (3–4 per message) before proceeding to Review. Do NOT re-dispatch agents that already returned — that would duplicate their file writes. **Before dispatching any re-dispatch (or fix-pass) batch, re-run the Pre-Dispatch Scope-Union Assertion (§ Scope Manifest #3, #796) for each re-dispatched agent** — `allowedPaths` MUST NOT shrink while sibling agents of this wave are still running, or the re-dispatched agent's legitimate writes will be denied by Gate 7.
120
- - Record `agent_count_planned` (from the plan) and `agent_count_started` (distinct agents that produced a tool-result, after any re-dispatch) in the wave metrics (see § Capture wave metrics). A persistent gap after re-dispatch is a deviation log it to STATE.md `## Deviations`.
129
+ | State | Signal | Action |
130
+ |---|---|---|
131
+ | **never-started** | no `meta.json` sidecar after the batch's acks returned | Silent drop. **Re-dispatch ONLY the missing agents in a fresh batch** (3–4 per message) before proceeding to Review. Do NOT re-dispatch agents that already started that would duplicate their file writes. **Before dispatching any re-dispatch (or fix-pass) batch, re-run the Pre-Dispatch Scope-Union Assertion Scope Manifest #3, #796) for each re-dispatched agent** — `allowedPaths` MUST NOT shrink while sibling agents of this wave are still running, or the re-dispatched agent's legitimate writes will be denied by Gate 7. |
132
+ | **started-but-never-returned** | sidecar present, no task-notification | The tailer's territory (step 2.0-bis). Do NOT re-dispatch blindly — the agent may be inside one long tool call (transcripts flush per turn, so it is invisible meanwhile), and a second copy would race it on the same file scope. Inspect its `agent-<id>.jsonl` transcript, then decide. |
133
+ | **completed** | task-notification with `<status>completed</status>` | Proceed to `### 2. Review Agent Outputs`. |
134
+
135
+ - Record `agent_count_planned` (from the plan), `agent_count_started` (distinct agents with a `meta.json` sidecar, after any re-dispatch) and `agent_count_completed` (distinct agents whose task-notification arrived) in the wave metrics (see § Capture wave metrics). A persistent `planned > started` gap after re-dispatch is a silent drop; a `started > completed` gap at wave end is an agent that never returned. Both are deviations — log them to STATE.md `## Deviations`.
121
136
 
122
137
  #### Pre-Dispatch New-Directory Detection (#243)
123
138
 
@@ -269,12 +284,30 @@ For each agent in this wave:
269
284
  - What NOT to touch (other agents' files)
270
285
  >",
271
286
  subagent_type: "<from session plan>", // resolved agent type
272
- run_in_background: false // CRITICAL: always false wait for completion
287
+ run_in_background: true // RECOMMENDED verify the started set via meta.json sidecars, never via the launch ack (§ Started-Set Verification)
273
288
  })
274
289
  - Turn budget and status reporting: "You have a maximum of [maxTurns] turns for this task. If you cannot complete within this budget, report STATUS: partial with what was accomplished and what remains. At the end of your work, report STATUS: done (all acceptance criteria met) or STATUS: partial (some criteria unmet — list which ones)."
275
290
  - Optional open-questions reporting (Close Handover-Alignment-Gate, PRD 2026-07-07): "If you encountered a genuinely unresolved, user-facing question you could not answer within your task scope, report it as an additional line: OPEN-QUESTIONS: <question> | context: <one-line why this is unresolved> | candidates: <opt A / opt B>. This line is optional — omit it entirely when you have no such question. Do not use it for questions you could resolve yourself by reading more code."
276
291
  ```
277
292
 
293
+ ##### Third branch: foreign-model dispatch (`cursor:<model>` — #1150)
294
+
295
+ The colon heuristic (§ "How to detect project agents") has **three** readings, not two: no colon = project agent, `session-orchestrator:<agent>` = plugin agent, and `cursor:<model>` = **foreign channel**. When the session plan or `agent-mapping` resolves an agent to `cursor:<model>` (e.g. `impl: cursor:composer-2.5`), do NOT call the Agent tool. Dispatch **coordinator-direct** via `dispatchForeign({ model, prompt, repoRoot, role, runId, timeoutSec })` from `scripts/lib/wave-executor/foreign-dispatch.mjs`. Contract, in the order it binds:
296
+
297
+ 1. **`never_foreign` gate first.** `isNeverForeignRole(role)` returns `{ok:false, reason:'never-foreign-role'}` before any worktree or spawn. `NEVER_FOREIGN_ROLES` (impl-core, security-review, migration, release, secrets, incident, refactor-crosscut) is hand-copied from the account-switch routing SSOT; `dispatch-cursor.sh` enforces none of it, so **this adapter is the only gate** — never route around it with a shell call.
298
+ 2. **Detached worktree**, `<tmpdir>/so-foreign/<runId>` by default, `git worktree add --detach`. Never under `.claude/worktrees/` — eight readdir scanners in this repo read that path. The stream log is KEPT beside it (`<runId>.log.jsonl`), not trap-deleted.
299
+ 3. **Verdict measured at the filesystem, never from the model's prose.** `ok` is true only on exit 0 + no timeout + a non-empty changed set (`git diff --name-only` ∪ `ls-files --others`, since a diff alone is blind to new files). **An empty diff is a failure regardless of what the report says.**
300
+ 4. **MANDATORY Claude diff-review gate.** Read `result.diff` and judge it SEMANTICALLY before any merge-back — test-green is not the bar (measured counterexample: the foreign #1149 solution was test-green and semantically wrong). The **coordinator** applies (`git apply`) and commits; the foreign model never touches this repo's index or worktree (PSA-007). On review failure, discard the diff. **`result.hookTampering === true` invalidates the run regardless of `ok`**: the child repointed or rewrote the shared `.git` hooks path (a linked worktree isolates the tree, not `core.hooksPath`), and that write is invisible in `result.diff` — discard and do not merge. `null` means the fingerprint could not be read (not measured), never "clean".
301
+ 5. **`removeForeignWorktree({ repoRoot, worktreePath })` after the review** — never before, because a failed run must stay inspectable. It **refuses a `worktreePath` outside `<tmpdir>/so-foreign*`** (returns `{ok:false, reason:'unsafe-...'}` and makes no git call), so it cannot `--force`-remove a sibling session's worktree.
302
+
303
+ **Trust boundary.** A foreign run bypasses the ENTIRE hook chain — no `PreToolUse:Agent`, no scope enforcement, no `SubagentStop` telemetry (verified against `hooks/hooks.json`, W1/D4). Everything inside the detached worktree is **untrusted** until a Claude coordinator has read the diff and applied it. The replacement ledger record is the `orchestrator.foreign_dispatch.completed` event the adapter emits (payload in `docs/events-schema.md`), which fires on refusals too — a blocked dispatch is a record, not a silence.
304
+
305
+ **Composition.** A foreign run is a background Bash task, so it composes with background `Agent()` dispatch in the same wave. It is **NOT** part of § Started-Set Verification: no `meta.json` sidecar exists for it and no task-notification arrives. Its lifecycle is the adapter's return value — do not count it as planned/started/completed, and do not read its absence from the started set as a drop.
306
+
307
+ **Model selection** is not decided here: the account-switch routing SSOT (ADR-002 / `tools/routing/routing.yaml`) owns it. Working defaults: `composer-2.5` for foreign impl; `cursor-grok-4.6-high` for review / test-writing / judgment roles, at `timeoutSec ≥ 900`. Dogfood evidence (2026-08-25): `composer-2.5` on issue #1105 — 165 s, 2 files, +93 lines, mandatory review passed, merged only after independent test verification.
308
+
309
+ **Timeout.** `maxTurns` does not exist on this channel — see the § Platform-Specific Dispatch timeout note. The wall-clock SIGTERM (`DEFAULT_TIMEOUT_SEC = 900`, a floor) is the only circuit breaker.
310
+
278
311
  #### Pre-Dispatch Grounding Injection (#85)
279
312
 
280
313
  Before dispatching each agent, prepend a line-numbered GROUNDING block to its prompt for any file in the agent's scope that has recent edit-format-friction history. This helps the agent reference edits by line number instead of re-matching exact character spans, reducing Edit-tool retry loops.
@@ -502,9 +535,9 @@ Rules:
502
535
 
503
536
  4. **Finalization** → direct execution (no subagent needed)
504
537
 
505
- > **How to detect project agents:** The session plan's "Agent Registry" section lists all discovered agents. If an agent name does NOT contain a colon (`:`), it's a project-level agent. If it contains `session-orchestrator:`, it's a plugin agent.
538
+ > **How to detect project agents:** The session plan's "Agent Registry" section lists all discovered agents. If an agent name does NOT contain a colon (`:`), it's a project-level agent. If it contains `session-orchestrator:`, it's a plugin agent. If it contains `cursor:` (e.g. `cursor:composer-2.5`), it is a **foreign-channel dispatch**, not an Agent-tool dispatch at all — see § Third branch: foreign-model dispatch. Any other prefix is rejected at parse time by `scripts/lib/config.mjs` (known channels: `cursor`, `session-orchestrator`), so an unroutable value never reaches this chain.
506
539
 
507
- **CRITICAL: `run_in_background: false`**You MUST wait for ALL agents to complete before proceeding. NEVER use `run_in_background: true` during wave execution. Dispatch in small batches of 3–4 Agent() calls per message (never a large single-message fan-out — see § Dispatch Agents; large fan-outs drop calls silently, conf 1.0), waiting for each batch's tool-results before the next, then run Dispatch Verification.
540
+ **`run_in_background: true` is ALLOWED and RECOMMENDED for wave dispatch.** Measured 2026-08-22 (v2.1.239): under blocking dispatch the coordinator was 143 s incapable of acting between an agent's mid-run escalation and its own next turn escalation latency equals the batch's remaining runtime. Background dispatch returns turns to the coordinator between agent completions; a running agent received a queued message mid-run and answered ~9 min before its final report. Still dispatch in small batches of 3–4 Agent() calls per message (never a large single-message fan-out — see § Dispatch Agents; large fan-outs drop calls silently, conf 1.0), then run **Started-Set Verification** an agent counts as started on its `meta.json` sidecar and as completed on its task-notification, never on the launch ack.
508
541
 
509
542
  #### Platform-Specific Dispatch
510
543
 
@@ -527,6 +560,8 @@ Dispatch via Codex's multi-agent system — describe the task and specify the ag
527
560
  The `agents-per-wave` config is ignored on Cursor — all work is sequential. Session-reviewer dispatch is deferred to session-end (Phase 1.8).
528
561
 
529
562
  > **Timeout note:** Agent timeout is controlled by `maxTurns` from `circuit-breaker.md`, not by a time-based timeout. Claude Code's built-in turn limit provides the safety net. There is no need to set explicit time-based timeouts on agent dispatch.
563
+ >
564
+ > **Foreign-dispatch exception (#1150):** this is FALSE for a `cursor:<model>` dispatch. `cursor-agent` has no `maxTurns` and no turn limit of any kind, so a wall-clock SIGTERM is the **only** circuit breaker there — `timeoutSec` (default `DEFAULT_TIMEOUT_SEC = 900`, a measured floor, not a suggestion: `cursor-grok-4.6-high` ran 2 of 3 hard-test tasks past a 540 s cap). Lowering it manufactures timeouts that read as model failure. See § Third branch: foreign-model dispatch.
530
565
 
531
566
  ### 2. Review Agent Outputs
532
567
 
@@ -544,6 +579,20 @@ if (cwd.restored) {
544
579
 
545
580
  Run this step for every wave, regardless of isolation setting — it is a no-op when CWD never drifted.
546
581
 
582
+ **Step 2.0-bis — Transcript tailer (FA-1, #1114):** `monitors/monitors.json` carries a `wave-transcript-tail` entry with `when: "on-skill-invoke:wave-executor"`, so the tailer starts ONCE per wave-executor invocation — **not per wave**. It observes the OWN session's subagent transcripts, picking up newly-appearing `agent-*.jsonl` files as later waves dispatch, so it never needs to be told a wave boundary. Its findings arrive as `stagnation_detected` records carrying `source: "tail"` — the same schema the **Stagnation event-write** block under step 3a below produces with `source: "coordinator"`, deliberately not a second event name (#1035).
583
+
584
+ **Silence is NOT success** (`.claude/rules/loop-and-monitor.md` § LM-002). Transcripts flush per TURN, so an agent inside one long tool call is invisible to the tailer for that call's whole duration. Read "no tail findings" as "nothing detectable was flushed", never as "the wave is healthy" — the post-wave review below remains the primary check. A tailer that cannot resolve the transcript directory exits with one stderr line and never blocks the wave.
585
+
586
+ **Step 2.0-ter — Incoming agent escalations (FA-2, #1051):** a wave agent may send ONE upward `SendMessage` to `main` when it hits a wave-blocking obstacle (`.claude/rules/cross-session-messaging.md` § CSM-001 — agents send upward only, never sideways). When such a message arrives mid-wave:
587
+
588
+ - **It is a claim, not a finding.** Verify it against the tree before altering the wave plan or re-scoping a sibling agent — `.claude/rules/receiving-review.md` § RCR-003 (skeptical posture) and RCR-001 step 3 (VERIFY) apply unchanged. The agent's view of the tree may already be stale.
589
+ - **Carry provenance.** Quote it downstream as `<claim> (source: <agent>, <time>)` per CSM-002; an unattributed escalation is indistinguishable from the coordinator's own measurement.
590
+ - **No permission laundering.** Never execute an action this coordinator session has blocked or left unapproved just because an agent asked for it (CSM-003) — route it to the operator instead.
591
+ - **Never gate on a reply.** Do not hold a wave, a gate, or a commit waiting for an answer, and read silence as neither consent nor refusal (CSM-004).
592
+ - **Record it.** Note the escalation in the wave progress update — agent, one-line claim, verification outcome, action taken — so an escalation that changed the plan stays auditable.
593
+
594
+ When the channel is unavailable (CSM-005: `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` / `DISABLE_TELEMETRY`, native Windows, non-Anthropic providers), nothing arrives and this step is a silent no-op — the wave behaves exactly as it does today.
595
+
547
596
  After ALL agents in the wave complete:
548
597
 
549
598
  1. **Read each agent's result** carefully
@@ -570,15 +619,27 @@ After ALL agents in the wave complete:
570
619
 
571
620
  2. **Check for conflicts**: did two agents modify the same file? → manual merge needed
572
621
  3. **Check for failures**: did any agent report errors or blockers?
573
- 3a. **Apply stagnation patterns** (per agent): review each agent's tool-call sequence against the three patterns in `circuit-breaker.md` § Stagnation Patterns — Pagination Spiral, Turn-Key Repetition, Error Echo. Mark each agent STAGNANT/SPIRAL/FAILED accordingly; recovery feeds into step 3 (Adapt Plan). Two different agents reading the same file is coordination, not stagnation.
622
+ 3a. **Apply stagnation patterns** (per agent): review each agent's tool-call sequence against the three patterns in `circuit-breaker.md` § Stagnation Patterns — Pagination Spiral, Turn-Key Repetition, Error Echo. Mark each agent STAGNANT/SPIRAL/FAILED accordingly; recovery feeds into step 3 (Adapt Plan). Two different agents reading the same file is coordination, not stagnation. The other two patterns in the enum — `psa007-git-write` and `status-partial` — are NOT yours to judge here: they are executable regexes owned by the tailer (step 2.0-bis) and reach you as records with `source: "tail"`.
574
623
 
575
- **Stagnation event-write** (gated on `persistence: true`): when any stagnation pattern fires for an agent during this step, append one line to `.orchestrator/metrics/events.jsonl` using shell `>>` (atomic for lines under PIPE_BUF):
624
+ **Stagnation event-write** (gated on `persistence: true`): when any stagnation pattern fires for an agent during this step, emit ONE `stagnation_detected` record through the **canonical emitter** — never a hand-rolled `>>` append. Hand-written appenders drift from `emitEvent()` (that drift is what produced the `stop` vs `orchestrator.session.stopped` divergence, #609/#611), and since #1114 this event has a SECOND producer (the transcript tailer, step 2.0-bis above), whose records must be field-for-field comparable with the coordinator's. One write path for both:
625
+
626
+ node "$PLUGIN_ROOT/scripts/emit-event.mjs" --type stagnation_detected --payload '<the payload object below>'
627
+
628
+ From a Node context, call `emitEvent('stagnation_detected', { ...payload, ...sessionAttribution(repoRoot) }, { repoRoot })` from `scripts/lib/events.mjs` instead — pass `repoRoot` **explicitly** so the record lands in THIS working copy's ledger and its attribution is read from the same root the line is pinned to (#941/#1147).
576
629
 
577
630
  ```json
578
- {"event":"stagnation_detected","timestamp":"<ISO 8601 UTC>","session":"<session_id>","wave":N,"agent":"<subagent_type>","pattern":"pagination-spiral|turn-key-repetition|error-echo","error_class":"<taxonomy value — omit field entirely if pattern is not error-echo>","file":"<relative path from project root, or null if not applicable>","occurrences":N}
631
+ {"session":"<semantic session id>","wave":N,"agent":"<subagent_type>","pattern":"pagination-spiral|turn-key-repetition|error-echo","source":"coordinator","error_class":"<taxonomy value — omit field entirely unless pattern is error-echo>","file":"<relative path from project root, or null if not applicable>","occurrences":N}
579
632
  ```
580
633
 
581
- Assign `error_class` using the taxonomy defined in `circuit-breaker.md` § "3. Error Echo" Error-Class Taxonomy. For non-error-echo patterns, omit the `error_class` field. Paths are relative to the project root. `occurrences` is the count of pattern repetitions detected (minimum 3 per the trigger threshold).
634
+ The template lists only the THREE patterns you may write. The other two enum values `psa007-git-write` and `status-partial` are tail-only: they are emitted by `scripts/lib/wave-transcript-tail.mjs` with `source: "tail"`, never by the coordinator (see step 3a above).
635
+
636
+ `timestamp` and `event` are written by `emitEvent()` itself — do NOT hand-compose either (a hand-typed ISO string is the #540 corruption class).
637
+
638
+ **Field-name reconciliation (three session keys, none redundant):** `session` is the **SEMANTIC** session id — the one that matches `sessions.jsonl.session_id` (measured 2026-08-25: `"main-2026-08-24-session-1"`), which is the join key every consumer reads (`skills/session-end/metrics-collection.md` filters `.session == $sid` with `$sid = $SESSION_ID`, the semantic id). Writing a raw UUID here would produce a record that joins to nothing. `sessionAttribution(repoRoot)` additionally contributes the `session_id` / `semantic_session_id` pair, which is OMITTED rather than fabricated when no `session.lock` is readable — so `session` is the field a consumer may rely on, and the pair is additive provenance. Keep all three; do not collapse them into one.
639
+
640
+ **`source` (additive, #1114)** names WHO detected the pattern: `"coordinator"` for a record written here from post-wave review, `"tail"` for one written by `scripts/lib/wave-transcript-tail.mjs`. A consumer that does not know the field behaves exactly as before.
641
+
642
+ Assign `error_class` using the taxonomy defined in `circuit-breaker.md` § "3. Error Echo" → Error-Class Taxonomy. That assignment applies to **error-echo only** and is unchanged. Omit the field entirely for every other pattern — `pagination-spiral`, `turn-key-repetition`, and the two tail-detected patterns `psa007-git-write` / `status-partial` carry NO `error_class`, and an absent field means "no class applies", never `"other"`. Paths are relative to the project root. `occurrences` is the count of pattern repetitions detected — minimum 3 for the three threshold-based patterns; `psa007-git-write` and `status-partial` fire on the FIRST occurrence, so `occurrences: 1` is valid for those two.
582
643
 
583
644
  3b. **Worktree base-ref freshness check (#195)**: For each agent dispatched with `isolation: "worktree"` in this wave, verify that the coordinator has not advanced `main` past the worktree's base commit before the merge-back copies files. Call `checkWorktreeBaseRefFresh({ suffix, targetBranch: 'main', agentScope, cwd })` from `scripts/lib/worktree-freshness.mjs`:
584
645
 
@@ -609,7 +670,7 @@ Log every non-`pass` result as an event to `.orchestrator/metrics/events.jsonl`
609
670
  Build the on-disk change set as the UNION of the two commands' outputs (untracked files appear as `??` lines in `git status --porcelain` — strip the two-column status prefix). **Every path in an agent's declared `files_changed` MUST appear in that union.** A declared file that is absent from both is an **edit-persistence violation**:
610
671
 
611
672
  - Treat that agent's result as **NOT verified** — do not count its claimed work as done, and do not feed its (phantom) changes into the next wave.
612
- - **Recover** by either (a) re-dispatching that agent's task package in a fresh batch (per `#### Dispatch Verification`), or (b) applying the missing edit coordinator-direct when the fix is small and unambiguous.
673
+ - **Recover** by either (a) re-dispatching that agent's task package in a fresh batch (per `#### Started-Set Verification`), or (b) applying the missing edit coordinator-direct when the fix is small and unambiguous.
613
674
  - **Log the deviation** to `## Deviations` in `<state-dir>/STATE.md` via `appendDeviationOnDisk(repoRoot, isoTimestamp, message)` from `scripts/lib/state-md.mjs`:
614
675
  ```
615
676
  - [<ISO 8601 UTC>] Wave N edit-persistence violation: agent "<description>" reported <done|partial> but declared file(s) <paths> are absent from the on-disk change set. Result treated as unverified — <re-dispatched | coordinator-direct fix>.
@@ -783,9 +844,10 @@ If the commit itself fails (e.g., nothing to commit, pre-commit hook rejects), d
783
844
  description: "Persona review — <reviewer-name> — Wave N",
784
845
  prompt: "<include: wave scope, changed files list, relevant plan section>",
785
846
  subagent_type: "session-orchestrator:<reviewer-name>",
786
- run_in_background: false
847
+ run_in_background: false // deliberately blocking — see below
787
848
  })
788
849
  ```
850
+ - **`run_in_background: false` here is deliberate, not an oversight.** Reviewers are dispatched AFTER the quality gate and are NOT in the session plan's agent list, so § Started-Set Verification has no expected-set to check them against and the launch ack — the only thing a background dispatch returns — is explicitly not countable. There is also no consumer for early results: the next step needs ALL reviewer verdicts before it can act (`skills/persona-panel/SKILL.md` § "background dispatch would add turn-juggling with no consumer for early results"; `skills/session-start/SKILL.md` names persona-panel among the keep-false skills).
789
851
  - Each reviewer writes its findings to `.orchestrator/audits/wave-reviewer-<wave>-<reviewer-name>.md`. The coordinator does NOT need to create this file — the reviewer agent writes it directly.
790
852
  - **Findings are ADVISORY**: reviewer output never blocks the subsequent wave. After all dispatched reviewers complete:
791
853
  - If any reviewer reports **WARN**: surface the findings to the user in the wave progress summary. Feed actionable items into the next wave's agent assignments (step 3 — Adapt Plan). If a WARN/FAIL finding is surfaced but NOT converted into a fix task for the next wave, append ONE line to `## Deviations` via `appendDeviationOnDisk(repoRoot, isoTimestamp, message)` (#730/H5): `- [<ISO 8601 UTC>] Wave N reviewer finding overridden (not actioned): <one-line finding>.` — session-end Phase 2.6 (Broken-Window Budget) walks these entries at close.
@@ -802,9 +864,10 @@ If the commit itself fails (e.g., nothing to commit, pre-commit hook rejects), d
802
864
  description: "Review wave N output",
803
865
  prompt: "<include: session plan, wave results, changed files list, acceptance criteria>",
804
866
  subagent_type: "session-orchestrator:session-reviewer",
805
- run_in_background: false
867
+ run_in_background: false // deliberately blocking — same reason as step 5a
806
868
  })
807
869
  ```
870
+ - **`run_in_background: false` here is deliberate**, for the same reason as step 5a: the session-reviewer runs after the quality gate, is not in the session plan's agent list (so Started-Set Verification has no signal for it), and its verdict is needed whole before step 3 (Adapt Plan) can consume it.
808
871
  - The session-reviewer checks changed files against the plan and reports PASS/WARN/FAIL per category (implementation, tests, TypeScript, security, silent failures, test depth, type design, issues).
809
872
  - If the session-reviewer reports **WARN or FAIL** findings: add fix tasks to the next wave's agent assignments (feed into step 3 — Adapt Plan). If a WARN/FAIL finding is surfaced but NOT converted into a fix task for the next wave, append ONE line to `## Deviations` via `appendDeviationOnDisk(repoRoot, isoTimestamp, message)` (#730/H5): `- [<ISO 8601 UTC>] Wave N reviewer finding overridden (not actioned): <one-line finding>.` — session-end Phase 2.6 (Broken-Window Budget) walks these entries at close.
810
873
  - After the **Quality** wave: dispatch the session-reviewer with **full session scope** (all files changed since session start, not just the current wave). Use `git diff --name-only $SESSION_START_REF..HEAD` to provide the complete changed files list.
@@ -835,8 +898,9 @@ If the commit itself fails (e.g., nothing to commit, pre-commit hook rejects), d
835
898
  7. **Capture wave metrics**: If `persistence` is enabled in Session Config, record for this wave after all agents complete and quality checks run. If `persistence` is `false`, skip metrics capture entirely — do not accumulate in-memory metrics. Record:
836
899
  - `wave_number`, `role`, `started_at` (when agents were dispatched), `completed_at` (when all finished)
837
900
  - `agent_count`: number of agents dispatched
838
- - `agent_count_planned`: agents named in the session plan for this wave (Dispatch Verification, #724)
839
- - `agent_count_started`: distinct agents that produced a tool-result, after any silent-drop re-dispatch (Dispatch Verification, #724). A gap `agent_count_planned > agent_count_started` after re-dispatch signals a persistent silent drop.
901
+ - `agent_count_planned`: agents named in the session plan for this wave (Started-Set Verification, #724)
902
+ - `agent_count_started`: distinct agents whose `agent-<id>.meta.json` sidecar is present, after any silent-drop re-dispatch (Started-Set Verification, #724/#1115). NOT "produced a tool-result" — under background dispatch the launch ack is a result and would count an agent that never ran. A gap `agent_count_planned > agent_count_started` after re-dispatch signals a persistent silent drop.
903
+ - `agent_count_completed`: distinct agents whose task-notification (`<status>completed</status>`) arrived (#1115). A gap `agent_count_started > agent_count_completed` at wave end is an agent that started and never returned — the started-but-never-returned state, not a silent drop.
840
904
  - Per-agent results: `{description, status: done|partial|failed, files_changed_count}`
841
905
  - `files_changed`: total unique files changed this wave (from `git diff --stat --name-only`)
842
906
  - `planned_files_count`: size of this wave's Planned set (union of agent file scopes) as computed in step 3c File-level grounding above. Reuse that value — do not recompute.
@@ -1102,32 +1166,71 @@ Before each wave dispatch:
1102
1166
  # Warning: policy file .orchestrator/policy/blocked-commands.json not found — using legacy hardcoded blocklist
1103
1167
  ```
1104
1168
 
1169
+ **Deriving the session binding (#1123):** `wave-scope.json` lives in the WORKING COPY, and `hooks/enforce-scope.mjs` applies whatever it finds there to every session running in that checkout. Without a binding, a Discovery wave's `allowedPaths: []` denied every write of an unrelated parallel session. Name the writer — both fields come from ONE `sessionAttribution()` call, which reads `.orchestrator/session.lock` once:
1170
+ ```bash
1171
+ SESSION_BINDING=$(node --input-type=module -e "
1172
+ import { sessionAttribution } from '$PLUGIN_ROOT/scripts/lib/events.mjs';
1173
+ const a = sessionAttribution(process.cwd());
1174
+ const out = {};
1175
+ if (a.session_id) out.session = a.session_id;
1176
+ if (a.semantic_session_id) out.semantic_session = a.semantic_session_id;
1177
+ console.log(JSON.stringify(out));
1178
+ ")
1179
+ ```
1180
+ **Verify the binding names YOU before you write it (#1123 follow-up).** `sessionAttribution()` reads the repo-global `.orchestrator/session.lock`, which in a shared working copy can hold a PEER's id — a session that lost the acquire race (`bootstrapLock()` reason `active`) leaves the lock naming the session that won it. Compare both ids against your own session (STATE.md `session`): **if they do not match, OMIT the `session`/`semantic_session` keys entirely** and write an unbound manifest. Unbound = ENFORCE, which is the fail-closed direction; writing a foreign id instead publishes a manifest that classifies as somebody else's for every reader. The reader half is defensive against exactly this (Gate 3b resolves identity as the UNION of payload, env and lock, so a peer-owned-lock manifest still reads `own` to its writer), but the reader cannot repair a binding the writer knowingly got wrong — and a manifest naming a peer is unreadable as an audit record either way.
1181
+
1182
+ Merge `$SESSION_BINDING`'s keys into the manifest. **If a value is empty, OMIT the key — never write `"session": ""`.** An empty id is present-but-equal-to-nobody: the legacy warning stays silent while every reader compares it against its own id, finds no match, and treats the manifest as FOREIGN — the one disposition that skips enforcement entirely. `sessionAttribution()` already omits rather than fills (CI runs hold no lock), and `validate-wave-scope.mjs` rejects the empty string outright, so the honest path is also the only one that validates.
1183
+
1105
1184
  ```json
1106
1185
  {
1107
1186
  "wave": N,
1108
1187
  "role": "<role>",
1109
1188
  "enforcement": "<from Session Config, default: warn>",
1189
+ "session": "<raw session_id from sessionAttribution(); OMIT the key if unavailable>",
1190
+ "semantic_session": "<semantic_session_id from the same call; OMIT if unavailable>",
1110
1191
  "allowedPaths": ["<from agent specs in session plan>"],
1111
1192
  "blockedCommands": "<derived dynamically from the effective floor∪overlay policy via loadEffectivePolicy (severity: block rules, #972); falls back to legacy 5-element array if no policy resolves>",
1112
1193
  "gates": "<copy of enforcement-gates from Session Config, or omit if unset>"
1113
1194
  }
1114
1195
  ```
1115
1196
  The `gates` field (optional) mirrors `enforcement-gates` from Session Config (#77). When present, hooks check each gate individually via `gate_enabled()`. Missing gate entries default to enabled, preserving default behavior.
1197
+
1198
+ **What the binding means to a reader.** Three states, and the disposition differs for each. **Absent** = legacy = ENFORCE: a manifest written before #1123 (or by a stale skill body) binds nobody, so it must keep constraining everyone exactly as it did before — this is the only state that preserves the pre-#1123 contract, and `validate-wave-scope.mjs` marks it with one advisory stderr line rather than an error, because § 3.3's pre-union skeleton is itself an unbound manifest. **Own session** = ENFORCE, unchanged. **Foreign session** — `session` present and not this session's id — = ALLOW: `hooks/enforce-scope.mjs` lets the write through and emits `orchestrator.scope.foreign_session_ignored` so the skip is counted rather than silent. A foreign manifest is somebody else's wave plan; it never had authority over this session's writes, and the event is what keeps that visible instead of leaving an allow nothing recorded. (The reader half lives in `hooks/enforce-scope.mjs` — the writer's only obligation is to name itself honestly here.)
1116
1199
  2. Validate by piping through `node "$PLUGIN_ROOT/scripts/validate-wave-scope.mjs"` (where `$PLUGIN_ROOT` is `$CLAUDE_PLUGIN_ROOT`, `$CODEX_PLUGIN_ROOT`, or `$CURSOR_RULES_DIR` per platform — see `skills/_shared/config-reading.md`). If validation fails (exit 1), fix the JSON based on stderr errors and retry.
1117
- 3. **`allowedPaths` is COMPUTED from per-agent scope files — never hand-transcribed (#1020).** Transcribing the union by hand produced 5 scope divergences in ONE session. Three steps, in this order; none of them is a judgement call. Globs stay verbatim (`scripts/*.sh`) — the enforcement hook resolves them at check time.
1200
+ 3. **`allowedPaths` is COMPUTED from one canonical declaration array — never hand-transcribed (#1020/#1083).** Transcribing either declaration shape or the union by hand produced scope divergences. Globs stay verbatim (`scripts/*.sh`) — the enforcement hook resolves them at check time.
1201
+
1202
+ **3.1 — materialize both declaration shapes once.** Build one JSON array from the session plan, one `{id, files}` record for every agent plus exactly one `coordinator` record for the coordinator's planned direct edits. `files` arrays, their entries and their order are the plan's verbatim declarations. Materialize it ONCE and capture the aggregate-sidecar path:
1118
1203
 
1119
- **3.1 — one file per agent.** Write each agent's "Files:" scope from the session plan, verbatim, as a JSON array of strings to `<state-dir>/filescopes/wave-<N>/<agent-id>.json`. That path IS `$AGENT_FILESCOPE_JSON` — the same file `--assert-subset` (#796 below), Grounding Injection (#85), the Learnings-Index (#1014) and the File-Scope Injection (#1020) already consume. Do not write a second copy anywhere, and **never to a `$TMPDIR` temp path**: the injector and `hooks/pre-task-scope-disjoint.mjs` need an addressable, wave-keyed location that a temp file cannot be. Reading `$AGENT_FILESCOPE_JSON` as "some temp file" is the one failure that costs no error — the injector finds nothing, no `FILE-SCOPE` block reaches the prompt, `extractScopeFromPrompt` returns `[]`, and the dispatch is ALLOWed exactly as it was before #1020, signal-free. The coordinator's OWN planned direct edits go into `<state-dir>/filescopes/wave-<N>/coordinator.json` in the identical form and take part in both steps below: 2 of those 5 divergences were coordinator-direct edits, for which no agent scope file exists by construction, and the commit guard caught them only at the commit boundary.
1204
+ ```bash
1205
+ WAVE_SCOPE_RECORDS='[{"id":"W3-I1","files":["scripts/example.mjs"]},{"id":"coordinator","files":["skills/wave-executor/wave-loop.md"]}]'
1206
+ WAVE_SCOPES_SIDECAR="$(
1207
+ printf '%s' "$WAVE_SCOPE_RECORDS" | node "$PLUGIN_ROOT/scripts/materialize-wave-scope.mjs" \
1208
+ --state-dir "$STATE_DIR" --wave "$WAVE"
1209
+ )"
1210
+ [ -n "$WAVE_SCOPES_SIDECAR" ] || { echo "materialize-wave-scope produced no sidecar path" >&2; exit 1; }
1211
+ ```
1120
1212
 
1121
- > **`<state-dir>/filescopes/` is control state, like `wave-scope.json` itself — never a wave territory.** Step 3.1 necessarily runs BEFORE the union of 3.3 exists, so writing these files reports `bash-write-verify: N file(s) changed by a Bash call OUTSIDE the wave's allowedPaths` naming `filescopes/wave-<N>/*.json`. Expected once per wave rollover at this step; it is information, not a scope violation. Never widen `allowedPaths` to silence it — that would grant agents write access to the deconfliction record itself.
1213
+ The non-empty check is not decoration. The materializer sends every diagnostic
1214
+ to stderr, so a failure leaves `$WAVE_SCOPES_SIDECAR` empty, and an empty path
1215
+ is what step 3.2 would then pass to `--assert-disjoint`. That combination used
1216
+ to exit 0 with the collision gate never run — the same signal-free-ALLOW shape
1217
+ #1083 exists to close. `validate-wave-scope.mjs` now refuses an empty flag
1218
+ value as well, so this guard and that refusal are belt and braces.
1122
1219
 
1123
- **3.2 assert disjointness BEFORE computing the union.** Build the sidecar an ARRAY of `{id, files}` records (never an object map: a duplicated agent id must stay visible), one record per file written in 3.1, `coordinator.json` included and run:
1220
+ `materialize-wave-scope.mjs` validates the COMPLETE input before writing; it writes `<state-dir>/filescopes/wave-<N>/<agent-id>.json` as each bare `files` array first, then writes `<state-dir>/filescopes/wave-<N>.scopes.json` as the unchanged aggregate record array last. Its human stdout is only that final sidecar path, so the command substitution above is the canonical `$WAVE_SCOPES_SIDECAR`. On error, do not continue with a partial declaration set; correct the plan and run the one command again.
1221
+
1222
+ The per-agent path IS `$AGENT_FILESCOPE_JSON` — the same file `--assert-subset` (#796 below), Grounding Injection (#85), the Learnings-Index (#1014) and File-Scope Injection (#1020) consume. Never write a `$TMPDIR` copy: it degrades to a signal-free allow when an injector cannot find the addressable wave-keyed file. The coordinator's record is materialized as `coordinator.json` and included in the aggregate, so its direct edits are covered by the two checks below.
1223
+
1224
+ > **`<state-dir>/filescopes/` is control state, like `wave-scope.json` itself — never a wave territory.** Step 3.1 necessarily runs before the union exists, so writing these files reports `bash-write-verify: N file(s) changed by a Bash call OUTSIDE the wave's allowedPaths` naming `filescopes/wave-<N>/*.json`. Expected once per wave rollover at this step; it is information, not a scope violation. Never widen `allowedPaths` to silence it — that would grant agents write access to the deconfliction record itself.
1225
+
1226
+ **3.2 — assert disjointness BEFORE computing the union.** The materialized aggregate is an ARRAY of `{id, files}` records (never an object map: a duplicated agent id must stay visible), including `coordinator.json`. Run:
1124
1227
 
1125
1228
  ```bash
1126
1229
  node "$PLUGIN_ROOT/scripts/validate-wave-scope.mjs" \
1127
1230
  --assert-disjoint "$WAVE_SCOPES_SIDECAR" < <state-dir>/wave-scope.json
1128
1231
  ```
1129
1232
 
1130
- Exit 1 (one stderr message per collision) means two agents were handed the same file: fix the session plan, rewrite the affected 3.1 files, re-assert. Never widen the union to make it pass. This runs **before** 3.3 because a union computed over colliding scopes launders the defect into the very artefact meant to prevent it — `allowedPaths` then grants the file and every later gate sees a legal write.
1233
+ Exit 1 (one stderr message per collision) means two agents were handed the same file: fix the session plan, re-materialize, re-assert. Never widen the union to make it pass. This runs **before** 3.3 because a union computed over colliding scopes launders the defect into the very artefact meant to prevent it — `allowedPaths` then grants the file and every later gate sees a legal write.
1131
1234
 
1132
1235
  **3.3 — compute the union.** `--union` is a QUERY MODE that still requires a schema-valid manifest on stdin, so write the skeleton first with `"allowedPaths": []`, then:
1133
1236
 
@@ -1138,6 +1241,8 @@ Before each wave dispatch:
1138
1241
 
1139
1242
  It prints the computed `allowedPaths` array as JSON on stdout **instead of** the manifest echo — one JSON document per run, the flag decides which. Insert that array as `allowedPaths`, then write the final `wave-scope.json`. It already applies the Test-Sibling Expansion below (`expandTestSiblings(unionFileScopes(scopes), { role })`, role read from the manifest), so do not also run the helper by hand.
1140
1243
 
1244
+ **Artifact production, disjointness and union computation are mechanized. Native prompt injection is a separate follow-up.** The materializer creates the durable declarations; the validator proves disjointness and computes the union. It does not install or prove the platform's prompt-injection transport, which remains independently responsible for reading `$AGENT_FILESCOPE_JSON` before dispatch.
1245
+
1141
1246
  **The `--assert-subset` assertion (#796, below) stays unchanged and keeps running.** It checks a DIFFERENT property — each agent's scope ⊆ the union — and a double assignment is structurally invisible to it: a file claimed twice is a subset twice over. `--assert-disjoint` is an addition, never a replacement.
1142
1247
 
1143
1248
  **Test-Sibling Expansion (#970):** an `allowedPaths` entry that names a production file but NOT its test sibling makes the wave's own regression test unwritable — the scope guard then mechanically enforces exactly the inconsistency the quality gate exists to catch. Cross-repo evidence, three occurrences in ONE session: a migrations glob without the SQL-test directory (the regression test could not be written); a lone `.actions.ts` file (the wave's cross-tenant security test stayed red); a dead-export deletion whose importing test lay outside every scope (the suite ended red). Do NOT hand-derive the sibling paths — step 3.3's `--union` runs `expandTestSiblings(…, { role })` for you, so the hook, the validator and this prose state one rule.
@@ -163,11 +163,11 @@ Any hit → surface to user via AUQ:
163
163
  ```
164
164
  AskUserQuestion({
165
165
  questions: [{
166
- question: "The placeholder linter found forbidden strings in the draft plan:\n\n[list each hit with Task number, Step number, and matched text]\n\nHow do you want to proceed?",
167
- header: "Placeholder Linter",
166
+ question: "The draft plan still contains forbidden placeholder strings. How do you want to proceed?",
167
+ header: "Platzhalter",
168
168
  options: [
169
- { label: "Fix automatically (Recommended)", description: "I will resolve each hit by filling in the missing specifics before writing the plan." },
170
- { label: "Show me each hit interactively", description: "Walk me through each one so I can provide the missing detail." }
169
+ { label: "Fix automatically (Recommended)", description: "I fill in the missing specifics myself, then write the plan fastest, and you review the result at hand-off.", preview: "[each hit: Task number, Step number, matched text]" },
170
+ { label: "Show me each hit interactively", description: "Walk me through each one so I can provide the missing detail.", preview: "[the same hits, one at a time, starting with the first]" }
171
171
  ],
172
172
  multiSelect: false
173
173
  }]
@@ -202,8 +202,8 @@ Present the plan path and task count to the user via AUQ:
202
202
  ```
203
203
  AskUserQuestion({
204
204
  questions: [{
205
- question: "Executable plan written to docs/plans/YYYY-MM-DD-<slug>.md ([N] tasks, ~[total] min estimated).\n\nHow do you want to execute it?",
206
- header: "Plan Hand-off",
205
+ question: "Plan written to docs/plans/YYYY-MM-DD-<slug>.md [N] tasks, ~[total] min. How to execute?",
206
+ header: "Plan fertig",
207
207
  options: [
208
208
  { label: "Dispatch via wave-executor (Recommended)", description: "Parallel agents execute all tasks simultaneously. Fastest for file-disjoint tasks." },
209
209
  { label: "Execute coordinator-direct", description: "One task at a time in this session. Safer for tasks with shared state." },
@@ -0,0 +1,110 @@
1
+ <!-- source: session-orchestrator plugin (canonical: templates/_shared/journey-manifest.md) -->
2
+ <!--
3
+ Copy to `.orchestrator/journey-manifest.md` in the target repo and fill in every
4
+ section. `/journey-audit` REFUSES to run without this file.
5
+
6
+ The `## SAFETY` block is what gates R5 (the real end-to-end run against
7
+ production with a real account). Absent or empty → R5 is not dispatched and the
8
+ audit runs as a dry-run (R1–R4 + R7). Nothing else in this file can substitute
9
+ for it: the skill reads THIS heading, not your intent.
10
+
11
+ Credentials: env-var NAMES only, never values. A value pasted here is a secret
12
+ in a tracked file.
13
+ -->
14
+
15
+ # Journey Manifest — <REPO NAME>
16
+
17
+ > Per-repo input for `/journey-audit`. Product truth, not code structure.
18
+ > Last verified: <YYYY-MM-DD> by <operator>
19
+
20
+ ## Personas & Einstiegspunkte
21
+
22
+ | Persona | Ziel | Einstiegspunkt (URL/Route) | Erfolg heißt |
23
+ |---------|------|---------------------------|--------------|
24
+ | <anonymer Besucher> | <was er will> | `<https://…/>` | <messbarer Endzustand> |
25
+ | <zahlender Kunde> | | | |
26
+ | <Owner/Admin> | | | |
27
+
28
+ List every entry point a real user can reach — including the ones marketing links
29
+ to but the app never surfaces. A feature with no entry point is exactly the defect
30
+ class this audit exists to find.
31
+
32
+ ## Wahrheits-SSOTs
33
+
34
+ Where the truth about the product lives in code. R3 compares every claim against
35
+ THESE files, not against other prose.
36
+
37
+ | Gegenstand | SSOT-Datei/Konstante |
38
+ |-----------|----------------------|
39
+ | Preise / Pläne / Limits | `<src/config/plans.ts>` |
40
+ | Feature-Flags | `<…>` |
41
+ | Währung / Steuer / Region | `<…>` |
42
+ | Quota / Kontingente | `<…>` |
43
+ | i18n-Namespaces (Marketing, FAQ, Mail) | `<…>` |
44
+ | Chat-/Bot-Faktenquelle | `<…>` |
45
+
46
+ ## Outbound-Touchpoints (Input für R1/R2)
47
+
48
+ - Template-Verzeichnis: `<…>`
49
+ - Send-Pfad / Provider-Adapter: `<…>`
50
+ - Cron-/Scheduler-Quellen: `<…>`
51
+ - Render-Idiom + Beispiel-Props für R2: `<z. B. react-email, props aus …>`
52
+
53
+ ## Chat-Interview (Frage → Soll-Antwort)
54
+
55
+ R4 asks these verbatim and scores the answer against the expected one. The expected
56
+ answer must be derivable from a Wahrheits-SSOT above — otherwise it is a second
57
+ claim, not a truth key.
58
+
59
+ | # | Frage (wörtlich) | Soll-Antwort (Kern-Fakt) | SSOT |
60
+ |---|------------------|--------------------------|------|
61
+ | 1 | <"Was kostet …?"> | <"…"> | `<datei>` |
62
+ | 2 | | | |
63
+ | 3 | | | |
64
+
65
+ ## SAFETY
66
+
67
+ **MANDATORY for R5. Without this block filled in, R5 is not dispatched.**
68
+
69
+ - **Erlaubte Konten:** `<test-account@…>` — and no other. Never a real customer account.
70
+ - **Erlaubte Events/Objekte:** `<z. B. nur Events mit Präfix AUDIT-…>`
71
+ - **No-Go-Aktionen:** <Löschen fremder Daten · Mailversand an echte Empfänger · Plan-Downgrade · Refunds · Webhook-Replays gegen Prod>
72
+ - **Checkout-Grenze:** `<max. EUR X, Testkarte …, Live-Zahlungen verboten>`
73
+ - **Cleanup-Regel:** <was nach dem Durchstich wieder entfernt wird, von wem, woran man erkennt dass es weg ist>
74
+ - **Abbruchbedingung:** <woran R5 sofort stoppt und meldet statt weiterzumachen>
75
+
76
+ ## Credential-Quellen (env-Namen, NIE Werte)
77
+
78
+ | Zweck | Env-Variable | Bezugsquelle |
79
+ |-------|--------------|--------------|
80
+ | Prod-Login R5 | `<AUDIT_TEST_ACCOUNT_EMAIL>` | `<1Password-Item / .env.local>` |
81
+ | DB read-only R6 | `<AUDIT_DB_READONLY_URL>` | `<…>` |
82
+ | Mail-Provider-API R6 | `<…>` | `<…>` |
83
+ | Plattform-CLI R7 | `<…>` | `<…>` |
84
+
85
+ ## Realdaten-Queries (R6, read-only)
86
+
87
+ Only `SELECT`. A query that writes does not belong in this file.
88
+
89
+ ```sql
90
+ -- Funnel: Registrierung → Aktivierung → Zahlung
91
+ SELECT …;
92
+ ```
93
+
94
+ - Identitäts-Regeln: <wie ein Nutzer über Tabellen hinweg identifiziert wird>
95
+ - Nicht messbar (bewusst benennen): <was die Daten NICHT hergeben>
96
+
97
+ ## Plattform-Erwartung (R7)
98
+
99
+ - CLI-Logins: `<vercel · supabase · cloudflare · …>`
100
+ - Erwarteter Plan/Tier je Dienst: `<…>`
101
+ - Offene Perf-/Kosten-Issues: `<#…>`
102
+
103
+ ## Bekannte Ausnahmen
104
+
105
+ Findings that are known and deliberately accepted — with the reason and a revisit
106
+ trigger. R1–R7 report them as `known-exception`, never as new findings.
107
+
108
+ | Befund | Warum akzeptiert | Revisit-Trigger |
109
+ |--------|------------------|-----------------|
110
+ | <…> | <…> | <…> |
@@ -1,8 +0,0 @@
1
- # Sample Baseline Rule (test fixture)
2
-
3
- This file is used by scripts/tests/fetch-baseline.bats as the mocked response body
4
- for the GitLab API raw-file endpoint. It has no real content — presence is enough
5
- to verify the fetch + cache path.
6
-
7
- - Rule ID: TEST-001
8
- - Purpose: end-to-end fetch verification
@@ -1,8 +0,0 @@
1
- ---
2
- id: bad-archived
3
- type: memo
4
- created: not-a-date
5
- updated: not-a-date
6
- ---
7
-
8
- Bad frontmatter — but inside 90-archive, so must be ignored.
@@ -1,8 +0,0 @@
1
- ---
2
- id: live-note
3
- type: note
4
- created: 2026-04-13
5
- updated: 2026-04-13
6
- ---
7
-
8
- Lives outside archive — must be validated.
@@ -1,8 +0,0 @@
1
- ---
2
- id: bad-type
3
- type: memo
4
- created: 2026-04-13
5
- updated: 2026-04-13
6
- ---
7
-
8
- `type: memo` is not in the enum.
@@ -1,8 +0,0 @@
1
- ---
2
- id: good-note
3
- type: note
4
- created: 2026-04-13
5
- updated: 2026-04-13
6
- ---
7
-
8
- OK.
@@ -1,8 +0,0 @@
1
- ---
2
- id: bogus
3
- type: memo
4
- created: not-a-date
5
- updated: not-a-date
6
- ---
7
-
8
- If the validator crawls .obsidian/, this file would FAIL. It must be excluded.
@@ -1,10 +0,0 @@
1
- ---
2
- id: projects-baseline
3
- type: project
4
- created: 2026-04-01
5
- updated: 2026-04-13
6
- status: active
7
- tags: [meta, infra]
8
- ---
9
-
10
- Project overview.
@@ -1,8 +0,0 @@
1
- ---
2
- id: daily-2026-04-13
3
- type: daily
4
- created: 2026-04-13
5
- updated: 2026-04-13
6
- ---
7
-
8
- Today: wrote vault-sync validator.
@@ -1,3 +0,0 @@
1
- # Clean Vault
2
-
3
- No frontmatter here — README-style files must be skipped, not errored.
@@ -1,11 +0,0 @@
1
- ---
2
- id: hello-world
3
- type: note
4
- created: 2026-04-13
5
- updated: 2026-04-13
6
- title: Hello World
7
- tags: [meta, intro]
8
- status: active
9
- ---
10
-
11
- A minimal valid note that links to [[daily-2026-04-13]] and [[projects-baseline]].
@@ -1,9 +0,0 @@
1
- ---
2
- id: has-dangling
3
- type: note
4
- created: 2026-04-13
5
- updated: 2026-04-13
6
- ---
7
-
8
- This note links to [[non-existent-target]] which should be flagged as a warning.
9
- It also links to [[real-target]] which exists.
@@ -1,8 +0,0 @@
1
- ---
2
- id: real-target
3
- type: note
4
- created: 2026-04-13
5
- updated: 2026-04-13
6
- ---
7
-
8
- Real.