session-orchestrator 3.24.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/.agents/skills/architecture/SKILL.md +18 -0
  2. package/.agents/skills/autopilot/SKILL.md +17 -0
  3. package/.agents/skills/bootstrap/SKILL.md +20 -0
  4. package/.agents/skills/brainstorm/SKILL.md +22 -0
  5. package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
  6. package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
  7. package/.agents/skills/debug/SKILL.md +22 -0
  8. package/.agents/skills/discovery/SKILL.md +20 -0
  9. package/.agents/skills/dispatcher/SKILL.md +15 -0
  10. package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
  11. package/.agents/skills/ecosystem-health/SKILL.md +20 -0
  12. package/.agents/skills/eli5/SKILL.md +20 -0
  13. package/.agents/skills/eval/SKILL.md +21 -0
  14. package/.agents/skills/evolve/SKILL.md +21 -0
  15. package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
  16. package/.agents/skills/gitlab-ops/SKILL.md +20 -0
  17. package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
  18. package/.agents/skills/grill/SKILL.md +22 -0
  19. package/.agents/skills/hook-development/SKILL.md +15 -0
  20. package/.agents/skills/mcp-builder/SKILL.md +15 -0
  21. package/.agents/skills/memory-cleanup/SKILL.md +21 -0
  22. package/.agents/skills/mode-selector/SKILL.md +17 -0
  23. package/.agents/skills/npm-publish/SKILL.md +16 -0
  24. package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
  25. package/.agents/skills/persona-panel/SKILL.md +17 -0
  26. package/.agents/skills/plan/SKILL.md +20 -0
  27. package/.agents/skills/playwright-driver/SKILL.md +20 -0
  28. package/.agents/skills/quality-gates/SKILL.md +20 -0
  29. package/.agents/skills/reconcile/SKILL.md +21 -0
  30. package/.agents/skills/remote-offload/SKILL.md +20 -0
  31. package/.agents/skills/repo-audit/SKILL.md +16 -0
  32. package/.agents/skills/session-end/SKILL.md +20 -0
  33. package/.agents/skills/session-plan/SKILL.md +20 -0
  34. package/.agents/skills/session-start/SKILL.md +20 -0
  35. package/.agents/skills/spinout/SKILL.md +16 -0
  36. package/.agents/skills/sunset-review/SKILL.md +16 -0
  37. package/.agents/skills/test-runner/SKILL.md +20 -0
  38. package/.agents/skills/tmux-layout/SKILL.md +21 -0
  39. package/.agents/skills/using-orchestrator/SKILL.md +17 -0
  40. package/.agents/skills/vault-mirror/SKILL.md +15 -0
  41. package/.agents/skills/vault-sync/SKILL.md +15 -0
  42. package/.agents/skills/wave-executor/SKILL.md +20 -0
  43. package/.agents/skills/write-executable-plan/SKILL.md +22 -0
  44. package/.claude-plugin/marketplace.json +1 -1
  45. package/.claude-plugin/plugin.json +1 -1
  46. package/.codex-plugin/plugin.json +1 -1
  47. package/.cursor/commands/autopilot.md +2 -2
  48. package/.cursor/commands/bootstrap.md +1 -1
  49. package/.cursor/commands/brainstorm.md +1 -1
  50. package/.cursor/commands/debug.md +1 -1
  51. package/.cursor/commands/discovery.md +1 -1
  52. package/.cursor/commands/dispatcher.md +2 -2
  53. package/.cursor/commands/eli5.md +2 -2
  54. package/.cursor/commands/eval.md +2 -2
  55. package/.cursor/commands/evolve.md +1 -1
  56. package/.cursor/commands/go.md +1 -1
  57. package/.cursor/commands/grill.md +2 -2
  58. package/.cursor/commands/memory-cleanup.md +2 -2
  59. package/.cursor/commands/persona-panel.md +1 -1
  60. package/.cursor/commands/plan.md +1 -1
  61. package/.cursor/commands/portfolio.md +1 -1
  62. package/.cursor/commands/reconcile.md +2 -2
  63. package/.cursor/commands/release.md +2 -2
  64. package/.cursor/commands/session.md +2 -2
  65. package/.cursor/commands/spinout.md +2 -2
  66. package/.cursor/commands/sunset-review.md +2 -2
  67. package/.cursor/commands/templates-ack.md +2 -2
  68. package/.cursor/commands/test.md +2 -2
  69. package/.cursor/skills/brainstorm/SKILL.md +1 -1
  70. package/.cursor/skills/eval/SKILL.md +1 -1
  71. package/.cursor/skills/quality-gates/SKILL.md +1 -1
  72. package/.cursor/skills/remote-offload/SKILL.md +1 -1
  73. package/.orchestrator/policy/blocked-commands.json +121 -0
  74. package/.orchestrator/policy/ecosystem.schema.json +66 -0
  75. package/.orchestrator/policy/quality-gates.example.json +16 -0
  76. package/.orchestrator/policy/quality-gates.schema.json +38 -0
  77. package/.orchestrator/policy/templates-policy.json +27 -0
  78. package/.orchestrator/policy/test-profiles.json +47 -0
  79. package/AGENTS.md +225 -0
  80. package/CHANGELOG.md +1125 -2
  81. package/NOTICE +11 -6
  82. package/README.md +127 -94
  83. package/agents/eval-judge.md +1 -1
  84. package/agents/skill-applied-judge.md +1 -1
  85. package/assets/wave-lifecycle.svg +98 -0
  86. package/commands/release.md +6 -3
  87. package/commands/session.md +18 -3
  88. package/docs/README.md +4 -0
  89. package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
  90. package/docs/baseline.md +67 -0
  91. package/docs/ci-setup.md +108 -62
  92. package/docs/codex-setup.md +65 -21
  93. package/docs/components.md +36 -15
  94. package/docs/cursor-setup.md +6 -2
  95. package/docs/events-schema.md +9 -6
  96. package/docs/instruction-delivery.md +62 -0
  97. package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
  98. package/docs/migration-v4.md +341 -0
  99. package/docs/pi-setup.md +6 -1
  100. package/docs/plugin-architecture-v3.md +1 -1
  101. package/docs/rule-authoring.md +85 -19
  102. package/docs/scope-collision-guard.md +5 -5
  103. package/docs/session-config-reference.md +57 -56
  104. package/docs/session-config-template.md +6 -29
  105. package/docs/telemetry.md +157 -3
  106. package/docs/vault-docs-architecture.md +50 -11
  107. package/hooks/_lib/hook-import-set.json +1487 -0
  108. package/hooks/_lib/subagent-transcript.mjs +562 -0
  109. package/hooks/config-protection.mjs +2 -2
  110. package/hooks/cwd-change-restore.mjs +2 -2
  111. package/hooks/enforce-commands.mjs +69 -0
  112. package/hooks/hooks-codex.json +1 -1
  113. package/hooks/hooks-cursor.json +10 -0
  114. package/hooks/hooks-pi.json +5 -0
  115. package/hooks/hooks.json +6 -1
  116. package/hooks/loop-guard.mjs +3 -3
  117. package/hooks/on-session-end.mjs +2 -2
  118. package/hooks/on-session-start.mjs +103 -2
  119. package/hooks/on-stop.mjs +36 -11
  120. package/hooks/operator-steer.mjs +2 -2
  121. package/hooks/post-bash-write-verify.mjs +85 -0
  122. package/hooks/post-edit-import-probe.mjs +344 -0
  123. package/hooks/post-subagent-discovery-validator.mjs +187 -431
  124. package/hooks/post-tool-batch-wave-signal.mjs +118 -4
  125. package/hooks/post-tool-failure-corrective-context.mjs +2 -2
  126. package/hooks/post-tooluse-frontend-slop.mjs +3 -3
  127. package/hooks/pre-bash-destructive-guard.mjs +39 -13
  128. package/hooks/skill-invocation-telemetry.mjs +17 -5
  129. package/hooks/subagent-telemetry.mjs +13 -4
  130. package/monitors/monitors.json +3 -3
  131. package/package.json +9 -1
  132. package/pi/prompts/session.md +2 -2
  133. package/plugin.json +27 -0
  134. package/scripts/backfill-abandoned-sessions.mjs +50 -4
  135. package/scripts/backfill-learnings-from-vault.mjs +9 -3
  136. package/scripts/dialectic-deriver.mjs +73 -8
  137. package/scripts/export-hw-learnings.mjs +113 -1
  138. package/scripts/generate-agents-skills.mjs +378 -0
  139. package/scripts/generate-cursor-adapter.mjs +45 -8
  140. package/scripts/generate-hook-import-set.mjs +249 -0
  141. package/scripts/lib/agent-status.mjs +13 -2
  142. package/scripts/lib/auto-dream.mjs +38 -36
  143. package/scripts/lib/autonomy/suitability.mjs +6 -0
  144. package/scripts/lib/autopilot/loop.mjs +2 -2
  145. package/scripts/lib/ci-status-banner.mjs +220 -75
  146. package/scripts/lib/codex/plugin-contract.mjs +82 -6
  147. package/scripts/lib/config/auto-dream.mjs +2 -1
  148. package/scripts/lib/config/block-header.mjs +8 -0
  149. package/scripts/lib/config/block-preprocess.mjs +177 -0
  150. package/scripts/lib/config/broken-window.mjs +2 -1
  151. package/scripts/lib/config/cold-start.mjs +2 -1
  152. package/scripts/lib/config/config-protection.mjs +22 -2
  153. package/scripts/lib/config/context-coverage.mjs +2 -1
  154. package/scripts/lib/config/cross-repo.mjs +2 -1
  155. package/scripts/lib/config/custom-phases.mjs +2 -1
  156. package/scripts/lib/config/dialectic.mjs +2 -1
  157. package/scripts/lib/config/discovery-validator.mjs +2 -1
  158. package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
  159. package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
  160. package/scripts/lib/config/docs-orchestrator.mjs +2 -1
  161. package/scripts/lib/config/docs-staleness.mjs +2 -1
  162. package/scripts/lib/config/drift-check.mjs +2 -1
  163. package/scripts/lib/config/eval.mjs +2 -1
  164. package/scripts/lib/config/events-rotation.mjs +2 -1
  165. package/scripts/lib/config/evolve.mjs +8 -2
  166. package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
  167. package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
  168. package/scripts/lib/config/handover-gate.mjs +2 -1
  169. package/scripts/lib/config/health-endpoints.mjs +7 -2
  170. package/scripts/lib/config/issue-budget.mjs +2 -1
  171. package/scripts/lib/config/loop-guard.mjs +2 -1
  172. package/scripts/lib/config/memory.mjs +2 -1
  173. package/scripts/lib/config/moc-staleness.mjs +2 -1
  174. package/scripts/lib/config/persona-gate-wave.mjs +2 -1
  175. package/scripts/lib/config/private-config-dir.mjs +67 -0
  176. package/scripts/lib/config/reconcile.mjs +2 -1
  177. package/scripts/lib/config/remote-hosts.mjs +2 -1
  178. package/scripts/lib/config/section-extractor.mjs +7 -1
  179. package/scripts/lib/config/skill-evolution.mjs +2 -1
  180. package/scripts/lib/config/slopcheck.mjs +2 -1
  181. package/scripts/lib/config/state-md-lock.mjs +2 -1
  182. package/scripts/lib/config/templates-first.mjs +2 -1
  183. package/scripts/lib/config/test.mjs +2 -1
  184. package/scripts/lib/config/vault-integration.mjs +7 -1
  185. package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
  186. package/scripts/lib/config/vault-staleness.mjs +2 -1
  187. package/scripts/lib/config/vault-sync.mjs +2 -1
  188. package/scripts/lib/config/verification-auto-fix.mjs +2 -1
  189. package/scripts/lib/config/wave-reviewers.mjs +2 -1
  190. package/scripts/lib/config/worktree-orphans.mjs +2 -1
  191. package/scripts/lib/convergence-monitor.mjs +82 -16
  192. package/scripts/lib/dispatcher/rank.mjs +124 -48
  193. package/scripts/lib/ecosystem-health.mjs +16 -2
  194. package/scripts/lib/eval/engine.mjs +9 -1
  195. package/scripts/lib/eval/session-resolve.mjs +23 -4
  196. package/scripts/lib/events.mjs +22 -6
  197. package/scripts/lib/frontmatter-guard.mjs +131 -13
  198. package/scripts/lib/gates/gate-full.mjs +26 -0
  199. package/scripts/lib/gates/gate-helpers.mjs +76 -0
  200. package/scripts/lib/hardware-pattern-detector.mjs +18 -1
  201. package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
  202. package/scripts/lib/host-identity.mjs +50 -11
  203. package/scripts/lib/instruction-budget-guard.mjs +171 -5
  204. package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
  205. package/scripts/lib/learnings/io.mjs +60 -6
  206. package/scripts/lib/memory-proposals/store.mjs +30 -22
  207. package/scripts/lib/owner-config-banner.mjs +43 -6
  208. package/scripts/lib/owner-config-loader.mjs +21 -10
  209. package/scripts/lib/owner-interview.mjs +3 -3
  210. package/scripts/lib/owner-yaml.mjs +207 -14
  211. package/scripts/lib/platform.mjs +108 -15
  212. package/scripts/lib/plugin-update-banner.mjs +406 -0
  213. package/scripts/lib/project-hygiene.mjs +38 -2
  214. package/scripts/lib/qg-command-drift-banner.mjs +50 -12
  215. package/scripts/lib/quality-gate.mjs +133 -44
  216. package/scripts/lib/reconcile/emitter.mjs +68 -6
  217. package/scripts/lib/reconcile/engine.mjs +13 -4
  218. package/scripts/lib/reconcile/idempotency.mjs +37 -4
  219. package/scripts/lib/reconcile/writer.mjs +40 -18
  220. package/scripts/lib/session-close-backfill.mjs +67 -9
  221. package/scripts/lib/session-id.mjs +12 -23
  222. package/scripts/lib/session-identity/own-session.mjs +125 -10
  223. package/scripts/lib/session-lock-shape.mjs +43 -0
  224. package/scripts/lib/session-lock.mjs +5 -10
  225. package/scripts/lib/session-registry.mjs +25 -9
  226. package/scripts/lib/session-schema/constants.mjs +36 -2
  227. package/scripts/lib/session-schema/validator.mjs +38 -4
  228. package/scripts/lib/session-start-probes.mjs +18 -1
  229. package/scripts/lib/sessions-staleness-banner.mjs +18 -11
  230. package/scripts/lib/skill-health/join.mjs +17 -4
  231. package/scripts/lib/state-md.mjs +78 -0
  232. package/scripts/lib/sunset/walker.mjs +6 -0
  233. package/scripts/lib/telemetry/schema.mjs +181 -9
  234. package/scripts/lib/telemetry/sync.mjs +368 -12
  235. package/scripts/lib/validate/check-agents-skills.mjs +327 -0
  236. package/scripts/lib/validate/check-agents.mjs +3 -3
  237. package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
  238. package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
  239. package/scripts/lib/validate/check-owner-leakage.mjs +281 -20
  240. package/scripts/lib/validate/check-skill-links.mjs +163 -0
  241. package/scripts/lib/validate/check-skill-script-paths.mjs +47 -28
  242. package/scripts/lib/validate/check-unwired-features.mjs +0 -2
  243. package/scripts/lib/validate/check-validator-registration.mjs +10 -4
  244. package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
  245. package/scripts/lib/vault-backfill/template.mjs +63 -6
  246. package/scripts/lib/vault-mirror/process.mjs +165 -42
  247. package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
  248. package/scripts/lib/vault-status/narrative-mirror.mjs +127 -18
  249. package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
  250. package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
  251. package/scripts/lib/wave-executor/remote-dispatch.mjs +5 -7
  252. package/scripts/lib/wave-resource-gate.mjs +8 -2
  253. package/scripts/lib/wave-sizing.mjs +4 -1
  254. package/scripts/lib/wave-transcript-tail.mjs +118 -4
  255. package/scripts/materialize-wave-scope.mjs +12 -5
  256. package/scripts/memory-propose.mjs +19 -5
  257. package/scripts/migrate-cold-start-seed.mjs +4 -1
  258. package/scripts/parse-config.mjs +60 -3
  259. package/scripts/release.mjs +337 -29
  260. package/scripts/repair-invalid-sessions.mjs +3 -3
  261. package/scripts/run-quality-gate.mjs +128 -11
  262. package/scripts/sweep-expired-learnings.mjs +90 -0
  263. package/scripts/sync-vault-schema.mjs +3 -1
  264. package/scripts/telemetry.mjs +2 -2
  265. package/scripts/validate-plugin.mjs +161 -0
  266. package/scripts/validate-wave-scope.mjs +28 -8
  267. package/scripts/wave-scope-binding.mjs +215 -0
  268. package/skills/_shared/instruction-file-resolution.md +10 -0
  269. package/skills/_shared/parallel-aware-preamble.md +1 -0
  270. package/skills/_shared/platform-tools.md +1 -1
  271. package/skills/_shared/state-ownership.md +1 -1
  272. package/skills/architecture/SKILL.md +7 -5
  273. package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
  274. package/skills/autopilot/SKILL.md +4 -18
  275. package/skills/claude-md-drift-check/SKILL.md +5 -1
  276. package/skills/claude-md-drift-check/checker.mjs +62 -2
  277. package/skills/convergence-monitoring/SIGNALS.md +55 -0
  278. package/skills/discovery/probes/vault-staleness.mjs +37 -13
  279. package/skills/discovery/probes-arch.md +20 -18
  280. package/skills/dispatcher/SKILL.md +3 -2
  281. package/skills/evolve/SKILL.md +65 -26
  282. package/skills/frontmatter-guard/SKILL.md +11 -5
  283. package/skills/npm-publish/SKILL.md +1 -1
  284. package/skills/reconcile/SKILL.md +33 -0
  285. package/skills/remote-offload/SKILL.md +1 -1
  286. package/skills/session-end/SKILL.md +18 -905
  287. package/skills/session-end/phase-3-6-tail.md +10 -3
  288. package/skills/session-end/plan-verification.md +221 -155
  289. package/skills/session-end/references/phase-2-quality-gate.md +93 -0
  290. package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
  291. package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
  292. package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
  293. package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
  294. package/skills/session-end/references/session-summary-template.md +62 -0
  295. package/skills/session-plan/SKILL.md +49 -0
  296. package/skills/session-start/SKILL.md +22 -904
  297. package/skills/session-start/phase-8-5-express-path.md +1 -1
  298. package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
  299. package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
  300. package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
  301. package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
  302. package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
  303. package/skills/session-start/references/phase-4-ssot-environment-check.md +155 -0
  304. package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
  305. package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
  306. package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
  307. package/skills/vault-sync/validator.mjs +21 -27
  308. package/skills/wave-executor/SKILL.md +15 -1
  309. package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
  310. package/skills/wave-executor/references/wave-loop-review.md +570 -0
  311. package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
  312. package/skills/wave-executor/wave-loop.md +14 -1309
  313. package/templates/_shared/journey-manifest.md +10 -6
  314. package/.cursor/commands/autopilot-multi.md +0 -14
  315. package/.cursor/commands/contract-version-bump.md +0 -14
  316. package/.cursor/commands/journey-audit.md +0 -14
  317. package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
  318. package/.cursor/skills/daily/SKILL.md +0 -12
  319. package/.cursor/skills/domain-model/SKILL.md +0 -13
  320. package/.cursor/skills/journey-audit/SKILL.md +0 -13
  321. package/.cursor/skills/skill-creator/SKILL.md +0 -13
  322. package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
  323. package/commands/autopilot-multi.md +0 -74
  324. package/commands/contract-version-bump.md +0 -28
  325. package/commands/journey-audit.md +0 -43
  326. package/pi/prompts/autopilot-multi.md +0 -12
  327. package/pi/prompts/contract-version-bump.md +0 -12
  328. package/pi/prompts/journey-audit.md +0 -12
  329. package/scripts/autopilot-multi.mjs +0 -885
  330. package/scripts/backfill-learnings-expires.mjs +0 -196
  331. package/scripts/backfill-learnings.mjs +0 -203
  332. package/scripts/fleet-instruction-scan.mjs +0 -141
  333. package/scripts/lib/autopilot/dep-graph.mjs +0 -417
  334. package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
  335. package/scripts/lib/webhook-url.mjs +0 -105
  336. package/scripts/lifecycle-sim-v6.mjs +0 -347
  337. package/scripts/migrate-learnings-jsonl.mjs +0 -189
  338. package/scripts/migrate-subagents-jsonl.mjs +0 -196
  339. package/scripts/upload-social-preview.mjs +0 -316
  340. package/skills/_shared/model-selection.md +0 -64
  341. package/skills/contract-version-bump/SKILL.md +0 -219
  342. package/skills/daily/SKILL.md +0 -222
  343. package/skills/daily/generate.sh +0 -92
  344. package/skills/daily/templates/daily.md.tpl +0 -36
  345. package/skills/journey-audit/SKILL.md +0 -270
  346. package/skills/skill-creator/SKILL.md +0 -168
  347. package/skills/ubiquitous-language/SKILL.md +0 -97
  348. package/skills/vault-sync/package-lock.json +0 -40
  349. /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
  350. /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
