session-orchestrator 3.23.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 (393) 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 +13 -0
  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 +1401 -0
  81. package/NOTICE +11 -6
  82. package/README.md +127 -92
  83. package/agents/db-specialist.md +0 -1
  84. package/agents/eval-judge.md +1 -1
  85. package/agents/skill-applied-judge.md +1 -1
  86. package/assets/wave-lifecycle.svg +98 -0
  87. package/commands/release.md +6 -3
  88. package/commands/session.md +18 -3
  89. package/docs/README.md +4 -0
  90. package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
  91. package/docs/baseline.md +67 -0
  92. package/docs/ci-setup.md +249 -48
  93. package/docs/codex-setup.md +66 -22
  94. package/docs/components.md +37 -16
  95. package/docs/cursor-setup.md +6 -2
  96. package/docs/events-schema.md +51 -10
  97. package/docs/instruction-delivery.md +62 -0
  98. package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
  99. package/docs/migration-v4.md +341 -0
  100. package/docs/pi-setup.md +6 -1
  101. package/docs/plugin-architecture-v3.md +1 -1
  102. package/docs/rule-authoring.md +85 -19
  103. package/docs/scope-collision-guard.md +8 -8
  104. package/docs/session-config-reference.md +120 -61
  105. package/docs/session-config-template.md +40 -33
  106. package/docs/telemetry/telemetry-claims.md +11 -10
  107. package/docs/telemetry.md +187 -4
  108. package/docs/vault-docs-architecture.md +50 -11
  109. package/hooks/_lib/atomic-json.mjs +111 -0
  110. package/hooks/_lib/hook-import-set.json +1487 -0
  111. package/hooks/_lib/subagent-paths.mjs +143 -0
  112. package/hooks/_lib/subagent-transcript.mjs +562 -0
  113. package/hooks/config-protection.mjs +2 -2
  114. package/hooks/cwd-change-restore.mjs +11 -31
  115. package/hooks/enforce-commands.mjs +69 -0
  116. package/hooks/enforce-scope.mjs +35 -6
  117. package/hooks/hooks-codex.json +1 -1
  118. package/hooks/hooks-cursor.json +10 -0
  119. package/hooks/hooks-pi.json +5 -0
  120. package/hooks/hooks.json +6 -1
  121. package/hooks/loop-guard.mjs +3 -3
  122. package/hooks/on-session-end.mjs +280 -14
  123. package/hooks/on-session-start.mjs +153 -4
  124. package/hooks/on-stop.mjs +371 -17
  125. package/hooks/operator-steer.mjs +2 -2
  126. package/hooks/post-bash-write-verify.mjs +189 -4
  127. package/hooks/post-edit-import-probe.mjs +344 -0
  128. package/hooks/post-subagent-discovery-validator.mjs +278 -392
  129. package/hooks/post-tool-batch-wave-signal.mjs +272 -44
  130. package/hooks/post-tool-failure-corrective-context.mjs +11 -34
  131. package/hooks/post-tooluse-frontend-slop.mjs +3 -3
  132. package/hooks/pre-bash-destructive-guard.mjs +39 -13
  133. package/hooks/pre-bash-memory-propose-audit.mjs +13 -7
  134. package/hooks/skill-invocation-telemetry.mjs +17 -5
  135. package/hooks/subagent-telemetry.mjs +24 -30
  136. package/monitors/monitors.json +3 -3
  137. package/package.json +9 -1
  138. package/pi/prompts/session.md +2 -2
  139. package/plugin.json +27 -0
  140. package/scripts/autopilot.mjs +26 -12
  141. package/scripts/backfill-abandoned-sessions.mjs +130 -15
  142. package/scripts/backfill-learnings-from-vault.mjs +9 -3
  143. package/scripts/dialectic-deriver.mjs +73 -8
  144. package/scripts/emit-event.mjs +10 -2
  145. package/scripts/export-hw-learnings.mjs +113 -1
  146. package/scripts/generate-agents-skills.mjs +378 -0
  147. package/scripts/generate-cursor-adapter.mjs +45 -8
  148. package/scripts/generate-hook-import-set.mjs +249 -0
  149. package/scripts/lib/agent-status.mjs +13 -2
  150. package/scripts/lib/auq/parse.mjs +5 -29
  151. package/scripts/lib/auto-dialectic.mjs +68 -0
  152. package/scripts/lib/auto-dream.mjs +38 -36
  153. package/scripts/lib/autonomy/suitability.mjs +6 -0
  154. package/scripts/lib/autopilot/loop.mjs +2 -2
  155. package/scripts/lib/autopilot/worktree-pipeline.mjs +82 -6
  156. package/scripts/lib/build-live-signals.mjs +25 -22
  157. package/scripts/lib/ci-status-banner.mjs +220 -75
  158. package/scripts/lib/codex/plugin-contract.mjs +82 -6
  159. package/scripts/lib/cold-start-detector.mjs +23 -14
  160. package/scripts/lib/config/auto-dream.mjs +2 -1
  161. package/scripts/lib/config/block-header.mjs +63 -0
  162. package/scripts/lib/config/block-preprocess.mjs +177 -0
  163. package/scripts/lib/config/broken-window.mjs +2 -1
  164. package/scripts/lib/config/cold-start.mjs +2 -1
  165. package/scripts/lib/config/config-protection.mjs +22 -2
  166. package/scripts/lib/config/context-coverage.mjs +2 -1
  167. package/scripts/lib/config/cross-repo.mjs +2 -1
  168. package/scripts/lib/config/custom-phases.mjs +2 -1
  169. package/scripts/lib/config/dialectic.mjs +2 -1
  170. package/scripts/lib/config/discovery-validator.mjs +9 -3
  171. package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
  172. package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
  173. package/scripts/lib/config/docs-orchestrator.mjs +2 -1
  174. package/scripts/lib/config/docs-staleness.mjs +2 -1
  175. package/scripts/lib/config/drift-check.mjs +2 -1
  176. package/scripts/lib/config/eval.mjs +2 -1
  177. package/scripts/lib/config/events-rotation.mjs +2 -1
  178. package/scripts/lib/config/evolve.mjs +8 -2
  179. package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
  180. package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
  181. package/scripts/lib/config/handover-gate.mjs +2 -1
  182. package/scripts/lib/config/health-endpoints.mjs +388 -0
  183. package/scripts/lib/config/issue-budget.mjs +2 -1
  184. package/scripts/lib/config/loop-guard.mjs +2 -1
  185. package/scripts/lib/config/memory.mjs +2 -1
  186. package/scripts/lib/config/moc-staleness.mjs +2 -1
  187. package/scripts/lib/config/persona-gate-wave.mjs +2 -1
  188. package/scripts/lib/config/private-config-dir.mjs +67 -0
  189. package/scripts/lib/config/reconcile.mjs +2 -1
  190. package/scripts/lib/config/remote-hosts.mjs +234 -0
  191. package/scripts/lib/config/section-extractor.mjs +7 -1
  192. package/scripts/lib/config/skill-evolution.mjs +2 -1
  193. package/scripts/lib/config/slopcheck.mjs +2 -1
  194. package/scripts/lib/config/state-md-lock.mjs +2 -1
  195. package/scripts/lib/config/templates-first.mjs +2 -1
  196. package/scripts/lib/config/test.mjs +2 -1
  197. package/scripts/lib/config/vault-integration.mjs +7 -1
  198. package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
  199. package/scripts/lib/config/vault-staleness.mjs +2 -1
  200. package/scripts/lib/config/vault-sync.mjs +2 -1
  201. package/scripts/lib/config/verification-auto-fix.mjs +2 -1
  202. package/scripts/lib/config/wave-reviewers.mjs +2 -1
  203. package/scripts/lib/config/worktree-orphans.mjs +2 -1
  204. package/scripts/lib/config.mjs +31 -3
  205. package/scripts/lib/convergence-monitor.mjs +82 -16
  206. package/scripts/lib/dispatcher/enumerate.mjs +2 -17
  207. package/scripts/lib/dispatcher/rank.mjs +124 -48
  208. package/scripts/lib/ecosystem-health.mjs +16 -2
  209. package/scripts/lib/eval/engine.mjs +9 -1
  210. package/scripts/lib/eval/session-resolve.mjs +23 -4
  211. package/scripts/lib/events-schema.mjs +48 -0
  212. package/scripts/lib/events.mjs +256 -7
  213. package/scripts/lib/evolve/autonomy-verdict.mjs +9 -4
  214. package/scripts/lib/evolve/autopilot-effectiveness.mjs +18 -1
  215. package/scripts/lib/frontmatter-guard.mjs +131 -13
  216. package/scripts/lib/gates/gate-full.mjs +26 -0
  217. package/scripts/lib/gates/gate-helpers.mjs +76 -0
  218. package/scripts/lib/gitlab-portfolio/cli.mjs +3 -15
  219. package/scripts/lib/hardware-pattern-detector.mjs +18 -1
  220. package/scripts/lib/harness-audit/categories/category1.mjs +17 -6
  221. package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
  222. package/scripts/lib/host-identity.mjs +50 -11
  223. package/scripts/lib/instruction-budget-guard.mjs +171 -5
  224. package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
  225. package/scripts/lib/learnings/io.mjs +60 -6
  226. package/scripts/lib/memory-banner.mjs +20 -8
  227. package/scripts/lib/memory-proposals/store.mjs +30 -22
  228. package/scripts/lib/owner-config-banner.mjs +43 -6
  229. package/scripts/lib/owner-config-loader.mjs +21 -10
  230. package/scripts/lib/owner-interview.mjs +3 -3
  231. package/scripts/lib/owner-yaml.mjs +207 -14
  232. package/scripts/lib/peer-discovery.mjs +20 -2
  233. package/scripts/lib/platform.mjs +108 -15
  234. package/scripts/lib/plugin-update-banner.mjs +406 -0
  235. package/scripts/lib/project-hygiene.mjs +38 -2
  236. package/scripts/lib/qg-command-drift-banner.mjs +50 -12
  237. package/scripts/lib/quality-gate.mjs +133 -44
  238. package/scripts/lib/reconcile/emitter.mjs +68 -6
  239. package/scripts/lib/reconcile/engine.mjs +249 -9
  240. package/scripts/lib/reconcile/idempotency.mjs +37 -4
  241. package/scripts/lib/reconcile/writer.mjs +40 -18
  242. package/scripts/lib/scope-gate.mjs +36 -0
  243. package/scripts/lib/session-close-backfill.mjs +125 -18
  244. package/scripts/lib/session-discovery.mjs +57 -3
  245. package/scripts/lib/session-end/phase-skip.mjs +2 -2
  246. package/scripts/lib/session-id.mjs +12 -23
  247. package/scripts/lib/session-identity/own-session.mjs +187 -11
  248. package/scripts/lib/session-lock-shape.mjs +43 -0
  249. package/scripts/lib/session-lock.mjs +5 -10
  250. package/scripts/lib/session-registry.mjs +25 -9
  251. package/scripts/lib/session-schema/constants.mjs +36 -2
  252. package/scripts/lib/session-schema/validator.mjs +38 -4
  253. package/scripts/lib/session-start-probes.mjs +18 -1
  254. package/scripts/lib/session-transition.mjs +1 -1
  255. package/scripts/lib/sessions-canonical.mjs +446 -0
  256. package/scripts/lib/sessions-staleness-banner.mjs +18 -11
  257. package/scripts/lib/skill-health/join.mjs +17 -4
  258. package/scripts/lib/state-md.mjs +78 -0
  259. package/scripts/lib/sunset/walker.mjs +6 -0
  260. package/scripts/lib/telemetry/schema.mjs +255 -17
  261. package/scripts/lib/telemetry/sync.mjs +417 -24
  262. package/scripts/lib/tmux-layout/telemetry.mjs +14 -2
  263. package/scripts/lib/validate/check-agents-skills.mjs +327 -0
  264. package/scripts/lib/validate/check-agents.mjs +3 -3
  265. package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
  266. package/scripts/lib/validate/check-doc-cli-commands.mjs +9 -33
  267. package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +370 -0
  268. package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
  269. package/scripts/lib/validate/check-owner-leakage.mjs +281 -20
  270. package/scripts/lib/validate/check-skill-links.mjs +163 -0
  271. package/scripts/lib/validate/check-skill-script-paths.mjs +455 -0
  272. package/scripts/lib/validate/check-untracked-test-deps.mjs +10 -0
  273. package/scripts/lib/validate/check-unwired-features.mjs +0 -9
  274. package/scripts/lib/validate/check-validator-registration.mjs +254 -0
  275. package/scripts/lib/validate/check-vcs-repo-flag.mjs +6 -28
  276. package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
  277. package/scripts/lib/validate/markdown-fences.mjs +196 -0
  278. package/scripts/lib/vault-backfill/template.mjs +63 -6
  279. package/scripts/lib/vault-mirror/process.mjs +165 -42
  280. package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
  281. package/scripts/lib/vault-status/board-lock.mjs +185 -0
  282. package/scripts/lib/vault-status/board-writer.mjs +174 -135
  283. package/scripts/lib/vault-status/narrative-mirror.mjs +129 -37
  284. package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
  285. package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
  286. package/scripts/lib/wave-executor/remote-dispatch.mjs +502 -0
  287. package/scripts/lib/wave-resource-gate.mjs +133 -7
  288. package/scripts/lib/wave-sizing.mjs +4 -1
  289. package/scripts/lib/wave-transcript-tail.mjs +142 -8
  290. package/scripts/materialize-wave-scope.mjs +32 -9
  291. package/scripts/memory-propose.mjs +146 -8
  292. package/scripts/migrate-cold-start-seed.mjs +4 -1
  293. package/scripts/parse-config.mjs +60 -3
  294. package/scripts/promote-vault-strict.mjs +4 -15
  295. package/scripts/release.mjs +337 -29
  296. package/scripts/repair-invalid-sessions.mjs +3 -3
  297. package/scripts/run-quality-gate.mjs +128 -11
  298. package/scripts/site-numbers.mjs +36 -4
  299. package/scripts/sweep-expired-learnings.mjs +90 -0
  300. package/scripts/sync-vault-schema.mjs +3 -1
  301. package/scripts/telemetry.mjs +2 -2
  302. package/scripts/validate-plugin.mjs +187 -0
  303. package/scripts/validate-wave-scope.mjs +28 -8
  304. package/scripts/vault-consolidate.mjs +3 -11
  305. package/scripts/vault-integration-watcher.mjs +2 -4
  306. package/scripts/vault-mirror.mjs +111 -26
  307. package/scripts/wave-scope-binding.mjs +215 -0
  308. package/skills/_shared/instruction-file-resolution.md +10 -0
  309. package/skills/_shared/parallel-aware-auq.md +31 -2
  310. package/skills/_shared/parallel-aware-preamble.md +18 -4
  311. package/skills/_shared/platform-tools.md +1 -1
  312. package/skills/_shared/state-ownership.md +1 -1
  313. package/skills/architecture/SKILL.md +7 -5
  314. package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
  315. package/skills/autopilot/SKILL.md +4 -18
  316. package/skills/claude-md-drift-check/SKILL.md +5 -1
  317. package/skills/claude-md-drift-check/checker.mjs +62 -2
  318. package/skills/convergence-monitoring/SIGNALS.md +55 -0
  319. package/skills/discovery/probes/vault-staleness.mjs +37 -13
  320. package/skills/discovery/probes-arch.md +20 -18
  321. package/skills/dispatcher/SKILL.md +3 -2
  322. package/skills/ecosystem-health/SKILL.md +4 -1
  323. package/skills/ecosystem-health/wizard.md +5 -0
  324. package/skills/evolve/SKILL.md +87 -11
  325. package/skills/frontmatter-guard/SKILL.md +11 -5
  326. package/skills/npm-publish/SKILL.md +1 -1
  327. package/skills/reconcile/SKILL.md +38 -2
  328. package/skills/remote-offload/SKILL.md +89 -0
  329. package/skills/session-end/SKILL.md +18 -905
  330. package/skills/session-end/phase-3-6-tail.md +19 -9
  331. package/skills/session-end/plan-verification.md +221 -155
  332. package/skills/session-end/references/phase-2-quality-gate.md +93 -0
  333. package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
  334. package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
  335. package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
  336. package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
  337. package/skills/session-end/references/session-summary-template.md +62 -0
  338. package/skills/session-plan/SKILL.md +49 -0
  339. package/skills/session-start/SKILL.md +41 -900
  340. package/skills/session-start/phase-8-5-express-path.md +1 -1
  341. package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
  342. package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
  343. package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
  344. package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
  345. package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
  346. package/skills/session-start/references/phase-4-ssot-environment-check.md +155 -0
  347. package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
  348. package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
  349. package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
  350. package/skills/vault-sync/validator.mjs +21 -27
  351. package/skills/wave-executor/SKILL.md +16 -2
  352. package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
  353. package/skills/wave-executor/references/wave-loop-review.md +570 -0
  354. package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
  355. package/skills/wave-executor/wave-loop.md +14 -1271
  356. package/templates/_shared/journey-manifest.md +10 -6
  357. package/.cursor/commands/autopilot-multi.md +0 -14
  358. package/.cursor/commands/contract-version-bump.md +0 -14
  359. package/.cursor/commands/journey-audit.md +0 -14
  360. package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
  361. package/.cursor/skills/daily/SKILL.md +0 -12
  362. package/.cursor/skills/domain-model/SKILL.md +0 -13
  363. package/.cursor/skills/journey-audit/SKILL.md +0 -13
  364. package/.cursor/skills/skill-creator/SKILL.md +0 -13
  365. package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
  366. package/commands/autopilot-multi.md +0 -74
  367. package/commands/contract-version-bump.md +0 -28
  368. package/commands/journey-audit.md +0 -43
  369. package/pi/prompts/autopilot-multi.md +0 -12
  370. package/pi/prompts/contract-version-bump.md +0 -12
  371. package/pi/prompts/journey-audit.md +0 -12
  372. package/scripts/autopilot-multi.mjs +0 -885
  373. package/scripts/backfill-learnings-expires.mjs +0 -196
  374. package/scripts/backfill-learnings.mjs +0 -203
  375. package/scripts/fleet-instruction-scan.mjs +0 -141
  376. package/scripts/lib/autopilot/dep-graph.mjs +0 -417
  377. package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
  378. package/scripts/lib/webhook-url.mjs +0 -105
  379. package/scripts/lifecycle-sim-v6.mjs +0 -347
  380. package/scripts/migrate-learnings-jsonl.mjs +0 -189
  381. package/scripts/migrate-subagents-jsonl.mjs +0 -196
  382. package/scripts/upload-social-preview.mjs +0 -316
  383. package/skills/_shared/model-selection.md +0 -64
  384. package/skills/contract-version-bump/SKILL.md +0 -219
  385. package/skills/daily/SKILL.md +0 -222
  386. package/skills/daily/generate.sh +0 -92
  387. package/skills/daily/templates/daily.md.tpl +0 -36
  388. package/skills/journey-audit/SKILL.md +0 -269
  389. package/skills/skill-creator/SKILL.md +0 -168
  390. package/skills/ubiquitous-language/SKILL.md +0 -97
  391. package/skills/vault-sync/package-lock.json +0 -40
  392. /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
  393. /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
