session-orchestrator 3.21.0 → 3.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.cursor/commands/autopilot-multi.md +14 -0
  5. package/.cursor/commands/autopilot.md +14 -0
  6. package/.cursor/commands/bootstrap.md +14 -0
  7. package/.cursor/commands/brainstorm.md +14 -0
  8. package/.cursor/commands/close.md +13 -0
  9. package/.cursor/commands/contract-version-bump.md +14 -0
  10. package/.cursor/commands/debug.md +14 -0
  11. package/.cursor/commands/discovery.md +14 -0
  12. package/.cursor/commands/dispatcher.md +14 -0
  13. package/.cursor/commands/eli5.md +14 -0
  14. package/.cursor/commands/eval.md +14 -0
  15. package/.cursor/commands/evolve.md +14 -0
  16. package/.cursor/commands/go.md +14 -0
  17. package/.cursor/commands/grill.md +14 -0
  18. package/.cursor/commands/harness-audit.md +13 -0
  19. package/.cursor/commands/journey-audit.md +14 -0
  20. package/.cursor/commands/memory-cleanup.md +14 -0
  21. package/.cursor/commands/persona-panel.md +14 -0
  22. package/.cursor/commands/plan.md +14 -0
  23. package/.cursor/commands/portfolio.md +14 -0
  24. package/.cursor/commands/reconcile.md +14 -0
  25. package/.cursor/commands/release.md +14 -0
  26. package/.cursor/commands/repo-audit.md +13 -0
  27. package/.cursor/commands/session.md +14 -0
  28. package/.cursor/commands/spinout.md +14 -0
  29. package/.cursor/commands/sunset-review.md +14 -0
  30. package/.cursor/commands/templates-ack.md +14 -0
  31. package/.cursor/commands/test.md +14 -0
  32. package/.cursor/hooks.json +60 -0
  33. package/.cursor/rules/000-session-orchestrator.mdc +11 -2
  34. package/.cursor/rules/010-session-workflow.mdc +9 -1
  35. package/.cursor/rules/020-quality-gates.mdc +1 -1
  36. package/.cursor/rules/030-wave-execution.mdc +1 -1
  37. package/.cursor/rules/040-discovery.mdc +6 -6
  38. package/.cursor/rules/050-plan.mdc +10 -10
  39. package/.cursor/rules/070-gitlab-ops.mdc +73 -57
  40. package/.cursor/rules/080-ecosystem-health.mdc +7 -7
  41. package/.cursor/skills/architecture/SKILL.md +13 -0
  42. package/.cursor/skills/autopilot/SKILL.md +12 -0
  43. package/.cursor/skills/bootstrap/SKILL.md +12 -0
  44. package/.cursor/skills/brainstorm/SKILL.md +13 -0
  45. package/.cursor/skills/claude-md-drift-check/SKILL.md +13 -0
  46. package/.cursor/skills/contract-version-bump/SKILL.md +12 -0
  47. package/.cursor/skills/convergence-monitoring/SKILL.md +12 -0
  48. package/.cursor/skills/daily/SKILL.md +12 -0
  49. package/.cursor/skills/debug/SKILL.md +13 -0
  50. package/.cursor/skills/discovery/SKILL.md +13 -0
  51. package/.cursor/skills/dispatcher/SKILL.md +13 -0
  52. package/.cursor/skills/docs-orchestrator/SKILL.md +13 -0
  53. package/.cursor/skills/domain-model/SKILL.md +13 -0
  54. package/.cursor/skills/ecosystem-health/SKILL.md +13 -0
  55. package/.cursor/skills/eli5/SKILL.md +13 -0
  56. package/.cursor/skills/eval/SKILL.md +12 -0
  57. package/.cursor/skills/evolve/SKILL.md +13 -0
  58. package/.cursor/skills/frontmatter-guard/SKILL.md +13 -0
  59. package/.cursor/skills/gitlab-ops/SKILL.md +13 -0
  60. package/.cursor/skills/gitlab-portfolio/SKILL.md +13 -0
  61. package/.cursor/skills/grill/SKILL.md +13 -0
  62. package/.cursor/skills/hook-development/SKILL.md +13 -0
  63. package/.cursor/skills/journey-audit/SKILL.md +13 -0
  64. package/.cursor/skills/mcp-builder/SKILL.md +13 -0
  65. package/.cursor/skills/memory-cleanup/SKILL.md +12 -0
  66. package/.cursor/skills/mode-selector/SKILL.md +13 -0
  67. package/.cursor/skills/npm-publish/SKILL.md +12 -0
  68. package/.cursor/skills/peekaboo-driver/SKILL.md +13 -0
  69. package/.cursor/skills/persona-panel/SKILL.md +12 -0
  70. package/.cursor/skills/plan/SKILL.md +13 -0
  71. package/.cursor/skills/playwright-driver/SKILL.md +13 -0
  72. package/.cursor/skills/quality-gates/SKILL.md +13 -0
  73. package/.cursor/skills/reconcile/SKILL.md +12 -0
  74. package/.cursor/skills/repo-audit/SKILL.md +13 -0
  75. package/.cursor/skills/session-end/SKILL.md +13 -0
  76. package/.cursor/skills/session-plan/SKILL.md +13 -0
  77. package/.cursor/skills/session-start/SKILL.md +13 -0
  78. package/.cursor/skills/skill-creator/SKILL.md +13 -0
  79. package/.cursor/skills/spinout/SKILL.md +12 -0
  80. package/.cursor/skills/sunset-review/SKILL.md +13 -0
  81. package/.cursor/skills/test-runner/SKILL.md +13 -0
  82. package/.cursor/skills/tmux-layout/SKILL.md +13 -0
  83. package/.cursor/skills/ubiquitous-language/SKILL.md +13 -0
  84. package/.cursor/skills/using-orchestrator/SKILL.md +13 -0
  85. package/.cursor/skills/vault-mirror/SKILL.md +13 -0
  86. package/.cursor/skills/vault-sync/SKILL.md +13 -0
  87. package/.cursor/skills/wave-executor/SKILL.md +13 -0
  88. package/.cursor/skills/write-executable-plan/SKILL.md +13 -0
  89. package/.mcp.json +4 -1
  90. package/CHANGELOG.md +269 -0
  91. package/README.md +18 -15
  92. package/agents/AGENTS.md +23 -4
  93. package/agents/code-implementer.md +2 -1
  94. package/agents/db-specialist.md +2 -1
  95. package/agents/docs-writer.md +3 -1
  96. package/agents/eval-judge.md +1 -1
  97. package/agents/memory-proposal-collector.md +6 -4
  98. package/agents/session-reviewer.md +7 -1
  99. package/agents/test-writer.md +2 -1
  100. package/agents/ui-developer.md +2 -1
  101. package/commands/bootstrap.md +2 -2
  102. package/commands/close.md +3 -1
  103. package/commands/eli5.md +33 -0
  104. package/commands/go.md +1 -1
  105. package/commands/journey-audit.md +43 -0
  106. package/commands/release.md +5 -3
  107. package/commands/test.md +2 -2
  108. package/docs/USER-GUIDE.md +2 -2
  109. package/docs/ci-setup.md +14 -0
  110. package/docs/codex-setup.md +64 -0
  111. package/docs/components.md +7 -6
  112. package/docs/cursor-setup.md +26 -47
  113. package/docs/events-schema.md +76 -4
  114. package/docs/github-mirror-protection.md +197 -0
  115. package/docs/pi-setup.md +2 -0
  116. package/docs/rule-authoring.md +3 -1
  117. package/docs/scope-collision-guard.md +52 -5
  118. package/docs/session-config-reference.md +57 -12
  119. package/docs/session-config-template.md +4 -3
  120. package/docs/telemetry.md +22 -0
  121. package/hooks/_lib/lock-bootstrap.mjs +26 -16
  122. package/hooks/_lib/vcs-create-matcher.mjs +397 -38
  123. package/hooks/enforce-scope.mjs +64 -0
  124. package/hooks/hooks-codex.json +1 -1
  125. package/hooks/hooks-cursor.json +201 -20
  126. package/hooks/hooks-pi.json +1 -1
  127. package/hooks/hooks.json +12 -2
  128. package/hooks/on-session-end.mjs +237 -104
  129. package/hooks/on-session-start.mjs +404 -110
  130. package/hooks/on-stop.mjs +48 -9
  131. package/hooks/post-subagent-discovery-validator.mjs +34 -3
  132. package/hooks/post-tool-batch-wave-signal.mjs +11 -2
  133. package/hooks/pre-auq-clarity.mjs +787 -0
  134. package/hooks/pre-bash-issue-budget.mjs +133 -21
  135. package/hooks/pre-bash-sessions-ledger-guard.mjs +159 -0
  136. package/hooks/pre-bash-staging-fence.mjs +4 -0
  137. package/hooks/pre-task-scope-disjoint.mjs +368 -35
  138. package/hooks/skill-invocation-telemetry.mjs +21 -10
  139. package/monitors/monitors.json +6 -0
  140. package/package.json +3 -1
  141. package/pi/prompts/eli5.md +12 -0
  142. package/pi/prompts/journey-audit.md +12 -0
  143. package/rules/_index.md +9 -1
  144. package/rules/always-on/ask-via-tool.md +62 -0
  145. package/rules/always-on/bash-harness-pitfalls.md +168 -0
  146. package/rules/always-on/build-value.md +47 -0
  147. package/rules/always-on/cross-session-messaging.md +59 -0
  148. package/rules/always-on/loop-and-monitor.md +221 -0
  149. package/rules/always-on/parallel-sessions.md +142 -12
  150. package/rules/always-on/receiving-review.md +108 -0
  151. package/rules/always-on/test-value.md +40 -0
  152. package/rules/always-on/verification-before-completion.md +77 -0
  153. package/scripts/archive-closed-prds.mjs +258 -18
  154. package/scripts/auq-audit.mjs +825 -0
  155. package/scripts/autopilot.mjs +12 -8
  156. package/scripts/backfill-evidence-digest.mjs +376 -0
  157. package/scripts/cursor-install.mjs +89 -48
  158. package/scripts/export-hw-learnings.mjs +143 -2
  159. package/scripts/express-path.mjs +299 -0
  160. package/scripts/generate-cursor-adapter.mjs +253 -0
  161. package/scripts/github-protection-audit.mjs +358 -0
  162. package/scripts/lib/auq/clarity.mjs +1314 -0
  163. package/scripts/lib/auq/parse.mjs +1006 -0
  164. package/scripts/lib/auq/schema.mjs +1457 -0
  165. package/scripts/lib/autopilot/worktree-pipeline.mjs +240 -16
  166. package/scripts/lib/build-live-signals.mjs +24 -5
  167. package/scripts/lib/ci-status-banner.mjs +221 -68
  168. package/scripts/lib/command-blocker.mjs +70 -0
  169. package/scripts/lib/config/dispatcher-autonomy-capture.mjs +32 -9
  170. package/scripts/lib/config/reconcile.mjs +79 -4
  171. package/scripts/lib/config/section-extractor.mjs +235 -36
  172. package/scripts/lib/config/vault-integration.mjs +12 -1
  173. package/scripts/lib/config-schema.mjs +9 -1
  174. package/scripts/lib/config.mjs +57 -6
  175. package/scripts/lib/convergence-monitor.mjs +13 -2
  176. package/scripts/lib/cursor-hook-bridge.mjs +443 -0
  177. package/scripts/lib/dispatcher/cli.mjs +2 -2
  178. package/scripts/lib/dispatcher/rank.mjs +4 -7
  179. package/scripts/lib/express-path.mjs +327 -0
  180. package/scripts/lib/file-lock.mjs +22 -4
  181. package/scripts/lib/gates/gate-full.mjs +84 -11
  182. package/scripts/lib/gates/gate-helpers.mjs +93 -21
  183. package/scripts/lib/git-config-drift.mjs +134 -5
  184. package/scripts/lib/host-identity.mjs +247 -2
  185. package/scripts/lib/instruction-budget-guard.mjs +31 -1
  186. package/scripts/lib/io.mjs +239 -0
  187. package/scripts/lib/issue-budget.mjs +284 -31
  188. package/scripts/lib/learnings/io.mjs +55 -10
  189. package/scripts/lib/learnings/schema.mjs +95 -28
  190. package/scripts/lib/lock-reaper.mjs +7 -1
  191. package/scripts/lib/locks/staging-fence-lock.mjs +5 -1
  192. package/scripts/lib/locks/state-md-lock.mjs +8 -1
  193. package/scripts/lib/memory-banner.mjs +5 -2
  194. package/scripts/lib/memory-paths.mjs +15 -6
  195. package/scripts/lib/mode-selector/scoring.mjs +53 -6
  196. package/scripts/lib/owner-interview.mjs +78 -32
  197. package/scripts/lib/peer-discovery.mjs +73 -22
  198. package/scripts/lib/platform.mjs +72 -9
  199. package/scripts/lib/plugin-root.mjs +143 -19
  200. package/scripts/lib/project-hygiene.mjs +104 -4
  201. package/scripts/lib/quality-gate.mjs +271 -13
  202. package/scripts/lib/reconcile/emitter.mjs +87 -19
  203. package/scripts/lib/reconcile/engine.mjs +281 -13
  204. package/scripts/lib/reconcile/idempotency.mjs +102 -1
  205. package/scripts/lib/reconcile/renderer.mjs +165 -7
  206. package/scripts/lib/reconcile/sanitize.mjs +40 -17
  207. package/scripts/lib/reconcile/writer.mjs +415 -84
  208. package/scripts/lib/resource-probe/evaluate.mjs +330 -149
  209. package/scripts/lib/resource-probe/probe-platform.mjs +35 -0
  210. package/scripts/lib/resource-probe.mjs +18 -2
  211. package/scripts/lib/rule-loader.mjs +37 -2
  212. package/scripts/lib/rules-sync.mjs +51 -8
  213. package/scripts/lib/scope-gate.mjs +90 -0
  214. package/scripts/lib/session-close-backfill.mjs +369 -28
  215. package/scripts/lib/session-discovery.mjs +13 -3
  216. package/scripts/lib/session-end/phase-skip.mjs +37 -4
  217. package/scripts/lib/session-end/worktree-cleanup.mjs +154 -7
  218. package/scripts/lib/session-id.mjs +30 -14
  219. package/scripts/lib/session-identity/own-session.mjs +159 -0
  220. package/scripts/lib/session-lock.mjs +85 -30
  221. package/scripts/lib/session-schema/normalizer.mjs +70 -3
  222. package/scripts/lib/session-schema/validator.mjs +40 -0
  223. package/scripts/lib/session-start-probes.mjs +608 -0
  224. package/scripts/lib/session-transition.mjs +277 -0
  225. package/scripts/lib/sessions-staleness-banner.mjs +124 -57
  226. package/scripts/lib/spiral-carryover.mjs +111 -9
  227. package/scripts/lib/state-md/frontmatter-mutators.mjs +41 -8
  228. package/scripts/lib/state-md/mission-status.mjs +467 -72
  229. package/scripts/lib/state-md/yaml-parser.mjs +145 -16
  230. package/scripts/lib/state-md.mjs +12 -2
  231. package/scripts/lib/telemetry/sync.mjs +46 -8
  232. package/scripts/lib/validate/check-agents.mjs +66 -0
  233. package/scripts/lib/validate/check-auq-clarity.mjs +274 -0
  234. package/scripts/lib/validate/check-cursor-adapter.mjs +102 -0
  235. package/scripts/lib/validate/check-dead-bridge.mjs +24 -2
  236. package/scripts/lib/validate/check-doc-cli-commands.mjs +16 -32
  237. package/scripts/lib/validate/check-hooks-symmetry.mjs +59 -63
  238. package/scripts/lib/validate/check-playwright-mcp-canary.mjs +13 -22
  239. package/scripts/lib/validate/check-plugin-monitors.mjs +10 -4
  240. package/scripts/lib/validate/check-rules.mjs +153 -9
  241. package/scripts/lib/validate/check-test-value-bans.mjs +165 -17
  242. package/scripts/lib/validate/check-unwired-features.mjs +340 -32
  243. package/scripts/lib/validate/repo-files.mjs +275 -0
  244. package/scripts/lib/validate-vendored-rules.mjs +229 -7
  245. package/scripts/lib/vault-backfill/glab.mjs +91 -58
  246. package/scripts/lib/vault-backfill/manifest.mjs +28 -8
  247. package/scripts/lib/vault-mirror/process.mjs +99 -43
  248. package/scripts/lib/vault-mirror/telemetry.mjs +210 -0
  249. package/scripts/lib/vault-staleness-banner.mjs +76 -6
  250. package/scripts/lib/vault-status/board-writer.mjs +211 -10
  251. package/scripts/lib/vault-status/narrative-mirror.mjs +188 -8
  252. package/scripts/lib/vcs-repo-spec.mjs +182 -13
  253. package/scripts/lib/wave-executor/foreign-dispatch.mjs +832 -0
  254. package/scripts/lib/wave-resource-gate.mjs +67 -73
  255. package/scripts/lib/wave-transcript-tail.mjs +869 -0
  256. package/scripts/materialize-wave-scope.mjs +478 -0
  257. package/scripts/mcp-server.sh +11 -2
  258. package/scripts/parse-config.mjs +65 -0
  259. package/scripts/release.mjs +443 -122
  260. package/scripts/run-quality-gate.mjs +14 -0
  261. package/scripts/token-audit.sh +9 -2
  262. package/scripts/validate-plugin.mjs +6 -0
  263. package/scripts/validate-wave-scope.mjs +73 -1
  264. package/scripts/vault-backfill.mjs +32 -5
  265. package/scripts/vault-mirror.mjs +203 -34
  266. package/skills/_shared/monitor-patterns.md +31 -5
  267. package/skills/_shared/parallel-aware-auq.md +31 -25
  268. package/skills/_shared/parallel-aware-preamble.md +35 -4
  269. package/skills/_shared/platform-tools.md +11 -5
  270. package/skills/_shared/state-ownership.md +61 -8
  271. package/skills/autopilot/SKILL.md +5 -1
  272. package/skills/bootstrap/SKILL.md +5 -4
  273. package/skills/bootstrap/_shared-template.md +18 -10
  274. package/skills/bootstrap/deep-template.md +10 -6
  275. package/skills/bootstrap/fast-template.md +15 -8
  276. package/skills/bootstrap/standard-template.md +10 -6
  277. package/skills/brainstorm/SKILL.md +18 -18
  278. package/skills/brainstorm/soul.md +12 -0
  279. package/skills/claude-md-drift-check/checker.mjs +39 -11
  280. package/skills/discovery/SKILL.md +28 -24
  281. package/skills/dispatcher/SKILL.md +1 -1
  282. package/skills/eli5/SKILL.md +43 -0
  283. package/skills/evolve/SKILL.md +8 -9
  284. package/skills/gitlab-ops/SKILL.md +30 -26
  285. package/skills/grill/SKILL.md +6 -6
  286. package/skills/grill/soul.md +16 -0
  287. package/skills/journey-audit/SKILL.md +269 -0
  288. package/skills/memory-cleanup/SKILL.md +2 -2
  289. package/skills/npm-publish/SKILL.md +4 -4
  290. package/skills/peekaboo-driver/SKILL.md +18 -6
  291. package/skills/persona-panel/SKILL.md +1 -1
  292. package/skills/plan/SKILL.md +18 -16
  293. package/skills/plan/mode-feature.md +1 -1
  294. package/skills/plan/mode-new.md +35 -23
  295. package/skills/plan/soul.md +12 -0
  296. package/skills/reconcile/SKILL.md +44 -4
  297. package/skills/session-end/SKILL.md +70 -24
  298. package/skills/session-end/metrics-collection.md +7 -4
  299. package/skills/session-end/phase-3-6-tail.md +48 -5
  300. package/skills/session-end/phase-3-7a-recommendations.md +16 -2
  301. package/skills/session-plan/SKILL.md +6 -1
  302. package/skills/session-plan/wave-template.md +1 -0
  303. package/skills/session-start/SKILL.md +96 -48
  304. package/skills/session-start/phase-2-5-docs-planning.md +8 -8
  305. package/skills/session-start/phase-4-5-resource-health.md +82 -19
  306. package/skills/session-start/phase-7-5-mode-selector.md +15 -3
  307. package/skills/session-start/phase-8-5-express-path.md +77 -12
  308. package/skills/session-start/soul.md +110 -0
  309. package/skills/test-runner/SKILL.md +2 -2
  310. package/skills/using-orchestrator/SKILL.md +1 -1
  311. package/skills/vault-sync/validator.mjs +31 -0
  312. package/skills/wave-executor/SKILL.md +4 -2
  313. package/skills/wave-executor/circuit-breaker.md +34 -9
  314. package/skills/wave-executor/wave-loop.md +129 -24
  315. package/skills/write-executable-plan/SKILL.md +6 -6
  316. package/templates/_shared/journey-manifest.md +110 -0
  317. package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +0 -8
  318. package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +0 -8
  319. package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
  320. package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +0 -8
  321. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
  322. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +0 -8
  323. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +0 -8
  324. package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +0 -8
  325. package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +0 -10
  326. package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +0 -8
  327. package/skills/vault-sync/tests/fixtures/clean-vault/README.md +0 -3
  328. package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +0 -11
  329. package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
  330. package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +0 -9
  331. package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +0 -8
  332. package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
  333. package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
  334. package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +0 -7
  335. package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +0 -9
  336. package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
  337. package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +0 -11
  338. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +0 -3
  339. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +0 -3
  340. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
  341. package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +0 -11
  342. package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
  343. package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +0 -11
  344. package/skills/vault-sync/tests/schema-drift.test.mjs +0 -133
  345. package/templates/_shared/rules/parallel-sessions.md +0 -77