@@ -61,6 +61,8 @@ Bypass via `SO_SKIP_CONFIG_VALIDATION=1`. Missing fields can be patched into an
61
61
 
62
62
  **Stale-citation note:** an older code comment on the `custom-phases:` key in this repo's own `CLAUDE.md` cites a per-key regex (`/^custom-phases:\s*$/`) as the mechanism. That citation predates the #830 generalisation — `custom-phases.mjs` (like all 37 consumers) now delegates to the shared `matchBlockHeader(line, 'custom-phases')`, which is strictly MORE tolerant than the old per-key regex (it additionally accepts the dash-bullet and bold-bullet renderings). The no-inline-comment failure mode is unchanged; only the underlying mechanism moved from a bespoke regex to the shared helper. Treat any remaining per-key regex citation in prose (including in this file, prior to this section's introduction) as documentation of the OLD mechanism — the general contract above is current.
63
63
 
64
+ **A second, orthogonal gotcha shares this section: a multi-line `<!-- … -->` comment (#1162).** Every block-shaped parser now strips commented-out lines before matching, via `scripts/lib/config/block-preprocess.mjs` — so a block commented out to disable it can no longer be read as live config, and a bold-bullet sub-key rendering (`- **enabled:** true`) is normalised before parsing instead of silently missing its regex. The one failure mode that still exists is an **unterminated** `<!--` — a stray opener with no matching `-->` anywhere in the rest of the document. `scripts/parse-config.mjs` detects this ONCE per session (not once per parser) and prints a single stderr WARN: `⚠ <file>: unterminated <!-- at line N — comment stripping disabled for the whole document`. The fail-closed direction differs by consumer: a block PARSER gets its lines back UNFILTERED (nothing may silently vanish), while the two destructive-bypass scanners (`allow-config-weakening`, `allow-destructive-ops`) treat an unterminated comment as the bypass being **NOT ARMED** — an ambiguous document must never grant an opt-in it cannot read cleanly.
65
+
64
66
  ## Policy Files
65
67
 
66
68
  Some sub-configs live in dedicated policy files under `.orchestrator/policy/`:
@@ -74,12 +76,46 @@ Some sub-configs live in dedicated policy files under `.orchestrator/policy/`:
74
76
 
75
77
  | Field | Type | Default | Description |
76
78
  |-------|------|---------|-------------|
77
- | `agents-per-wave` | integer or integer with overrides | `6` | Maximum parallel subagents per wave. Supports session-type overrides: `6 (deep: 18)` outputs `{"default": 6, "deep": 18}`. Plain integers remain plain. The override key names a session type but does **not** create one: there is no `session-type:` Session Config key — `parseSessionConfig()` emits none, so writing one into a repo's `## Session Config` block is inert prose. The session type comes from the `/session` argument (default `deep`, see `commands/session.md`) and is persisted to STATE.md frontmatter as `session-type:`, which is the only live read (`scripts/print-applicable-rules.mjs` rule mode-gating). |
79
+ | `agents-per-wave` | integer or integer with overrides | `6` | Maximum parallel subagents per wave. Supports session-type overrides: `6 (deep: 18)` outputs `{"default": 6, "deep": 18}`. The override key set is OPEN — `_coerceInteger` (`scripts/lib/config/coercers.mjs`) parses whatever keys the parentheses contain, so `6 (deep: 18, ultradeep: 18)` outputs `{"default": 6, "deep": 18, "ultradeep": 18}` with no code change (see § Session Profile below). Plain integers remain plain. The override key names a session type but does **not** create one: there is no `session-type:` Session Config key — `parseSessionConfig()` emits none, so writing one into a repo's `## Session Config` block is inert prose. The session type comes from the `/session` argument (default `deep`, see `commands/session.md`) and is persisted to STATE.md frontmatter as `session-type:`, which is the only live read (`scripts/print-applicable-rules.mjs` rule mode-gating). |
78
80
  | `agent-mapping` | object | null | Optional mapping of role keys to agent names for explicit agent binding. Keys: `impl`, `test`, `db`, `ui`, `security`, `compliance`, `docs`, `perf`. Example: `{ impl: code-editor, test: test-specialist }`. Overrides auto-discovery when present. Values may carry a channel prefix — see § `agent-mapping` values below. |
79
81
  | `waves` | integer | `5` | Number of execution waves for feature and deep sessions. |
80
82
  | `recent-commits` | integer | `20` | Number of recent commits to display during session start git analysis. |
81
83
  | `special` | string | none | Repo-specific instructions. Freeform text that the orchestrator reads and follows during sessions. |
82
84
 
85
+ ### Session Profile — `session-profile` (NOT a Session Config key)
86
+
87
+ `session-profile` names a WAVE-SHAPE variant on top of an unchanged `session-type`. It is listed here because it is easy to look for in the wrong place: **it is not a Session Config key and `parseSessionConfig()` does not emit one.** Writing `session-profile:` into a repo's `## Session Config` block is inert prose, exactly like `session-type:` (see the `agents-per-wave` row above).
88
+
89
+ | Aspect | Value |
90
+ |---|---|
91
+ | Where it lives | STATE.md frontmatter (`session-profile: ultradeep`), written per session |
92
+ | Who writes it | The `/session ultradeep` argument alias — `commands/session.md` |
93
+ | Read/write API | `readSessionProfile` / `setSessionProfile` / `SESSION_PROFILE_FIELD` in `scripts/lib/state-md.mjs` |
94
+ | Absent means | No profile. Never an empty string, never `none` — `readSessionProfile` returns `null` |
95
+ | Session record | Optional `session_profile` field (`scripts/lib/session-schema/constants.mjs` `OPTIONAL_FIELDS`); records without it validate unchanged |
96
+ | Defined values | `ultradeep` (7 waves, coordinator-direct Synthesis-Gate at wave 2) — spec: `docs/prd/2026-09-06-ultradeep-session-profile.md` |
97
+
98
+ `session-type` NEVER becomes `ultradeep`: that value is a closed set in `scripts/lib/session-schema/constants.mjs`, `scripts/lib/wave-sizing.mjs` and `scripts/lib/session-close-backfill.mjs`, and an unknown member degrades SILENTLY there (telemetry maps it to `"other"`, the close-backfill labels it `housekeeping`). The profile field exists so no closed set has to change.
99
+
100
+ **Sizing an ultradeep session** uses the open override key set:
101
+
102
+ ```yaml
103
+ agents-per-wave: 6 (deep: 18, ultradeep: 18)
104
+ waves: 5 # must be >= 7 for the ultradeep wave shape
105
+ ```
106
+
107
+ Verified against the parser (2026-09-06, `scripts/lib/config/coercers.mjs`):
108
+
109
+ ```
110
+ $ node -e "import('./scripts/lib/config/coercers.mjs').then(m => console.log(JSON.stringify(
111
+ m._coerceInteger(new Map([['agents-per-wave','6 (deep: 18, ultradeep: 18)']]), 'agents-per-wave', 6))))"
112
+ {"default":6,"deep":18,"ultradeep":18}
113
+ ```
114
+
115
+ Two consumers resolve that object to `.default` rather than to a mode key — `scripts/lib/resource-probe/evaluate.mjs` and `scripts/lib/wave-resource-gate.mjs` (see `heavy-repo` in § Environment Awareness) — so an `ultradeep: 18` override does NOT raise the resource gate's cap.
116
+
117
+ **Budgets are deliberately absent.** The PRD's `ultradeep.max-agents-total` / `max-wall-clock-hours` / `max-output-tokens` / `on-breach` block (§ 7) is NOT implemented and no key of that name is read anywhere. It stays deferred until three ultradeep runs have been measured, per `.claude/rules/host-resources.md` HR-105 — a threshold whose firing rate nothing records is unfalsifiable. Do not add one ahead of the measurement.
118
+
83
119
  ### `agent-mapping` values — channel prefixes (#1150)
84
120
 
85
121
  A mapping value has three forms, distinguished by the colon:
@@ -281,7 +317,7 @@ slopcheck:
281
317
  | `grounding-injection-max-files` | integer | `3` | Max files with recent `edit-format-friction` stagnation history to inject as line-numbered GROUNDING blocks into each agent's prompt before dispatch (wave-executor pre-dispatch step). Per-agent scope; selects top N by recency. `0` disables the feature. Gated on `persistence: true`. (#85) |
282
318
  | `isolation` | string | `auto` | Agent isolation mode: `worktree`, `none`, or `auto`. `auto` resolves per-wave via the graduated default (#194): ≤2 agents → `none`, 3–4 agents on feature/deep → `worktree`, ≥5 agents → `worktree`, housekeeping 3–4 → `none`. Explicit `worktree` or `none` overrides the graduation. See [isolation graduation](#isolation-graduation) below. |
283
319
  | `max-turns` | integer or string | `auto` | Maximum agent turns before PARTIAL. Auto: housekeeping=8, feature=15, deep=25. |
284
- | `auto-commit-per-wave` | boolean | `false` | Automatically commit each wave's work after the Quality-Lite gate passes. Checkpoint commits per wave reduce the risk of data loss from `git stash` collisions in parallel sessions (V3.3 RESCUE incident — see GitLab #214). When `false`, all work is committed at session-end via `/close`. Requires `persistence: true`; the flag is silently ignored when `persistence: false`. Trade-off: each wave produces an additional commit; git log shows N+1 commits instead of 1. Use `/simplify` or `git rebase -i --autosquash` before final close to squash if a clean history is desired. **Implementation note:** the procedural commit sequence (`scripts/lib/auto-commit.mjs`) is deferred to V3.6. Until then, setting this flag to `true` triggers a session-start warning that auto-commits are not yet active — the flag is a no-op but is validated so projects can opt in early. |
320
+ | `auto-commit-per-wave` | boolean | `false` | Automatically commit each wave's work after the Quality-Lite gate passes. Checkpoint commits per wave reduce the risk of data loss from `git stash` collisions in parallel sessions (V3.3 RESCUE incident — see GitLab #214). When `false`, all work is committed at session-end via `/close`. Requires `persistence: true`; the flag is silently ignored when `persistence: false`. Trade-off: each wave produces an additional commit; git log shows N+1 commits instead of 1. Use `/simplify` or `git rebase -i --autosquash` before final close to squash if a clean history is desired. **Implementation note:** the procedural commit sequence (`scripts/lib/auto-commit.mjs`) is deferred to V3.6. Until then, setting this flag to `true` triggers a session-start warning that auto-commits are not yet active — the flag is a no-op but is validated so projects can opt in early. <!-- path-check: historical --> |
285
321
 
286
322
  ### enforcement-gates: the five gate keys (#800/#915)
287
323
 
@@ -704,6 +740,8 @@ vault-integration:
704
740
 
705
741
  > **Host-local override (#653; extended #819).** `vault-dir` resolves host-locally with precedence: env-var (`SO_VAULT_DIR`) > `owner.yaml` `paths.vault-dir` > the committed default. `plan-baseline-path` resolves with an extra per-context tier in between: `SO_BASELINE_PATH` env > `owner.yaml` `baselines:` directory-prefix match against cwd > `owner.yaml` `paths.baseline-path` (legacy scalar) > the committed default. This keeps maintainer-specific absolute paths out of version control. Resolvers: `scripts/lib/config/host-paths.mjs` (both keys) and `scripts/lib/named-baseline-resolver.mjs` (the `baselines:` match tier).
706
742
 
743
+ > **`SO_CONFIG_HOME` — the host-private config directory itself.** A sibling override, one layer below `owner.yaml`'s own contents rather than a key inside it: `scripts/lib/host-identity.mjs` `_privateDir()` resolves the directory holding `owner.yaml`, `host-private.json`, and the host-alias ledger (`SO_HOST_ALIASES_FILE`, see `host-identity.mjs`) with precedence env-var (`SO_CONFIG_HOME`, names the private dir ITSELF) > `XDG_CONFIG_HOME` (names its PARENT — `owner-config-loader.mjs` uses the same variable the same way) > the homedir default `~/.config/session-orchestrator`. Both env vars are read with `.trim() || fallback`, not a bare `||` (`.claude/rules/development.md` § Error Handling env-var-fallback-whitespace trap).
744
+
707
745
  > **Parser accepts three key-line renderings (#823).** The `vault-integration:` key line is recognized in plain form (`vault-integration:`), dash-bullet form (`- vault-integration:`), and bold-bullet form (`- **vault-integration:**`) — each paired with either the inline-object shape (`{ enabled: true, ... }` on the same line) or the indented block shape shown above. Parser: `scripts/lib/config/vault-integration.mjs` (`_parseVaultIntegration`).
708
746
 
709
747
  | Field | Type | Default | Description |
@@ -844,7 +882,7 @@ Memory proposals are one of five Epic #498 Phase 2 features that share the same
844
882
 
845
883
  Together: F2.1 captures fresh insight mid-flight, F2.2 consolidates old insight at scale, F2.3 surfaces it at the start, F2.4/F2.5 distill it into the durable peer-card profiles.
846
884
 
847
- **Used by:** `scripts/lib/memory-proposals/{schema,store,collector,sink}.mjs`, `scripts/memory-propose.mjs`, `agents/memory-proposal-collector.md`, `hooks/pre-bash-memory-propose-audit.mjs`, `skills/session-end/SKILL.md` Phase 3.6.3.
885
+ **Used by:** `scripts/lib/memory-proposals/{schema,store,collector,sink}.mjs`, `scripts/memory-propose.mjs`, `docs/memory-proposal-flow.md`, `hooks/pre-bash-memory-propose-audit.mjs`, `skills/session-end/SKILL.md` Phase 3.6.3.
848
886
 
849
887
  **Cross-reference:** issue #501, PRD F2.1 in the Learning-Memory Modernization PRD; issue #741.3 (`--dry-run` flag + `dry-run-ok` status). Sibling features: `memory.banner` (above, F2.3 / #505), `dialectic.cadence` (F2.5 / #506), Auto-Dream (F2.2 / #502, surfaced via `memory-cleanup-soft-limit`).
850
888
 
@@ -1240,7 +1278,7 @@ remote-hosts:
1240
1278
 
1241
1279
  **Two enums, never conflated.** `roles-allowed` holds `agent-mapping` roles (`test`, `ui`, `perf`) — NOT wave roles (`Impl-Core`, `Quality`, …). The wave→role translation is `OFFLOADABLE_WAVE_ROLES` in `scripts/lib/wave-resource-gate.mjs`; a wave role absent from that map is local-only by default.
1242
1280
 
1243
- **Placement contract.** The gate applies its offload arm only after the HR-004 heavy-repo cap, and only when the resource verdict was `reduce` or `coordinator-direct`. It does NOT probe the network: the coordinator supplies a readiness witness (`remoteReady: { m5: true }`, or an async `probeFn`). With no witness, no host counts as ready and the decision stays local — the gate fails toward local, never toward an unverified host. A role in `NEVER_FOREIGN_ROLES` (`scripts/lib/wave-executor/foreign-dispatch.mjs`) is never offloaded regardless.
1281
+ **Placement contract.** The gate applies its offload arm only after the HR-004 heavy-repo cap, and only when the resource verdict was `reduce` or `coordinator-direct`. It does NOT probe the network: the coordinator supplies a readiness witness (`remoteReady: { m5: true }`, or an async `probeFn`). With no witness, no host counts as ready and the decision stays local — the gate fails toward local, never toward an unverified host. A role in `NEVER_FOREIGN_ROLES` (`scripts/lib/wave-executor/dispatch-common.mjs`) is never offloaded regardless.
1244
1282
 
1245
1283
  **agent-mapping interaction.** A declared alias is what an `agent-mapping` value of the form `<role>: ssh:<alias>` validates against; naming an undeclared host throws at parse time, naming the `ssh` channel with no target throws as for any other channel.
1246
1284
 
@@ -1542,43 +1580,7 @@ SO_DISABLED_HOOKS=enforce-scope,enforce-commands claude ...
1542
1580
 
1543
1581
  Each hook handler imports `shouldRunHook` from `hooks/_lib/profile-gate.mjs` at the top level and calls `process.exit(0)` immediately when gated off. The exit is silent (no stdout, no stderr), so Claude Code sees an allow as if the hook had never run.
1544
1582
 
1545
- ## Webhooks (#228)
1546
-
1547
- Opt-in webhook notifications delivered by `scripts/lib/webhook-url.mjs`. The helper centralizes URL resolution so no personal-domain default ever silently fires — callers must supply a URL explicitly.
1548
-
1549
- ### Resolution order
1550
-
1551
- For every supported kind the resolver checks sources in this order; the first non-empty string wins:
1552
-
1553
- 1. **Environment variable** `SO_WEBHOOK_<KIND>_URL` — uppercase kind, hyphens → underscores
1554
- e.g. `SO_WEBHOOK_SLACK_URL`, `SO_WEBHOOK_GITLAB_PIPELINE_STATUS_URL`
1555
- 2. **Session Config** `webhooks.<kind>.url`
1556
- 3. **Error** — `WebhookConfigError` is thrown. No silent personal-domain fallback.
1557
-
1558
- ### Supported kinds
1559
-
1560
- | Kind | Env variable | Config key |
1561
- |------|-------------|------------|
1562
- | `slack` | `SO_WEBHOOK_SLACK_URL` | `webhooks.slack.url` |
1563
- | `discord` | `SO_WEBHOOK_DISCORD_URL` | `webhooks.discord.url` |
1564
- | `generic` | `SO_WEBHOOK_GENERIC_URL` | `webhooks.generic.url` |
1565
- | `gitlab-pipeline-status` | `SO_WEBHOOK_GITLAB_PIPELINE_STATUS_URL` | `webhooks.gitlab-pipeline-status.url` |
1566
-
1567
- ### Session Config example
1568
-
1569
- ```yaml
1570
- webhooks:
1571
- slack:
1572
- url: https://hooks.slack.com/services/REDACTED/REDACTED/REDACTED
1573
- discord:
1574
- url: https://discord.com/api/webhooks/REDACTED/REDACTED
1575
- generic:
1576
- url: https://example.com/hooks/session-events
1577
- gitlab-pipeline-status:
1578
- url: https://gitlab.example.com/hooks/pipeline
1579
- ```
1580
-
1581
- ### Clank Event Bus (events.mjs / on-stop.mjs)
1583
+ ## Clank Event Bus (events.mjs / on-stop.mjs)
1582
1584
 
1583
1585
  The internal Clank Event Bus webhook is controlled by two environment variables:
1584
1586
 
@@ -1647,24 +1649,23 @@ Set `express-path.enabled: false` when:
1647
1649
  - `skills/session-plan/SKILL.md` — Express Path Short-Circuit section (1-wave plan emission)
1648
1650
  - GitLab issue `#214` (foundation and codification)
1649
1651
 
1650
- ## Autopilot Multi-Story (#431)
1651
-
1652
- Opt-in configuration for `autopilot --multi-story` (`scripts/autopilot-multi.mjs`). Controls how parallel story pipelines are isolated when N stories run concurrently. Projects that do not use `--multi-story` leave this block unset and are unaffected.
1653
-
1654
- All fields live under a top-level `autopilot` object in your Session Config host file (`CLAUDE.md` or `AGENTS.md`), for example:
1655
-
1656
- ```yaml
1657
- autopilot:
1658
- bg-isolation: worktree # worktree | none (default: worktree)
1659
- ```
1652
+ ## Autopilot Multi-Story (#431) — removed
1660
1653
 
1661
- | Field | Type | Default | Description |
1662
- |-------|------|---------|-------------|
1663
- | `autopilot.bg-isolation` | `worktree` \| `none` | `worktree` | Isolation mode for concurrent story pipelines. `worktree` (default): each story creates its own git worktree — safe for parallel writes, costs disk space and EnterWorktree latency. `none`: no worktrees; sub-sessions spawn directly in the main working tree — faster for monorepos with heavy build state but requires explicit file-scope deconfliction (see below). |
1654
+ The `autopilot` block and its single field `autopilot.bg-isolation` are **gone**, not
1655
+ deprecated. Their only reader was `scripts/autopilot-multi.mjs`, retired together with <!-- path-check: historical -->
1656
+ `commands/autopilot-multi.md` by the 2026-09-06 360°-Audit 5A: 0 telemetry, 0 fleet
1657
+ invocations in 90 days, no runtime consumer).
1664
1658
 
1665
- **`bg-isolation: none` hard-error guard:** when `bg-isolation: none` AND `--max-stories > 1`, `autopilot-multi` requires `--deconflict-paths=<glob>` on the CLI to confirm that per-story file ownership is planned. Omitting the flag exits with code 1. This enforces the parallel-session discipline defined in `.claude/rules/parallel-sessions.md` PSA-001/002/003 — two agents editing the same file in the main tree simultaneously will corrupt each other's work.
1659
+ Verified 2026-09-06 at `e4674109`:
1660
+ `rg -n "bg-isolation|bgIsolation|deconflict-paths" scripts hooks tests` returns nothing;
1661
+ `scripts/parse-config.mjs` never parsed an `autopilot` key at all; `scripts/autopilot.mjs`
1662
+ has no `--multi-story` mode. Documenting the field as functional would therefore have been
1663
+ the exact failure the audit found elsewhere — a key an operator can set and no code can
1664
+ read. <!-- path-check: historical -->
1666
1665
 
1667
- **Feature introduced by:** GitLab issue #431 (CC 2.1.143 `worktree.bgIsolation` changelog adoption). Implementation: `scripts/autopilot-multi.mjs` reads `config?.autopilot?.['bg-isolation']` via `scripts/parse-config.mjs`. Documentation: `skills/autopilot/SKILL.md` § Configuration.
1666
+ **If your Session Config still carries an `autopilot:` block, delete it.** It is inert: no
1667
+ parser reads it, so removing it changes no behaviour. Single-story `/autopilot` is
1668
+ unaffected and takes no Session Config block.
1668
1669
 
1669
1670
  ## Wave Reviewers
1670
1671
 
@@ -57,6 +57,10 @@ special: "any repo-specific instructions" # freeform — orchestrator reads +
57
57
 
58
58
  Read by: `skills/session-start/SKILL.md` (Phase 4.5), `skills/session-plan/SKILL.md`, `skills/wave-executor/wave-loop.md`.
59
59
 
60
+ **The override key set is open.** `_coerceInteger` (`scripts/lib/config/coercers.mjs`) parses whatever keys stand inside the parentheses, so `agents-per-wave: 6 (deep: 18, ultradeep: 18)` is valid today with no code change — it yields `{"default": 6, "deep": 18, "ultradeep": 18}`.
61
+
62
+ **`session-profile` is NOT a Session Config key — do not add one here.** The wave-shape profile (`ultradeep`) lives in STATE.md frontmatter, written per session by the `/session ultradeep` argument alias, and is absent by default. `parseSessionConfig()` emits no such key, so writing one into a repo's `## Session Config` block is inert prose — the same trap as `session-type:`. Full contract: [`session-config-reference.md` § Session Profile](./session-config-reference.md). The PRD's `ultradeep.max-*` budget block is deliberately NOT implemented and no key of that name is read anywhere (deferred until measured, HR-105).
63
+
60
64
  ## VCS & Infrastructure
61
65
 
62
66
  ```yaml
@@ -274,7 +278,7 @@ memory:
274
278
 
275
279
  Agents invoke via `SO_WAVE_AGENT=1 node scripts/memory-propose.mjs …`. The `SO_WAVE_AGENT=1` env-var is set automatically by the wave-executor boilerplate; direct CLI calls without it exit `3` (`rejected-wrong-context`).
276
280
 
277
- Read by: `scripts/lib/memory-proposals/{schema,store,collector,sink}.mjs`, `scripts/memory-propose.mjs`, `agents/memory-proposal-collector.md`, `hooks/pre-bash-memory-propose-audit.mjs`, `skills/session-end/SKILL.md` Phase 3.6.3.
281
+ Read by: `scripts/lib/memory-proposals/{schema,store,collector,sink}.mjs`, `scripts/memory-propose.mjs`, `docs/memory-proposal-flow.md`, `hooks/pre-bash-memory-propose-audit.mjs`, `skills/session-end/SKILL.md` Phase 3.6.3.
278
282
 
279
283
  ## Auto-Dream Proposal Filter (#566)
280
284
 
@@ -612,26 +616,6 @@ express-path:
612
616
 
613
617
  Read by: `skills/session-start/phase-8-5-express-path.md`, `skills/session-plan/SKILL.md` (express-path short-circuit).
614
618
 
615
- ## Webhooks
616
-
617
- Opt-in webhook notifications. The `scripts/lib/webhook-url.mjs` resolver checks env first (`SO_WEBHOOK_<KIND>_URL`), then this Session Config block. **No personal-domain default** — callers must supply a URL or the resolver throws.
618
-
619
- ```yaml
620
- webhooks:
621
- slack:
622
- url: https://hooks.slack.com/services/REDACTED/REDACTED/REDACTED
623
- discord:
624
- url: https://discord.com/api/webhooks/REDACTED/REDACTED
625
- generic:
626
- url: https://example.com/hooks/session-events
627
- gitlab-pipeline-status:
628
- url: https://gitlab.example.com/hooks/pipeline
629
- ```
630
-
631
- Measured: `scripts/lib/webhook-url.mjs` (`resolveWebhookUrl`) is the only reader of this `webhooks:` block, and it currently has **zero callers repo-wide** (`grep -rn "webhook-url" scripts/ hooks/` outside itself and one exemption comment in `check-unwired-features.mjs`) — the block is unreachable at HEAD; follow-up issue pending.
632
-
633
- What actually fires a webhook today is a **separate** mechanism: `scripts/lib/events.mjs`'s `emitEvent()` reads `CLANK_EVENT_SECRET` + `CLANK_EVENT_URL` directly from the environment (never from this Session Config block) and, when both are set, fire-and-forget POSTs every emitted event to the internal Clank Event Bus. Every hook that calls `emitEvent()` — which is most of `hooks/` — participates in that path; none of them reads `webhooks:` here.
634
-
635
619
  ## Hook Runtime Profile (env-only, not config)
636
620
 
637
621
  `SO_HOOK_PROFILE` and `SO_DISABLED_HOOKS` are environment variables, **not Session Config fields**. They control hook execution at runtime without editing `hooks.json`.
@@ -682,7 +666,7 @@ That's enough for `/session feature` → `/go` → `/close` to work end-to-end.
682
666
 
683
667
  ## Full opt-in baseline (copy-paste)
684
668
 
685
- Everything turned on for a project that wants the full feature surface (vault, docs, drift checks, env-aware sizing, webhooks). Trim to taste:
669
+ Everything turned on for a project that wants the full feature surface (vault, docs, drift checks, env-aware sizing). Trim to taste:
686
670
 
687
671
  ```yaml
688
672
  ## Session Config
@@ -969,13 +953,6 @@ config-protection:
969
953
  mode: warn # warn | strict (strict blocks loosening, exit 2)
970
954
  allow-config-weakening: false # per-session bypass (mirrors allow-destructive-ops)
971
955
 
972
- # Webhooks (URLs are required when used — no defaults)
973
- # webhooks:
974
- # slack:
975
- # url: https://hooks.slack.com/services/...
976
- # gitlab-pipeline-status:
977
- # url: https://gitlab.example.com/hooks/pipeline
978
-
979
956
  # Agent mapping
980
957
  agent-mapping:
981
958
  impl: code-implementer
package/docs/telemetry.md CHANGED
@@ -39,8 +39,11 @@ projection unit test enforces the drop of any non-whitelisted input field.
39
39
  | `arch` | CPU architecture (e.g. `arm64`, `x64`). |
40
40
  | `node_major` | Major Node.js version in use. |
41
41
  | `ci` | Boolean — whether the run was detected as a CI environment. |
42
- | `fleet` | Boolean — whether this send came from an operator's own fleet-mode host (`owner.yaml` opt-in), as opposed to an external install. |
43
- | `session_type` | One of `housekeeping`, `feature`, `deep`, `other`. |
42
+ | `fleet` | Boolean — **DEPRECATED since 2026-09-06, removal 2027-03-06.** Identical in value to `fleet_self_declared` for the whole deprecation generation; kept so the server's existing `fleet` column stays comparable across the rename. |
43
+ | `fleet_self_declared` | Boolean, optional — the client's own claim that this send came from an operator host. **Self-declared, and the name says so on purpose:** the authoritative classification is server-side (see below). Derived from the *resolved consent state* (`enabled-fleet` from an `owner.yaml` opt-in, or `enabled-env` from `SO_TELEMETRY=1`), no longer from a raw `owner.yaml` read. |
44
+ | `session_profile` | Optional — the STATE.md frontmatter `session-profile`, and **whitelisted profile names only** (today exactly `ultradeep`). Anything else — a value your repo invented, a client name, a typo — is **omitted from the ping entirely**: never sent verbatim, and never flattened to `other` either. A SECOND axis beside `session_type`, never a substitute for it: an ultradeep session is `session_type: "deep"` PLUS `session_profile: "ultradeep"`. **Absent when no profile is set or the profile is not on the whitelist** (the key is omitted, never `null`), including on derived pings, which never invent one. The whitelist is enforced twice — client-side before the send, and again server-side, which rejects a record carrying an unlisted profile rather than storing it. |
45
+ | `session_record` | Optional — WHICH source the session facts in this ping came from: `ledger` (a matching `sessions.jsonl` record), `derived` (reconstructed from `events.jsonl`), `absent` (neither). When `absent`, `session_type` is `unknown` and `duration_bucket` is **not a measurement**. |
46
+ | `session_type` | One of `housekeeping`, `feature`, `deep`, `other`, `unknown`. `other` means MEASURED but not one of the three modes; `unknown` means NOT MEASURED. Before 2026-09-06 both collapsed to `other`. |
44
47
  | `duration_bucket` | One of `<15m`, `15-60m`, `1-3h`, `>3h` — a coarse bucket, never an exact duration. |
45
48
  | `skills[]` | Names of invoked skills, filtered against the shipped plugin roster — any name not in that roster becomes `"other"`. |
46
49
  | `commands[]` | Names of invoked slash-commands, same filtering rule as `skills[]`. |
@@ -79,7 +82,12 @@ towards anonymizing rather than towards attributing:
79
82
  This list is a hard invariant, not a deferral:
80
83
 
81
84
  - No repository names, no file paths, no git remotes.
82
- - No prompts, no session transcripts, no free-form text of any kind.
85
+ - No prompts, no session transcripts, no free-form text of any kind. Every
86
+ field on the wire is either a number, a boolean, or a value from a closed
87
+ set this repository ships. The last free-text field, `session_profile`, was
88
+ closed on 2026-09-06: it now carries whitelisted profile names only, and an
89
+ unlisted value is dropped before the payload is built (and refused again by
90
+ the server, so it cannot be stored even if some other client sent it).
83
91
  - No command arguments — only whitelisted command/skill *names*, and only
84
92
  from the shipped roster (anything else is reduced to `"other"`).
85
93
  - No hostnames.
@@ -180,6 +188,38 @@ the offline queue is non-empty or a session has completed since — the latter
180
188
  clause is what lets the fallback originate a ping instead of only retrying a
181
189
  failed one (#1138).
182
190
 
191
+ ## The other thing that leaves the host: the update check
192
+
193
+ Telemetry is not the only outbound request this plugin can make, so the second
194
+ one is documented here rather than somewhere an egress audit would miss it.
195
+
196
+ On **SessionStart**, `hooks/on-session-start.mjs` calls the plugin-update banner
197
+ (`scripts/lib/plugin-update-banner.mjs`), which asks npm whether a newer release
198
+ exists:
199
+
200
+ ```
201
+ GET https://registry.npmjs.org/session-orchestrator/latest
202
+ ```
203
+
204
+ What it is, precisely:
205
+
206
+ - **No payload and no identifier.** It is a plain `GET` of a constant, public
207
+ URL — no body, no query string, no `anon_id`, no headers this plugin adds.
208
+ npm sees a request for a public package's metadata, as `npm view` would.
209
+ - **At most once per 24 h.** The answer is cached
210
+ (`plugin-latest.json`, `CACHE_TTL_MS = 24 h`); within the TTL no request is
211
+ made at all. The request has a short timeout and every failure is silent.
212
+ - **Independent of telemetry consent.** It is not a ping and sends nothing about
213
+ you — but it is still traffic, so it honours the offline flags below.
214
+
215
+ **Kill switches** (any one of them, set to anything other than empty / `0` /
216
+ `false`, turns the whole check off — not merely the request; the SessionStart
217
+ record then also omits `plugin_version_latest`):
218
+
219
+ - `SO_DISABLE_UPDATE_CHECK` — this probe's own switch.
220
+ - `DO_NOT_TRACK` — the standard flag, also honoured by the telemetry path.
221
+ - `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`.
222
+
183
223
  ## Retention
184
224
 
185
225
  - **Raw records:** kept 24 months, then pruned. The retention window exists
@@ -190,6 +230,99 @@ failed one (#1138).
190
230
  - **Anonymous ID rotation:** every 90 days, independent of retention — a
191
231
  rotated ID cannot be linked back to the one it replaced.
192
232
 
233
+ ## When a ping is sent
234
+
235
+ Two triggers, deliberately independent of each other:
236
+
237
+ 1. **SessionEnd** (`hooks/on-session-end.mjs`) — the mechanical close-time
238
+ flush.
239
+ 2. **SessionStart** (`backfillOnSessionStart` in
240
+ `scripts/backfill-abandoned-sessions.mjs`) — drains whatever the PREVIOUS
241
+ session left queued. Since 4.0.0 every queued record is re-projected and
242
+ `session_profile`-whitelisted at the transport boundary before it is sent,
243
+ and a batch the server rejects with HTTP 400/422 is EVICTED (breadcrumb
244
+ `reason: rejected-evicted`) instead of being re-queued forever — a poison
245
+ record can no longer block every later flush.
246
+
247
+ Trigger 2 exists because trigger 1 fires only on a REGULAR close, and most
248
+ sessions do not have one: measured 2026-09-06 over 90 fleet days, **429 clean
249
+ closes against 2.016 distinct `session.started` ids = 21,3 %**. Roughly four
250
+ sessions in five never reached the only code path that sends. SessionStart is
251
+ the trigger that survives whatever killed the previous session — the same
252
+ argument the abandoned-session backfill already makes for the ledger.
253
+
254
+ The start-time flush is bounded (1,5 s POST budget), lazily imported, gated by
255
+ the same consent check, and swallows every error: it can never delay or break a
256
+ session start. A timeout is lossless — the batch lands in the offline queue.
257
+
258
+ A ping no longer depends on `sessions.jsonl`. When the ledger has no matching
259
+ record, `session_type` and `duration_bucket` are reconstructed from
260
+ `events.jsonl` and the ping is stamped `session_record: "derived"`; when neither
261
+ source has a type, it is `session_type: "unknown"` with `session_record:
262
+ "absent"` — never a measured-looking `other`.
263
+
264
+ ### Sandbox guard
265
+
266
+ The sender refuses to send when it is not running in a real operator session.
267
+ This is not a nicety: on 2026-09-06 six agent sandboxes ran the SessionEnd hook
268
+ from a repo checkout and sent **six real pings to the production ingest server**,
269
+ minted against the operator's real `anon_id`, because `telemetry/paths.mjs`
270
+ resolves `~/.config/session-orchestrator/` from `homedir()` and does not honour
271
+ `SO_CONFIG_HOME` — faking the source never faked the destination.
272
+
273
+ A send is refused (no network, no queue write, no anon-ID mint) when **any** of:
274
+
275
+ - `SO_TELEMETRY_DISABLED=1` or `DO_NOT_TRACK` is set;
276
+ - `SO_CONFIG_HOME` / `XDG_CONFIG_HOME` points somewhere other than the directory
277
+ the telemetry state is actually read from (unless the caller redirected the
278
+ state path too — that redirect succeeded, which is the opposite of the leak);
279
+ - `CLAUDE_PROJECT_DIR`, or the cwd, sits under the OS temp directory or `/tmp`.
280
+
281
+ The guard also **fails closed**: if any of its own probes throws, the send is
282
+ refused with `sandbox:probe-failed` rather than permitted. An environment the
283
+ guard could not classify is treated as one it would have refused; nothing is
284
+ lost, because the next session re-probes from scratch.
285
+
286
+ If you invoke any telemetry writer by hand, export `SO_TELEMETRY_DISABLED=1`.
287
+
288
+ ## Server-side fleet attribution
289
+
290
+ The `fleet` flag on the wire is **self-declared and was measurably wrong**.
291
+ Until 2026-09-06 the client derived it as `ownerConfig?.telemetry?.enabled
292
+ === true` — a statement about a FILE, not about a person. The operator's
293
+ second Mac has consent granted but no `telemetry:` block in `owner.yaml`,
294
+ so it declared itself external: **394 of 490 server records (80,4 %)**
295
+ counted the operator as an external user, and every week's
296
+ `fleet_vs_external` was wrong by that margin.
297
+
298
+ Two independent repairs, because the client alone cannot close this:
299
+
300
+ 1. **Client-side** — `fleet_self_declared` is derived from the resolved
301
+ consent state (`enabled-fleet` / `enabled-env`), so a host opted in via
302
+ `SO_TELEMETRY=1` is no longer mistaken for an external install. The name
303
+ states the limit: a sandbox, or a host whose `owner.yaml` is unreachable,
304
+ still declares `false` however honest it is.
305
+ 2. **Server-side (authoritative)** — set `SO_INGEST_FLEET_ANON_IDS` on the
306
+ ingest server to a comma-separated list of the operator's own `anon_id`
307
+ values. A matching record is **stored** as fleet regardless of what it
308
+ claims. The allowlist can only PROMOTE, never demote: a host that honestly
309
+ declares itself fleet stays fleet even if the operator forgot to list it.
310
+
311
+ ```
312
+ SO_INGEST_FLEET_ANON_IDS=a3bb4907-…,c29cac99-…
313
+ ```
314
+
315
+ The record survives verbatim in `raw_json`, including its own `fleet` /
316
+ `fleet_self_declared` claim, so the client's declaration and the server's
317
+ verdict remain separable forever and the disagreement rate stays measurable.
318
+ `fleet_vs_external` in the weekly digest reads the stored column, i.e. the
319
+ server verdict. **The allowlist applies at INSERT time**, so it cannot repair
320
+ rows already written — a `fleet_vs_external` computed over a range that
321
+ predates the change is known-wrong and re-running the digest will not fix it.
322
+
323
+ Unset by default: with no `SO_INGEST_FLEET_ANON_IDS`, storage takes the
324
+ client's word exactly as it did before.
325
+
193
326
  ## Schema evolution
194
327
 
195
328
  The schema is **additive-only** within a given `schema_version`: new
@@ -198,6 +331,27 @@ without a version bump. The server accepts both the current and the
198
331
  immediately previous `schema_version`, so a slightly-outdated client is
199
332
  never hard-broken by a server-side schema update.
200
333
 
334
+ Unknown top-level fields are accepted by the server and preserved verbatim
335
+ inside `raw_json`, so an additive field round-trips through a server that
336
+ predates it — which is what makes a *rename* safe: emit both names for one
337
+ generation, then drop the old one.
338
+
339
+ **In flight now (added 2026-09-06, schema v1, additive):**
340
+
341
+ | Field | Status | Removal |
342
+ |---|---|---|
343
+ | `fleet_self_declared` | new name for `fleet` | — |
344
+ | `fleet` | deprecated alias, same value | **2027-03-06** |
345
+ | `session_record` | new (`ledger` \| `derived` \| `absent`) | — |
346
+ | `session_profile` | new (STATE.md `session-profile`, whitelisted names only; omitted when unset or unlisted) | — |
347
+
348
+ Client-side the frozen whitelist is split in two: `USAGE_PING_FIELDS` (the
349
+ REQUIRED v1 contract, which `tests/telemetry/parity.test.mjs` asserts the
350
+ server independently requires field by field) and
351
+ `USAGE_PING_OPTIONAL_FIELDS`. `projectUsagePing` projects the UNION, so the
352
+ data-minimization tripwire still holds — a field must be on a reviewed list
353
+ before it can reach the wire.
354
+
201
355
  ## Relationship to `telemetry-claims.md`
202
356
 
203
357
  This page describes the **opt-in, client-side usage-telemetry pipeline**
@@ -1,12 +1,19 @@
1
1
  # Vault & Docs Architecture — Umbrella Narrative
2
2
 
3
3
  **Audience:** Plugin contributors (Dev). New contributors who need to understand
4
- how the four documentation skills, one orchestrator skill, one agent, and two
5
- discovery probes fit together — what fires when, who owns which file, and how
4
+ how the three documentation skills, one orchestrator skill, one agent, two
5
+ vault-status writers, and two discovery probes fit together — what fires when, who owns which file, and how
6
6
  to recover when something breaks.
7
7
 
8
8
  **Status:** Living document. Tracks Epic #229 (Vault & Docs Orchestration).
9
9
 
10
+ **Last verified:** 2026-09-06 at `e4674109`. The vault-file layout below was
11
+ re-measured against the code that writes it — `scripts/lib/vault-status/narrative-mirror.mjs`
12
+ (`resolveNarrativePath`) and `scripts/lib/vault-status/board-writer.mjs` (`resolveBoardPath`) —
13
+ after the pre-#673/#674/#675 layout in this file was found stale by the 2026-09-06
14
+ 360°-Audit (`docs/audits/2026-09-06-360-audit/w1/d6-docs-drift.md`). The `daily` skill
15
+ rows are gone with the skill itself (§ 5A of that audit).
16
+
10
17
  ---
11
18
 
12
19
  ## 1. Purpose
@@ -48,6 +55,8 @@ Data flow within a single `/session feature → /go → /close` cycle:
48
55
 
49
56
  ┌──────────────────────────────────────────────────────────────────┐
50
57
  │ session-start │
58
+ │ Phase 1.7 vault-status board (opt-in) │
59
+ │ └─ this repo → in-progress in _active-sessions.md │
51
60
  │ Phase 2.5 docs-orchestrator (opt-in) │
52
61
  │ └─ audience detection → docs-tasks block in STATE.md │
53
62
  │ Source: skills/session-start/phase-2-5-docs-planning.md │
@@ -78,6 +87,8 @@ Data flow within a single `/session feature → /go → /close` cycle:
78
87
  │ Phase 2.3 vault-staleness (opt-in: stale projects) │
79
88
  │ Phase 3.2 docs-verify (per-task ok/partial/gap) │
80
89
  │ Phase 3.7 vault-mirror (sessions.jsonl → 50-sessions/) │
90
+ │ Phase 3.7 narrative-mirror (STATE.md → _session-narrative) │
91
+ │ Phase 3.7c vault board (this repo's row → closed) │
81
92
  │ Source: skills/session-end/SKILL.md (phase markers) │
82
93
  └──────────────────────────────────────────────────────────────────┘
83
94
 
@@ -86,7 +97,10 @@ Data flow within a single `/session feature → /go → /close` cycle:
86
97
  │ 01-projects/<slug>/ ← context.md / decisions.md / people.md │
87
98
  │ (docs-writer, Vault audience) │
88
99
  │ 01-projects/<slug>/ ← _overview.md (vault-mirror, no humans) │
89
- 03-daily/YYYY-MM-DD.md (daily skill, idempotent)
100
+ 01-projects/<slug>/ ← _session-narrative.md
101
+ │ (narrative-mirror, session-end 3.7) │
102
+ │ 01-projects/_active-sessions.md │
103
+ │ (board-writer, start 1.7 / end 3.7c) │
90
104
  │ 40-learnings/<slug>.md (vault-mirror, evolve hook) │
91
105
  │ 50-sessions/<id>.md (vault-mirror, session-end Phase 3.7) │
92
106
  └──────────────────────────────────────────────────────────────────┘
@@ -109,7 +123,8 @@ Data flow within a single `/session feature → /go → /close` cycle:
109
123
  | `vault-staleness` probes | `skills/discovery/probes-vault.md` + `skills/discovery/probes/vault-staleness.mjs` | `/discovery vault` (on-demand) and session-end Phase 2.3 (opt-in close-time gate) | `VAULT_DIR/01-projects/*/` `_overview.md` + narrative files | JSONL findings under `.orchestrator/metrics/vault-staleness.jsonl` and `vault-narrative-staleness.jsonl` | Vault/Ops (telemetry) |
110
124
  | `docs-orchestrator` | `skills/docs-orchestrator/SKILL.md` | session-start Phase 2.5, session-plan Step 1.5/1.8, session-end Phase 3.2 (all gated on `enabled: true`) | Session scope + Session Config audience list | `docs-tasks` block in STATE.md (write side); `### Documentation Coverage` block in final report (verify side) | All three (User / Dev / Vault) |
111
125
  | `docs-writer` agent | `agents/docs-writer.md` | Dispatched by `wave-executor` for each `Docs`-classified task | `diff`, `git-log`, `session-memory`, `affected-files` | Audience-targeted Markdown writes (Edit/Write); `[docs-orchestrator] Docs task complete` report line | All three (per task) |
112
- | `daily` | `skills/daily/SKILL.md` | User-invocable (`/daily`), idempotent | `VAULT_DIR/03-daily/`, `templates/daily.md.tpl` | `<vault>/03-daily/YYYY-MM-DD.md` (created or no-op) | Vault/Ops (PKM anchor) |
126
+ | `narrative-mirror` | `scripts/lib/vault-status/narrative-mirror.mjs` (`mirrorNarrative`) | session-end Phase 3.7, gated on `vault-integration.enabled` | `.claude/STATE.md` narrative sections + the session record | `<vault>/01-projects/<repo-slug>/_session-narrative.md` (generator-marked) | Vault/Ops (durable per-repo narrative) |
127
+ | `board-writer` | `scripts/lib/vault-status/board-writer.mjs` (`sweepBoard` / `mirrorBoard`) | session-start Phase 1.7 (`in-progress`) and session-end Phase 3.7c (`closed`), gated on `vault-integration.enabled` | live session registry + this repo's root | `<vault>/01-projects/_active-sessions.md` — one row per repo (generator-marked, idempotent, never touches `_overview.md`) | Vault/Ops (cross-repo occupancy board) |
113
128
  | `vault-mirror` | `skills/vault-mirror/SKILL.md` + `scripts/vault-mirror.mjs` | session-end Phase 3.7 (sessions); evolve Phase 3.5 (learnings) | `.orchestrator/metrics/sessions.jsonl`, `.orchestrator/metrics/learnings.jsonl` | `<vault>/50-sessions/<id>.md`, `<vault>/40-learnings/<slug>.md` (`_generator` marker `session-orchestrator-vault-mirror@1`) | Vault/Ops (telemetry → Markdown) |
114
129
  | `vault-backfill` CLI | `scripts/vault-backfill.mjs` | Manual, also surfaced via `/plan retro vault-backfill` sub-mode | `vault-integration.gitlab-groups` config + GitLab API | `.vault.yaml` per repo + Vault stub directories | Vault/Ops (one-shot migration) |
115
130
 
@@ -131,6 +146,15 @@ which audience. Never inline this table elsewhere — always cross-link.
131
146
  `<vault>/01-projects/<slug>/context.md`, `decisions.md`, `people.md`.
132
147
  Source: same.
133
148
 
149
+ Those three are the **authored** half of a project folder and are the only vault
150
+ files docs-writer may touch. The other four under `01-projects/` are **generated**
151
+ and carry a `_` prefix plus a generator marker: `_overview.md` (vault-mirror),
152
+ `_session-narrative.md` (narrative-mirror, #675), and — one level up, per vault
153
+ rather than per project — `_active-sessions.md` (board-writer, #674). Editing a
154
+ generated file by hand is not forbidden by a rule; it is simply overwritten on the
155
+ next run. Source: the `resolveNarrativePath` / `resolveBoardPath` path builders in
156
+ `scripts/lib/vault-status/`.
157
+
134
158
  The Session Config field `docs-orchestrator.audiences` accepts any subset of
135
159
  `[user, dev, vault]`; narrowing it (e.g., `[user, dev]` on a project without a
136
160
  Vault) suppresses Vault-targeted docs without disabling the orchestrator
@@ -169,7 +193,7 @@ silent REVIEW-marker-only output. Source:
169
193
 
170
194
  ## 6. Non-Overlap Discipline
171
195
 
172
- Three forbidden cross-writes are enforced by the architecture, not just by
196
+ Four forbidden cross-writes are enforced by the architecture, not just by
173
197
  convention:
174
198
 
175
199
  - **`<vault>/01-projects/*/_overview.md` is owned by `vault-mirror`.** The
@@ -179,11 +203,24 @@ convention:
179
203
  `skills/docs-orchestrator/audience-mapping.md` § Non-Overlap (vault-mirror
180
204
  row) and `skills/vault-mirror/SKILL.md` § Idempotency (the `_generator`
181
205
  marker `session-orchestrator-vault-mirror@1` is the discriminator).
182
- - **`<vault>/03-daily/YYYY-MM-DD.md` is owned by `daily`.** Idempotent by
183
- design — re-running `/daily` opens the existing note, never overwrites.
184
- Source: `skills/daily/SKILL.md` § Idempotency Guarantee. A second writer
185
- would corrupt the day's scratch notes. Source:
186
- `skills/docs-orchestrator/audience-mapping.md` § Non-Overlap (daily row).
206
+ - **`<vault>/01-projects/_active-sessions.md` and
207
+ `<vault>/01-projects/<slug>/_session-narrative.md` are owned by the two
208
+ vault-status writers.** Both are generator-marked and rewritten wholesale on
209
+ the next session-start / session-end, so a hand edit is silently lost rather
210
+ than merged. `board-writer` additionally holds a cross-repo mutex at
211
+ `<vault>/.orchestrator/board.lock` while writing
212
+ (`scripts/lib/vault-status/board-lock.mjs`, #1180 — deliberately not beside
213
+ the board) because the board is the one vault file several repos write
214
+ concurrently.
215
+ - **`<vault>/03-daily/*` remains a forbidden path for `docs-writer`, but no
216
+ plugin component writes it any more.** The `daily` skill was retired on
217
+ 2026-09-06 (360°-Audit § 5A) and `templates/daily.md.tpl` is gone with it;
218
+ measured the same day, `03-daily/` survives in code only inside
219
+ `scripts/lib/frontmatter-guard.mjs`'s vault-subdirectory *detector*, which
220
+ reads paths and writes none. The forbidden-path entries in
221
+ `skills/docs-orchestrator/audience-mapping.md` § Non-Overlap and
222
+ `agents/docs-writer.md` still name `daily` as the owner; that is stale
223
+ attribution for a path the operator's own PKM now owns, not a live contract.
187
224
  - **`CLAUDE.md` may be remediated by `docs-writer` (Dev audience), but
188
225
  `claude-md-drift-check` only diagnoses it.** The two skills must not run
189
226
  on `CLAUDE.md` in parallel within the same wave. Source:
@@ -201,6 +238,7 @@ Concrete answer to "when does each component fire":
201
238
 
202
239
  | Phase | Skill / Probe | Gating |
203
240
  |-------|---------------|--------|
241
+ | `/session` start, Phase 1.7 | `board-writer` marks this repo `in-progress` on `_active-sessions.md` | `vault-integration.enabled: true` |
204
242
  | `/session` start, Phase 2.5 | `docs-orchestrator` audience detection | `docs-orchestrator.enabled: true` |
205
243
  | `/session` start, Phase 4.5 | resource-health probe | always (env-aware) |
206
244
  | session-plan Step 1.5/1.8 | `docs-writer` registered + Docs role classified | `docs-orchestrator.enabled: true` |
@@ -210,9 +248,10 @@ Concrete answer to "when does each component fire":
210
248
  | `/close` Phase 2.3 | `vault-staleness` + `vault-narrative-staleness` probes | `vault-staleness.enabled: true` |
211
249
  | `/close` Phase 3.2 | `docs-orchestrator` verification | `docs-orchestrator.enabled: true` AND `docs-tasks` block present |
212
250
  | `/close` Phase 3.7 | `vault-mirror` (sessions) | `vault-integration.enabled: true` AND `mode != off` |
251
+ | `/close` Phase 3.7 | `narrative-mirror` → `_session-narrative.md` | `vault-integration.enabled: true`; `mode: strict` blocks the close on failure, otherwise WARN |
252
+ | `/close` Phase 3.7c | `board-writer` transitions this repo's row to `closed` | `vault-integration.enabled: true`; non-blocking |
213
253
  | evolve Phase 3.5 | `vault-mirror` (learnings) | same as above |
214
254
  | `/discovery vault` | `vault-staleness` probes (on-demand) | `.vault.yaml` present OR `vault-integration.enabled: true` |
215
- | `/daily` | `daily` skill | user-invocable; no Session Config gate |
216
255
 
217
256
  Sources: `skills/session-end/SKILL.md` (Phase markers), `docs/session-config-reference.md`
218
257
  (per-skill enabled-flag semantics), `skills/discovery/probes-vault.md` (probe