session-orchestrator 3.21.0 → 3.22.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 (117) 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/rules/000-session-orchestrator.mdc +3 -2
  5. package/.cursor/rules/040-discovery.mdc +6 -6
  6. package/.cursor/rules/050-plan.mdc +8 -8
  7. package/CHANGELOG.md +101 -0
  8. package/README.md +10 -10
  9. package/agents/memory-proposal-collector.md +6 -4
  10. package/commands/eli5.md +33 -0
  11. package/commands/release.md +5 -3
  12. package/commands/test.md +2 -2
  13. package/docs/components.md +6 -5
  14. package/docs/scope-collision-guard.md +3 -3
  15. package/docs/session-config-reference.md +31 -8
  16. package/hooks/_lib/lock-bootstrap.mjs +19 -13
  17. package/hooks/hooks-codex.json +1 -1
  18. package/hooks/hooks.json +11 -1
  19. package/hooks/on-session-end.mjs +24 -92
  20. package/hooks/on-session-start.mjs +195 -104
  21. package/hooks/pre-auq-clarity.mjs +787 -0
  22. package/hooks/pre-bash-issue-budget.mjs +17 -18
  23. package/package.json +3 -1
  24. package/pi/prompts/eli5.md +12 -0
  25. package/scripts/auq-audit.mjs +825 -0
  26. package/scripts/autopilot.mjs +7 -8
  27. package/scripts/lib/auq/clarity.mjs +1314 -0
  28. package/scripts/lib/auq/parse.mjs +1006 -0
  29. package/scripts/lib/auq/schema.mjs +1457 -0
  30. package/scripts/lib/ci-status-banner.mjs +63 -57
  31. package/scripts/lib/config/dispatcher-autonomy-capture.mjs +32 -9
  32. package/scripts/lib/config/vault-integration.mjs +12 -1
  33. package/scripts/lib/dispatcher/rank.mjs +4 -7
  34. package/scripts/lib/gates/gate-full.mjs +3 -3
  35. package/scripts/lib/gates/gate-helpers.mjs +17 -6
  36. package/scripts/lib/io.mjs +239 -0
  37. package/scripts/lib/issue-budget.mjs +63 -9
  38. package/scripts/lib/owner-interview.mjs +78 -32
  39. package/scripts/lib/peer-discovery.mjs +73 -22
  40. package/scripts/lib/project-hygiene.mjs +64 -4
  41. package/scripts/lib/reconcile/renderer.mjs +17 -4
  42. package/scripts/lib/resource-probe/evaluate.mjs +330 -149
  43. package/scripts/lib/resource-probe/probe-platform.mjs +35 -0
  44. package/scripts/lib/resource-probe.mjs +18 -2
  45. package/scripts/lib/spiral-carryover.mjs +23 -2
  46. package/scripts/lib/state-md/mission-status.mjs +147 -50
  47. package/scripts/lib/validate/check-auq-clarity.mjs +274 -0
  48. package/scripts/lib/validate/check-hooks-symmetry.mjs +30 -0
  49. package/scripts/lib/validate/check-rules.mjs +153 -9
  50. package/scripts/lib/vault-backfill/glab.mjs +91 -58
  51. package/scripts/lib/vault-backfill/manifest.mjs +28 -8
  52. package/scripts/lib/vcs-repo-spec.mjs +182 -13
  53. package/scripts/lib/wave-resource-gate.mjs +67 -73
  54. package/scripts/materialize-wave-scope.mjs +281 -0
  55. package/scripts/release.mjs +443 -122
  56. package/scripts/run-quality-gate.mjs +14 -0
  57. package/scripts/validate-plugin.mjs +3 -0
  58. package/scripts/validate-wave-scope.mjs +6 -1
  59. package/scripts/vault-backfill.mjs +32 -5
  60. package/skills/_shared/parallel-aware-auq.md +30 -24
  61. package/skills/_shared/parallel-aware-preamble.md +31 -2
  62. package/skills/_shared/state-ownership.md +32 -6
  63. package/skills/bootstrap/SKILL.md +2 -1
  64. package/skills/brainstorm/SKILL.md +18 -18
  65. package/skills/brainstorm/soul.md +12 -0
  66. package/skills/discovery/SKILL.md +28 -24
  67. package/skills/eli5/SKILL.md +43 -0
  68. package/skills/evolve/SKILL.md +8 -9
  69. package/skills/gitlab-ops/SKILL.md +30 -26
  70. package/skills/grill/SKILL.md +6 -6
  71. package/skills/grill/soul.md +16 -0
  72. package/skills/memory-cleanup/SKILL.md +2 -2
  73. package/skills/npm-publish/SKILL.md +4 -4
  74. package/skills/peekaboo-driver/SKILL.md +3 -3
  75. package/skills/plan/SKILL.md +18 -16
  76. package/skills/plan/mode-feature.md +1 -1
  77. package/skills/plan/mode-new.md +35 -23
  78. package/skills/plan/soul.md +12 -0
  79. package/skills/reconcile/SKILL.md +3 -3
  80. package/skills/session-end/SKILL.md +53 -20
  81. package/skills/session-end/phase-3-6-tail.md +37 -2
  82. package/skills/session-start/SKILL.md +69 -35
  83. package/skills/session-start/phase-2-5-docs-planning.md +8 -8
  84. package/skills/session-start/phase-4-5-resource-health.md +82 -19
  85. package/skills/session-start/soul.md +110 -0
  86. package/skills/test-runner/SKILL.md +2 -2
  87. package/skills/using-orchestrator/SKILL.md +1 -1
  88. package/skills/wave-executor/wave-loop.md +27 -5
  89. package/skills/write-executable-plan/SKILL.md +6 -6
  90. package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +0 -8
  91. package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +0 -8
  92. package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
  93. package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +0 -8
  94. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
  95. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +0 -8
  96. package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +0 -8
  97. package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +0 -8
  98. package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +0 -10
  99. package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +0 -8
  100. package/skills/vault-sync/tests/fixtures/clean-vault/README.md +0 -3
  101. package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +0 -11
  102. package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
  103. package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +0 -9
  104. package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +0 -8
  105. package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
  106. package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
  107. package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +0 -7
  108. package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +0 -9
  109. package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
  110. package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +0 -11
  111. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +0 -3
  112. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +0 -3
  113. package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
  114. package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +0 -11
  115. package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
  116. package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +0 -11
  117. package/skills/vault-sync/tests/schema-drift.test.mjs +0 -133