@@ -48,12 +48,12 @@ Run a single AUQ to let the user characterize the problem in their own words:
48
48
  ```
49
49
  AskUserQuestion({
50
50
  questions: [{
51
- question: "Describe the feature or problem you want to brainstorm. What's the core pain point it addresses, and what's still ambiguous to you?",
52
- header: "Feature Frame",
51
+ question: "What do you want to brainstorm, and what is still unclear about it?",
52
+ header: "Frame",
53
53
  options: [
54
- { label: "UX / user-facing surface is unclear", description: "You know what you want to build but not how it should work for the user." },
55
- { label: "Scope / boundaries are unclear", description: "You're not sure how much to build, or where one feature ends and another begins." },
56
- { label: "Multiple approaches exist need to pick one", description: "You see 2-3 ways to solve this and aren't sure which is best." },
54
+ { label: "The user-facing surface", description: "You know what to build but not how it should work for the user." },
55
+ { label: "Scope / boundaries", description: "You're not sure how much to build, or where one feature ends and another begins." },
56
+ { label: "Several approaches — pick one", description: "You see 2-3 ways to solve this and aren't sure which is best." },
57
57
  { label: "Other / describe below", description: "Free-form: describe the ambiguity in the next prompt." }
58
58
  ],
59
59
  multiSelect: false
@@ -98,9 +98,9 @@ Example round (adapt to the actual feature):
98
98
  AskUserQuestion({
99
99
  questions: [{
100
100
  question: "Who is the primary user of this feature, and how do they trigger it?",
101
- header: "User Surface — Round 2",
101
+ header: "User surface",
102
102
  options: [
103
- { label: "Authenticated user via UI action (Recommended)", description: "Pro: fits existing session model. Con: requires UI component work." },
103
+ { label: "Signed-in user, UI action (Recommended)", description: "Pro: fits existing session model. Con: requires UI component work." },
104
104
  { label: "Automated trigger (webhook, cron, event)", description: "Pro: no manual user step. Con: harder to debug and test." },
105
105
  { label: "Admin-only operation", description: "Pro: simpler access control. Con: limits who can self-serve." },
106
106
  { label: "Other / describe below", description: "Describe the trigger mechanism." }
@@ -128,12 +128,12 @@ Once the dialogue has enough signal, synthesize 2-3 concrete implementation appr
128
128
  ```