@@ -0,0 +1,341 @@
1
+ # Migrating to Session Orchestrator v4.0.0
2
+
3
+ v4.0.0 removes public surfaces. Nothing about the core loop changes — `/session`, `/go`,
4
+ `/close`, `/plan`, `/discovery`, `/evolve`, `/bootstrap` and the rest of the 25 commands
5
+ behave exactly as in 3.x — but five skills, three commands and eight top-level scripts are
6
+ gone, two files leave `agents/`, and two telemetry names are deprecated on a dated clock.
7
+ This guide is for someone running 3.x today: which invocations break, what replaces them,
8
+ what happens to your state, and how to go back.
9
+
10
+ The removals follow a two-signal rule measured over a 90-day window — **0 telemetry ∧ 0
11
+ fleet invocation ∧ no runtime consumer** — not a judgement call. Evidence:
12
+ [`docs/audits/2026-09-06-360-audit.md`](./audits/2026-09-06-360-audit.md), per-agent raw
13
+ reports under [`audits/2026-09-06-360-audit/w1/`](./audits/2026-09-06-360-audit/w1/). Full
14
+ change list: [CHANGELOG.md](../CHANGELOG.md).
15
+
16
+ ## 1. What Changes
17
+
18
+ | Area | 3.x | 4.0.0 |
19
+ |------|-----|-------|
20
+ | Skills (`SKILL.md` files) | 49 | 43 — 5 removed, 1 merged into `architecture` |
21
+ | Commands | 28 | 25 — `/contract-version-bump`, `/journey-audit`, `/autopilot-multi` removed |
22
+ | Top-level scripts | 8 more than today | 8 removed (0 runtime callers each) |
23
+ | `agents/*.md` | 16 (2 of them not agents) | 14 — the two non-agents moved to `docs/` |
24
+ | `.claude/rules/` | 61 files (43 generated) | 26 files (43 generated → 8 thematic) |
25
+ | Turn-stop event | `orchestrator.session.stopped` | `orchestrator.turn.stopped` (both emitted until 2027-03-06) |
26
+ | Telemetry field | `fleet` | `fleet_self_declared` (both sent until 2027-03-06) |
27
+ | Cross-harness manifest | none | root `AGENTS.md` + root `plugin.json` + `.agents/skills/` |
28
+ | Session shapes | `housekeeping` / `feature` / `deep` | unchanged, plus the `ultradeep` PROFILE over `deep` |
29
+ | Runtime | Node 24+, npm | unchanged |
30
+
31
+ ### 1a. Invocations that break
32
+
33
+ | You invoke | Status in 4.0.0 | What to do instead |
34
+ |---|---|---|
35
+ | `/contract-version-bump` | **removed** | No replacement. Bump the contract's version literal by hand and note it in your CHANGELOG. |
36
+ | `/journey-audit` | **removed** | No replacement. It required a journey manifest; zero existed fleet-wide, so it was never runnable. |
37
+ | `/autopilot-multi` | **removed** | No replacement. `/autopilot` (single-story) is unaffected and stays. |
38
+ | the `daily` skill | **removed** | No replacement. A recurring daily note is a scheduled task / cloud Routine, not a session skill — see `.claude/rules/loop-and-monitor.md` § LM-004. |
39
+ | the `skill-creator` skill | **removed** | Anthropic's official `skill-creator` plugin, or `claude plugin init`. |
40
+ | the `ubiquitous-language` skill | **removed** | No replacement. |
41
+ | the `domain-model` skill | **merged** | `/architecture` — the material now lives at `skills/architecture/references/domain-model.md` (with `ADR-FORMAT.md` and `CONTEXT-FORMAT.md` beside it). |
42
+ | `node scripts/autopilot-multi.mjs` | **removed** | No replacement. | <!-- path-check: historical -->
43
+ | `node scripts/backfill-learnings.mjs`, `…-expires.mjs` | **removed** | No replacement — one-shot ledger repairs, already applied. | <!-- path-check: historical -->
44
+ | `node scripts/migrate-learnings-jsonl.mjs`, `migrate-subagents-jsonl.mjs` | **removed** | No replacement — one-shot format migrations; the target format has been canonical for several releases. | <!-- path-check: historical -->
45
+ | `node scripts/fleet-instruction-scan.mjs`, `lifecycle-sim-v6.mjs`, `upload-social-preview.mjs` | **removed** | No replacement. | <!-- path-check: historical -->
46
+ | dispatching `AGENTS.md` as an agent | **removed as a dispatch target** | It was never an agent — it is the authoring spec, now [`docs/agent-authoring.md`](./agent-authoring.md). Read it; do not dispatch it. |
47
+ | dispatching `memory-proposal-collector` as an agent | **removed as a dispatch target** | Now [`docs/memory-proposal-flow.md`](./memory-proposal-flow.md). |
48
+ | citing `skills/_shared/model-selection.md` | **removed** | No replacement; it had zero consumers. |
49
+ | Session Config `autopilot.bg-isolation` | **removed** | No replacement — delete the `autopilot:` block from your `CLAUDE.md`/`AGENTS.md`, it was the block's only field and is now inert (`scripts/parse-config.mjs` never parsed it). |
50
+ | Session Config `webhooks.<kind>.url` | **removed** (pre-4.0.0, `ce6a28aa`) | No replacement — `scripts/lib/webhook-url.mjs` had zero callers. The still-live Clank Event Bus webhook (`CLANK_EVENT_SECRET`/`CLANK_EVENT_URL`) is unrelated and unaffected. | <!-- path-check: historical -->
51
+
52
+ ### 1b. Telemetry consumers (dated deprecations, nothing breaks yet)
53
+
54
+ If you read `.orchestrator/metrics/events.jsonl` or the ingest server, two names change and
55
+ **both old and new are emitted for one generation**, so no reader breaks on upgrade day:
56
+
57
+ - **`orchestrator.session.stopped` → `orchestrator.turn.stopped`.** The emitter always fired
58
+ per assistant TURN, not per session — measured 15,538 records against 2,016 distinct
59
+ `orchestrator.session.started` ids over 90 days, i.e. 7.7 per session, 184 for one id. Any
60
+ "sessions stopped" count you derived from it is a **turn count**, wrong by that factor. Both
61
+ names carry an identical payload from the same object, so they cannot disagree; every record
62
+ under the legacy name additionally carries `deprecated: true`. **Switch the name you match
63
+ on and change nothing else. Removal of the legacy name: 2027-03-06.** To count sessions,
64
+ count `session.started` ids; to count closes, count `sessions.jsonl` records with
65
+ `status: completed`. Unaffected: `orchestrator.agent.stopped`, whose per-agent cardinality
66
+ was always correct.
67
+ - **`fleet` → `fleet_self_declared`.** The old name claimed something the client cannot know.
68
+ The new name says what it is, and the authoritative classification is now server-side (an
69
+ anon-id allowlist). `fleet` keeps being sent with an identical value for the whole
70
+ generation so an existing column stays comparable. **Removal: 2027-03-06.** Contract:
71
+ [`docs/telemetry.md`](./telemetry.md).
72
+
73
+ ## 2. Prerequisites
74
+
75
+ - **Node.js 24 or later** — unchanged from v3. `node --version`.
76
+ - **Git** — any recent version.
77
+ - **`npm install` after updating.** Still mandatory, and now slightly less punishing: in 3.x,
78
+ 4 of 27 hooks died at module-load with `ERR_MODULE_NOT_FOUND: js-yaml` when `node_modules`
79
+ was absent. In 4.0.0 all 27 hooks exit 0 without `node_modules` — but they run degraded, so
80
+ install anyway.
81
+ - **`glab` / `gh`** — optional, unchanged.
82
+
83
+ Nothing new is required. There is no data migration, no schema bump, and no config key you
84
+ must add.
85
+
86
+ ## 3. Upgrade Steps
87
+
88
+ ### 3a. Claude Code
89
+
90
+ ```bash
91
+ # 1. Update the plugin — run this INSIDE Claude Code, not in a shell:
92
+ # /plugin update session-orchestrator@kanevry
93
+ # A marketplace-installed plugin lives in a managed cache, not a git checkout,
94
+ # so `git pull` does not apply to it.
95
+
96
+ # 2. Install Node dependencies in the cache copy:
97
+ SO_DIR="$(dirname "$(find ~/.claude/plugins/cache -path '*session-orchestrator*' -name package.json 2>/dev/null | head -1)")"
98
+ cd "$SO_DIR" && npm install
99
+
100
+ # 3. Restart Claude Code so hooks.json is re-read.
101
+ ```
102
+
103
+ From 4.0.0 on, session-start compares the version that is **running** against the npm
104
+ `dist-tags.latest` and prints a one-line banner when they differ. It fails silent: offline, a
105
+ non-2xx response, malformed JSON or a timeout each produce no statement — never a false "you
106
+ are up to date".
107
+
108
+ ### 3b. Codex CLI
109
+
110
+ ```bash
111
+ codex plugin marketplace upgrade kanevry # omit the name to refresh all marketplaces
112
+ codex plugin add session-orchestrator@kanevry
113
+ codex plugin list --available --json
114
+ ```
115
+
116
+ For the maintainer / local-clone path instead:
117
+
118
+ ```bash
119
+ cd ~/Projects/session-orchestrator
120
+ git pull && npm install
121
+ node scripts/codex-install.mjs
122
+ ```
123
+
124
+ New in 4.0.0 and relevant here: this repository now ships a **root `AGENTS.md`**
125
+ (byte-identical to `CLAUDE.md`). Before, a Codex-family harness resolving project
126
+ instructions from `AGENTS.md` found nothing in this repo. If you keep your own
127
+ `AGENTS.md`, nothing changes for you.
128
+
129
+ ### 3c. Cursor IDE
130
+
131
+ ```bash
132
+ cd ~/Projects/session-orchestrator
133
+ git pull && npm install
134
+ node scripts/cursor-install.mjs /path/to/your-project # links NEW commands/rules/skills only
135
+ # Restart Cursor
136
+ ```
137
+
138
+ **The installer adds new files; it never overwrites or removes an existing one.** `linkPath()`
139
+ skips whenever the destination already exists as a symlink or a file
140
+ (`scripts/cursor-install.mjs:69-73`), and the `hooks.json` writer skips outright when one is
141
+ already there (`:139-140`). Two consequences on this upgrade: the malformed `argument-hint` fix
142
+ (the 3.x generator wrote it into 24 of 28 command files, GH#54) reaches you for free through
143
+ your existing symlinks the moment `git pull` updates this checkout — no re-run needed for that.
144
+ But the three retired commands do **not** disappear from `.cursor/commands/` on their own, and a
145
+ `hooks.json` written before 4.0.0 is never synchronised with a new hook event automatically.
146
+ Both need the manual step in § 5 ("Cursor still shows the removed commands") below.
147
+
148
+ A root `plugin.json` following the [agent-plugins.org](https://agent-plugins.org) 1.0.0
149
+ schema now ships as well, for Cursor's plugin system.
150
+
151
+ ### 3d. Pi
152
+
153
+ ```bash
154
+ pi install npm:session-orchestrator
155
+ ```
156
+
157
+ Or against a local checkout:
158
+
159
+ ```bash
160
+ cd ~/Projects/session-orchestrator
161
+ git pull && npm install
162
+ node scripts/pi-install.mjs /path/to/your-project --settings-only
163
+ ```
164
+
165
+ `pi/prompts/` drops from 28 to 25 files, matching `commands/`.
166
+
167
+ ### 3e. Verify
168
+
169
+ Run `/session housekeeping` in any configured repo. You should see the session-start banner;
170
+ `/contract-version-bump`, `/journey-audit` and `/autopilot-multi` should no longer be offered.
171
+ Then:
172
+
173
+ ```bash
174
+ node scripts/parse-config.mjs --json # now works — in 3.x this failed with "File not found: --json"
175
+ node scripts/validate-plugin.mjs
176
+ ```
177
+
178
+ ## 4. What Stays the Same
179
+
180
+ **Your state is untouched. There is no migration step and no format change.**
181
+
182
+ - **`.orchestrator/` in every repo** — `metrics/sessions.jsonl`, `metrics/learnings.jsonl`,
183
+ `metrics/events.jsonl`, `current-session.json`, `session.lock`, `state.lock`, filescopes and
184
+ wave-scope manifests are all read-write compatible in both directions. Nothing is rewritten
185
+ on upgrade, and nothing 4.0.0 writes is unreadable by 3.24.0.
186
+ - **`STATE.md`** — same frontmatter schema. 4.0.0 adds ONE optional scalar,
187
+ `session-profile`, and absent is not empty: a STATE.md without it behaves exactly as before.
188
+ A 3.x build reading a 4.0.0 STATE.md simply ignores the extra key.
189
+ - **Session Config in `CLAUDE.md` / `AGENTS.md` / Cursor rules** — no key you are likely using
190
+ is renamed or given a new default. Two keys ARE removed — `autopilot.bg-isolation` and
191
+ `webhooks.<kind>.url` — see § 1a above; everything else you configured in 3.x is still
192
+ correct as-is.
193
+ - **Session memory** (`~/.claude/projects/<project>/memory/`) — untouched.
194
+ - **`session-type`** — still the closed set `housekeeping` / `feature` / `deep`. `ultradeep`
195
+ is an argument ALIAS that resolves to `session-type: deep` plus
196
+ `session-profile: ultradeep`; it is deliberately not a fourth type, because a fourth member
197
+ would degrade silently in the telemetry mapper and the close-backfiller.
198
+ - **The whole core loop** — `/session`, `/go`, `/close`, `/plan`, `/discovery`, `/evolve`,
199
+ `/bootstrap`, `/debug`, `/autopilot` and the other remaining commands take the same
200
+ arguments and run the same flow.
201
+ - **VCS integration** — `glab` / `gh` commands, label taxonomy, issue and MR templates.
202
+
203
+ ## 5. Known Issues & Workarounds
204
+
205
+ ### Your CLAUDE.md cites a removed skill
206
+
207
+ **Nothing breaks at runtime.** A citation in a consumer repo's `CLAUDE.md` (or `AGENTS.md`) to
208
+ `daily`, `skill-creator`, `ubiquitous-language`, `contract-version-bump`, `journey-audit`,
209
+ `domain-model` or `skills/_shared/model-selection.md` is prose. No loader resolves it, no hook
210
+ reads it, and the session starts normally.
211
+
212
+ **But `claude-md-drift-check` may flag it** as a dangling citation, and if you run it with
213
+ `enforcement: strict` that finding is an error rather than a warning. Two ways out, both fine:
214
+
215
+ 1. Delete or rewrite the citation. `domain-model` becomes
216
+ `skills/architecture/references/domain-model.md`; the other five have no successor, so the
217
+ sentence usually goes away with the skill.
218
+ 2. Leave it and accept the warning until your next docs pass. It is a documentation-parity
219
+ finding, not a functional one.
220
+
221
+ Same story for a rule file or ADR that names one of the removed scripts.
222
+
223
+ ### A wrapper script or Routine calls a removed top-level script
224
+
225
+ `node scripts/<name>.mjs` on any of the eight removed scripts now exits with a Node
226
+ `ERR_MODULE_NOT_FOUND`-class failure — a loud error, not a silent no-op. If a cron job,
227
+ Routine or CI step calls one, delete that step: the two `migrate-*` scripts had already
228
+ completed their one-shot migration, and the two `backfill-learnings*` scripts their one-shot
229
+ repair, before this release.
230
+
231
+ ### A dashboard suddenly reports far fewer "sessions stopped"
232
+
233
+ Expected, and it was wrong before, not now. See § 1b — you were counting turns. Match on
234
+ `orchestrator.turn.stopped`, or better, count `session.started` ids for sessions and
235
+ `sessions.jsonl` records with `status: completed` for closes.
236
+
237
+ ### `npm install` fails with `ERESOLVE` or peer-dep warnings
238
+
239
+ Use Node 24 or later, matching `engines.node: ">=24.0.0"`. If it persists:
240
+ `npm install --legacy-peer-deps`.
241
+
242
+ ### Hooks silently no-op after the update
243
+
244
+ Node must be on `PATH` inside the **editor** process, not only in your terminal. Verify from
245
+ inside Claude Code with `!node --version`. This is unchanged from v3.
246
+
247
+ ### Cursor still shows the removed commands
248
+
249
+ Re-running the installer does **not** fix this: `linkPath()` skips any destination that already
250
+ exists (`scripts/cursor-install.mjs:69-73`), including a stale symlink pointing at a command
251
+ that no longer exists in this repo. Remove only the three dead **symlinks** from your project —
252
+ never an unconditional `rm -f`, which would just as happily delete a regular file, including a
253
+ command you wrote yourself under one of these three names (the installer itself never overwrites
254
+ or deletes a regular file; this recipe must not either):
255
+
256
+ ```bash
257
+ for f in autopilot-multi contract-version-bump journey-audit; do
258
+ p=/path/to/your-project/.cursor/commands/$f.md
259
+ [ -L "$p" ] && rm "$p" # -L: true only for a symlink, so a real file with this name survives
260
+ done
261
+ ```
262
+
263
+ Then restart Cursor. Same story for `.cursor/hooks.json`: the installer skips it outright once
264
+ it exists (`:139-140`), so a `hooks.json` written before 4.0.0 is never re-synced with a new hook
265
+ event automatically — re-check it by hand (diff it against a fresh `node scripts/cursor-install.mjs`
266
+ run in an empty scratch directory if you suspect drift).
267
+
268
+ ## 6. Rollback
269
+
270
+ 4.0.0 removes surfaces; it does not migrate data. **Rolling back is therefore a plain version
271
+ switch — no state has to be converted, and nothing you wrote under 4.0.0 becomes unreadable.**
272
+
273
+ **Claude Code / plugin cache.** A marketplace install tracks the marketplace's current
274
+ version; this repo documents no version-pin flag for `/plugin install`, so do not guess one.
275
+ Roll back by pointing Claude Code at a local clone parked on the old tag:
276
+
277
+ ```bash
278
+ git clone https://github.com/Kanevry/session-orchestrator ~/so-3.24.0
279
+ cd ~/so-3.24.0 && git checkout v3.24.0 && npm install
280
+ ```
281
+
282
+ Then, inside Claude Code, remove the marketplace-installed copy and add the clone as the
283
+ plugin source (`/plugin marketplace add ~/so-3.24.0`, then install from it) and restart the
284
+ editor. If you are unsure of the exact `/plugin` subcommands on your build, run `/plugin`
285
+ with no arguments — it lists them.
286
+
287
+ **Local checkout (Codex / Cursor / Pi / maintainer):**
288
+
289
+ ```bash
290
+ cd /path/to/session-orchestrator
291
+ git fetch --tags
292
+ git checkout v3.24.0
293
+ npm install # the 3.24.0 lockfile, not the 4.0.0 one
294
+ node scripts/codex-install.mjs # Codex — no target-project argument
295
+ node scripts/cursor-install.mjs /path/to/your-project # Cursor — pass YOUR project, not this checkout
296
+ node scripts/pi-install.mjs /path/to/your-project --settings-only # Pi — same argument contract
297
+ # Restart your editor
298
+ ```
299
+
300
+ Run only the line for your platform. `cursor-install.mjs` and `pi-install.mjs` both default their
301
+ target to `process.cwd()` when no argument is given (`scripts/cursor-install.mjs:24-51`) — omit
302
+ the project path here and the installer links into `/path/to/session-orchestrator` itself, not
303
+ into your project.
304
+
305
+ **npm consumers:** `npm install session-orchestrator@3.24.0`.
306
+
307
+ What you get back, and what you do not:
308
+
309
+ - **Your state survives in both directions.** `sessions.jsonl`, `learnings.jsonl`,
310
+ `events.jsonl` and `STATE.md` written by 4.0.0 are readable by 3.24.0. The one 4.0.0
311
+ addition to STATE.md, `session-profile`, is an unknown key to 3.24.0 and is ignored, not
312
+ rejected.
313
+ - **`orchestrator.turn.stopped` records written under 4.0.0 stay in your `events.jsonl` after
314
+ a rollback.** 3.24.0 does not know the name and will not count them; the
315
+ `orchestrator.session.stopped` twin of every one of those records is right beside it, so no
316
+ data is lost — only a 3.x reader sees each turn once instead of twice.
317
+ - **The removed skills, commands and scripts come back with the checkout.** They were deleted
318
+ from the repository, not from your disk history.
319
+ - **What does NOT roll back automatically** is anything an installer wrote into YOUR project:
320
+ `.cursor/commands/`, `.cursor/hooks.json`, Pi settings. Both `cursor-install.mjs` and
321
+ `pi-install.mjs` skip any destination that already exists — a symlink or a file
322
+ (`scripts/cursor-install.mjs:69-73`; the `hooks.json` writer at `:139-140`) — so re-running
323
+ the installer from the 3.24.0 checkout only **adds** files missing from your project. It does
324
+ not restore a symlink you removed yourself, and it does not resync an existing `hooks.json` or
325
+ Pi settings file. To get those back: remove the stale file first (§ "Cursor still shows the
326
+ removed commands" has a safe, symlink-only removal recipe), then re-run the installer with
327
+ your project path, as shown above.
328
+
329
+ If 4.0.0 blocks you, please open an issue describing the blocker before rolling back — a
330
+ removal we got wrong is fixable in a 4.0.x patch.
331
+
332
+ ## 7. Support
333
+
334
+ - **GitHub:** [Kanevry/session-orchestrator/issues](https://github.com/Kanevry/session-orchestrator/issues)
335
+ — preferred for bugs and feature requests.
336
+ - **Homepage:** [session-orchestrator.com](https://session-orchestrator.com)
337
+
338
+ When filing a bug, include `node --version`, your OS and arch, the harness (Claude Code /
339
+ Codex CLI / Cursor / Pi), the plugin version actually running (the session-start banner prints
340
+ it), and a minimal reproduction. For hook failures, attach the relevant entries from
341
+ `.orchestrator/metrics/events.jsonl`.
package/docs/pi-setup.md CHANGED
@@ -18,7 +18,12 @@ pi install npm:session-orchestrator
18
18
 
19
19
  This is the primary install path — the package is published to npm (since v3.16.0, 2026-07-19) and carries the `pi-package` keyword for the [Pi packages gallery](https://pi.dev/packages) index; no local checkout required. The dev-fallback options below remain available for working against a local checkout.
20
20
 
21
- The short git form documented for Pi packages (`pi install git:github.com/user/repo`) is **unverified against this repo as of 2026-08-28** the `pi` CLI was not installed on the verifying host (`which pi` not found), so no install attempt could be made. The npm form above remains the only measured, documented install path; do not elevate the `git:` short form to this guide or to README until it has been run against this repo and the result dated.
21
+ The short git form for Pi packages is `pi install git:github.com/user/repo@ref`. Two separate questions were previously collapsed into one "unverified" verdict; they are split here because only one of them is still open (measured 2026-09-06, W1 d10):
22
+
23
+ - **Is the syntax real?** Yes — the `git:` short form, including the `@ref` suffix, is documented upstream. The 2026-08-28 line that called the *syntax* unverified was stale and is corrected here.
24
+ - **Does it work against THIS repo?** Still unverified. The `pi` CLI is not installed on the verifying host (`which pi` → not found), so no install attempt has been made against `github.com/Kanevry/session-orchestrator`. The npm form above remains the only *measured* install path.
25
+
26
+ Do not elevate the `git:` form to README as a recommended install line until it has been run against this repo and the result dated. Citing it as valid Pi syntax is fine; citing it as a verified install path for this package is not.
22
27
 
23
28
  ### Option 2: Project-local registration (dev fallback)
24
29
 
@@ -114,7 +114,7 @@ Under `scripts/lib/`. Each module is a focused concern and exports only what cal
114
114
  | Module | 1-liner | Key exports |
115
115
  |--------|---------|-------------|
116
116
  | **`io.mjs`** | Hook stdin/stdout helpers matching the Claude Code contract | `readStdin`, `emitAllow`, `emitDeny`, `emitWarn`, `emitSystemMessage` |
117
- | **`platform.mjs`** | OS + editor detection | `SO_OS`, `SO_IS_WINDOWS`, `SO_IS_WSL`, `SO_PATH_SEP`, `SO_STATE_DIR`, `detectPlatform()` |
117
+ | **`platform.mjs`** | OS + editor detection | `SO_OS`, `SO_IS_WINDOWS`, `SO_IS_WSL`, `SO_PATH_SEP`, `getStateDir()`, `detectPlatform()` |
118
118
  | **`path-utils.mjs`** | CWE-23-safe path helpers (null-byte rejection, UNC block, cross-drive escape, locale-stable casing) | `normalizeForMatching`, `isWithin`, `CWE_23_ATTACK_PATTERNS` |
119
119
  | **`config.mjs`** | CRLF-tolerant Session Config parser (originally parse-config.sh in v2; byte-exact parity preserved) | `parseSessionConfig`, `readConfigFile`, `getConfigValue` |
120
120
  | **`config-schema.mjs`** | Plain-JS validator; validates the 7 mandatory Session Config fields (3 required strings + 4 typed fields) | `validateSessionConfig` (internal `REQUIRED_STRING_FIELDS` constant covers the 3 required strings — not exported) |
@@ -194,32 +194,33 @@ The `learning-key` field above links a rule to a learning record, but does not b
194
194
 
195
195
  ### The type-taxonomy + per-type TTL registry (single source of truth)
196
196
 
197
- `LEARNING_TYPE_REGISTRY` in [`scripts/lib/learnings/schema.mjs`](../scripts/lib/learnings/schema.mjs) (~L80107) is the **single source of truth** for every learning `type`'s TTL policy and its two cross-module capability flags. Before this registry existed (pre-#733), three modules independently hand-maintained overlapping type lists that drifted out of sync. `LEARNING_TTL_DAYS` (this file), `PROPOSAL_TYPES` (`scripts/lib/memory-proposals/schema.mjs`), and `CONVERT_TYPES` (`scripts/lib/reconcile/eligibility.mjs`) are now all **derived** from this one registry — no hand-maintained duplicate lists remain.
197
+ `LEARNING_TYPE_REGISTRY` in [`scripts/lib/learnings/schema.mjs`](../scripts/lib/learnings/schema.mjs) (~L92127) is the **single source of truth** for every learning `type`'s TTL policy and its three cross-module capability flags (`agentProposable`, `ruleConvertible`, `hostScoped` — four axes in total, counting `ttlDays`). Before this registry existed (pre-#733), three modules independently hand-maintained overlapping type lists that drifted out of sync. `LEARNING_TTL_DAYS` (this file), `PROPOSAL_TYPES` (`scripts/lib/memory-proposals/schema.mjs`), and `CONVERT_TYPES` (`scripts/lib/reconcile/eligibility.mjs`) are now all **derived** from this one registry — no hand-maintained duplicate lists remain.
198
198
 
199
199
  Transcribed verbatim from `LEARNING_TYPE_REGISTRY` (16 types):
200
200
 
201
- | Type | ttlDays | agentProposable | ruleConvertible |
202
- |------|---------|------------------|------------------|
203
- | `mode-selector-accuracy` | 30 | true | false |
204
- | `hardware-pattern` | 60 | true | false |
205
- | `fragile-file` | 45 | true | true |
206
- | `effective-sizing` | 45 | true | false |
207
- | `recurring-issue` | 45 | true | true |
208
- | `workflow-pattern` | 90 | true | true |
209
- | `proven-pattern` | 90 | true | true |
210
- | `anti-pattern` | 90 | true | true |
211
- | `autopilot-effectiveness` | 90 | true | false |
212
- | `autonomy-verdict` | 90 | false | false |
213
- | `domain-regression` | 60 | true | false |
214
- | `convention` | 90 | true | true |
215
- | `architecture-pattern` | 90 | true | true |
216
- | `design-pattern` | 90 | true | true |
217
- | `fragile-pattern` | 45 | false | true |
218
- | `stagnation-class-frequency` | 60 | false | true |
201
+ | Type | ttlDays | agentProposable | ruleConvertible | hostScoped |
202
+ |------|---------|------------------|------------------|------------|
203
+ | `mode-selector-accuracy` | 30 | true | false | false |
204
+ | `hardware-pattern` | 60 | true | false | true |
205
+ | `fragile-file` | 45 | true | true | false |
206
+ | `effective-sizing` | 45 | true | false | false |
207
+ | `recurring-issue` | 45 | true | true | false |
208
+ | `workflow-pattern` | 90 | true | true | false |
209
+ | `proven-pattern` | 90 | true | true | false |
210
+ | `anti-pattern` | 90 | true | true | false |
211
+ | `autopilot-effectiveness` | 90 | true | false | false |
212
+ | `autonomy-verdict` | 90 | false | false | false |
213
+ | `domain-regression` | 60 | true | false | false |
214
+ | `convention` | 90 | true | true | false |
215
+ | `architecture-pattern` | 90 | true | true | false |
216
+ | `design-pattern` | 90 | true | true | false |
217
+ | `fragile-pattern` | 45 | false | true | false |
218
+ | `stagnation-class-frequency` | 60 | false | true | false |
219
219
 
220
220
  Capability axes:
221
221
  - **`agentProposable`** — the type may appear in `PROPOSAL_TYPES` (a wave-agent may `memory.propose()` this type). `autonomy-verdict`, `fragile-pattern`, and `stagnation-class-frequency` are `false` — these are analyzer-synthesized classes, not agent-observed, so they are never agent-proposable.
222
222
  - **`ruleConvertible`** — the type may appear in `CONVERT_TYPES` (the FA2 reconciliation engine may convert a learning of this type into a conditional `.claude/rules/*.md` proposal). `fragile-file`, `recurring-issue`, `anti-pattern`, `convention`, `architecture-pattern`, `design-pattern`, `fragile-pattern`, `stagnation-class-frequency`, `workflow-pattern`, and `proven-pattern` are the ten `ruleConvertible: true` types (issue #900 flipped the last two from `false` — the real corpus census showed a large volume of these records carrying usable `file_paths` scope that were structurally unconvertible before the flip).
223
+ - **`hostScoped`** — `reconcile/emitter.mjs` may copy the record's `host_class` through as the emitted rule's `host-class` activation axis (issue #1090; derived set: that module's `HOST_SPECIFIC_TYPES`). `hardware-pattern` is the ONLY `true` type today — its content IS the chip/OS, so gating the emitted rule by host-class is faithful rather than an accidental one-machine restriction. For every other type `host_class` merely records the machine the learning was authored on and must never gate the rule.
223
224
 
224
225
  ### Type aliasing (issue #900)
225
226
 
@@ -257,6 +258,71 @@ Field-by-field:
257
258
 
258
259
  **Provenance is session-slug based, not issue-number based.** The only session-identity field the schema carries is `source_session` (a kebab-slug like `main-2026-07-03-session-1`) — there is currently no issue-number provenance field on a learning record or a generated rule. Adding issue-number provenance (linking a rule back to the GitHub/GitLab issue that motivated the learning) would require a schema addition to `scripts/lib/learnings/schema.mjs` — out of scope for this documentation pass.
259
260
 
261
+ ### Consolidated rules: N provenance pairs in ONE file (the merge contract)
262
+
263
+ A rule file may ABSORB several generated rules. This is the supported way to
264
+ stop `.claude/rules/` growing one 2.6 kB file per learning — measured
265
+ 2026-09-06 @ `e4674109`: 43 generated files / 112,443 B, 46.2 % of it pure
266
+ frontmatter + provenance overhead, consolidated to 8 thematic files (33
267
+ absorbed, 10 dropped). Four rules make a merge safe, and skipping any one of
268
+ them silently loses a learning or regenerates it:
269
+
270
+ 1. **Frontmatter `learning-key:` is a SCALAR — so N−1 markers live in the
271
+ body.** `engine.mjs` reads BOTH forms: the frontmatter
272
+ `FRONTMATTER_LEARNING_KEY_RE` (`^learning-key: <value>`) and the body
273
+ bullets `BODY_LEARNING_KEY_RE` / `BODY_LEARNING_ID_RE`
274
+ (`` - learning-key: `<value>` `` / `` - learning-id: `<value>` ``). A merged
275
+ file therefore carries **one `- learning-key:` + `- learning-id:` bullet
276
+ PAIR per absorbed learning** in its `## Provenance` section, and may omit
277
+ the frontmatter scalar entirely. Removing a pair does not "tidy up" the
278
+ file — it makes that learning look unmaterialized, and the next
279
+ `/reconcile` regenerates it as a standalone rule.
280
+
281
+ 2. **`expires-at` is the EARLIEST of the absorbed dates.** A merged file must
282
+ not outlive its shortest-lived content: one date now covers several
283
+ learnings, so it must expire when the FIRST of them is due for review, not
284
+ the last. (Taking the latest would silently extend every other learning's
285
+ TTL past what its type registry granted it.) State the rule in the file
286
+ itself, so the next editor does not "fix" it upward.
287
+
288
+ 3. **Keep `globs:` only, and take the UNION.** The merged file loads for any
289
+ path any of its parts covered, so its `globs:` is the union of theirs.
290
+ `rule-loader.mjs` resolves `globs:` and `paths:` with `globs:` winning
291
+ SILENTLY when both are present (issue #795, `parseGlobsFrontmatter`) — so a
292
+ file carrying both duplicate keys ships the `paths:` block as dead bytes.
293
+ Write `globs:` alone.
294
+
295
+ 4. **Substance in, boilerplate out.** Each absorbed learning becomes an `###`
296
+ heading carrying its original rule sentence, plus its evidence line. What is
297
+ dropped is only the per-file repetition (the `# Auto-generated rule:` title,
298
+ the untrusted-content wrapper repeated 43×, the `evidence-digest` /
299
+ `evidence-digest-input` / `source-session` fields). Never drop an evidence
300
+ line to hit a byte target — an insight with no measurement behind it is the
301
+ thing `.claude/rules/measurement-discipline.md` exists to forbid.
302
+
303
+ **Dropping a learning requires a stamp BEFORE the delete.** Deleting a
304
+ generated rule file whose insight is already carried verbatim by a hand-written
305
+ always-on rule is legitimate — but `rm` alone does not stick. `engine.mjs`
306
+ treats a learning as `alreadyMaterialized` if EITHER the idempotency sidecar
307
+ holds a terminal verdict for its `learning_key` (`isProcessed`) OR a
308
+ `.claude/rules/*.md` file still carries its marker. Delete the file without
309
+ stamping and both conditions go false, so the next `/reconcile` proposes it
310
+ again. Stamp it first, via the store's only sanctioned writer:
311
+
312
+ ```js
313
+ import { markCandidateProcessed } from '../scripts/lib/reconcile/idempotency.mjs';
314
+ markCandidateProcessed({
315
+ learningKey: 'anti-pattern/<subject-slug>',
316
+ outcome: 'rejected', // or 'already-on-disk' when it lives elsewhere
317
+ fallbackSlug: '<the .claude/rules slug>',
318
+ repoRoot,
319
+ });
320
+ ```
321
+
322
+ This writes `.orchestrator/runtime/reconcile-candidates.jsonl` (creating it if
323
+ absent). Verify with a dry run: `alreadyMaterialized` must equal
324
+ absorbed + dropped, not absorbed alone.
325
+
260
326
  ## Authoring Examples
261
327
 
262
328
  ### (a) Hand-authored always-on rule (no frontmatter)
@@ -41,22 +41,22 @@ The coordinator's **own** planned direct edits belong in `coordinator.json` in t
41
41
 
42
42
  `wave-scope.json` is written into the WORKING COPY, not into the session — so until #1123 one session's manifest governed every session sharing that checkout. Measured 2026-08-22 (#1082): a Discovery wave's `allowedPaths: []` — which `wave-loop.md` prescribes for *every* Discovery wave — denied every write of an unrelated parallel session, with a deny reason that could only tell it to fix a wave plan it does not own.
43
43
 
44
- Two OPTIONAL manifest fields close that: `session` (the raw `session_id`) and its human-readable twin `semantic_session`. Both come from ONE `sessionAttribution(repoRoot)` call (`scripts/lib/events.mjs`, reads `.orchestrator/session.lock` once) in the same coordinator step that writes the rest of the manifest — `skills/wave-executor/wave-loop.md` § Scope Manifest 1.
44
+ Two OPTIONAL manifest fields close that: `session_id` (the raw harness session id) and its human-readable twin `semantic_session_id` — the same spelling `.orchestrator/session.lock` and `current-session.json` use (renamed from `session` / `semantic_session` in #1153 P2; readers accept the legacy pair until the next minor release, see `MANIFEST_SESSION_KEYS` in `scripts/lib/session-identity/own-session.mjs`). Both come from ONE `attributionForRecord(repoRoot)` call (`scripts/lib/events.mjs`) in the same coordinator step that writes the rest of the manifest — `skills/wave-executor/wave-loop.md` § Scope Manifest 1. Unlike a raw lock read, `attributionForRecord()` reads `.orchestrator/session.lock` and then confirms the lock's raw `session_id` against `readProcessLocalSessionIds()` before returning anything — a mismatch (or no process-local id at all) yields `{}`, never a peer's ids (#1207).
45
45
 
46
- The reader is `hooks/enforce-scope.mjs` **Gate 3b**, between the manifest parse (G3) and the path-guard gate (G4). It resolves identity via `readOwnSessionIds(projectRoot, { hookInput: input })` and classifies via `classifyManifestSession(scope, ownIds)`, both from [`scripts/lib/session-identity/own-session.mjs`](../scripts/lib/session-identity/own-session.mjs):
46
+ The reader is `hooks/enforce-scope.mjs` **Gate 3b**, between the manifest parse (G3) and the path-guard gate (G4). It resolves identity via `new Set(readProcessLocalSessionIds({ hookInput: input }))` and classifies via `classifyManifestSession(scope, ownIds)`, both from [`scripts/lib/session-identity/own-session.mjs`](../scripts/lib/session-identity/own-session.mjs):
47
47
 
48
48
  | Manifest state | `classifyManifestSession` verdict | Gate 3b disposition |
49
49
  |---|---|---|
50
- | no `session` / `semantic_session` (legacy, pre-#1123) | `unknown` | ENFORCE — falls through unchanged |
50
+ | no `session_id` / `semantic_session_id` (and no legacy `session` / `semantic_session`; pre-#1123) | `unknown` | ENFORCE — falls through unchanged |
51
51
  | an id present and matching one of our own | `own` | ENFORCE — falls through unchanged |
52
52
  | ids present, none matching, own identity resolvable | `foreign` | **ALLOW** + one `orchestrator.scope.foreign_session_ignored` event |
53
53
  | ids present, own identity unresolvable (empty id set) | `unknown` | ENFORCE |
54
54
 
55
55
  Five properties are choices, not omissions — and every one of them points the fail-**closed** way, the deliberate inverse of § 4.1's posture for the dispatch hook:
56
56
 
57
- - **Only what is PROVABLY foreign is foreign.** `readOwnSessionIds()` returns the **UNION** of three sources — hook input (`session_id`/`sessionId`/`parent_session_id`) `CLAUDE_CODE_SESSION_ID` ∪ `session.lock` (`session_id`/`semantic_session_id`) — and an EMPTY set when none yields an id, which can only produce `unknown`. A gate that guessed would turn "cannot tell" into a silent enforcement-off on every harness exporting no session id. Every value is trimmed on the way in, so a whitespace-only env var cannot enter as a phantom id that matches nothing (`.claude/rules/development.md` § env-var whitespace trap).
58
- - **Union, not first-tier-wins.** Any id the process can legitimately claim names this session; only an id in NO source is somebody else's. Gating the sources made the READER's identity a strict subset of the WRITER's — the manifest's `session` comes from `sessionAttribution()` = the same repo-global lock — and three divergences all produced the same silent failure, the OWN manifest read `foreign` and the write gate switched itself off for the whole wave, logging an event indistinguishable from correct behaviour: (a) a nested harness where payload `session_id` ≠ `CLAUDE_CODE_SESSION_ID` (measured in `hooks/pre-bash-issue-budget.mjs` `resolveSessionId`); (b) a sub-agent invocation, whose own id is the subagent's while the manifest names the coordinator; (c) a session that lost the lock race and therefore wrote a PEER's id into its own manifest (see the writer guard below). The union only ADDS ids the process actually carries, so the security direction is unchanged: a manifest whose id appears in no source still classifies `foreign`. Its cost is named and points fail-**closed** — a peer-owned lock makes us enforce a peer's wave plan, which is a visible deny rather than a silent enforcement-off.
59
- - **The writer verifies the binding names itself.** Because the lock is repo-global, `skills/wave-executor/wave-loop.md` § Scope Manifest 1 requires the coordinator to compare `sessionAttribution()`'s ids against its own session (STATE.md `session`) and to OMIT the `session`/`semantic_session` keys when they diverge — unbound = ENFORCE. The reader's union covers the case anyway; the writer guard keeps the manifest readable as an audit record instead of publishing a foreign name.
57
+ - **Only what is PROVABLY foreign is foreign.** `readProcessLocalSessionIds()` returns the ids that are PROCESS-LOCAL — hook input (`session_id`/`sessionId`/`parent_session_id`) and `CLAUDE_CODE_SESSION_ID` — and an EMPTY set when neither yields an id, which can only produce `unknown`. The repo-global `session.lock` is deliberately NOT a tier here (#1194): it is ONE file shared by every session in the checkout, so unioning it let a peer's manifest match a peer-written lock id, classify `own`, and have Gate 7 deny the second session's legitimate writes — the exact lockout G3b exists to end. A better signal REPLACES a worse one (`.claude/rules/host-resources.md` § HR-102). A gate that guessed would turn "cannot tell" into a silent enforcement-off on every harness exporting no session id. Every value is trimmed on the way in, so a whitespace-only env var cannot enter as a phantom id that matches nothing (`.claude/rules/development.md` § env-var whitespace trap).
58
+ - **Union across the process-local tiers, not first-tier-wins.** Both process-local tiers are read and merged; only an id in NEITHER is somebody else's. Gating them against each other made the READER's identity a strict subset of the WRITER's — the manifest's `session` comes from `sessionAttribution()` = the same repo-global lock — and two divergences inside these tiers produce the same silent failure, the OWN manifest read `foreign` and the write gate switched itself off for the whole wave, logging an event indistinguishable from correct behaviour: (a) a nested harness where payload `session_id` ≠ `CLAUDE_CODE_SESSION_ID` (measured in `hooks/pre-bash-issue-budget.mjs` `resolveSessionId`); (b) a sub-agent invocation, whose own id is the subagent's while the manifest names the coordinator. A third divergence — a session that lost the lock race and wrote a PEER's id into its own manifest is NO longer covered here since #1194 dropped the lock tier; it is the accepted cost in limit 11, and its defense is the writer guard. The merge only ADDS ids the process actually carries, so the security direction is unchanged: a manifest whose id appears in neither tier still classifies `foreign`. Its cost is named in limit 11 below.
59
+ - **The writer's binding check is mechanical, not a prose comparison (#1207).** Because the lock is repo-global, `skills/wave-executor/wave-loop.md` § Scope Manifest 1 calls `attributionForRecord(repoRoot)`, which confirms the lock's raw `session_id` against `readProcessLocalSessionIds()` internally and returns `{}` on any mismatch or absent process-local id — the coordinator writes whatever comes back, with no further comparison to perform. STATE.md is deliberately not part of this check: it is a shared working-copy artefact written by whichever session holds the lock, so under a peer-owned lock STATE.md's `session` field would agree with the lock about the same peer and "confirm" exactly the wrong id. OMIT the `session_id`/`semantic_session_id` keys whenever `attributionForRecord()` returns `{}` — unbound = ENFORCE. The reader's union covers the case anyway; the writer guard keeps the manifest readable as an audit record instead of publishing a foreign name.
60
60
  - **Gate 3b runs after the parse, never on the raw bytes.** A corrupt manifest yields `{}`, hence no ids, hence `unknown` — and keeps failing closed. A gate that peeked at the bytes first would let a truncated manifest disarm the guard.
61
61
  - **The empty string is a validator ERROR, not a third flavour of absent.** `validateSession()` → `validateOptionalSessionId()` in `scripts/validate-wave-scope.mjs` rejects `"session": ""` with *"an empty id attributes to nothing; omit the key entirely to declare the manifest unbound"*. An empty id satisfies a truthiness check while matching nobody, so every reader would classify the manifest FOREIGN where the writer meant UNBOUND — opposite dispositions, not a cosmetic ambiguity. An ABSENT key only WARNS, because the § 3.3 pre-union skeleton is itself an unbound manifest and so is every manifest written before #1123.
62
62
 
@@ -89,7 +89,7 @@ false
89
89
  true true
90
90
  ```
91
91
 
92
- Both globs match `scripts/lib/x.mjs`, yet the direct comparison says `false`. For `assertFileScopeSubset()` that inexactness is *safe*: its glob branch reduces to verbatim presence plus literal-prefix coverage and therefore **over-approximates coverage**, which at worst accepts a union it could not fully prove. For a **collision** check the sign flips — the same over-approximation becomes a **false negative**, i.e. a missed collision, i.e. the incident. That is why the two exact stages decide first and stage 3 is reached only for pairs neither can settle.
92
+ Both globs match `scripts/lib/x.mjs`, yet the direct comparison says `false`. For `assertFileScopeSubset()` that inexactness is *safe*: its glob branch reduces to verbatim presence plus literal-prefix coverage and therefore **over-approximates coverage**, which at worst accepts a union it could not fully prove. For a **collision** check the sign flips — the same over-approximation becomes a **false negative**, i.e. a missed collision, i.e. the incident. That is why the two exact stages decide first and stage 3 is reached only for pairs neither can settle. <!-- path-check: example -->
93
93
 
94
94
  ## 4. The hook
95
95
 
@@ -174,7 +174,7 @@ Complete list of what this guard does **not** see, or sees only approximately:
174
174
  8. **Lock loss reopens the race.** On lock timeout the cycle runs unlocked (row 14) — two dispatches starting together can then read the same ledger state and one record is lost. That is the pre-lock behaviour, chosen over denying on a lock-file problem.
175
175
  9. **The session binding is self-declared** (§ 2.3). `session` is a plain field in a file any process in this working copy can write, so writing a foreign id into it turns the write gate off for that manifest. Named rather than hidden: it is the SAME power `enforcement: "off"` already grants in the same file, so Gate 3b adds no new authority — the manifest is the coordinator's own artefact either way.
176
176
  10. **Only the WRITE gate is session-bound.** The dispatch ledger of § 4 takes its session component from the harness's own `input.session_id` (`waveKeyOf(projectDir, sessionId, …)`), and reads only `wave` and `role` out of `wave-scope.json` — the `session` field is not consulted there at all. So a peer session's manifest cannot bind this session's writes since #1123, but the two hooks reach that property by different routes, and a change to one does not carry to the other.
177
- 11. **A peer-owned lock costs enforcement precision, deliberately.** `readOwnSessionIds()` unions the `session.lock` ids in (§ 2.3), and the lock is repo-global so while a peer holds it, that peer's manifest classifies `own` here and its `allowedPaths` bind this session's writes: the #1082 shape, but now as a visible deny naming a wave plan we do not own. The alternative (gating the lock behind the payload) fails the other way and silently, because the manifest's own `session` is written from that same lock. Bounded by the writer guard in `skills/wave-executor/wave-loop.md` § Scope Manifest 1 (omit rather than write a foreign id) and by `session.lock` heartbeat-liveness. Revisit if a manifest ever carries the writer's PROCESS identity alongside the lock-derived one, which would let the reader distinguish the two cases instead of unioning them.
177
+ 11. **A session that published a peer's id reads its OWN manifest as `foreign` (#1194) the writer's defense is now mechanical (#1207).** Dropping the lock tier (§ 2.3) moved the cost to the other side of the trade: with a raw `sessionAttribution()` read, a session that lost the `bootstrapLock()` race could get the peer's id and write it into its own manifest, and Gate 3b would then classify the manifest `foreign`, standing its own write guard down. Since #1207 the writer calls `attributionForRecord(repoRoot)` instead `skills/wave-executor/wave-loop.md` § Scope Manifest 1 — which confirms the lock's raw `session_id` against `readProcessLocalSessionIds()` before returning anything and yields `{}` on any mismatch, so the coordinator performs no manual comparison and STATE.md plays no part in it (a shared working-copy artefact written by whichever session holds the lock, not a process-local witness). A filled binding is therefore provably this session's own; unbound (`{}`) = ENFORCE. CEILING (BV-004): on a harness that exports no session env var and puts no `session_id` in the hook payload (Codex CLI, Cursor today) both tiers are empty, so G3b is permanently `unknown` = enforce = pre-#1123 behaviour there. Revisit when Codex/Cursor hook payloads carry a session id.
178
178
 
179
179
  ## 7. Debugging
180
180