@@ -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:
@@ -246,8 +246,8 @@ const peerWarning = wt.activePeer
246
246
 
247
247
  AskUserQuestion({
248
248
  questions: [{
249
- question: `Stale auto-promoted worktree found: ${path.basename(wt.wtPath)} (age ${ageDays}d, branch=${wt.branch}).${peerWarning} Remove?`,
250
- header: "Stale-Worktree",
249
+ question: `Auto-promoted worktree ${path.basename(wt.wtPath)} is ${ageDays}d old (branch ${wt.branch}).${peerWarning} Remove?`,
250
+ header: "Worktree",
251
251
  multiSelect: false,
252
252
  options: [
253
253
  {
@@ -7,7 +7,7 @@ description: Use when publishing this package to npm — a version release (npm
7
7
 
8
8
  # npm-publish — token auth, and the calls the script cannot make
9
9
 
10
- > **The release itself is `/release` → `scripts/release.mjs`.** That script mechanizes the whole sequence: version surfaces, CHANGELOG gate, drift sweep, tag/registry collision, CI, leakage gate, publish, tag-after-publish, push to both remotes, live-site poll. This skill does not restate it.
10
+ > **The release itself is `/release` → `scripts/release.mjs`.** That script mechanizes the whole sequence: version surfaces, CHANGELOG gate, drift sweep, tag/registry collision, CI, leakage gate, publish, the target-confirmed npm receipt boundary, tag-after-receipt, push to both remotes, GitHub-release handling, and live-site poll. This skill does not restate it.
11
11
  >
12
12
  > What lives here is the half a script cannot own: the **token setup**, the **auth failure diagnosis**, and the **judgement calls** — which version, what a leak means, when to abort rather than repair.
13
13
 
@@ -39,7 +39,7 @@ The script gates mechanics. These three are yours, and it will not make them for
39
39
 
40
40
  **2. What a leak means when one is found.** A hit from the leakage gate is not a pattern to silence. Decide which of two it is: a real leak (fix `package.json` `files`, re-pack, re-check) or genuine over-matching (fix `LEAKAGE_PATTERNS` in `scripts/release.mjs` **with a test**). There is no third option, and neither is "publish anyway and clean it up in the next version" — an npm publish is not revocable, and unpublishing burns the version number permanently. Operator handling detail: `docs/distribution/npm-publish-checklist.md` § 3.
41
41
 
42
- **3. When to abort instead of repair.** Abort — do not patch forward — when the failure is upstream of the publish: a red preflight row, a lagging `github` mirror, CI not green on the exact commit, a dead token. These are cheap to fix and re-run from the top. Repair-in-place is only ever appropriate *after* a verified publish, where the version is already immutable: a missing GitHub release or a lagging site deploy can be reconciled, because npm already has the correct artifact. The dividing line is whether the registry has accepted the tarball before that point, restart; after it, reconcile. `commands/release.md` § Abort criteria is the operative list.
42
+ **3. When to abort instead of repair.** Abort — do not patch forward — when the failure is upstream of the target-confirmed npm receipt: a red preflight row, a lagging `github` mirror, CI not green on the exact commit, a dead token, or a publish that did not issue the target receipt. These are cheap to fix and re-run from the top. Repair-in-place is only appropriate *after* that receipt, where the version is already immutable: registry propagation, a missing GitHub release, or a lagging site deploy can be reconciled because npm already has the correct artifact. When `--publish` reports **Post-publish reconciliation required**, **do not rerun `--publish`**; repair the listed state directly. `commands/release.md` § Abort criteria is the operative list.
43
43
 
44
44
  ## Failure-mode table
45
45
 
@@ -48,13 +48,13 @@ The script gates mechanics. These three are yours, and it will not make them for
48
48
  | `E403 ... Two-factor authentication or granular access token with bypass 2fa enabled is required` — no OTP prompt | Account has no 2FA enrolled AND token (if any) lacks Bypass-2FA | Create granular token with all four requirements above, or enroll 2FA |
49
49
  | Same E403 despite a fresh token | Token created without the Bypass-2FA checkbox, or Read-only, or package-scoped on a first publish | Re-create: RW + All packages + Bypass-2FA |
50
50
  | `npm whoami` silent or non-zero | Token expired, or `.env.local` missing | Re-create the token; do not proceed — the preflight fails this row on purpose |
51
- | `E404` on `npm view` after publish | Registry propagation (rare, seconds) or publish actually failed | Re-check the publish output for `+ <name>@<version>` |
51
+ | `E404` on `npm view` after a target-confirmed publish receipt | Registry propagation (rare, seconds) | Let the script finish its tag/push/GitHub/site tail, then reconcile the registry result; do **not** rerun `--publish` |
52
52
  | `ENEEDAUTH` | No login/token at all | Token flow above, or `npm login` |
53
53
  | OTP prompt appears but flow is non-interactive (`!`-prefix, script) | No TTY for the prompt | Use the token flow, or a real terminal |
54
54
 
55
55
  ## Post-publish — the human half
56
56
 
57
- `--publish` verifies the registry and polls the live site itself, and prints the rest. What still needs a person:
57
+ `--publish` attempts registry verification and polls the live site itself. A target-confirmed receipt plus a delayed registry result is a reconciliation outcome, not a failed publish or a retry instruction. What still needs a person:
58
58
 
59
59
  1. **Rotate/delete the token** at https://www.npmjs.com/settings/<user>/tokens. A token that ever transited a conversation, a screenshot, or any log is burned — rotate immediately.
60
60
  2. **pi.dev gallery**: indexing is asynchronous — check https://pi.dev/packages later; do not block on it.
@@ -96,10 +96,10 @@ For each `${PERM_NAME}` in `$MISSING`:
96
96
  ```
97
97
  AskUserQuestion({
98
98
  questions: [{
99
- question: `${PERM_NAME} permission is required but not granted. Open System Settings > Privacy & Security > ${PERM_NAME}, enable the terminal entry, then confirm here.`,
100
- header: `Missing Permission: ${PERM_NAME}`,
99
+ question: `${PERM_NAME} is not granted. Enable the terminal entry under System Settings > Privacy & Security, then confirm.`,
100
+ header: "Zugriff",
101
101
  options: [
102
- { label: "Granted — continue (Recommended)", description: `I have enabled ${PERM_NAME} in System Settings.` },
102
+ { label: "Granted — continue (Recommended)", description: "I enabled the terminal entry in that pane — the driver then checks again and carries on if the grant took effect." },
103
103
  { label: "Skip this run", description: "Abort peekaboo-driver. Test-runner will record a framework-error finding." }
104
104
  ],
105
105
  multiSelect: false
@@ -281,17 +281,17 @@ If any criterion is FAIL:
281
281
  2. Revise the affected PRD sections
282
282
  3. Re-submit to the reviewer
283
283
 
284
- Maximum 3 iterations. After 3 iterations with remaining issues, present them to the user via AskUserQuestion:
284
+ Maximum 3 iterations. After 3 iterations with remaining issues, list the flagged points in plain text (they are context, not a choice), then ask via AskUserQuestion:
285
285
 
286
286
  ```
287
287
  AskUserQuestion({
288
288
  questions: [{
289
- question: "The PRD reviewer flagged these remaining issues after 3 revision rounds:\n\n[list issues]\n\nHow do you want to proceed?",
289
+ question: "The reviewer still flags [N] points after 3 revision rounds. Proceed anyway?",
290
290
  header: "PRD Review",
291
291
  options: [
292
- { label: "Accept as-is (Recommended)", description: "Issues are minor, proceed with current PRD." },
293
- { label: "Manual edit", description: "I'll edit the PRD myself before continuing." },
294
- { label: "Re-run review", description: "Try one more revision round." }
292
+ { label: "Accept as-is (Recommended)", description: "Three rounds did not close them, so a fourth probably will not. The flags then stay in the PRD and travel into the issues filed from it." },
293
+ { label: "Manual edit", description: "You edit the PRD yourself; the flow waits, then re-runs the reviewer on your version." },
294
+ { label: "Re-run review", description: "One more revision round. Cost: another reviewer pass, and the same points may come back unchanged." }
295
295
  ],
296
296
  multiSelect: false
297
297
  }]
@@ -300,16 +300,16 @@ AskUserQuestion({
300
300
 
301
301
  ### 5.3 User Review Gate
302
302
 
303
- After the reviewer passes (or user accepts), present the final PRD:
303
+ After the reviewer passes (or user accepts), present the final PRD in plain text — path plus a short section summary — then ask:
304
304
 
305
305
  ```
306
306
  AskUserQuestion({
307
307
  questions: [{
308
- question: "PRD is ready for your review. It has been saved to [path].\n\nPlease review the document and confirm.",
308
+ question: "Approve the PRD at [path]?",
309
309
  header: "PRD Approval",
310
310
  options: [
311
- { label: "Approve PRD (Recommended)", description: "PRD looks good, proceed to issue creation." },
312
- { label: "Request changes", description: "I have feedback let me describe what to change." }
311
+ { label: "Approve PRD (Recommended)", description: "Nothing further is checked after this: approval commits the PRD to HEAD (Phase 5.5), then issue creation starts." },
312
+ { label: "Request changes", description: "Describe what to change; the PRD is rewritten and comes back here. No limit on rounds." }
313
313
  ],
314
314
  multiSelect: false
315
315
  }]
@@ -395,18 +395,20 @@ Assign labels from the standard taxonomy:
395
395
 
396
396
  ### 6.3 User Review
397
397
 
398
- Present the full issue structure via AskUserQuestion before creating anything:
398
+ Present the full issue structure via AskUserQuestion before creating anything. The table is the text that is about to be filed, so it belongs in `preview` — not in the question:
399
399
 
400
400
  ```
401
401
  AskUserQuestion({
402
402
  questions: [{
403
- question: "Proposed issue structure:\n\n**Epic:** [title]\n\n| # | Sub-Issue | Priority | Labels | Blocked By |\n|---|----------|----------|--------|------------|\n| 1 | [title] | critical | [labels] | — |\n| 2 | [title] | high | [labels] | #1 |\n| ... | ... | ... | ... | ... |\n\nTotal: [N] issues. Confirm or adjust.",
404
- header: "Issue Review",
403
+ question: "Create these [N] issues from the PRD?",
404
+ header: "Issues",
405
405
  options: [
406
- { label: "Create all issues (Recommended)", description: "Proceed with the proposed structure." },
407
- { label: "Adjust priorities", description: "I want to change some priorities before creating." },
408
- { label: "Remove issues", description: "Some issues should not be created." },
409
- { label: "Cancel", description: "Do not create any issues." }
406
+ { label: "Create all [N] (Recommended)",
407
+ description: "Priorities and blocked-by links come straight from the approved PRD. Cost: one API call per issue, ~1s apart.",
408
+ preview: "**Epic:** [title]\n\n| # | Sub-Issue | Priority | Labels | Blocked By |\n|---|----------|----------|--------|------------|\n| 1 | [title] | critical | [labels] | — |\n| 2 | [title] | high | [labels] | #1 |\n| ... | ... | ... | ... | ... |" },
409
+ { label: "Adjust priorities", description: "Same [N] issues, different priority labels. Name them and this question comes back with the table updated." },
410
+ { label: "Remove issues", description: "Name the ones to drop; the rest are created unchanged." },
411
+ { label: "Cancel", description: "Nothing is created. The PRD stays committed, so Phase 6 can run again later." }
410
412
  ],
411
413
  multiSelect: false
412
414
  }]
@@ -127,7 +127,7 @@ Apply per gitlab-ops skill label taxonomy:
127
127
 
128
128
  ### User Review Gate
129
129
 
130
- Present the full issue structure via AskUserQuestion before creation:
130
+ Present the full issue structure via the AskUserQuestion payload in `SKILL.md` § 6.3 — the issue table belongs in the option's `preview` field, not in the question text:
131
131
 
132
132
  - Epic title and description
133
133
  - Each sub-issue: title, priority, labels, dependency links
@@ -38,7 +38,7 @@ Agent({ subagent_type: "Explore", description: "Check ecosystem for conflicts",
38
38
  3. **Target audience** — Options informed by market research agent. User selects or provides custom.
39
39
  4. **User-Story-Schicht** — "User-Story-Schicht für dieses Feature erzeugen?" Immer fragen (kein Audience-Heuristik-Gate). Drei Antwortoptionen: **Ja (Als/möchte/damit)** — klassische Persona-Story-Form; **Ja (job-story)** — job-story-Form ("When [situation], I want [motivation], so I can [outcome]"); **Nein** — byte-identisches Status-quo-Verhalten. Bei einer der beiden "Ja"-Optionen emittiert die PRD eine optionale ## User Stories Sektion (je Story ein ↳ AC-Pointer) in der gewählten Form; bei "Nein" wird die Sektion vollständig weggelassen.
40
40
  5. **Core problem being solved** — Open-ended. Claude suggests structure if answer is vague.
41
- 6. **GitLab group** — Discover available groups dynamically. Run `ls $BASELINE_PATH/templates/` for project types, and check for a groups config in `$BASELINE_PATH/config/` or run `glab api "groups?per_page=100&min_access_level=10"` to discover GitLab groups — read each entry's `full_path` field. (`glab` has no `group` subcommand at all — invoking one exits 1 with `Unknown command "group"`.) Present findings via AskUserQuestion.
41
+ 6. **GitLab group** — Select the GitLab host explicitly, then discover available groups dynamically. Run `ls $BASELINE_PATH/templates/` for project types, and check for a groups config in `$BASELINE_PATH/config/` or run `glab api --hostname "$GITLAB_HOST" "groups?per_page=100&min_access_level=10"` to discover GitLab groups — read each entry's `full_path` field. (`glab` has no `group` subcommand at all — invoking one exits 1 with `Unknown command "group"`.) Present findings via AskUserQuestion.
42
42
 
43
43
  ### Wave 2 — Technical Details (5 questions, dynamic per archetype)
44
44
 
@@ -134,6 +134,12 @@ Map gathered answers to script input choices:
134
134
  # namespace in a separate variable — every later step addresses the project as
135
135
  # "<group-path>/<project>", and a numeric index there silently targets nothing.
136
136
  GROUP_PATH="$(...)" # e.g., "products" — the full_path of the chosen group
137
+
138
+ # These values identify the NEW project, not the directory in which this plan runs.
139
+ # Select the host with the group; do not let glab infer it from an ambient remote.
140
+ GITLAB_HOST="<selected GitLab hostname>"
141
+ PROJECT_PATH="$GROUP_PATH/$PROJECT_NAME"
142
+ ENCODED_PROJECT_PATH="$(node -e 'process.stdout.write(encodeURIComponent(process.argv[1]))' "$PROJECT_PATH")"
137
143
  (
138
144
  echo "$TYPE_CHOICE" # e.g., "1" for nextjs-saas
139
145
  echo "$STYLE_CHOICE" # e.g., "1" for vega (only if nextjs-saas)
@@ -145,10 +151,12 @@ GROUP_PATH="$(...)" # e.g., "products" — the full_path of the chosen group
145
151
 
146
152
  ### Step 2: Verify success
147
153
 
148
- Check exit code. Confirm repo exists:
154
+ Check the setup script exit code. Confirm the selected path exists without fetching a full REST project object:
149
155
 
150
156
  ```bash
151
- glab repo view "$GROUP_PATH/$PROJECT_NAME"
157
+ glab api --hostname "$GITLAB_HOST" graphql \
158
+ -f query='query($fullPath: ID!) { project(fullPath: $fullPath) { fullPath } }' \
159
+ -f fullPath="$PROJECT_PATH" | jq -er '.data.project.fullPath'
152
160
  ```
153
161
 
154
162
  ### Step 3: Adjust visibility
@@ -157,27 +165,29 @@ If visibility is not `internal` (the default):
157
165
 
158
166
  ```bash
159
167
  # There is no `glab repo edit`, and `glab repo update` carries no --visibility
160
- # flag (its FLAGS are --archive/--defaultBranch/-d/--description). Go through the
161
- # API, addressing the project by its URL-encoded path `projects/:id` resolves
162
- # from the CWD remote, which is the wrong project right after scaffolding.
163
- ENCODED="${GROUP_PATH}%2F${PROJECT_NAME}"
164
- glab api -X PUT "projects/${ENCODED}" -f visibility=private # or visibility=public
165
-
166
- # Verify (this GET is the read-only proof the PUT landed). Note `glab api` has
167
- # no --jq flag that is `gh api`'s. Pipe to jq instead.
168
- glab api "projects/${ENCODED}" | jq -r '.visibility'
168
+ # flag (its FLAGS are --archive/--defaultBranch/-d/--description). The encoded
169
+ # endpoint and explicit host target the new project independently of the CWD.
170
+ glab api --silent --hostname "$GITLAB_HOST" -X PUT \
171
+ "projects/${ENCODED_PROJECT_PATH}" \
172
+ -f visibility=private # or visibility=public
173
+
174
+ # Fetch only the scalar needed to verify the mutation, never a full REST object.
175
+ glab api --hostname "$GITLAB_HOST" graphql \
176
+ -f query='query($fullPath: ID!) { project(fullPath: $fullPath) { visibility } }' \
177
+ -f fullPath="$PROJECT_PATH" | jq -er '.data.project.visibility'
169
178
  ```
170
179
 
171
- > The GET path above is verified against glab 1.91.0; the PUT is the documented
172
- > GitLab API shape but was **not** executed during authoring (write operations
173
- > were out of scope). Confirm with the GET before relying on it.
180
+ > The GraphQL verification query is read-only and requests only `visibility`; the
181
+ > PUT's unused response is intentionally suppressed. Confirm the selected host and
182
+ > path before running the mutation.
174
183
 
175
184
  For public/OSS, also configure GitHub mirror if applicable.
176
185
 
177
186
  ### Step 4: Set branch protection
178
187
 
179
188
  ```bash
180
- glab api -X PUT projects/:id/protected_branches \
189
+ glab api --silent --hostname "$GITLAB_HOST" -X POST \
190
+ "projects/${ENCODED_PROJECT_PATH}/protected_branches" \
181
191
  -f name=main \
182
192
  -f push_access_level=30 \
183
193
  -f merge_access_level=30
@@ -282,7 +292,7 @@ Always use the `priority::<level>` format in VCS CLI commands, not P0/P1/P2/P3.
282
292
 
283
293
  ### Step 4: Present for user confirmation
284
294
 
285
- Use AskUserQuestion to present the full issue structure:
295
+ Use the AskUserQuestion payload in `SKILL.md` § 6.3 verbatim — the issue table belongs in the option's `preview` field, not in the question text:
286
296
 
287
297
  - Epic title and description
288
298
  - Sub-issues with: title, priority, labels, dependency links
@@ -292,11 +302,11 @@ Use AskUserQuestion to present the full issue structure:
292
302
 
293
303
  ```bash
294
304
  # Create epic
295
- glab issue create -R "$GROUP_PATH/$PROJECT_NAME" --title "$EPIC_TITLE" --description "$EPIC_DESC" \
305
+ glab issue create -R "https://${GITLAB_HOST}/${PROJECT_PATH}" --title "$EPIC_TITLE" --description "$EPIC_DESC" \
296
306
  --label "type:epic,priority::$PRIORITY" --milestone "$MILESTONE"
297
307
 
298
308
  # Create sub-issues
299
- glab issue create -R "$GROUP_PATH/$PROJECT_NAME" --title "$ISSUE_TITLE" --description "$ISSUE_DESC" \
309
+ glab issue create -R "https://${GITLAB_HOST}/${PROJECT_PATH}" --title "$ISSUE_TITLE" --description "$ISSUE_DESC" \
300
310
  --label "type:feature,priority::$PRIORITY,status:ready,area:$AREA,appetite:$APPETITE"
301
311
  ```
302
312
 
@@ -305,10 +315,12 @@ glab issue create -R "$GROUP_PATH/$PROJECT_NAME" --title "$ISSUE_TITLE" --descri
305
315
  For issues with technical dependencies, set `blocks`/`is-blocked-by` relationships:
306
316
 
307
317
  ```bash
308
- # Issue #2 is blocked by Issue #1
309
- glab api -X POST projects/:id/issues/:issue2_iid/links \
310
- -f target_project_id=:id \
311
- -f target_issue_iid=:issue1_iid \
318
+ # Issue #2 is blocked by Issue #1 in this project. An encoded project path is
319
+ # valid for target_project_id, so no numeric project ID is needed.
320
+ glab api --silent --hostname "$GITLAB_HOST" -X POST \
321
+ "projects/${ENCODED_PROJECT_PATH}/issues/${ISSUE_2_IID}/links" \
322
+ -f target_project_id="$ENCODED_PROJECT_PATH" \
323
+ -f target_issue_iid="$ISSUE_1_IID" \
312
324
  -f link_type=is_blocked_by
313
325
  ```
314
326
 
@@ -69,6 +69,18 @@ The active level is `efficiency.output-level` in `~/.config/session-orchestrator
69
69
  - Shape: name the alternatives you rejected and why, spell out the appetite and the scope cuts, define unfamiliar terms on first use.
70
70
  - Escalation: `expand <topic>` — see § Escalation above.
71
71
 
72
+ ### Register — how a sentence reads
73
+
74
+ The budgets above set *how much* you say; the register sets *how*. It is
75
+ defined once, in `skills/session-start/soul.md` § "Register — how a sentence
76
+ reads", and binds here unchanged: the frame ("write for someone who knows this
77
+ project but has not seen what you just saw"), the plain-words test with its
78
+ five worked cases, and its precedence over § "Never traded for brevity" above.
79
+ Read it there. It is not repeated here on purpose — the § Output Levels intro
80
+ sentence already exists in four copies across the four souls with nothing
81
+ checking their parity, and a fifth copied rule would drift the same way. A
82
+ pointer cannot.
83
+
72
84
  ### Companion dials
73
85
 
74
86
  Same file, same lookup, same fallback-to-default rule:
@@ -243,12 +243,12 @@ For each batch (proposals sliced into groups of 4):
243
243
  ```
244
244
  AskUserQuestion({
245
245
  questions: [{
246
- question: "Which rule proposals should be written to .claude/rules/? (batch K of N)",
247
- header: "Reconcile — Approve Rule Proposals",
246
+ question: "Batch K of N — which rule proposals should be written into .claude/rules/?",
247
+ header: "Regeln",
248
248
  options: [
249
249
  {
250
250
  label: "<slug>.md (confidence: 0.72)",
251
- description: "Learning: <learningKey> | Path: .claude/rules/<slug>.md | <first 100 chars of rendered content>"
251
+ description: "From learning <learningKey>. Becomes a file under .claude/rules/ where this repo keeps its rules. Text: <first 100 chars of rendered content>"
252
252
  },
253
253
  ...up to 4 options per batch...
254
254
  {