129
129
  AskUserQuestion({
130
130
  questions: [{
131
- question: "Based on your answers, here are the viable approaches. Which fits best?",
132
- header: "Design Approach",
131
+ question: "Which of these approaches fits best?",
132
+ header: "Approach",
133
133
  options: [
134
- { label: "Approach A — [1-sentence summary] (Recommended)", description: "Trade-offs: [key pro]. [key con]. Complexity: low/medium/high." },
135
- { label: "Approach B — [1-sentence summary]", description: "Trade-offs: [key pro]. [key con]. Complexity: low/medium/high." },
136
- { label: "Approach C — [1-sentence summary]", description: "Trade-offs: [key pro]. [key con]. Complexity: low/medium/high." }
134
+ { label: "A — [short summary] (Recommended)", description: "Best balance of value, reversibility and scope, because [key pro]. Cost: [key con]. Complexity: low/medium/high." },
135
+ { label: "B — [short summary]", description: "Trade-offs: [key pro]. Cost: [key con]. Complexity: low/medium/high." },
136
+ { label: "C — [short summary]", description: "Trade-offs: [key pro]. Cost: [key con]. Complexity: low/medium/high." }
137
137
  ],
138
138
  multiSelect: false
139
139
  }]
@@ -231,13 +231,13 @@ Present the spec path and summary to the user. Then ask via AUQ:
231
231
  ```
232
232
  AskUserQuestion({
233
233
  questions: [{
234
- question: "The design spec has been written to docs/specs/YYYY-MM-DD-<slug>-design.md. How do you want to proceed?",
235
- header: "Design Hand-off",
234
+ question: "The spec is at docs/specs/YYYY-MM-DD-<slug>-design.md. How do you want to proceed?",
235
+ header: "Hand-off",
236
236
  options: [
237
- { label: "Proceed to /plan feature (Recommended)", description: "Formalize this spec into a PRD with acceptance criteria and issue creation." },
238
- { label: "Proceed to /write-executable-plan", description: "Skip the formal PRD and go straight to an executable plan (issue #39, once shipped)." },
239
- { label: "Revise the spec", description: "I have feedback describe what to change and I'll update the spec." },
240
- { label: "Done for now", description: "Keep the spec as a reference; no immediate next step." }
237
+ { label: "Proceed to /plan feature (Recommended)", description: "Turns the spec into a PRD with acceptance criteria, then files the issues. Cost: one more question round." },
238
+ { label: "Proceed to /write-executable-plan", description: "Skips the formal PRD and writes a step-by-step executable plan instead — tracked in #39 (a numbered ticket, not yet released)." },
239
+ { label: "Revise the spec", description: "Describe what to change; the spec is updated and comes back here." },
240
+ { label: "Done for now", description: "The spec stays on disk as a reference. Nothing else runs." }
241
241
  ],
242
242
  multiSelect: false
243
243
  }]
@@ -62,6 +62,18 @@ The active level is `efficiency.output-level` in `~/.config/session-orchestrator
62
62
  - Shape: explain WHY each option is on the table, name the paths you already discarded and why, define unfamiliar terms on first use.
63
63
  - Escalation: `expand <topic>` — see § Escalation above.
64
64
 
65
+ ### Register — how a sentence reads
66
+
67
+ The budgets above set *how much* you say; the register sets *how*. It is
68
+ defined once, in `skills/session-start/soul.md` § "Register — how a sentence
69
+ reads", and binds here unchanged: the frame ("write for someone who knows this
70
+ project but has not seen what you just saw"), the plain-words test with its
71
+ five worked cases, and its precedence over § "Never traded for brevity" above.
72
+ Read it there. It is not repeated here on purpose — the § Output Levels intro
73
+ sentence already exists in four copies across the four souls with nothing
74
+ checking their parity, and a fifth copied rule would drift the same way. A
75
+ pointer cannot.
76
+
65
77
  ### Companion dials
66
78
 
67
79
  Same file, same lookup, same fallback-to-default rule:
@@ -910,11 +910,18 @@ function main() {
910
910
  // kebab(s) = s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')
911
911
  //
912
912
  // WARN (never error) when:
913
- // - learnings.jsonl is present AND no entry's derived key matches the rule's
914
- // `learning-key` (absent learning), OR
913
+ // - NEITHER a learnings.jsonl entry NOR a valid `evidence-digest` frontmatter
914
+ // scalar is present (the rule's provenance is unresolvable), OR
915
915
  // - the matching entry's `expires_at` < now (expired learning).
916
- // When learnings.jsonl is absent, every key counts as absent — warn on each
917
- // generated rule's key.
916
+ //
917
+ // #1101 the digest branch. `.orchestrator/metrics/learnings.jsonl` is
918
+ // GITIGNORED, so in a fresh clone it does not exist and every generated rule
919
+ // used to warn (measured 2026-08-26: 23 of 23). A rule carrying a valid
920
+ // `evidence-digest: sha256-v1:<64 hex>` is SELF-CONTAINED — its `## Evidence`
921
+ // block plus its own `## Provenance` header fields re-derive the seal with no
922
+ // jsonl lookup — so its provenance is resolvable offline and it must not warn.
923
+ // The digest is EVIDENCE, not a second pointer; see
924
+ // `scripts/lib/reconcile/renderer.mjs` § computeEvidenceDigest.
918
925
  // The check is silently skipped (no id pushed) when .claude/rules/ is absent
919
926
  // or contains no .md files with auto-generated: true.
920
927
  if (!args.skipGeneratedRuleStaleness) {
@@ -926,15 +933,17 @@ function main() {
926
933
  // Reads the opening --- ... --- block from a markdown file.
927
934
  function extractFrontmatterFields(mdContent) {
928
935
  const m = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(stripLeadingRuleHeaderLines(mdContent));
929
- if (!m) return { autoGenerated: false, learningKey: null, expiresAt: null };
936
+ if (!m) return { autoGenerated: false, learningKey: null, expiresAt: null, evidenceDigest: null };
930
937
  const block = m[1];
931
938
  const autoGenM = /^auto-generated:\s*(.+)$/m.exec(block);
932
939
  const learningKeyM = /^learning-key:\s*(.+)$/m.exec(block);
933
940
  const expiresAtM = /^expires-at:\s*(.+)$/m.exec(block);
941
+ const evidenceDigestM = /^evidence-digest:\s*(.+)$/m.exec(block);
934
942
  return {
935
943
  autoGenerated: autoGenM ? autoGenM[1].trim() === 'true' : false,
936
944
  learningKey: learningKeyM ? learningKeyM[1].trim() : null,
937
945
  expiresAt: expiresAtM ? expiresAtM[1].trim() : null,
946
+ evidenceDigest: evidenceDigestM ? evidenceDigestM[1].trim() : null,
938
947
  };
939
948
  }
940
949
 
@@ -955,12 +964,20 @@ function main() {
955
964
  relPath: relative(vaultDir, absPath),
956
965
  learningKey: fields.learningKey,
957
966
  expiresAt: fields.expiresAt,
967
+ evidenceDigest: fields.evidenceDigest,
958
968
  });
959
969
  }
960
970
 
961
971
  // No generated rules found → silently skip (don't push the check id).
962
972
  if (generatedRules.length === 0) return;
963
973
 
974
+ // #1101 — a LITERAL COPY of `EVIDENCE_DIGEST_RE` from
975
+ // `scripts/lib/reconcile/renderer.mjs`. Copied, not imported: this checker
976
+ // is a standalone skill script, and importing across the skill/script
977
+ // boundary would add real coupling for one 30-character literal. The two
978
+ // copies are pinned equal by `tests/lib/reconcile/renderer.test.mjs`.
979
+ const EVIDENCE_DIGEST_RE = /^sha256-v1:[0-9a-f]{64}$/;
980
+
964
981
  // Slugify function mirroring emitter.mjs `kebab()`.
965
982
  const kebab = (s) =>
966
983
  String(s)
@@ -1001,26 +1018,37 @@ function main() {
1001
1018
  const nowMs = Date.now();
1002
1019
 
1003
1020
  for (const rule of generatedRules) {
1004
- const { relPath, learningKey, expiresAt } = rule;
1021
+ const { relPath, learningKey, expiresAt, evidenceDigest } = rule;
1005
1022
 
1006
1023
  // If we cannot evaluate the key (no learning-key frontmatter), skip
1007
1024
  // silently — avoid false positives on malformed rules.
1008
1025
  if (!learningKey) continue;
1009
1026
 
1010
- if (!learningsPresent || !knownKeys.has(learningKey)) {
1011
- // Learning is absent from learnings.jsonl (or the file is missing entirely).
1027
+ const hasJsonlEntry = learningsPresent && knownKeys.has(learningKey);
1028
+ const hasValidDigest =
1029
+ typeof evidenceDigest === 'string' && EVIDENCE_DIGEST_RE.test(evidenceDigest);
1030
+
1031
+ if (!hasJsonlEntry && !hasValidDigest) {
1032
+ // NEITHER resolution path is available: the learning is absent from
1033
+ // learnings.jsonl (or the file is missing entirely) AND the rule carries
1034
+ // no self-contained evidence digest, so nothing can vouch for it.
1012
1035
  warnings.push({
1013
1036
  check: 'generated-rule-staleness',
1014
1037
  file: relPath,
1015
1038
  line: 1,
1016
- message: `Auto-generated rule references learning-key '${learningKey}' which is absent from .orchestrator/metrics/learnings.jsonl`,
1039
+ message: `Auto-generated rule references learning-key '${learningKey}' which is absent from .orchestrator/metrics/learnings.jsonl, and the rule carries no valid evidence-digest to verify it offline`,
1017
1040
  extracted: learningKey,
1018
1041
  });
1019
1042
  continue;
1020
1043
  }
1021
1044
 
1022
- // Learning exists — check expiry.
1023
- const storedExpiresAt = knownKeys.get(learningKey);
1045
+ // Provenance resolves — check expiry.
1046
+ // `storedExpiresAt` is only meaningful when the jsonl entry actually
1047
+ // exists. A digest-only rule (fresh clone, or a learning aged out of the
1048
+ // store) is AUTHORITATIVE FOR ITS OWN EXPIRY via frontmatter; reading a
1049
+ // stored value that is not there would make `expiryStr` null and silently
1050
+ // skip the expiry gate for a rule that has a perfectly good `expires-at`.
1051
+ const storedExpiresAt = hasJsonlEntry ? knownKeys.get(learningKey) : null;
1024
1052
  // Prefer the frontmatter expires-at on the rule file; fall back to the
1025
1053
  // stored expires_at from the learning entry (both should agree, but the
1026
1054
  // rule file is authoritative for its own expiry).
@@ -104,13 +104,13 @@ When the gate above is satisfied, present exactly this AskUserQuestion (AUQ-003
104
104
  ```
105
105
  AskUserQuestion({
106
106
  questions: [{
107
- question: "Scope `feature` was requested. How should this run handle feature discovery?",
108
- header: "Feature Scope",
107
+ question: "Scope `feature` was requested. How should this run handle it?",
108
+ header: "Scope",
109
109
  options: [
110
- { label: "Grounded scan (Recommended)", description: "Run the evidence-anchored feature probes (intent-drift, stubbed-dead-feature) on the probe→verify→triage rails." },
111
- { label: "Also judgment topics", description: "Grounded scan PLUS collect judgment-based product questions (opportunity framing, personas). A follow-up prompt after Phase 5 offers inline synthesis or hand-off to /brainstorm or /plan feature — judgment items never enter the verified-findings pipeline." },
112
- { label: "Route out", description: "No scan; hand off to /brainstorm (product ideation) or /grill (assumption stress-test) instead." },
113
- { label: "Skip", description: "Drop `feature` from this run's scope set." }
110
+ { label: "Grounded scan (Recommended)", description: "Every finding is tied to a file and line and is verified before it can become an issue. Cost: two extra probes (intent-drift, stubbed-dead-feature)." },
111
+ { label: "Also judgment topics", description: "Same scan, plus open product questions (opportunity framing, personas) kept as notes. They never become issues; after Phase 5 you pick where they go." },
112
+ { label: "Route out", description: "No scan at all. You get a pointer to /brainstorm (product ideation) or /grill (assumption stress-test) instead." },
113
+ { label: "Skip", description: "Drops `feature` (the probes for half-built and drifted features) from this run; the other scopes still run." }
114
114
  ],
115
115
  multiSelect: false
116
116
  }]
@@ -125,12 +125,12 @@ AskUserQuestion({
125
125
  ```
126
126
  AskUserQuestion({
127
127
  questions: [{
128
- question: "Judgment topics were collected alongside the grounded scan. How should they be handled?",
129
- header: "Judgment Topics",
128
+ question: "Where should the collected judgment topics go?",
129
+ header: "Topics",
130
130
  options: [
131
- { label: "Inline synthesis (Recommended)", description: "Sketch a lightweight OST/persona pass directly in the report's `### Judgment Topics (non-verified)` appendix explicitly marked non-verified, no separate skill invocation needed." },
132
- { label: "Route to /brainstorm", description: "Hand the collected topics off as pre-filled context to /brainstorm for a full Socratic ideation dialogue." },
133
- { label: "Route to /plan feature", description: "Hand the collected topics off as pre-filled context to /plan feature for feature-PRD scoping." }
131
+ { label: "Inline synthesis (Recommended)", description: "Sketches an outcome/persona pass into `### Judgment Topics (non-verified)` (a report section that never becomes issues). Cost: no second run." },
132
+ { label: "Route to /brainstorm", description: "Hands the topics to /brainstorm as its opening context, for a full question-and-answer design dialogue." },
133
+ { label: "Route to /plan feature", description: "Hands the topics to /plan feature as its opening context, for feature-PRD scoping." }
134
134
  ],
135
135
  multiSelect: false
136
136
  }]
@@ -370,14 +370,16 @@ For each Critical or High finding, use AskUserQuestion (on Codex CLI where AskUs
370
370
  ```
371
371
  AskUserQuestion({
372
372
  questions: [{
373
- question: "<finding title>\n\n<file_path>:<line_number>\n```\n<matched_text with +/-3 lines context>\n```\n\n<description>\n\nRecommended fix: <recommended_fix>",
374
- header: "<severity>",
373
+ question: "<severity> finding in <file_path> what should happen with it?",
374
+ header: "Finding",
375
375
  options: [
376
- { label: "Create issue (<severity>)", description: "Create a priority::<severity> issue for this finding" },
377
- { label: "Adjust priority", description: "Create issue with different priority" },
378
- { label: "Dismiss -- intentional", description: "This is by design, skip" },
379
- { label: "Dismiss -- false positive", description: "Detection was wrong, skip" }
380
- ]
376
+ { label: "Create issue (<severity>)", description: "Files it as priority::<severity>, so it is tracked outside this session. The code below is copied into the issue body.",
377
+ preview: "<finding title>\n\n<file_path>:<line_number>\n```\n<matched_text with +/-3 lines context>\n```\n\n<description>\n\nRecommended fix: <recommended_fix>" },
378
+ { label: "Adjust priority", description: "Same issue, a priority you pick — this question then comes back with the new label." },
379
+ { label: "Dismiss -- intentional", description: "The code is deliberate. Nothing is filed, and the finding stays only in this run's report." },
380
+ { label: "Dismiss -- false positive", description: "The probe misread the code. Nothing is filed; worth reporting if the same probe misfires again." }
381
+ ],
382
+ multiSelect: false
381
383
  }]
382
384
  })
383
385
  ```
@@ -391,13 +393,15 @@ Group remaining findings by category. For each category with medium/low findings
391
393
  ```
392
394
  AskUserQuestion({
393
395
  questions: [{
394
- question: "[N] medium/low findings in [category]:\n\n1. [title] -- [file_path]:[line] ([severity])\n2. [title] -- [file_path]:[line] ([severity])\n...",
395
- header: "[Category]",
396
+ question: "Create issues for all [N] medium/low findings in [category]?",
397
+ header: "Findings",
396
398
  options: [
397
- { label: "Accept all (Recommended)", description: "Create issues for all [N] findings" },
398
- { label: "Review individually", description: "Walk through each finding one by one" },
399
- { label: "Dismiss all", description: "Skip all medium/low findings in this category" }
400
- ]
399
+ { label: "Accept all (Recommended)", description: "Medium and low findings are cheap to file and cheap to close. Cost: [N] issues, roughly one second apart.",
400
+ preview: "1. [title] -- [file_path]:[line] ([severity])\n2. [title] -- [file_path]:[line] ([severity])\n..." },
401
+ { label: "Review individually", description: "One question per finding, same options as the critical ones. Cost: [N] more prompts." },
402
+ { label: "Dismiss all", description: "Nothing is filed for this category. The findings stay in this run's report only." }
403
+ ],
404
+ multiSelect: false
401
405
  }]
402
406
  })
403
407
  ```
@@ -113,7 +113,7 @@ const res = claimRepo({ repoRoot: R, sessionId, mode, ttlHours, semanticSessionI
113
113
  Or reuse the primitive directly: `acquire({ sessionId, mode, ttlHours, repoRoot, semanticSessionId })` from `scripts/lib/session-lock.mjs`. The claim is a `linkSync` create-or-fail = **atomic**.
114
114
 
115
115
  - **`ok: true`** → the claim is held. Proceed to Phase 4.
116
- - **`ok: false`** (race lost / busy — reasons: `active`, `stale-pid-alive`, `stale-pid-dead`, `fs-error`, …) → **exclude R**, re-rank the remaining free candidates (drop R from `free`, re-run Phase 1's rank step), and re-present Phase 2. Loop until a claim succeeds or no free candidate remains (then Phase 5).
116
+ - **`ok: false`** (race lost / busy — reasons: `active`, `stale-heartbeat`, `fs-error`, …) → **exclude R**, re-rank the remaining free candidates (drop R from `free`, re-run Phase 1's rank step), and re-present Phase 2. Loop until a claim succeeds or no free candidate remains (then Phase 5).
117
117
 
118
118
  Do NOT reinvent the claim — always go through `claimRepo`/`acquire`. The `ok:false` path is the load-bearing concurrency guard: two parallel dispatchers can both recommend R, but only one wins the `linkSync`; the loser must re-rank, never force.
119
119
 
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: eli5
3
+ description: Explain a topic like I'm a 5 year old — restate my last output, or a named topic, in plain words without dropping a single fact. Use when the user types /eli5 [topic], or says an answer was too technical, too long, or unclear about what he now has to do.
4
+ model: inherit
5
+ tools: Read, Grep, Glob, Bash
6
+ ---
7
+
8
+ # eli5
9
+
10
+ Say it again in plain words. Same facts, in the order he needs them.
11
+
12
+ ## The frame
13
+
14
+ **Write for someone who knows this project but has not seen what you just saw.**
15
+
16
+ He owns this repo. He is not missing knowledge, he is missing observation — he did not watch the command run or read the file it points at. Write across, not down. `.claude/rules/ask-via-tool.md` § AUQ-006 and `skills/session-start/soul.md` § Register are the canonical statement of this register; apply them, do not restate them.
17
+
18
+ ## What to restate
19
+
20
+ - `/eli5` — my last substantial output in this conversation. If there is none yet, say so instead of inventing a topic.
21
+ - `/eli5 <topic>` — that topic (`$ARGUMENTS`), grounded in what THIS session already measured: name the file, command, or record it came from. If it was never measured here, say that rather than filling the gap from memory.
22
+
23
+ Answer in the operator's language: `owner.language` in `~/.config/session-orchestrator/owner.yaml`, falling back to `en` when that file is missing, unreadable, or the key is absent — and follow the operator's own language the moment he writes in another one.
24
+
25
+ ## Two limits, and they are the whole skill
26
+
27
+ **1. Say more simply what actually happens — introduce nothing that does not exist.**
28
+ Test: delete every noun the system does not contain. Sentence still true and complete → it was no analogy. Sentence collapses → describe what actually happens instead.
29
+ ✓ "Waiting means the other session finishes first." ✗ "Think of the session as a level crossing."
30
+
31
+ **2. Simplifying removes words, never facts.** If a path, a number, an error code, an identifier, or an instruction to act disappears, that is data loss, not simplification. The mechanical decider: **could the token you are about to cut ever appear in a `grep`? Then it stays.** `skills/session-start/soul.md` § "Never traded for brevity" outranks every brevity instruction in this file.
32
+
33
+ ## Shape
34
+
35
+ Line 1 answers the question he actually has: **do I have to do something now, and what happens if I don't?** Then the facts, in the order he needs them — not the order you found them.
36
+
37
+ This is a terminal, so there is no picture to draw. The second channel is the `preview` field on an `AskUserQuestion` option: when the options differ in something literal — a diff, a title, a config block, a file list — put that text there and he reads the thing instead of a description of it.
38
+
39
+ ## What this is not
40
+
41
+ - **Not shorter by default.** Eight lines before may be eight lines after. Reorder first; cut only filler.
42
+ - **Not a children's explanation.** The label is a poster, the reader is an expert who was not in the room.
43
+ - **Not a second attempt at the answer.** If the first answer was wrong, fix the answer — `/eli5` restates, it never re-derives.
@@ -248,8 +248,8 @@ Present extracted patterns to the user for confirmation. Use AskUserQuestion wit
248
248
  ```
249
249
  AskUserQuestion({
250
250
  questions: [{
251
- question: "Which learnings should be saved?\n\nExtracted patterns from session history:",
252
- header: "Evolve — Confirm Learnings",
251
+ question: "Which of the patterns extracted from this session's history should be saved?",
252
+ header: "Speichern?",
253
253
  options: [
254
254
  {
255
255
  label: "[type] subject",
@@ -455,19 +455,18 @@ Use AskUserQuestion with options:
455
455
  AskUserQuestion({
456
456
  questions: [{
457
457
  question: "What would you like to do with your learnings?",
458
- header: "Evolve — Review",
458
+ header: "Learnings",
459
459
  options: [
460
- { label: "Boost confidence", description: "Select learnings to boost (+0.15)" },
461
- { label: "Reduce confidence", description: "Select learnings to reduce (-0.2)" },
462
- { label: "Delete specific learnings", description: "Select learnings to remove" },
463
- { label: "Extend expiry", description: "Reset expires_at by learning-expiry-days from now" },
464
- { label: "Done — no changes", description: "Exit without changes" }
460
+ { label: "Confidence ändern", description: "Pick the learnings, then the direction: +0.15 or -0.2. Cheapest fix when a learning is merely mis-weighted." },
461
+ { label: "Ablauf verlängern", description: "Keeps a still-useful learning alive: its expiry date moves to today plus the configured window. Confidence is untouched." },
462
+ { label: "Delete specific learnings", description: "Takes the selected learnings out of the store. They are archived rather than shredded, but they stop influencing anything." },
463
+ { label: "Done — no changes", description: "Leaves the store exactly as it is and ends the review. Nothing is written." }
465
464
  ]
466
465
  }]
467
466
  })
468
467
  ```
469
468
 
470
- If user selects "Boost confidence", "Reduce confidence", "Delete specific learnings", or "Extend expiry", present a follow-up AskUserQuestion with `multiSelect: true` listing all learnings by `# | type | subject` so the user can select which ones to modify.
469
+ If user selects "Confidence ändern", "Ablauf verlängern", or "Delete specific learnings", present a follow-up AskUserQuestion with `multiSelect: true` listing all learnings by `# | type | subject` so the user can select which ones to modify. For "Confidence ändern" the same follow-up also asks for the direction — **Boost** (+0.15) or **Reduce** (-0.2). Both operations are unchanged; only the point at which the direction is chosen moved, because a single AskUserQuestion accepts at most 4 options and the previous list had 5.
471
470
 
472
471
  > On Codex CLI where AskUserQuestion is unavailable, present as a numbered Markdown list.
473
472
 
@@ -9,7 +9,7 @@ model-preference-cursor: claude-sonnet-4-6
9
9
  description: >
10
10
  Use this skill when performing VCS operations on GitLab or GitHub repositories — creating, updating, or
11
11
  closing issues and MRs, applying label taxonomy, running `glab`/`gh` CLI commands, or resolving project
12
- IDs dynamically. Acts as the single source of truth for CLI command syntax and label conventions;
12
+ paths dynamically. Acts as the single source of truth for CLI command syntax and label conventions;
13
13
  consuming skills reference this rather than duplicating logic. Triggers: "create a GitLab issue", "list
14
14
  open MRs", "apply priority label", "how do I resolve the project ID", "what's the carryover issue
15
15
  template". <example>Context: session-end needs to file a carryover issue for an incomplete task. user:
@@ -45,7 +45,7 @@ syntax inline. This skill is the single source of truth for all VCS operations.
45
45
  When a skill needs VCS operations, include this reference block in its instructions:
46
46
 
47
47
  > **VCS Reference:** Detect the VCS platform per the "VCS Auto-Detection" section of the gitlab-ops skill.
48
- > Use CLI commands per the "Common CLI Commands" section. For cross-project queries, see "Dynamic Project Resolution."
48
+ > Use CLI commands per the "Common CLI Commands" section. For GitLab API operations, see "Canonical Project Identity."
49
49
 
50
50
  **Canonical commands:** All `glab` and `gh` command syntax — flags, output formats,
51
51
  pagination options — is defined in the "Common CLI Commands" section below. Consuming
@@ -57,34 +57,35 @@ command variant not listed there, add it to this file first, then reference it.
57
57
  - Any skill-specific *parameters* they pass to commands (e.g., label names, issue templates)
58
58
  - They should NOT include raw `glab`/`gh` invocations or detection snippets
59
59
 
60
- ## Dynamic Project Resolution
60
+ ## Canonical Project Identity
61
61
 
62
- Never hardcode project IDs. Resolve them at runtime and re-resolve live each session; never cache a project ID across sessions (a stale ID silently targets the wrong project on rename/fork/mirror-drift, and is the root cause behind the close-verification incident documented below).
62
+ GitLab REST endpoints accept a URL-encoded `namespace/project` path. Select the GitLab host and project path explicitly; never derive a numeric project ID from `glab repo view`, search `projects?search=`, or use `:id` placeholders. Those forms can resolve through the ambient working directory or a stale search result and target another project after a rename, fork, or scaffold.
63
63
 
64
- ### Current project
64
+ Set the identity once per operation sequence and reuse the encoded identifier without encoding it again:
65
65
 
66
66
  ```bash
67
- # GitLab — get numeric project ID
68
- glab repo view -R <OWNER>/<REPO> --output json | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])"
67
+ GITLAB_HOST="<selected GitLab hostname>"
68
+ GROUP_PATH="<selected group path>"
69
+ PROJECT_NAME="<selected project name>"
70
+ PROJECT_PATH="$GROUP_PATH/$PROJECT_NAME"
71
+ ENCODED_PROJECT_PATH="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1]))' "$PROJECT_PATH")"
72
+ ```
69
73
 
70
- # GitHub get owner/name identifier (gh repo takes the repo POSITIONALLY; it rejects -R)
71
- gh repo view --json nameWithOwner -q '.nameWithOwner'
74
+ For a link target in another project, use the same path-first shape instead of a numeric ID:
75
+
76
+ ```bash
77
+ TARGET_PROJECT_PATH="<target namespace>/<target project>"
78
+ TARGET_ENCODED_PROJECT_PATH="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1]))' "$TARGET_PROJECT_PATH")"
72
79
  ```
73
80
 
74
- ### Cross-project queries
81
+ Pass `--hostname "$GITLAB_HOST"` to every `glab api` call. The endpoint itself then pins the project, including directly after creating a repository when the current directory does not yet identify the new project.
75
82
 
76
- When a skill needs to reference other projects (e.g., from `cross-repos` in Session Config):
83
+ GitHub continues to use an `owner/repo` slug; `gh repo` takes it positionally and rejects `-R`:
77
84
 
78
85
  ```bash
79
- # GitLab resolve project ID by name
80
- glab api "projects?search=<project-name>" | python3 -c "import json,sys; [print(p['id'], p['path_with_namespace']) for p in json.load(sys.stdin)]"
81
-
82
- # GitHub — resolve repo details
83
- gh api "repos/<owner>/<name>" --jq '.full_name'
86
+ gh repo view --json nameWithOwner -q '.nameWithOwner'
84
87
  ```
85
88
 
86
- **Note:** Some API calls require numeric project IDs (GitLab) or `owner/repo` slugs (GitHub). Always resolve dynamically from the project name.
87
-
88
89
  ### Canonical enumeration pattern
89
90
 
90
91
  To enumerate ALL projects (or issues) in a group, a single page is never the whole result — paginate and guard against silent truncation:
@@ -93,7 +94,7 @@ To enumerate ALL projects (or issues) in a group, a single page is never the who
93
94
  # GitLab — paginate a group's projects, following x-next-page until empty
94
95
  page=1
95
96
  while [ -n "$page" ]; do
96
- resp=$(glab api "groups/<group-id>/projects?include_subgroups=true&per_page=100&page=$page" --include)
97
+ resp=$(glab api --hostname "$GITLAB_HOST" "groups/<group-id>/projects?simple=true&include_subgroups=true&per_page=100&page=$page" --include)
97
98
  # parse the response body ($resp) for project ids/paths here, deduping by id.
98
99
  # Then advance by reading the `x-next-page` response header — an empty value
99
100
  # means this was the last page, so the loop exits (the guard above is what breaks).
@@ -146,13 +147,16 @@ done
146
147
 
147
148
  ## Issue Linking (`blocks` / `is_blocked_by`)
148
149
 
149
- GitLab's native issue-link types `blocks` and `is_blocked_by` (`glab api -X POST projects/:id/issues/:issue_iid/links -f link_type=blocks|is_blocked_by`) are a **Premium/Ultimate license feature**. On a Free/Core-tier GitLab instance this call returns **HTTP 403** — a license-gate signal, not an auth/permission failure. Do not retry with different credentials or escalate as an auth bug.
150
+ GitLab's native issue-link types `blocks` and `is_blocked_by` (`glab api --silent --hostname "$GITLAB_HOST" -X POST "projects/${ENCODED_PROJECT_PATH}/issues/${ISSUE_IID}/links" -f target_project_id="$TARGET_ENCODED_PROJECT_PATH" -f target_issue_iid="$OTHER_ISSUE_IID" -f link_type="$LINK_TYPE"`) are a **Premium/Ultimate license feature**. Set `LINK_TYPE` to `blocks` or `is_blocked_by`; the target accepts an encoded project path, so no numeric project ID is needed. On a Free/Core-tier GitLab instance this call returns **HTTP 403** — a license-gate signal, not an auth/permission failure. Do not retry with different credentials or escalate as an auth bug.
150
151
 
151
152
  **Fallback (non-Premium instances):**
152
153
  1. **Use `relates_to` instead** — `link_type=relates_to` is available on every GitLab tier (no ordering semantics, just an unscoped relation). Same API shape, only the `link_type` value changes:
153
154
  ```bash
154
- glab api -X POST "projects/:id/issues/:issue_iid/links" \
155
- -f target_project_id=:id -f target_issue_iid=:other_iid -f link_type=relates_to
155
+ glab api --silent --hostname "$GITLAB_HOST" -X POST \
156
+ "projects/${ENCODED_PROJECT_PATH}/issues/${ISSUE_IID}/links" \
157
+ -f target_project_id="$TARGET_ENCODED_PROJECT_PATH" \
158
+ -f target_issue_iid="$OTHER_ISSUE_IID" \
159
+ -f link_type=relates_to
156
160
  ```
157
161
  2. **Document the blocking semantics in the issue body** — since `relates_to` carries no ordering meaning, add an explicit ordering note to both issues, e.g. `⚠ Ordering: erst #<blocker_iid>, dann dieses Issue — blocks-Link nicht verfügbar (non-Premium)`.
158
162
  3. **Recognize the 403 as a license signal, not an auth error** — before assuming a token/scope problem, try `relates_to` on the same project pair: if `relates_to` succeeds where `blocks`/`is_blocked_by` 403s, the license gate — not authentication — is the cause.
@@ -187,14 +191,14 @@ glab mr merge -R <OWNER>/<REPO> <MR_IID> # Mer
187
191
  glab pipeline list -R <OWNER>/<REPO> --per-page 5 # Recent pipelines
188
192
  glab pipeline status -R <OWNER>/<REPO> <ID> # Pipeline details
189
193
 
190
- # API (no --repo exists here — the endpoint path IS the target; pin the host with --hostname)
191
- glab api "projects/$(glab repo view -R <OWNER>/<REPO> --output json | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")/issues?state=opened&per_page=50"
192
- glab api "projects/$(glab repo view -R <OWNER>/<REPO> --output json | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")/milestones?state=active"
194
+ # API (no --repo exists here — the encoded endpoint and explicit host identify the target)
195
+ glab api --hostname "$GITLAB_HOST" "projects/${ENCODED_PROJECT_PATH}/issues?state=opened&per_page=50"
196
+ glab api --hostname "$GITLAB_HOST" "projects/${ENCODED_PROJECT_PATH}/milestones?state=active"
193
197
  ```
194
198
 
195
199
  **Label update caveat (PUT-replaces, not additive):** `glab issue update --label` (and the underlying GitLab labels API) PUT-REPLACES the entire label set — it does not add to the existing set. To change a single label you must pass the FULL desired label list, or use the dedicated add/remove operations, which are themselves unreliable across `glab` versions. Preferred safe pattern: use `--label` (adds) together with `--unlabel` (removes) on `glab issue update` when your installed `glab` version supports both; otherwise read the current labels first, compute the full new set, and PUT once. The same PUT-replace semantics apply to `glab mr update --label`.
196
200
 
197
- **Close verification:** after `glab issue close <IID>`, always verify the close actually landed — re-read the issue (`glab issue view <IID>`) and confirm `state: closed` in the output. A stale/wrong project ID or a silent 404 can report local success while closing nothing; a documented incident closed 32 issues into the void this way (project ID pointed at the wrong project see "Dynamic Project Resolution" above for the re-resolve-each-session rule that prevents it).
201
+ **Close verification:** after `glab issue close <IID>`, always verify the close actually landed — re-read the issue (`glab issue view <IID>`) and confirm `state: closed` in the output. A stale or wrong project path, or a silent 404, can report local success while closing nothing; use the canonical project identity above for API operations rather than resolving a numeric ID.
198
202
 
199
203
  **Commit-body close-keyword footgun:** GitLab (and GitHub) auto-close an issue when a commit pushed to the default branch contains a close keyword — `close`/`closes`/`closed`/`fix`/`fixes`/`fixed`/`resolve`/`resolves`/`resolved` — followed by `#N` ANYWHERE in the commit body, not just the subject line. This fires even inside a negation ("does NOT close #N") — the platform pattern-matches the keyword + issue reference; it does not parse English negation, so the negation offers no protection. Rule: when a commit body needs to MENTION an issue without closing intent, always use a non-closing reference — `refs #N`, `part of #N`, `siehe #N` — never a close-keyword verb next to the number, negated or not.
200
204
 
@@ -66,7 +66,7 @@ Walk the decision tree **one question at a time**. For each branch, in order:
66
66
  AskUserQuestion({
67
67
  questions: [{
68
68
  question: "Your code cancels whole Orders, but the PRD says a customer can cancel one line item. Which is the real model?",
69
- header: "Cancellation Scope",
69
+ header: "Cancel scope",
70
70
  options: [
71
71
  { label: "Line-item cancellation (Recommended)", description: "Matches the PRD intent. Cost: new partial-refund path + Order stays open after one item is voided." },
72
72
  { label: "Whole-order only", description: "Matches today's code. Cost: contradicts the stated user story — re-scope the PRD." },
@@ -118,12 +118,12 @@ Per the user's configured behaviour, the grill ends with a hand-off and an OPTIO
118
118
  AskUserQuestion({
119
119
  questions: [{
120
120
  question: "Grill complete. How do you want to proceed?",
121
- header: "Grill Hand-off",
121
+ header: "Hand-off",
122
122
  options: [
123
- { label: "Write grill summary + hand off to /plan feature (Recommended)", description: "Persist resolved decisions to docs/specs/, then formalize into a PRD." },
124
- { label: "Write grill summary only", description: "Keep the resolved decisions as a reference; no further step now." },
125
- { label: "Hand off to /plan feature — no file", description: "Carry the resolved decisions straight into planning; nothing persisted." },
126
- { label: "Done — no file, no hand-off", description: "The grilling itself was the value; leave no artifact." }
123
+ { label: "Summary + /plan feature (Recommended)", description: "Writes the resolved decisions to docs/specs/ (a folder of markdown notes), then hands them to planning. Cost: one file." },
124
+ { label: "Write grill summary only", description: "Same file, no planning step. The decisions keep, so you can hand them off later." },
125
+ { label: "Hand off to /plan feature — no file", description: "Carries the decisions straight into planning. They then live only in this conversation." },
126
+ { label: "Done — no file, no hand-off", description: "The grilling itself was the value. Nothing is written, nothing is handed on." }
127
127
  ],
128
128
  multiSelect: false
129
129
  }]
@@ -76,6 +76,22 @@ This is the tightest of the orchestrator's budgets by design. A turn is structur
76
76
  - Shape: explain which tactic you are applying and why it bites here, name the branches of the decision tree you are deferring, define unfamiliar terms on first use.
77
77
  - Escalation: `expand <topic>` — see § Escalation above.
78
78
 
79
+ ### Register — how a sentence reads
80
+
81
+ The budgets above set *how much* you say; the register sets *how*. It is
82
+ defined once, in `skills/session-start/soul.md` § "Register — how a sentence
83
+ reads", and binds here unchanged: the frame ("write for someone who knows this
84
+ project but has not seen what you just saw"), the plain-words test with its
85
+ five worked cases, and its precedence over § "Never traded for brevity" above.
86
+ Read it there. It is not repeated here on purpose — the § Output Levels intro
87
+ sentence already exists in four copies across the four souls with nothing
88
+ checking their parity, and a fifth copied rule would drift the same way. A
89
+ pointer cannot.
90
+
91
+ Note the one place register and adversarial posture meet: a challenge is
92
+ plainer, never softer. "Say it more simply" never becomes "say it less
93
+ sharply" — the contradiction still lands, in shorter words.
94
+
79
95
  ### Companion dials
80
96
 
81
97
  Same file, same lookup, same fallback-to-default rule: