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,93 @@
1
+ # Phase 2: Quality Gate
2
+
3
+ > Sub-file of the session-end skill (#1157 — agentskills.io: SKILL.md core < 500 lines, procedure in `references/`). Extracted VERBATIM from `skills/session-end/SKILL.md`; `SKILL.md` keeps a one-line stub naming this phase and its gate condition.
4
+
5
+ ## Phase 2: Quality Gate
6
+
7
+ > **Verification Reference:** See `verification-checklist.md` in this skill directory for the full quality gate checklist.
8
+
9
+ Run ALL checks listed in the verification checklist. If any check fails: fix if quick (<2 min), otherwise create a `priority::high` issue. Do NOT commit broken code.
10
+
11
+ ### Phase 2.0a: Echo-Stub Detection (GH #42)
12
+
13
+ `gate-full.mjs` emits a top-level `stubbed: {}` map in its JSON result, keyed by check name (`typecheck`, `test`, `lint`); value is `{ kind: 'echo'|'noop' }`. When any check was short-circuited as a stub, `runCheck()` already returned `status: 'pass'` — so the overall gate verdict is green, but the result is meaningless.
14
+
15
+ **Detection:** immediately after parsing the `gate-full` JSON result, evaluate:
16
+
17
+ ```js
18
+ const stubbedEntries = Object.entries(result.stubbed ?? {});
19
+ ```
20
+
21
+ **If `stubbedEntries.length > 0`**, surface a HIGH WARN block in the close summary:
22
+
23
+ ```
24
+ ⚠ QUALITY GATE STUBBED — <N> command(s) are echo/noop stubs, not real checks:
25
+ - <check-name>: <kind> stub (configured: "<command string>")
26
+ Re-configure with a real test command in CLAUDE.md Session Config before /close,
27
+ OR document this exception in /close --reason.
28
+ ```
29
+
30
+ **Behavior by `enforcement` mode:**
31
+
32
+ - `enforcement: strict` — **block /close**. Treat as a Phase 2 failure. Present the WARN block and exit without committing.
33
+ - `enforcement: warn` (default) — continue, but write `quality-gate-stubbed: true` to STATE.md Deviations so the metrics writer captures it.
34
+ - `enforcement: off` — silent. Emit a single-line `stderr` log only (`echo-stub detected: <check-name>`).
35
+
36
+ **Recipe:** for container-based test runners (e.g. EspoCRM PHPUnit) where an echo-stub was the historical workaround, see [`docs/recipes/quality-gate-container-pattern.md`](../../../docs/recipes/quality-gate-container-pattern.md).
37
+
38
+ **Source issue:** GH #42 (root cause: a consumer-project #251 V0.15.7-close incident — silent false-positive close-verdicts from echo-stub test commands).
39
+
40
+ ### 2.1 Vault Validation (if configured)
41
+
42
+ Read `skills/session-end/vault-operations.md` for validator bash contract and reporting matrix.
43
+
44
+ ### 2.2 CLAUDE.md (or AGENTS.md) Drift Check (if configured)
45
+
46
+ Read `skills/session-end/drift-operations.md` for checker bash contract and reporting matrix. Complements 2.1: vault-sync validates frontmatter inside the vault tree; drift-check validates narrative claims (paths, counts, issue refs, session-file refs) in top-level repo docs.
47
+
48
+ ### 2.3 Vault Staleness Check (if configured)
49
+
50
+ > Skip this subsection if `vault-staleness.enabled` is not `true` (default: `false`).
51
+
52
+ #### Step 1 — Resolve mode
53
+
54
+ Read `vault-staleness.mode` from `$CONFIG` (default: `warn`). Valid values: `off | warn | strict`.
55
+
56
+ If `mode === 'off'`, skip Phase 2.3 entirely.
57
+
58
+ #### Step 2 — Invoke staleness probes
59
+
60
+ Both probes already ship in `skills/discovery/probes/`. Invoke each via Node import (no shell-out):
61
+
62
+ ```js
63
+ import { runProbe as runStaleness } from '$REPO_ROOT/skills/discovery/probes/vault-staleness.mjs';
64
+ import { runProbe as runNarrative } from '$REPO_ROOT/skills/discovery/probes/vault-narrative-staleness.mjs';
65
+
66
+ const projectStaleness = await runStaleness(projectRoot, config);
67
+ const narrativeStaleness = await runNarrative(projectRoot, config);
68
+ ```
69
+
70
+ Each probe returns `{ findings: Array, metrics: Object, duration_ms: Number }` and auto-appends a JSONL summary record to its respective metrics file.
71
+
72
+ #### Step 3 — Aggregate and route by mode
73
+
74
+ ```
75
+ totalFindings = projectStaleness.findings.length + narrativeStaleness.findings.length
76
+ ```
77
+
78
+ - `mode === 'warn'` (default): report findings to closing report Docs Health line. Never block close.
79
+ - `mode === 'strict'`:
80
+ - If `totalFindings === 0`: continue, log `Vault staleness: clean (mode=strict)`.
81
+ - If `totalFindings > 0`: do NOT block the close. Present the findings list and surface an AskUserQuestion whose Recommended default is **warn + carryover + continue**:
82
+ - On Claude Code: AskUserQuestion with options:
83
+ 1. "Warn + carryover and close (Recommended)" — file a carryover issue (labels `carryover`, `priority::high`) titled `[Carryover] Vault staleness (strict) — <count> findings` documenting the stale projects/narratives for a follow-up session, log a Deviation entry in STATE.md `## Deviations`, then continue the close:
84
+ `- [<ISO timestamp>] Phase 2.3: Vault staleness strict-mode findings carried over. Findings: <count> (projects: <N>, narratives: <M>) → issue #<IID>.`
85
+ 2. "Override and close" — proceed without a carryover issue, log a Deviation entry in STATE.md `## Deviations`:
86
+ `- [<ISO timestamp>] Phase 2.3: Vault staleness strict-mode findings overridden by user. Findings: <count> (projects: <N>, narratives: <M>).`
87
+ In addition to the Deviation entry, emit an override-ratio event so the override feeds the `override_ratio` metric (#730/H5): `node scripts/emit-event.mjs --type orchestrator.finding.overridden --payload '{"phase":"2.3","kind":"vault-staleness-strict","count":N}'`.
88
+ - On Codex CLI / Cursor IDE: same options as numbered Markdown list.
89
+
90
+ #### Step 4 — Surface to closing report
91
+
92
+ Pass the aggregated counts and mode forward to Phase 6 Final Report (Docs Health line — see Phase 6 below).
93
+
@@ -0,0 +1,229 @@
1
+ # Phase 3: Documentation Updates
2
+
3
+ > Sub-file of the session-end skill (#1157 — agentskills.io: SKILL.md core < 500 lines, procedure in `references/`). Extracted VERBATIM from `skills/session-end/SKILL.md`; `SKILL.md` keeps a one-line stub naming this phase and its gate condition.
4
+
5
+ ## Phase 3: Documentation Updates
6
+
7
+ > **Final heartbeat (#590-3)** — at Phase 3 entry, refresh the session-lock heartbeat BEFORE the multi-minute close-out chain (vault-mirror, dialectic, durable-commit, metrics). A long-idle deep session may not have had PostToolBatch activity for >4h; without a refresh the 4h-TTL lock would lapse mid-close and appear stale to a concurrent session. Place this call BEFORE Phase 3.8 Session Lock Release (which deletes the lock — refreshing a deleted lock is a no-op). Best-effort: a failure must NOT block the close.
8
+ >
9
+ > ```js
10
+ > // Final heartbeat (#590-3) — refresh before the multi-minute close-out (vault-mirror, dialectic, durable-commit)
11
+ > // so a long-idle deep session's 4h-TTL lock does not lapse mid-close.
12
+ > // BEFORE Phase 3.8 lock-release (which deletes the lock).
13
+ > import { updateHeartbeat } from 'scripts/lib/session-lock.mjs';
14
+ > updateHeartbeat({ sessionId, repoRoot: process.cwd() });
15
+ > ```
16
+ >
17
+ > Skip silently if `persistence: false` in Session Config (no session.lock exists in that mode).
18
+
19
+ ### 3.0 Defensive Cleanup
20
+
21
+ Delete `<state-dir>/wave-scope.json` if it still exists:
22
+
23
+ ```bash
24
+ rm -f <state-dir>/wave-scope.json
25
+ ```
26
+
27
+ This should have been cleaned up by wave-executor after the final wave, but crashed sessions or interrupted executions may leave it behind. A stale scope manifest from a previous session could incorrectly restrict the next session's enforcement hooks.
28
+
29
+ ### 3.1 SSOT Files
30
+ - Update `STATUS.md` / `STATE.md` if they exist (metrics, dates, status)
31
+ - Update `CLAUDE.md` (or `AGENTS.md` on Codex CLI) if patterns or conventions changed during this session
32
+ - Check `<state-dir>/rules/` — if a new pattern was established, suggest a new rule file
33
+
34
+ ### 3.2 Docs Verification (docs-orchestrator integration)
35
+
36
+ > Skip this subsection if `docs-orchestrator.enabled` config is not `true` (default: `false`). Also skip entirely if `docs-orchestrator.mode` is `off`.
37
+
38
+ Reads `docs-tasks` from STATE.md frontmatter (written by wave-executor Pre-Wave 1b), computes `CHANGED_FILES` via `git diff --name-only "$SESSION_START_REF..HEAD"`, and runs a per-task verification loop (outcome: `ok`/`partial`/`gap`). In `warn` mode logs results non-blocking; in `strict` mode blocks on any gap and presents an AskUserQuestion override prompt. Emits a `### Documentation Coverage (docs-orchestrator)` block for inclusion in the Phase 6 Final Report.
39
+
40
+ **See `phase-3-2-docs-verification.md` for full details.**
41
+
42
+ ### 3.2a Session Handover (for significant sessions)
43
+ If this session made substantial changes, create or update:
44
+ - `<state-dir>/session-handover/` doc with: tasks completed, resume point, metrics changed, issues opened/closed
45
+ - Or update `<state-dir>/STATE.md` with session digest
46
+
47
+ ### 3.3 Claude Rules Freshness
48
+ Review `<state-dir>/rules/` files that are relevant to this session's work:
49
+ - Are the rules still accurate after this session's changes?
50
+ - Should any rule be updated with new patterns?
51
+ - Should a new path-scoped rule be created?
52
+ - Suggest changes but DO NOT modify without user confirmation
53
+
54
+ ### 3.4 Update STATE.md
55
+
56
+ > **Ownership Reference:** See `skills/_shared/state-ownership.md`. session-end is authorized to set `status: completed` plus the optional `updated` timestamp (#184), and — as of Phase A of Epic #271 — the 5 Recommendation fields written by Phase 3.7a. No other fields.
57
+
58
+ > **Runtime Ordering Note (Epic #271 Phase A):** Phase 3.4's `status: completed` write executes LAST in Phase 3, AFTER Phase 3.7 (sessions.jsonl) and Phase 3.7a (Compute and Write Recommendations). The ordinal position here (3.4) is kept for historical compatibility; the canonical runtime order is `3.1 → 3.2 → 3.3 → 3.4a → 3.5 → 3.5a → 3.6 → 3.6.3 → 3.6.4 → 3.6.5 → 3.6.6 → 3.6.7 → 3.6.8 → 3.7 → 3.45 → 3.7a → 3.7b → 3.7c → 3.7d → 3.4` (3.6.3/3.6.4/3.6.6 were missing from this note pre-#724; the Tail-Diät skip-plan dispatcher now dispatches the full six-phase tail mechanically, so the note is corrected to list all six). Rationale: Phase 3.7a reads in-memory session metrics and writes the 5 Recommendation fields via `updateFrontmatterFields`; that write must complete BEFORE the STATE.md frontmatter is finalized with `status: completed` so the Recommendation fields are visible to the next session-start while STATE.md is still `status: active`. Crash-resilience: if `/close` aborts between 3.7a and 3.4, STATE.md carries `status: active` + Recommendations; session-start Phase 1.5 offers resume (and the banner renders). If the reverse ordering were used (status: completed first), a crash would leave `status: completed` without Recommendations — the Reader would silently no-op the banner, losing the handoff. Phase 3.45 (Telemetry Flush, #844) sits AFTER Phase 3.7 because it drains the send-queue with the just-written `sessions.jsonl` record already included, and BEFORE Phase 3.7a because it is a fire-and-forget side-effect with no dependency on the Recommendation-write ordering below it. Phase 3.7d (Session-Eval, #803) sits AFTER Phase 3.7 because it scores the `sessions.jsonl` record that phase just wrote — the record must exist first — and BEFORE Phase 3.4 because its `eval.jsonl` output is advisory and must never block the close.
59
+
60
+ > Gate: Only run if `persistence` is enabled in Session Config and `<state-dir>/STATE.md` exists.
61
+ 1. Set frontmatter `status: completed`
62
+ 2. Record final wave count and completion time in the frontmatter
63
+ 3. Touch `updated: <ISO 8601 UTC>` in the frontmatter (issue #184). Use `scripts/lib/state-md.mjs` → `touchUpdatedField` for safety:
64
+ ```bash
65
+ node --input-type=module -e "
66
+ import {readFileSync, writeFileSync} from 'node:fs';
67
+ import {touchUpdatedField} from '${PLUGIN_ROOT}/scripts/lib/state-md.mjs';
68
+ const p = '<state-dir>/STATE.md';
69
+ writeFileSync(p, touchUpdatedField(readFileSync(p, 'utf8'), new Date().toISOString()));
70
+ "
71
+ ```
72
+ Silent no-op if the file has no frontmatter.
73
+ 4. Keep the file as a record — do NOT delete it (next session-start reads it)
74
+
75
+ If STATE.md doesn't exist, skip this subsection.
76
+
77
+ ### 3.4a Coordinator Snapshot Cleanup (#196)
78
+
79
+ Pre-dispatch snapshots (`refs/so-snapshots/<sessionId>/wave-*`) are created by wave-executor before each wave dispatch so that session-start can offer recovery if a session is interrupted mid-wave. On a clean close those snapshots are no longer needed and should be deleted. In addition, orphaned refs from older sessions that were never cleaned up (e.g. after a hard crash) are garbage-collected using an age-based policy (14 days).
80
+
81
+ > Gate: Only run if `persistence` is `true` in Session Config. Skip entirely when persistence is off (snapshots are never written in that mode).
82
+
83
+ ```bash
84
+ node --input-type=module -e "
85
+ import { listSnapshots, deleteSnapshot, gcSnapshots } from '${PLUGIN_ROOT}/scripts/lib/coordinator-snapshot.mjs';
86
+
87
+ // Step A: delete this session's snapshots (clean close → we don't need them)
88
+ const mine = await listSnapshots({ sessionId: '${SESSION_ID}' });
89
+ for (const s of mine) {
90
+ const r = await deleteSnapshot({ refName: s.ref });
91
+ if (!r.ok) console.error('snapshot cleanup:', r.error);
92
+ }
93
+
94
+ // Step B: GC orphans older than 14 days (non-fatal)
95
+ const gc = await gcSnapshots({ olderThanDays: 14 });
96
+ console.log(\`snapshot cleanup: deleted \${mine.length} from this session + \${gc.deletedCount} expired orphans (scanned \${gc.scanned}).\`);
97
+ "
98
+ ```
99
+
100
+ Failures in either step are logged to stderr but do **not** block session close — a missed cleanup is self-healing via the 14-day GC on the next session.
101
+
102
+ This cleanup is the counterpart to the session-start Phase 1.5 recovery prompt: once a session closes cleanly, future sessions must not be offered recovery for its snapshots.
103
+
104
+ ### 3.45: Telemetry Flush (advisory, #844)
105
+
106
+ > Skip silently when `persistence: false` in Session Config. There is **no dedicated config key** for this phase — the send-gate is `resolveConsent()` inside `sync.mjs` itself (fail-closed: a `disabled` / `no-consent` / headless posture makes `flush()` a no-op in <5ms, sending nothing). This phase runs late in the close, after Phase 3.7 has written `sessions.jsonl`, so any session-summary event enqueued at metrics-write time is included in the drain; the ordinal position `3.45` is kept for readability (mirrors the Phase 3.4 Runtime Ordering Note idiom of ordinal ≠ runtime order).
107
+
108
+ Drain the host-local telemetry send-queue once, fire-and-forget. The flush is **advisory** — the close must never fail, stall, or surface an error because of telemetry:
109
+
110
+ ```javascript
111
+ import { flush } from '${PLUGIN_ROOT}/scripts/lib/telemetry/sync.mjs';
112
+
113
+ // Fire-and-forget. flush() is contractually never-throw + internally gated (resolveConsent)
114
+ // + 3s-timeout-bounded; the try/catch is defense-in-depth, never a real failure path.
115
+ try { await flush(); } catch { /* nie blockierend — der Close darf durch Telemetrie nie scheitern */ }
116
+ ```
117
+
118
+ **Semantics.** `flush()` is fire-and-forget with an internal ~3s timeout. When the ingest endpoint is unreachable (offline), events stay in the bounded host-local queue (oldest-dropped on overflow) and are retried on a later close — nothing is lost or blocked. A one-line result MAY be surfaced in the Phase 6 close summary (`Telemetry: sent` / `queued` / `gated`), but a failure NEVER renders an error banner: under no circumstances may telemetry make `/close` fail or take materially longer than ~3s. The gate lives in the module (fail-closed via `resolveConsent`), so this phase carries no config-key check of its own beyond the `persistence: false` skip above.
119
+
120
+ Cross-reference: GitLab #844 (Epic #841); `docs/prd/2026-07-20-anonymous-usage-telemetry.md` FA3; `docs/telemetry.md`; flush API in `scripts/lib/telemetry/sync.mjs` (`flush` — fire-and-forget, gated, never-throw).
121
+
122
+ ### 3.5 Session Memory
123
+
124
+ > Gate: Only run if `persistence` is enabled in Session Config AND platform is Claude Code (session memory at `~/.claude/projects/` is Claude Code-only). Learnings (Phase 3.5a) and metrics (Phase 3.7) still write to `.orchestrator/metrics/` on all platforms.
125
+
126
+ 1. Create `~/.claude/projects/<project>/memory/session-<YYYY-MM-DD>.md` with:
127
+ - Frontmatter: `name`, `description` (1-line summary), `type: project`
128
+ - `## Outcomes` — per-issue status (completed / partial / not started) with evidence
129
+ - `## Learnings` — patterns discovered, architectural insights, gotchas
130
+ - `## Next Session` — priority recommendations, suggested session type, blockers
131
+ 2. Update `~/.claude/projects/<project>/memory/MEMORY.md`:
132
+ - Under a `## Sessions` heading (create if missing), add:
133
+ `- [Session <date>](session-<date>.md) — <one-line summary>`
134
+
135
+ ### 3.5a Learning Extraction + 3.6 Memory Cleanup & Learnings Write
136
+
137
+ Read `skills/session-end/learning-patterns.md` for extraction heuristics, confidence updates, passive decay, and JSONL write procedure.
138
+
139
+ ### Phase 3.6.x Tail — Mechanical Skip-Plan (#724)
140
+
141
+ > The Phase 3.6.x tail (3.6.3 Memory-Proposals, 3.6.4 Expired-Sweep, 3.6.5 Auto-Dream, 3.6.6 Skill-Judge, 3.6.7 Auto-Dialectic, 3.6.8 Reconcile) is the historical close-out abort-attractor: six phases that in the overwhelming majority of sessions do nothing (no proposals queued, nothing expired, under cadence, judge off, reconcile off). Each already ships a mechanical fast-path in its own lib. This dispatcher computes — side-effect-free — WHICH of the six actually need to run, so you load ONLY the detail procedure for the `run: true` phases and emit a one-line skip report for the rest.
142
+
143
+ Run the aggregator ONCE. Config gates short-circuit FIRST (no disk touch); the input-detection helpers run only when the config gate passed. It NEVER throws — a per-phase probe error fail-opens to `run: true` (run the phase rather than silently lose it):
144
+
145
+ ```javascript
146
+ import { planTailPhases } from '${PLUGIN_ROOT}/scripts/lib/session-end/phase-skip.mjs';
147
+
148
+ const { plan, skippedReport } = await planTailPhases({
149
+ repoRoot: process.cwd(),
150
+ config, // parsed Session Config (from $CONFIG)
151
+ sessionId, // physical session.lock `session_id` only (or null), never STATE.md `session`
152
+ platform, // 'claude' | 'codex' | 'cursor'
153
+ });
154
+ // plan: Array<{ phase, run, reason, inputSource }>, already in ascending phase order.
155
+ ```
156
+
157
+ Then:
158
+
159
+ 1. **For every entry with `run: true`** — load its detail procedure from [`phase-3-6-tail.md`](../phase-3-6-tail.md) (the phase headings there match the `phase` id) and execute it exactly as written. The aggregator only DECIDES; the sub-file holds the full unabridged procedure.
160
+ 2. **For every entry with `run: false`** — do nothing for that phase; its `reason` is already captured for the report.
161
+ 3. **Execute `run: true` phases in ascending phase order** (3.6.3 → 3.6.4 → 3.6.5 → 3.6.6 → 3.6.7 → 3.6.8), matching the Phase 3.4 Runtime Ordering Note. The returned `plan` is already in that order.
162
+ 4. **Emit `skippedReport`** as a single line in the Phase 6 Final Report (under the Learnings/metrics block), e.g. `Tail-Diät: 3.6.3 skipped (proposals empty) · 3.6.5 skipped (under-threshold) · 3.6.7 RUN (2 new sessions) · …`.
163
+
164
+ **Full detail procedures:** [`phase-3-6-tail.md`](../phase-3-6-tail.md).
165
+
166
+ ### 3.7 Write Session Metrics
167
+
168
+ Read `skills/session-end/session-metrics-write.md` for JSONL append, vault-mirror invocation, and behavior matrix.
169
+
170
+ > **Token Rollup (#644):** Before emitting the JSONL record (step 2 of session-metrics-write.md), step 1a calls `rollupSessionTokens({ parentSessionId })` from `scripts/lib/session-token-rollup.mjs` and merges three optional fields onto the in-memory record: `total_token_input`, `total_token_output`, and `subagents_with_tokens` (coverage count). Null totals mean "no token data captured" — not zero cost. The rollup is non-blocking: a missing `subagents.jsonl` or all-null session still writes cleanly with null/0 values.
171
+
172
+ ### 3.7a Compute and Write Recommendations (Epic #271 Phase A)
173
+
174
+ > Gate: Only run if `persistence` is `true` in Session Config AND `<state-dir>/STATE.md` exists. Skip silently otherwise.
175
+
176
+ > **Ownership Reference:** See `skills/_shared/state-ownership.md`. session-end is the ONLY writer of the 5 Recommendation fields (`recommended-mode`, `top-priorities`, `carryover-ratio`, `completion-rate`, `rationale`). No other skill may write these keys.
177
+
178
+ > **Ordering:** Runs AFTER Phase 3.7 (sessions.jsonl is just-written — reads in-memory session metrics, NOT JSONL) and BEFORE Phase 3.4 `status: completed` setting. See the Phase 3.4 Runtime Ordering Note for rationale.
179
+
180
+ Calls `computeV0Recommendation({completionRate, carryoverRatio, carryoverIssues})` from in-memory session metrics and writes 5 fields to STATE.md frontmatter via `updateFrontmatterFields`. Inputs MUST come from in-memory metrics, NOT re-read from `sessions.jsonl`. On any exception writes `recommendation-compute-failed` to `sweep.log` and does NOT block Phase 3.4.
181
+
182
+ **See `phase-3-7a-recommendations.md` for full details.**
183
+
184
+ ### 3.7b Durable-Commit Session Telemetry (#490 AC2)
185
+
186
+ > Gate: Always runs when persistence is enabled. Local execution is a no-op (`enabled: false`).
187
+
188
+ > **Ordering:** Runs AFTER Phase 3.7a (Recommendations written to STATE.md) and BEFORE Phase 3.4 (`status: completed`). See the Phase 3.4 Runtime Ordering Note canonical order.
189
+
190
+ Wraps the already-completed Phase 3.7 + 3.7a writes with `withDurableCommit` (from `scripts/lib/autopilot/durable-telemetry.mjs`) for the two session-end-owned files: `.orchestrator/metrics/sessions.jsonl` and `<state-dir>/STATE.md`. `enabled: false` keeps local closes a no-op (`{ok: true, skipped: true}`); the flag flips `true` only in cloud Routines execution so telemetry survives ephemeral-clone reclamation. `autopilot.jsonl` is NOT in scope here — `scripts/lib/autopilot/loop.mjs` owns its commit (#490 Wave-2).
191
+
192
+ **See `phase-3-7a-recommendations.md` § Phase 3.7b for the full `withDurableCommit` invocation.**
193
+
194
+ ### Phase 3.7c: Vault Board → Closed (#674)
195
+
196
+ > Gate: Skip silently when `vault-integration.enabled` is not `true` in Session Config (the underlying helper also self-no-ops, so this is defense-in-depth, not the sole gate).
197
+
198
+ > **Ordering:** Runs AFTER Phase 3.7b (durable-commit) and BEFORE Phase 3.7d (Session-Eval, #803), Phase 3.4 (`status: completed`) and Phase 3.8 (Session Lock Release). See the Phase 3.4 Runtime Ordering Note canonical order. Running before lock-release is deliberate — the session-lock lease still exists when the board is finalized, so the board's `in-progress → closed` transition is derived against a live lock rather than a phantom one. This mirrors the #490 durableCommit ordering discipline: persist/finalize the cross-repo status while the lease is still held, then release.
199
+
200
+ Transition THIS repo's live-status board row to `closed` so a cross-repo observer sees the session has ended. Invoke `mirrorBoard` from `scripts/lib/vault-status/board-writer.mjs` with an explicit `closed` status for the current repo:
201
+
202
+ ```javascript
203
+ import { mirrorBoard } from 'scripts/lib/vault-status/board-writer.mjs';
204
+
205
+ const boardResult = await mirrorBoard({
206
+ repoRoot: process.cwd(),
207
+ explicitStatus: 'closed', // force THIS repo's row to `closed`
208
+ });
209
+ // boardResult.action ∈ { 'written', 'skipped-noop', 'skipped-handwritten', 'skipped-vault-disabled', 'dry-run' }
210
+ ```
211
+
212
+ > **Note (single-repo close path):** with `repos` omitted, `mirrorBoard` builds the repo descriptor itself as `[{ repoRoot, status: explicitStatus }]` — this is the supported single-repo shape, so `explicitStatus: 'closed'` lands on THIS repo's row. (When a caller DOES pass `repos`, each element must be a `{ repoRoot, repoName?, status? }` object, NOT a bare path string — bare strings are silently skipped by `collectRows`.) The board at `<vault-dir>/01-projects/_active-sessions.md` is generator-owned: `mirrorBoard` refuses to touch any file lacking the `session-orchestrator-active-sessions@1` marker, hard-refuses `_overview.md`, and is idempotent (a re-run after the row is already `closed` returns `skipped-noop`). Rows for repos NOT in this update are preserved verbatim by the idempotent merge.
213
+
214
+ **Non-blocking:** a `mirrorBoard` failure (any non-`written`/`skipped-*` outcome, thrown error, or unreachable vault) MUST NOT block the close. Log a single `WARNING: vault board → closed failed — <reason>; continuing close` line and proceed to Phase 3.4 / 3.8. The board is an observability convenience, not a close-out invariant.
215
+
216
+ ### Phase 3.7d: Session-Eval (opt-in — #803)
217
+
218
+ > Gate: Run ONLY when Session Config has `eval.enabled: true` AND `eval.mode` is not `off` (the `eval:` block is parsed by `scripts/lib/config/eval.mjs`; defaults are `enabled:false / mode:warn / judge:off / report:html / handle:null`). With no `eval:` block at all, skip silently — zero overhead and byte-identical close behaviour to a repo that never adopted eval (FA6 Gherkin 2).
219
+
220
+ > **Ordering:** Runs AFTER Phase 3.7 (sessions.jsonl) and Phase 3.7c (vault board) and BEFORE Phase 3.4 (`status: completed`) and Phase 4 (commit). The position AFTER Phase 3.7 is load-bearing: the eval scores the session record that Phase 3.7 just appended to `sessions.jsonl`, so that record MUST already exist. The position BEFORE Phase 3.4 keeps the resulting `eval.jsonl` record inside the same session commit — but the record is purely advisory, so a failure here NEVER blocks the close. See the Phase 3.4 Runtime Ordering Note canonical order.
221
+
222
+ Evaluate the just-closed session deterministically and, when configured, with an advisory LLM judge. This phase is a thin hook — the full evaluation flow lives in `skills/eval/SKILL.md`; only the close-out integration is described here.
223
+
224
+ 1. **Deterministic run.** Invoke `node scripts/eval-session.mjs --json` — with no `--session`, the cascade (`resolveSession`, revised #822) walks records newest-to-oldest (source order) and evaluates the first one that is either `status:'completed'` or non-abandoned with evidence of completed work (typically the record Phase 3.7 just appended). Model capture: the coordinator passes `--model-id <id> --model-source self-report`; the `$ANTHROPIC_MODEL` env var wins automatically when set. Pass the configured pseudonym through with `--handle <eval.handle>` (omit when `null`). The CLI appends the eval record to `.orchestrator/metrics/eval.jsonl` (`appendEvalRecord` is never-throw).
225
+ 2. **Advisory judge (opt-in).** When `eval.judge` is not `off`, run the judge flow per `skills/eval/SKILL.md` § Phase 3: the coordinator dispatches the read-only `eval-judge` agent (DI'd dispatch, untrusted-data nonce fence), merges the advisory judge dimensions (`method: "judge"`, `advisory: true`, `calibration_status: "uncalibrated"`) into the record, and appends the merged record. When `eval.judge: off`, no agent is dispatched and no judge dimensions are produced.
226
+ 3. **HTML report (opt-in).** When `eval.report: html` (the default), call `writeEvalReport(record, …)` from `scripts/lib/eval/report.mjs` to emit the self-contained run report under `.orchestrator/eval/reports/<run-id>.html` (gitignored, regenerable from the record). When `eval.report: none`, skip the report.
227
+
228
+ **Advisory — never blocks the close (FA6):** an eval failure — a non-zero `eval-session.mjs` exit, a judge-dispatch error, or a report-write error — MUST NOT abort `/close`. Under `mode: warn`, log a single `WARNING: session-eval failed — <reason>; continuing close` line to stderr and proceed to Phase 3.8 / Phase 4. There is NO exit-code gate on this phase. See `skills/eval/SKILL.md` for the full deterministic-engine + judge + report detail flow.
229
+
@@ -0,0 +1,120 @@
1
+ # Phase 4a: Auto-Promoted Worktree Cleanup (#575 P3.2)
2
+
3
+ > Sub-file of the session-end skill (#1157 — agentskills.io: SKILL.md core < 500 lines, procedure in `references/`). Extracted VERBATIM from `skills/session-end/SKILL.md`; `SKILL.md` keeps a one-line stub naming this phase and its gate condition.
4
+
5
+ ## Phase 4a: Auto-Promoted Worktree Cleanup (#575 P3.2)
6
+
7
+ > Skip if `persistence: false` in Session Config. Skip silently if the current worktree is NOT an Auto-promoted sibling (the common case).
8
+
9
+ After Phase 4 commit+push has durably persisted `sessions.jsonl` + `STATE.md` to origin, check whether the current session ran in an Auto-promoted sibling worktree (created via the P3.1 PROMOTION_OFFER path). If yes, apply Hybrid Cleanup-Pattern: clean → auto-remove, dirty → AUQ.
10
+
11
+ > **Ordering rationale (#490 durableCommit dependency):** Phase 4a runs AFTER Phase 4 commit+push, NOT before. Removing the promoted worktree before commit+push would lose the worktree's `STATE.md` before Phase 3.4 metrics writes (`sessions.jsonl`) are committed, violating the #490 durableCommit ordering invariant. Once Phase 4 has pushed all metrics + STATE.md to origin, the promoted worktree can be safely removed without data loss.
12
+
13
+ ### Detection: is the current worktree an Auto-promoted sibling?
14
+
15
+ Auto-promoted sibling worktrees are created by `enterWorktree()` during the Phase 0.5 PROMOTION_OFFER path. Their path layout is `<basePath>/<repo-name>-<sessionId>/`. Detection uses `parseSessionId()` from `scripts/lib/session-id.mjs` (#572) — never custom regex.
16
+
17
+ > **Authoritative impl:** `scripts/lib/session-end/worktree-cleanup.mjs` — `detectAutoPromotedWorktree(repoRoot, sessionId, opts)`. Import and call; do NOT re-implement from this doc.
18
+ >
19
+ > Two keys, tried in this order:
20
+ >
21
+ > 1. **Marker (primary).** Reads `<repoRoot>/.orchestrator/promoted-from.json` (`PROMOTION_MARKER_RELPATH`), written by `enterWorktree()` at creation time with `branch`, `source_session_id`, `source_root_hash`, `source_root_basename`, `promoted_at`. Accepted when the file parses, carries a non-empty `branch` + `source_session_id`, and the worktree's current branch (`git branch --show-current`) either matches the recorded one or cannot be read at all — an unverifiable branch never triggers auto-removal by itself, since that is gated separately by `isWorktreeClean()`, which fails closed on any git error. On match returns `{ wtPath, sessionId: marker.source_session_id, branch: marker.branch, source: 'marker' }`. This is the only key that survives the #1069 process boundary: since #1069 the session that RUNS in the promoted worktree is a brand-new session with its own id (see ADR-0013), so the current session's id appears in neither the worktree's directory name nor its branch — key 2 below can never match a #1069-promoted worktree.
22
+ > 2. **Basename (legacy fallback).** Parse `sessionId` via `parseSessionId()`; return `null` immediately for UUID-format sessions (never auto-promoted). Derive the MAIN checkout root from the first `worktree ` entry of `git worktree list --porcelain` (NOT `path.basename(repoRoot)` — the promoted worktree's basename IS the comparison target). If `repoRoot` resolves to the main checkout, return `null`. Otherwise compare `path.basename(repoRoot)` against `<main-repo-name>-<sessionId>` (the CURRENT session id); on match return `{ wtPath, sessionId, branch: parsed.branch, source: 'basename' }` — still correct for worktrees created before the marker existed. Returns `null` on no match.
23
+ >
24
+ > All git invocation is via the injection-safe `opts.execFileFn` (default `execFileSync` with an args array — #577 HARDEN-001).
25
+
26
+ ### Clean-check
27
+
28
+ A worktree is clean iff ALL three conditions hold:
29
+
30
+ 1. **No uncommitted changes**: `git status --porcelain` is empty
31
+ 2. **No untracked files**: implicit in #1 (porcelain includes `??` entries)
32
+ 3. **No unpushed commits**: `git status --short --branch` does NOT contain `ahead` indicator
33
+
34
+ > **Authoritative impl:** `scripts/lib/session-end/worktree-cleanup.mjs` — `isWorktreeClean(wtPath, opts)`. Import and call; do NOT re-implement from this doc.
35
+ >
36
+ > Algorithm: run `git status --porcelain`; filter blank lines, then discount EXACTLY the one untracked line the promotion marker itself produces (`?? .orchestrator/promoted-from.json` — in a repo where `.orchestrator/` is only partly gitignored, or on a worktree whose branch predates the ignore line, the marker `enterWorktree()` writes would otherwise make every promoted worktree read "dirty"; a modified/staged/renamed/conflicted marker still counts as dirty). If any lines remain → dirty (`false`). Else run `git status --short --branch`; if it matches `/\bahead\b/` → unpushed (`false`). Otherwise `true`. On ANY git error → `false` (conservative PSA-003 default: never auto-remove a worktree we could not verify). Git invocation is via the injection-safe `opts.execFileFn` (default `execFileSync` with an args array — #577 HARDEN-001).
37
+
38
+ ### Clean path: auto-remove + WARN (PRD §3 P3 Gherkin row 2)
39
+
40
+ When detection returns a worktree object AND `isWorktreeClean()` returns `true`, auto-remove via `git worktree remove` (NO `--force`) and log a WARN line. The main checkout's git dir (`repoMainRoot`) is derived via the first entry of `git worktree list --porcelain`.
41
+
42
+ > **Authoritative impl:** import `detectAutoPromotedWorktree` + `isWorktreeClean` from `scripts/lib/session-end/worktree-cleanup.mjs`. All git invocation MUST go through the injection-safe arg-array form (`execFileSync('git', ['-C', dir, …])`, #577 HARDEN-001) — never the legacy `execSync(\`git -C ${var} …\`)` template-literal shell form.
43
+
44
+ ```js
45
+ import { execFileSync } from 'node:child_process';
46
+ import { detectAutoPromotedWorktree, isWorktreeClean } from '${PLUGIN_ROOT}/scripts/lib/session-end/worktree-cleanup.mjs';
47
+
48
+ const promoted = detectAutoPromotedWorktree(process.cwd(), sessionId);
49
+ if (!promoted) {
50
+ // Not auto-promoted — skip Phase 4a entirely. Continue to Phase 5.
51
+ } else {
52
+ // Derive main checkout root from first worktree-list entry (arg-array, no shell)
53
+ const wtList = execFileSync('git', ['-C', promoted.wtPath, 'worktree', 'list', '--porcelain'], { encoding: 'utf8' });
54
+ const mainLine = wtList.split('\n').find((l) => l.startsWith('worktree '));
55
+ const repoMainRoot = mainLine ? mainLine.slice('worktree '.length).trim() : null;
56
+
57
+ if (isWorktreeClean(promoted.wtPath)) {
58
+ // Clean path: PRD §3 P3 Gherkin row 2 — auto-remove
59
+ console.warn(`session-end Phase 4a: auto-promoted worktree ${promoted.wtPath} is clean — removing via 'git worktree remove'`);
60
+ execFileSync('git', ['-C', repoMainRoot, 'worktree', 'remove', promoted.wtPath], { encoding: 'utf8' });
61
+ } else {
62
+ // Dirty path: PRD §3 P3 Gherkin row 3 — AUQ before any destructive action
63
+ // [AUQ block — see Dirty path subsection below]
64
+ }
65
+ }
66
+ ```
67
+
68
+ ### Dirty path: AUQ before destructive action (PRD §3 P3 Gherkin row 3)
69
+
70
+ When the worktree is dirty (uncommitted, untracked, OR unpushed), render this AUQ via the coordinator's `AskUserQuestion` tool. The AUQ is coordinator-only — per `.claude/rules/ask-via-tool.md` AUQ-004, dispatched agents cannot call AUQ. Calling `git worktree remove --force` without explicit operator confirmation would violate PSA-003 (destructive action safeguards) — the dirty state may contain another session's work-in-progress or unmerged commits.
71
+
72
+ ```js
73
+ // What is actually at stake, shown beside the options via `preview` (AUQ-006):
74
+ // the operator must see WHICH changes he would lose before he authorises the delete.
75
+ // Capped at 10 lines so the preview never outgrows the option list next to it.
76
+ const dirtyDetail = execFileSync('git', ['-C', promoted.wtPath, 'status', '--short', '--branch'], { encoding: 'utf8' })
77
+ .trim()
78
+ .split('\n')
79
+ .slice(0, 10)
80
+ .join('\n');
81
+
82
+ AskUserQuestion({
83
+ questions: [{
84
+ question: `Auto-promoted worktree at ${promoted.wtPath} has uncommitted/untracked/unpushed changes. How should I proceed?`,
85
+ header: "Worktree",
86
+ multiSelect: false,
87
+ options: [
88
+ { label: "Behalten (Recommended)", description: "Keeps the worktree exactly as it is — nothing is deleted, and you can still remove it by hand later.", preview: `Stays on disk:\n${dirtyDetail}` },
89
+ { label: "Löschen", description: "I confirm the changes are handled or expendable. Run 'git worktree remove --force' on the worktree.", preview: `Deleted with the worktree:\n${dirtyDetail}` },
90
+ { label: "Manuell", description: "Exit /close. I will inspect the worktree before re-running /close.", preview: `You would inspect this first:\n${dirtyDetail}` },
91
+ ],
92
+ }],
93
+ });
94
+ ```
95
+
96
+ **Codex CLI / Cursor IDE fallback** (numbered Markdown list):
97
+
98
+ ```
99
+ Worktree cleanup options (the changes at stake are the `git status --short --branch` lines printed above):
100
+ 1. **Behalten (Recommended)** — Keeps the worktree exactly as it is; nothing is deleted, and you can still remove it by hand later.
101
+ 2. **Löschen** — I confirm the changes are handled or expendable. Run 'git worktree remove --force'.
102
+ 3. **Manuell** — Exit /close. I will inspect the worktree before re-running /close.
103
+ Reply with the number of your choice.
104
+ ```
105
+
106
+ **On user choice:**
107
+
108
+ - **Behalten** → log `session-end Phase 4a: auto-promoted worktree retained (dirty); operator chose Behalten`. Continue to Phase 5.
109
+ - **Löschen** → `execFileSync('git', ['-C', repoMainRoot, 'worktree', 'remove', '--force', promoted.wtPath])` (arg-array, no shell — #577 HARDEN-001). Log WARN: `session-end Phase 4a: auto-promoted worktree force-removed by user choice`. Continue to Phase 5.
110
+ - **Manuell** → exit `/close` cleanly. Print: `session-end aborted at Phase 4a by user choice. Re-run /close after handling the worktree manually.`
111
+
112
+ ### Cross-references
113
+
114
+ - **PRD:** "Parallel-Aware Sessions" (#568; archived in the private Meta-Vault) §3 P3 Gherkin rows 2-3 + §3.A P3 EARS event-driven clauses
115
+ - **PSA-003:** `.claude/rules/parallel-sessions.md` — destructive action safeguards (`git worktree remove --force` requires explicit user authorization)
116
+ - **#490 durableCommit dependency:** Phase 4a runs AFTER Phase 4 commit+push to guarantee `sessions.jsonl` + `STATE.md` are persisted to origin BEFORE worktree removal
117
+ - **Detection helper:** `parseSessionId()` from `scripts/lib/session-id.mjs` (#572)
118
+ - **AUQ rule:** `.claude/rules/ask-via-tool.md` AUQ-004 — coordinator-only invocation
119
+ - **Companion phases:** P3.1 PROMOTION_OFFER (`enterWorktree()` in `parallel-aware-auq.md`) creates the worktree; this phase removes it.
120
+
@@ -0,0 +1,58 @@
1
+ # Phase 4b: Worktree-Orphan Sweep (#831/B5)
2
+
3
+ > Sub-file of the session-end skill (#1157 — agentskills.io: SKILL.md core < 500 lines, procedure in `references/`). Extracted VERBATIM from `skills/session-end/SKILL.md`; `SKILL.md` keeps a one-line stub naming this phase and its gate condition.
4
+
5
+ ## Phase 4b: Worktree-Orphan Sweep (#831/B5)
6
+
7
+ > Skip if `persistence: false` in Session Config. Skip silently unless `worktree-orphans.enabled: true` (opt-in; default `false`).
8
+
9
+ Sweep the repo's worktree set for branches with **0 commits ahead of the base branch** — orphans left behind by finished sessions. Distinct from Phase 4a: 4a asks "did *this* session run in a promoted worktree?", 4b asks "which worktrees from *past* sessions have nothing left in them?".
10
+
11
+ > **Ordering rationale (#490 durableCommit dependency):** Phase 4b runs AFTER the Phase 4 commit+push, NOT before — the same invariant that governs Phase 4a. Removing a worktree before commit+push would lose its `STATE.md` before the Phase 3.4 `sessions.jsonl` metrics writes are committed. See `docs/adr/0008-worktree-cleanup-ordering.md`.
12
+
13
+ ### The module proposes; the coordinator disposes
14
+
15
+ > **Authoritative impl:** `scripts/lib/session-end/worktree-orphan-sweep.mjs` — `checkWorktreeOrphans({ repoRoot, mainCheckoutRoot, config, execFileFn })`. Import and call; do NOT re-implement from this doc.
16
+
17
+ `checkWorktreeOrphans()` **executes zero mutating commands.** Its complete argv set is four read-only shapes — `worktree list --porcelain`, `rev-list --count --end-of-options <base>..<branch>`, and (via the reused Phase 4a helper `isWorktreeClean`) `status --porcelain` and `status --short --branch` — all via the injection-safe arg-array form (`execFileSync('git', ['-C', dir, …])`, #577 HARDEN-001), never a template-literal shell string. It returns `null` (silent no-op) or ONE object `{ severity: 'warn', message, candidates: [{ wtPath, branch, sessionId, aheadCount: 0 }] }`.
18
+
19
+ **`--end-of-options` is load-bearing, not decoration.** `base-branch` comes from Session Config, and a value shaped like a git flag (e.g. `--glob=refs/heads/*`) is otherwise parsed by `rev-list` as an OPTION rather than a revision range — which exits 0 and prints `0`, silently marking EVERY worktree as a 0-ahead orphan and offering the operator "Löschen" for worktrees full of live work. That is not an error path the conservative default catches, because `0` parses fine. `--end-of-options` turns the payload into a hard git error that DOES fall into the conservative `continue`, and `_isSafeBaseBranch` in the config parser rejects leading-dash values at the source. No attacker is needed for this — a typo reaches the same outcome.
20
+
21
+ **The gate is opt-in and fails CLOSED:** the module returns `null` unless `enabled === true`. It accepts either the full config object or the already-indexed `worktree-orphans` block, so neither call shape can accidentally open the gate.
22
+
23
+ **A dirty worktree is never a candidate.** Orphan-ness is not decided by commit count alone — `isWorktreeClean()` is consulted first, and any uncommitted, staged or untracked work (or any git error while checking) excludes the worktree entirely. A worktree that is 0-ahead but holds live work is exactly the case where a deletion prompt would cost real data.
24
+
25
+ The return field is named `candidates`, not `orphans` or `removals`, and the name is load-bearing: **the coordinator decides, the module never does.** Grounding: `.claude/rules/parallel-sessions.md` § PSA-003 — *"Did I create this file/commit/change? If not, it is not mine to touch."* A sweep probe created none of the worktrees it inspects.
26
+
27
+ **Nothing is removed without explicit operator confirmation.** The rendered banner always ends with the literal clause `nothing was removed.` — the operator-visible proof of the invariant.
28
+
29
+ **Conservative default (safety-critical):** any git error, unparseable `rev-list` output, detached HEAD, unresolvable branch, or ambiguity of any kind → that worktree is NOT reported as a candidate. A failing sibling never suppresses a healthy finding, and silence is never to be read as "safe to delete".
30
+
31
+ ### The AUQ is rendered by the coordinator, never by the module
32
+
33
+ `AskUserQuestion` is unavailable inside dispatched subagents (`.claude/rules/ask-via-tool.md` AUQ-004), so the module returns data only and the **coordinator** renders the picker — one call per candidate.
34
+
35
+ **Option order is locked and is itself a safety property (#580-AUQ-001): the non-destructive option goes FIRST and is marked `(Recommended)`, so an accidental Enter keypress can never destroy a worktree.**
36
+
37
+ `[ Behalten (Recommended) / Löschen / Manuell ]`
38
+
39
+ - **Behalten (Recommended)** — leave the worktree in place; re-surfaces next session.
40
+ - **Löschen** — operator explicitly authorises removal; the coordinator performs it, subject to PSA-003.
41
+ - **Manuell** — operator handles it outside the session; no further prompting this session.
42
+
43
+ ```js
44
+ import { checkWorktreeOrphans } from '${PLUGIN_ROOT}/scripts/lib/session-end/worktree-orphan-sweep.mjs';
45
+
46
+ const sweep = checkWorktreeOrphans({
47
+ repoRoot: process.cwd(),
48
+ config: config['worktree-orphans'],
49
+ });
50
+
51
+ if (sweep) {
52
+ console.warn(sweep.message);
53
+ // → coordinator renders the AUQ per sweep.candidates entry.
54
+ // [ Behalten (Recommended) / Löschen / Manuell ]
55
+ // Nothing is removed unless the operator picks "Löschen".
56
+ }
57
+ ```
58
+
@@ -0,0 +1,104 @@
1
+ # Phase 5: Issue Cleanup
2
+
3
+ > Sub-file of the session-end skill (#1157 — agentskills.io: SKILL.md core < 500 lines, procedure in `references/`). Extracted VERBATIM from `skills/session-end/SKILL.md`; `SKILL.md` keeps a one-line stub naming this phase and its gate condition.
4
+
5
+ ## Phase 5: Issue Cleanup
6
+
7
+ > **VCS Reference:** Use CLI commands per the "Common CLI Commands" section of the gitlab-ops skill.
8
+
9
+ 1. **Close resolved issues**: Before closing each issue, strip `status:*` workflow labels using `stripStatusLabels` from `scripts/lib/issue-close-strip-labels.mjs` (#308). A closed issue carrying `status:in-progress` or `status:ready` skews dashboard filters and discovery heuristics. Then close and add a note using the issue close and note commands per the "Common CLI Commands" section of the gitlab-ops skill. Note: some VCS platforms require separate note and close commands.
10
+
11
+ ```js
12
+ import { stripStatusLabels } from '${PLUGIN_ROOT}/scripts/lib/issue-close-strip-labels.mjs';
13
+
14
+ // For each resolved issue IID:
15
+ const { stripped, error } = await stripStatusLabels({ issueId: iid, vcs: '<from Session Config>' });
16
+ if (error) {
17
+ console.warn(`⚠ label strip failed for #${iid}: ${error} — proceeding with close`);
18
+ } else if (stripped.length) {
19
+ console.log(`Stripped ${stripped.join(', ')} from #${iid}`);
20
+ }
21
+ // then: glab issue close <iid> / gh issue close <iid>
22
+ ```
23
+
24
+ The call is idempotent: if the issue has no `status:*` labels, no update CLI call is made. Failures from `stripStatusLabels` are non-fatal — log and proceed with close.
25
+
26
+ 2. **Update in-progress issues**: ensure labels reflect actual state using the issue update command
27
+ 3. **Create carryover issues — from the Phase 1.65 gate's carry-list ONLY (#769):** file an issue for each item on the carry-list produced by the Handover Alignment Gate — i.e. the non-deselectable **auto-carry** class (`priority::critical|high`, SPIRAL/FAILED, or no-origin-issue candidates) PLUS the middle-band items the operator LEFT SELECTED in triage. Do NOT file anything the gate dropped, and do NOT file directly from Phase 1.2/1.3/1.4/1.6 — those phases only collected candidates.
28
+ - **Template stays source-specific:** 1.2 Partially-Done → `[Carryover] <task>` (labels `priority::<original>`, `status:ready`); 1.4 unfinished Emergent → a **normal** issue (NOT the `[Carryover]` template); 1.6 SPIRAL/FAILED → fire the deferred `createSpiralCarryoverIssue({ taskDescription, kind, context, priority: 'high', vcs })` (idempotent task-hash dedup — payload comes from the candidate's `_spiral` annotation set in Phase 1.6 step 5). 1.3 files no NEW issue: a carried 1.3 candidate simply keeps its ORIGINAL issue `status:ready`.
29
+ - **Dropped middle-band items:** file NO `[Carryover]` duplicate; the origin issue stays open and unchanged. Record each drop in the Phase 6 Final Report under `### Dropped at Handover Gate` with its origin-issue reference and a reason slot.
30
+ - **Fail-open / gate skipped:** when Phase 1.65 skipped fail-open, the carry-list is ALL candidates (status quo) and there is no drop-list.
31
+ - **Mark answered open questions `[x]` durably — atomic with the filing above (#769):** now, on the completed side of the Quality Gate, persist each answered open question captured in-memory at Phase 1.65 Step 4 to STATE.md via the lock-guarded sibling helper (PSA-005). Co-locating this write with the carryover-issue filing is the load-bearing correctness invariant: an earlier Quality-Gate abort leaves every question `- [ ]` on disk, so it correctly re-surfaces via `readOpenQuestions().filter(!answered)` on re-close — the `[x]` mark now reflects a COMPLETED handover, never a mid-close state a later abort would invalidate. Any implied-work candidate an answered question enqueued in Phase 1.65 is filed by the carry-list step above, so the mark and its issue land together:
32
+
33
+ ```js
34
+ import { markOpenQuestionAnsweredOnDisk } from '${PLUGIN_ROOT}/scripts/lib/state-md.mjs';
35
+ // answeredQuestions captured in Phase 1.65 Step 4 (in-memory, un-persisted until now)
36
+ for (const { question, answer } of answeredQuestions) {
37
+ await markOpenQuestionAnsweredOnDisk(repoRoot, question, answer); // "- [ ] Q" → "- [x] Q → Antwort: <answer>"
38
+ }
39
+ ```
40
+
41
+ Fail-open: a `markOpenQuestionAnsweredOnDisk` failure is non-fatal — log a WARN and proceed with the close; the question simply stays `- [ ]` and roundtrips to the next session.
42
+
43
+ 3b. **Drain the issue-budget overflow — exactly ONE collector artefact (issue-budget):** when this session's budget file (`budgetStatePath(repoRoot, accountingSessionId)` → `.orchestrator/runtime/issue-budget/<hash>.json`, #1141) has a non-empty `overflow[]`, the session hit its `issue-budget.max-per-session` cap and every over-cap creation was PARKED rather than filed. Fold the whole list into a single artefact so nothing is silently dropped.
44
+
45
+ **Ordering (load-bearing):** run this as the LAST issue-creating action of Phase 5 — after step 3, after "Discovery Issue Creation", after step 4 — and re-read the counter file at that moment. Those steps can themselves push new entries into `overflow[]`; draining early would leave them unfiled.
46
+
47
+ ```js
48
+ import { readFileSync } from 'node:fs';
49
+ import {
50
+ readBudgetState,
51
+ budgetStatePath,
52
+ resolveIssueBudgetSessionId,
53
+ } from '${PLUGIN_ROOT}/scripts/lib/issue-budget.mjs';
54
+
55
+ // `sessionId` is the physical raw lock/registry identity from session-start.
56
+ const rawSessionId = sessionId;
57
+ let currentSession = null;
58
+ try {
59
+ currentSession = JSON.parse(
60
+ readFileSync(`${repoRoot}/.orchestrator/current-session.json`, 'utf8'),
61
+ );
62
+ } catch { /* no verified semantic accounting bridge */ }
63
+ const accountingSessionId = resolveIssueBudgetSessionId(rawSessionId, currentSession);
64
+ const state = readBudgetState(repoRoot, accountingSessionId);
65
+ // { sessionId, count, exempt, overflow: [...] }
66
+ ```
67
+
68
+ `accountingSessionId` may be semantic only after
69
+ `currentSession.session_id === rawSessionId`; this is budget accounting, not
70
+ lock/registry ownership. When that proof is absent it remains the raw id.
71
+ A host rotation that changes both raw and semantic values has no guaranteed
72
+ budget continuity.
73
+
74
+ - **`issue-budget.overflow: collect-issue` (default)** — create exactly ONE issue:
75
+ - Title: `[Backlog-Sammel] <accountingSessionId>, <N> zurückgestellte Punkte`
76
+ - Labels: `type::backlog`, `priority::low`
77
+ - Body: a Markdown checklist with one `- [ ]` line per `overflow[]` entry (`title` when present, otherwise the truncated `command`, plus its `at` timestamp).
78
+ - This collector issue is itself EXEMPT from the cap (`[Backlog-Sammel]` is in the exemption list in `scripts/lib/issue-budget.mjs`), so it always lands even at count == max.
79
+ - **`issue-budget.overflow: vault-note`** — create NO issue. Write one Markdown file `vault/00-inbox/<accountingSessionId>-backlog-sammel.md` (path relative to `vault-integration.vault-dir`) with valid vault frontmatter and the same checklist body.
80
+ - After the artefact exists, reset `overflow` to `[]` in the counter file and record the collector issue ID / note path in the Phase 6 Final Report under `### Zurückgestellt (issue-budget)`.
81
+ - **Never exempt-by-accident:** the cap never applied to `priority::critical`, the carryover class (`[Carryover]`, SPIRAL/FAILED, `type::carryover`), or `broken-window` closure issues, so nothing on the Phase 1.65 carry-list can ever appear in `overflow[]`. The promises at Phase 1.8 ("SPIRAL / FAILED agent carryover … non-deselectable") and the Critical Rule "ALWAYS create issues for unfinished PLANNED work" stay intact by construction.
82
+ - Fail-open: a missing or malformed counter file means "no overflow" — log a WARN and continue the close.
83
+ - **Then reap stale counter files (#1151):** the per-session split (#1141) writes one file per accounting session and nothing ever deleted them, so `.orchestrator/runtime/issue-budget/` grew without bound in every working copy. After the drain, sweep files older than 14 days; THIS session's file is exempt regardless of age, and the call is best-effort (it never throws, so it can never abort the close).
84
+
85
+ ```js
86
+ import { reapStaleBudgetFiles } from '${PLUGIN_ROOT}/scripts/lib/issue-budget.mjs';
87
+
88
+ const { removed } = reapStaleBudgetFiles({ repoRoot, sessionId: accountingSessionId });
89
+ if (removed.length) console.log(`issue-budget: reaped ${removed.length} stale counter file(s) (> 14 d)`);
90
+ ```
91
+
92
+ #### Discovery Issue Creation (if discovery ran in Phase 1.5)
93
+
94
+ For each finding with severity `critical` or `high` from Phase 1.5:
95
+ 1. Create a VCS issue using the detected platform CLI:
96
+ - Title: `[Discovery] <description>` (truncated to 70 chars)
97
+ - Body: `**Probe:** <probe>\n**File:** <file>:<line>\n**Severity:** <severity>\n**Confidence:** <confidence>%\n**Recommendation:** <recommendation>`
98
+ - Labels: `type:discovery`, `priority::<severity>` (critical→critical, high→high)
99
+ 2. Log each created issue ID for the Final Report
100
+ 3. Update `discovery_stats.issues_created` count
101
+
102
+ 4. **Create gap issues for HIGH+/blocking newly-discovered problems only** — MED/LOW review findings are recorded in the Final Report, not filed as issues (#617; see the Phase 1.8 severity-disposition table). This mirrors the Phase 5 "Discovery Issue Creation" gate (critical/high only).
103
+ 5. **Update milestones**: if milestone progress changed
104
+