session-orchestrator 3.17.0 → 3.19.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 (187) 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/030-wave-execution.mdc +17 -1
  5. package/CHANGELOG.md +105 -412
  6. package/README.md +12 -9
  7. package/SECURITY.md +190 -27
  8. package/agents/AGENTS.md +20 -3
  9. package/agents/code-implementer.md +6 -6
  10. package/agents/db-specialist.md +1 -1
  11. package/agents/qa-strategist.md +31 -6
  12. package/agents/schemas/qa-strategist.schema.json +27 -0
  13. package/agents/schemas/test-writer.schema.json +60 -2
  14. package/agents/security-reviewer.md +1 -1
  15. package/agents/session-reviewer.md +1 -1
  16. package/agents/test-writer.md +29 -10
  17. package/agents/ui-developer.md +1 -1
  18. package/commands/contract-version-bump.md +28 -0
  19. package/commands/portfolio.md +1 -1
  20. package/docs/USER-GUIDE.md +8 -3
  21. package/docs/ci-setup.md +121 -7
  22. package/docs/codex-setup.md +1 -1
  23. package/docs/components.md +6 -6
  24. package/docs/cursor-setup.md +22 -9
  25. package/docs/events-schema.md +5 -1
  26. package/docs/instruction-delivery.md +444 -0
  27. package/docs/rule-authoring.md +58 -9
  28. package/docs/session-config-reference.md +244 -9
  29. package/docs/session-config-template.md +39 -3
  30. package/hooks/_lib/guard-source-loader.mjs +467 -0
  31. package/hooks/_lib/lock-bootstrap.mjs +21 -0
  32. package/hooks/_lib/vcs-create-matcher.mjs +119 -0
  33. package/hooks/config-protection.mjs +0 -0
  34. package/hooks/enforce-commands.mjs +10 -2
  35. package/hooks/hooks-codex.json +1 -1
  36. package/hooks/hooks-cursor.json +11 -2
  37. package/hooks/hooks-pi.json +10 -0
  38. package/hooks/hooks.json +21 -1
  39. package/hooks/on-session-end.mjs +178 -18
  40. package/hooks/on-session-start.mjs +23 -0
  41. package/hooks/post-bash-write-verify.mjs +977 -0
  42. package/hooks/post-subagent-discovery-validator.mjs +256 -41
  43. package/hooks/pre-bash-destructive-guard.mjs +525 -160
  44. package/hooks/pre-bash-issue-budget.mjs +167 -0
  45. package/hooks/pre-bash-sessions-ledger-guard.mjs +627 -0
  46. package/hooks/pre-bash-templates-first.mjs +96 -63
  47. package/hooks/subagent-telemetry.mjs +527 -37
  48. package/package.json +5 -2
  49. package/pi/prompts/contract-version-bump.md +12 -0
  50. package/rules/README.md +32 -0
  51. package/scripts/archive-closed-prds.mjs +12 -22
  52. package/scripts/autopilot-multi.mjs +103 -20
  53. package/scripts/backfill-abandoned-sessions.mjs +160 -4
  54. package/scripts/check-doc-consistency.sh +17 -1
  55. package/scripts/eval-session.mjs +50 -9
  56. package/scripts/fleet-instruction-scan.mjs +141 -0
  57. package/scripts/lib/autopilot/mr-draft.mjs +31 -1
  58. package/scripts/lib/autopilot/worktree-pipeline.mjs +113 -5
  59. package/scripts/lib/backlog-scan.mjs +39 -6
  60. package/scripts/lib/blocked-commands-policy.mjs +340 -0
  61. package/scripts/lib/ci-status-banner.mjs +75 -12
  62. package/scripts/lib/claude-md-budget-lint.mjs +283 -34
  63. package/scripts/lib/command-blocker.mjs +1013 -58
  64. package/scripts/lib/config/config-protection.mjs +2 -1
  65. package/scripts/lib/config/drift-check.mjs +9 -1
  66. package/scripts/lib/config/gitlab-portfolio.mjs +1 -1
  67. package/scripts/lib/config/issue-budget.mjs +123 -0
  68. package/scripts/lib/config/reconcile.mjs +21 -0
  69. package/scripts/lib/config/section-extractor.mjs +121 -1
  70. package/scripts/lib/config-schema.mjs +23 -3
  71. package/scripts/lib/config.mjs +17 -0
  72. package/scripts/lib/convergence-monitor.mjs +49 -3
  73. package/scripts/lib/description-surface.mjs +535 -0
  74. package/scripts/lib/dispatcher/enumerate.mjs +26 -40
  75. package/scripts/lib/ecosystem-wizard/config-writer.mjs +26 -24
  76. package/scripts/lib/ecosystem-wizard/wizard-prompt.mjs +1 -1
  77. package/scripts/lib/eval/engine.mjs +47 -5
  78. package/scripts/lib/events.mjs +59 -7
  79. package/scripts/lib/gates/gate-full.mjs +15 -3
  80. package/scripts/lib/gates/gate-helpers.mjs +132 -6
  81. package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +28 -8
  82. package/scripts/lib/gitlab-portfolio/aggregator.mjs +8 -2
  83. package/scripts/lib/gitlab-portfolio/cli.mjs +1 -1
  84. package/scripts/lib/handover-gate.mjs +7 -3
  85. package/scripts/lib/harness-audit/categories/category4.mjs +9 -3
  86. package/scripts/lib/instruction-budget-guard.mjs +402 -51
  87. package/scripts/lib/io.mjs +345 -10
  88. package/scripts/lib/issue-budget.mjs +269 -0
  89. package/scripts/lib/issue-close-strip-labels.mjs +39 -9
  90. package/scripts/lib/label-scope.mjs +47 -0
  91. package/scripts/lib/learnings/schema.mjs +43 -3
  92. package/scripts/lib/lock-reaper.mjs +1 -2
  93. package/scripts/lib/memory-proposals/schema.mjs +36 -1
  94. package/scripts/lib/peer-discovery.mjs +645 -0
  95. package/scripts/lib/pi-hook-bridge.mjs +146 -17
  96. package/scripts/lib/product-repo-detect.mjs +9 -8
  97. package/scripts/lib/project-hygiene.mjs +432 -0
  98. package/scripts/lib/quality-gate.mjs +167 -0
  99. package/scripts/lib/recommendations-v0.mjs +1 -1
  100. package/scripts/lib/reconcile/eligibility.mjs +1 -1
  101. package/scripts/lib/reconcile/emitter.mjs +23 -4
  102. package/scripts/lib/reconcile/engine.mjs +147 -39
  103. package/scripts/lib/reconcile/idempotency.mjs +114 -14
  104. package/scripts/lib/reconcile-nudge-banner.mjs +65 -9
  105. package/scripts/lib/resource-probe/evaluate.mjs +70 -4
  106. package/scripts/lib/resource-probe.mjs +19 -0
  107. package/scripts/lib/rule-loader.mjs +6 -0
  108. package/scripts/lib/scope-baseline.mjs +564 -0
  109. package/scripts/lib/scope-gate.mjs +399 -98
  110. package/scripts/lib/session-close-backfill.mjs +61 -6
  111. package/scripts/lib/session-end/phase-skip.mjs +1 -0
  112. package/scripts/lib/session-id.mjs +221 -41
  113. package/scripts/lib/session-lock.mjs +304 -6
  114. package/scripts/lib/session-schema/constants.mjs +22 -3
  115. package/scripts/lib/session-schema/validator.mjs +16 -0
  116. package/scripts/lib/sessions-integrity-banner.mjs +294 -0
  117. package/scripts/lib/sessions-staleness-banner.mjs +121 -12
  118. package/scripts/lib/skill-evolution/idempotency.mjs +135 -16
  119. package/scripts/lib/skill-evolution/mr-opener.mjs +9 -1
  120. package/scripts/lib/spiral-carryover.mjs +142 -30
  121. package/scripts/lib/state-md/mission-status.mjs +53 -3
  122. package/scripts/lib/subagents-schema.mjs +43 -9
  123. package/scripts/lib/test-runner/issue-reconcile.mjs +53 -13
  124. package/scripts/lib/tests-src-ratio.mjs +484 -0
  125. package/scripts/lib/validate/check-agents.mjs +56 -0
  126. package/scripts/lib/validate/check-hooks-symmetry.mjs +244 -10
  127. package/scripts/lib/validate/check-rules.mjs +217 -35
  128. package/scripts/lib/validate/check-test-value-bans.mjs +782 -0
  129. package/scripts/lib/validate/check-unicode-safety.mjs +1 -0
  130. package/scripts/lib/validate-vendored-rules.mjs +10 -2
  131. package/scripts/lib/vault-archive.mjs +17 -2
  132. package/scripts/lib/vault-backfill/glab.mjs +8 -0
  133. package/scripts/lib/vault-mirror/process.mjs +30 -0
  134. package/scripts/lib/vault-mirror/render-sessions.mjs +293 -36
  135. package/scripts/lib/vcs-repo-spec.mjs +362 -0
  136. package/scripts/lib/wave-resource-gate.mjs +115 -11
  137. package/scripts/lib/worktree/listing.mjs +44 -7
  138. package/scripts/mcp-server.sh +17 -3
  139. package/scripts/measure-context-overhead.sh +151 -0
  140. package/scripts/memory-propose.mjs +72 -9
  141. package/scripts/print-applicable-rules.mjs +51 -12
  142. package/scripts/release.mjs +534 -0
  143. package/scripts/run-quality-gate.mjs +123 -5
  144. package/scripts/validate-wave-scope.mjs +182 -17
  145. package/scripts/vault-integration-watcher.mjs +32 -10
  146. package/skills/_shared/config-reading.md +2 -2
  147. package/skills/bootstrap/fast-template.md +1 -1
  148. package/skills/claude-md-drift-check/checker.mjs +145 -28
  149. package/skills/contract-version-bump/SKILL.md +219 -0
  150. package/skills/discovery/SKILL.md +4 -4
  151. package/skills/discovery/issue-templates.md +11 -11
  152. package/skills/discovery/probes-audit.md +1 -1
  153. package/skills/discovery/probes-feature.md +1 -1
  154. package/skills/discovery/probes-session.md +26 -5
  155. package/skills/ecosystem-health/SKILL.md +1 -1
  156. package/skills/ecosystem-health/wizard.md +4 -4
  157. package/skills/evolve/SKILL.md +1 -0
  158. package/skills/gitlab-ops/SKILL.md +20 -12
  159. package/skills/gitlab-portfolio/SKILL.md +2 -2
  160. package/skills/hook-development/SKILL.md +1 -1
  161. package/skills/mode-selector/SKILL.md +1 -1
  162. package/skills/npm-publish/SKILL.md +17 -1
  163. package/skills/plan/SKILL.md +5 -5
  164. package/skills/plan/mode-feature.md +4 -4
  165. package/skills/plan/mode-new.md +10 -10
  166. package/skills/plan/mode-retro.md +1 -1
  167. package/skills/quality-gates/SKILL.md +1 -1
  168. package/skills/reconcile/SKILL.md +21 -4
  169. package/skills/session-end/SKILL.md +34 -13
  170. package/skills/session-end/discovery-scan.md +4 -2
  171. package/skills/session-end/drift-operations.md +4 -4
  172. package/skills/session-end/metrics-collection.md +13 -0
  173. package/skills/session-end/phase-3-2-docs-verification.md +1 -1
  174. package/skills/session-end/phase-3-6-tail.md +2 -1
  175. package/skills/session-end/plan-verification.md +5 -2
  176. package/skills/session-end/vault-operations.md +1 -1
  177. package/skills/session-end/verification-checklist.md +1 -1
  178. package/skills/session-plan/SKILL.md +6 -2
  179. package/skills/session-plan/wave-template.md +2 -0
  180. package/skills/session-start/SKILL.md +73 -7
  181. package/skills/session-start/phase-4-5-resource-health.md +15 -2
  182. package/skills/test-runner/SKILL.md +2 -2
  183. package/skills/vault-sync/validator.mjs +108 -7
  184. package/skills/wave-executor/SKILL.md +5 -2
  185. package/skills/wave-executor/circuit-breaker.md +2 -0
  186. package/skills/wave-executor/wave-loop.md +163 -10
  187. package/templates/_shared/loop.md +4 -4
@@ -91,7 +91,7 @@ gh issue list --limit 100 --json number,title,labels,updatedAt,assignees --jq '.
91
91
  # Flag:
92
92
  # - Issues with no activity in stale-issue-days (default: 30 days)
93
93
  # - Issues assigned but with no associated branch
94
- # - Issues labeled priority:high or priority:critical that are stale
94
+ # - Issues labeled priority::high or priority::critical that are stale
95
95
 
96
96
  # Check for associated branches:
97
97
  git branch -r | grep -i "<issue_number>"
@@ -109,7 +109,7 @@ Has Branch: true | false
109
109
  Priority: <priority label or NONE>
110
110
  ```
111
111
 
112
- **Default Severity:** Low. Medium if `priority:high` or `priority:critical` is stale.
112
+ **Default Severity:** Low. Medium if `priority::high` or `priority::critical` is stale.
113
113
 
114
114
  ---
115
115
 
@@ -231,14 +231,35 @@ Actual: <what was found or NOT found>
231
231
 
232
232
  5. Token efficiency — CLAUDE.md size:
233
233
  ```bash
234
- # Count lines in CLAUDE.md
235
- wc -l CLAUDE.md
234
+ # Delegate the threshold — do NOT re-implement it here. This probe used to
235
+ # carry its own numbers (`wc -l` > 150 warn / > 250 high); both the unit and
236
+ # the value were wrong. The ceiling is `DEFAULT_MAX_LINES = 80` and it applies
237
+ # to NON-EXEMPT effective lines: the runtime-critical `## Session Config` block
238
+ # is machine-parsed configuration, not trimmable prose, so a raw `wc -l` gate
239
+ # flags a compliant lean-root file (this plugin's own CLAUDE.md: 209 raw lines,
240
+ # 61 non-exempt — a `wc -l` rule fires, the lint passes).
241
+ REPO_ROOT="$(git rev-parse --show-toplevel)"
242
+ node "${PLUGIN_ROOT}/scripts/lib/claude-md-budget-lint.mjs" \
243
+ --repo-root "$REPO_ROOT" --mode warn --json
244
+ # → {"status":"ok"|"invalid","file":…,"lineCount":…,"exemptLines":…,
245
+ # "effectiveLineCount":…,"maxLineCharsSeen":…,"hasProvenance":…,
246
+ # "violations":[{"rule":"max-lines"|"max-line-chars"|…,"message":…}]}
247
+ # Flag ONLY on a violations[] entry — never on lineCount vs a local number.
248
+ # `--mode warn` keeps the exit code 0, so read the JSON, not `$?`.
249
+
250
+ # Ceiling value, when a finding needs to quote it (e.g. a 2x high tier):
251
+ node --input-type=module -e "
252
+ import { DEFAULT_MAX_LINES } from '${PLUGIN_ROOT}/scripts/lib/claude-md-budget-lint.mjs';
253
+ process.stdout.write(String(DEFAULT_MAX_LINES));
254
+ "
236
255
 
237
- # Flag if > 150 lines (warning) or > 250 lines (high)
256
+ # Heuristics the lint does NOT measure still eyeball these:
238
257
  # Identify sections > 30 lines that could move to <state-dir>/rules/ or <state-dir>/docs/
239
258
  # Check for inline code blocks > 10 lines (should be in separate files)
240
259
  ```
241
260
 
261
+ **Dependencies (probe 5):** requires `${PLUGIN_ROOT}/scripts/lib/claude-md-budget-lint.mjs` (#722 Epic A). Degrades gracefully when the helper is absent (pre-#722 plugin install) — skip the size finding with a note, do not fall back to a hand-rolled `wc -l` threshold, which is the drift this delegation removes.
262
+
242
263
  6. Token efficiency — .claudeignore coverage:
243
264
  ```bash
244
265
  # Check if .claudeignore exists
@@ -91,7 +91,7 @@ and skip this section.
91
91
  Using the detected VCS CLI (per gitlab-ops "Common CLI Commands" and "Dynamic Project Resolution" sections):
92
92
 
93
93
  1. Resolve the project ID or owner/repo slug for each cross-repo
94
- 2. Query open issues with `priority:critical` or `priority:high` labels (limit 5 per repo)
94
+ 2. Query open issues with `priority::critical` or `priority::high` labels (limit 5 per repo)
95
95
  3. Collect results across all configured repos
96
96
 
97
97
  ## CI Pipeline Status
@@ -78,8 +78,8 @@ CI pipeline identifiers (format "id" or "id:label", comma-separated, blank to sk
78
78
  ### Prompt 2c — Critical Issue Labels
79
79
 
80
80
  ```
81
- Critical issue labels (comma-separated, e.g. "priority:critical,severity:blocker", blank to skip):
82
- > priority:critical, severity:blocker
81
+ Critical issue labels (comma-separated, e.g. "priority::critical,severity:blocker", blank to skip):
82
+ > priority::critical, severity:blocker
83
83
  ```
84
84
 
85
85
  - Raw label strings as they appear in the VCS issue tracker.
@@ -121,7 +121,7 @@ ecosystem-health:
121
121
  pipelines:
122
122
  - id: main
123
123
  - id: deploy-production # Deploy
124
- critical-issue-labels: ["priority:critical", "severity:blocker"]
124
+ critical-issue-labels: ["priority::critical", "severity:blocker"]
125
125
  ```
126
126
 
127
127
  **Idempotency:** If an `ecosystem-health:` key already exists in Session Config,
@@ -141,7 +141,7 @@ exits 0. Re-run to edit: remove the existing block first, then re-run.
141
141
  { "id": "main" },
142
142
  { "id": "deploy-production", "label": "Deploy" }
143
143
  ],
144
- "criticalIssueLabels": ["priority:critical", "severity:blocker"]
144
+ "criticalIssueLabels": ["priority::critical", "severity:blocker"]
145
145
  }
146
146
  ```
147
147
 
@@ -261,6 +261,7 @@ For confirmed learnings, use atomic rewrite strategy:
261
261
  - `source_session`: **non-empty kebab-slug string** identifying the session from which the pattern was extracted (e.g. `main-2026-04-27-1942`). MUST be a string — never an object, array, number, or null. If multiple sessions contributed, use the earliest. If unknown, use `"unknown"` (the string). **Never** pass `String(<object>)` — that yields `"[object Object]"` and breaks the YAML mirror downstream (#307). Optional pre-write validation: `jq -e 'select(.source_session | type == "string" and length > 2)'`.
262
262
  - `created_at`: current ISO 8601 date
263
263
  - `expires_at`: preserve the candidate's derived expiry when supplied; otherwise derive from `LEARNING_TTL_DAYS[type]` via `deriveExpiresAt()` (falling back to the schema default) rather than hard-coding a 30-day horizon
264
+ - `file_paths` (optional): repo-relative path(s) scoping the learning to specific files/directories. Required for a learning to ever become `/reconcile`-eligible (issue #900; see `docs/rule-authoring.md` § "Learning Type-Taxonomy, TTL & Provenance Standard"). For a `fragile-file` candidate, `file_paths: [subject]` is mechanically derivable — `subject` already IS the file path.
264
265
  5. **Verify write**: Read back the first line of the written file to confirm valid JSON. If read-back fails or is not valid JSON, report error to user.
265
266
  6. **Prune:** remove entries where `expires_at` < current date OR `confidence` <= 0.0
266
267
  7. **Consolidate duplicates (NULL-SUBJECT SAFE):** if same `type` + `subject` appears more than once
@@ -6,7 +6,7 @@ model: haiku
6
6
  model-preference: sonnet
7
7
  model-preference-codex: gpt-5.4-mini
8
8
  model-preference-cursor: claude-sonnet-4-6
9
- description: Use this skill when performing VCS operations on GitLab or GitHub repositories — creating, updating, or closing issues and MRs, applying label taxonomy, running `glab`/`gh` CLI commands, or resolving project IDs dynamically. Acts as the single source of truth for CLI command syntax and label conventions; consuming skills reference this rather than duplicating logic. Triggers: "create a GitLab issue", "list open MRs", "apply priority label", "how do I resolve the project ID", "what's the carryover issue template". <example>Context: session-end needs to file a carryover issue for an incomplete task. user: "/close" assistant: "Creating carryover issue via glab with the Carryover Template from gitlab-ops — labels: carryover, priority:high."</example>
9
+ description: Use this skill when performing VCS operations on GitLab or GitHub repositories — creating, updating, or closing issues and MRs, applying label taxonomy, running `glab`/`gh` CLI commands, or resolving project IDs dynamically. Acts as the single source of truth for CLI command syntax and label conventions; consuming skills reference this rather than duplicating logic. Triggers: "create a GitLab issue", "list open MRs", "apply priority label", "how do I resolve the project ID", "what's the carryover issue template". <example>Context: session-end needs to file a carryover issue for an incomplete task. user: "/close" assistant: "Creating carryover issue via glab with the Carryover Template from gitlab-ops — labels: carryover, priority::high."</example>
10
10
  ---
11
11
 
12
12
  # VCS Operations Reference
@@ -99,13 +99,21 @@ done
99
99
 
100
100
  ## Label Taxonomy
101
101
 
102
- **Taxonomy convention (decided 2026-07-05, #727):** labels use the SINGLE-COLON form exclusively (`priority:high`, `status:ready`, `area:vcs`, `type:chore`, `from:<agent>`). The `::`-scoped form (`priority::high`, GitLab scoped-labels) is DEPRECATED baseline-scaffold legacy and MUST NOT be introduced — this repo mirrors to GitHub, which has no scoped-label semantics (no mutual-exclusion enforcement), so `::` yields zero benefit on the mirror while a migration would break every existing label reference and issue.
102
+ **Taxonomy convention `priority` REVERSED to scoped `::` (supersedes #727 for this one axis).**
103
+
104
+ - **`priority::<level>` is canonical.** #727's stated rationale was that "this repo mirrors to GitHub, which has no scoped-label semantics … while a migration would break every existing label reference and issue." Both halves were checked on 2026-07-25 and neither holds:
105
+ - **Issues are not mirrored at all.** `aiat-poc-infra/docs/github-mirror-runbook.md:1,5` describes a git **push-mirror** with GitHub as "read-only downstream"; `docs/gitlab-team-org-2026-06-21.md:45` confirms there is no two-way GitLab issue sync. Nothing crosses the boundary that a label rename could break.
106
+ - **GitHub already uses the scoped form.** `gh api "repos/AIAT-AIandBusinessgrowth/aiat-barrierefrei-engine/labels"` returns `priority::high`, `priority::low`, `priority::med`, `priority::medium` across 77 open issues, and **zero** `priority:high`. Same pattern on `aiat-doc-vlm`. GitHub treats `::` as an ordinary string; it merely does not enforce mutual exclusion.
107
+ - Volume agrees independently: **416 `priority::` against 249 `priority:` and 7 bare** at the time of the decision. Chasing the minority spelling would mean re-labelling the majority.
108
+ Producers were migrated FIRST (this change); the label-data migration follows separately, because migrating data before producers means the divergence returns within a day.
109
+ - **`area:` / `type:` / `status:` / `from:` stay SINGLE-COLON** — but NOT on #727's rationale, which is disproven above. They stay because nothing measured argues for flipping them, and because each axis is its own migration cost. Flipping them is a separate decision and is explicitly NOT made here. Note that `status` in particular is the worst-disciplined axis on the instance (354 assignments, only 48 percent scoped, 5 genuine value conflicts), so any future flip there needs a conflict-resolution pass first.
110
+ - **Readers accept both spellings.** Every consumer that MATCHES a label compares through `scripts/lib/label-scope.mjs` `normalizeLabel()`, which collapses `::` to `:` — so issues still carrying `priority:high` keep being counted until the data migration lands. Only WRITES are canonical.
103
111
 
104
112
  ### Priority Labels
105
- - `priority:critical` — blocking production or users
106
- - `priority:high` — important, schedule this sprint
107
- - `priority:medium` — plan for next sprint
108
- - `priority:low` — backlog, nice-to-have
113
+ - `priority::critical` — blocking production or users
114
+ - `priority::high` — important, schedule this sprint
115
+ - `priority::medium` — plan for next sprint
116
+ - `priority::low` — backlog, nice-to-have
109
117
 
110
118
  ### Status Labels
111
119
  - `status:ready` — defined, ready to pick up
@@ -151,11 +159,11 @@ GitHub has no native issue-blocking relation at all — the body-ordering-note f
151
159
  # Issues
152
160
  glab issue list --per-page 50 # All open issues
153
161
  glab issue list --label "status:ready" --per-page 10 # Ready to work on
154
- glab issue list --label "priority:high" --per-page 10 # High priority
162
+ glab issue list --label "priority::high" --per-page 10 # High priority
155
163
  glab issue list --closed --per-page 10 # Recently closed
156
164
  glab issue view <IID> # View issue details
157
165
  glab issue view <IID> --comments # With comments
158
- glab issue create --title "title" --label "priority:high,status:ready"
166
+ glab issue create --title "title" --label "priority::high,status:ready"
159
167
  glab issue update <IID> --label "status:in-progress" # WARNING: --label REPLACES the full set — see caveat below
160
168
  glab issue close <IID> # then VERIFY: glab issue view <IID> must show state=closed
161
169
  glab issue note <IID> -m "Comment text" # Add comment
@@ -188,11 +196,11 @@ glab api "projects/$(glab repo view --output json | python3 -c "import json,sys;
188
196
  # Issues
189
197
  gh issue list --limit 50 # All open issues
190
198
  gh issue list --label "status:ready" --limit 10 # Ready to work on
191
- gh issue list --label "priority:high" --limit 10 # High priority
199
+ gh issue list --label "priority::high" --limit 10 # High priority
192
200
  gh issue list --state closed --limit 10 # Recently closed
193
201
  gh issue view <NUMBER> # View issue details
194
202
  gh issue view <NUMBER> --comments # With comments
195
- gh issue create --title "title" --label "priority:high,status:ready"
203
+ gh issue create --title "title" --label "priority::high,status:ready"
196
204
  gh issue edit <NUMBER> --add-label "status:in-progress"
197
205
  gh issue close <NUMBER>
198
206
  gh issue comment <NUMBER> --body "Comment text" # Add comment
@@ -272,7 +280,7 @@ Relates to #ORIGINAL_IID
272
280
  ## [Discovery] <finding title>
273
281
 
274
282
  **Probe:** <probe_name>
275
- **Severity:** <priority:critical|high|medium|low>
283
+ **Severity:** <priority::critical|high|medium|low>
276
284
  **Category:** <code|infra|ui|arch|session|audit|vault|feature>
277
285
 
278
286
  ### Finding
@@ -302,7 +310,7 @@ Relates to #ORIGINAL_IID
302
310
  - [ ] Quality gates pass after fix
303
311
  ```
304
312
 
305
- Labels: `type:discovery`, `priority:<level>`, `area:<inferred>`, `status:ready`
313
+ Labels: `type:discovery`, `priority::<level>`, `area:<inferred>`, `status:ready`
306
314
 
307
315
  ## Template-First Enforcement (PSA-005 + #519)
308
316
 
@@ -93,7 +93,7 @@ gitlab-portfolio:
93
93
  enabled: true
94
94
  mode: warn # warn | strict | off
95
95
  stale-days: 30
96
- critical-labels: ["priority:critical", "priority:high"]
96
+ critical-labels: ["priority::critical", "priority::high"]
97
97
  ```
98
98
 
99
99
  | Field | Default | Meaning |
@@ -101,7 +101,7 @@ gitlab-portfolio:
101
101
  | `enabled` | `false` | Master switch. |
102
102
  | `mode` | `warn` | `warn` / `strict` / `off` — failure handling; `off` ≡ disabled. |
103
103
  | `stale-days` | `30` | Issues older than N days are flagged stale. |
104
- | `critical-labels` | `["priority:critical","priority:high"]` | Label substrings that classify an issue as critical (case-insensitive). |
104
+ | `critical-labels` | `["priority::critical","priority::high"]` | Label substrings that classify an issue as critical (case-insensitive). |
105
105
 
106
106
  ### Security
107
107
 
@@ -292,7 +292,7 @@ enabled=$(jq -r '.strictMode // false' "$CONFIG_FILE" 2>/dev/null)
292
292
 
293
293
  ## Our in-house examples (read these, not the upstream `examples/`)
294
294
 
295
- - `hooks/pre-bash-destructive-guard.mjs` — policy-driven command blocker, 13 rules in `.orchestrator/policy/blocked-commands.json`
295
+ - `hooks/pre-bash-destructive-guard.mjs` — policy-driven command blocker, 14 rules in `.orchestrator/policy/blocked-commands.json`
296
296
  - `hooks/enforce-scope.mjs` — wave-scope boundary enforcement using `.orchestrator/wave-scope.json`
297
297
  - `hooks/on-session-start.mjs` — banner + session init
298
298
  - `hooks/post-edit-validate.mjs` — validates edits after the fact
@@ -216,7 +216,7 @@ Missing signal fields contribute 0 to the score — no NaN propagation.
216
216
  ## Open Questions (for Phase B-1 follow-up)
217
217
 
218
218
  - Learnings freshness window — default 30d? Configurable per-type or a single global TTL?
219
- - Backlog priority weighting — rule-based (`priority:critical = +0.2` confidence bonus) vs.
219
+ - Backlog priority weighting — rule-based (`priority::critical = +0.2` confidence bonus) vs.
220
220
  learned from historical completion rates?
221
221
  - Alternative-generation algorithm — top-N non-selected modes scored by partial signal match, or
222
222
  fixed set derived from v0 heuristic branches?
@@ -29,7 +29,23 @@ Create at https://www.npmjs.com/settings/<user>/tokens → Generate New Token
29
29
 
30
30
  **Never** put the token in the tracked `.npmrc` (it holds `ignore-scripts=true` per SEC-020 and is committed), never persist it into `~/.npmrc`, never echo it into logs.
31
31
 
32
- ## Publish flow
32
+ ## Canonical path: `scripts/release.mjs` (Release als ein Dispatch, #978)
33
+
34
+ Since v3.19.0 the release is ONE dispatch — the script mechanizes every step below plus the
35
+ version-surface sync this skill previously left to operator memory (the gap that let v3.18.0
36
+ ship tagged but unpublished):
37
+
38
+ ```bash
39
+ node scripts/release.mjs --set-version X.Y.Z # rewrite all 12 version literals (10 files) + codex cachebuster + lock sync
40
+ # … author CHANGELOG entry + README highlights (enforced by --check) …
41
+ node scripts/release.mjs --check # preflight: surfaces, CHANGELOG, tag/registry collision, CI green, leakage gate
42
+ node scripts/release.mjs --publish # token publish → registry verify → tag AFTER publish → push origin+github
43
+ ```
44
+
45
+ The tag is created only AFTER a registry-verified publish — never before. The manual flow
46
+ below remains as the fallback and as documentation of what the script does.
47
+
48
+ ## Publish flow (manual fallback)
33
49
 
34
50
  ```bash
35
51
  # 1. Pre-flight (first publish: expect E404 = name free; upgrade: expect the previous version)
@@ -376,9 +376,9 @@ The Epic issue's number is not known during § 5.5.1. Once Phase 6 creates it, s
376
376
 
377
377
  Score each issue using three factors:
378
378
 
379
- 1. **Technical dependencies (highest weight):** Issues that other issues depend on get `priority:critical` or `priority:high`. Identify dependency chains: DB schema before API, API before frontend, shared libs before consumers, infrastructure before application.
379
+ 1. **Technical dependencies (highest weight):** Issues that other issues depend on get `priority::critical` or `priority::high`. Identify dependency chains: DB schema before API, API before frontend, shared libs before consumers, infrastructure before application.
380
380
 
381
- 2. **Business value (medium weight):** Issues the user marked as core MVP features in the PRD get `priority:high`. Nice-to-haves and polish items get `priority:medium` or `priority:low`.
381
+ 2. **Business value (medium weight):** Issues the user marked as core MVP features in the PRD get `priority::high`. Nice-to-haves and polish items get `priority::medium` or `priority::low`.
382
382
 
383
383
  3. **Risk (tiebreaker) — Impact × Risk 2×2 triage:** Classify each issue by Impact (high/low) and Risk (high/low) before applying the bump:
384
384
  - **High-Impact + Low-Risk → Implement.** Proceed directly; apply the one-level priority bump from the tiebreaker rule.
@@ -387,7 +387,7 @@ Score each issue using three factors:
387
387
  - **Low-Impact + High-Risk → Reject.** Do not create an issue for this candidate; note the rejection rationale in the PRD's Risks & Dependencies section instead.
388
388
 
389
389
  Assign labels from the standard taxonomy:
390
- - `priority:critical` / `priority:high` / `priority:medium` / `priority:low`
390
+ - `priority::critical` / `priority::high` / `priority::medium` / `priority::low`
391
391
  - `type:feature` / `type:enhancement` / `type:bug` / `type:chore` / `type:discovery`
392
392
  - `status:ready`
393
393
  - `area:<inferred from content>` (e.g., `area:api`, `area:frontend`, `area:infra`)
@@ -422,8 +422,8 @@ If user selects "Adjust priorities" or "Remove issues", handle the adjustments i
422
422
  For each approved issue:
423
423
 
424
424
  1. Create via VCS CLI using comma-separated labels in a single `--label` flag:
425
- - **GitLab**: `glab issue create --title "[Plan] <title>" --label "type:feature,priority:high,status:ready" --description "<body>"`
426
- - **GitHub**: `gh issue create --title "[Plan] <title>" --label "type:feature,priority:high,status:ready" --body "<body>"`
425
+ - **GitLab**: `glab issue create --title "[Plan] <title>" --label "type:feature,priority::high,status:ready" --description "<body>"`
426
+ - **GitHub**: `gh issue create --title "[Plan] <title>" --label "type:feature,priority::high,status:ready" --body "<body>"`
427
427
  2. Brief pause (1s) between creations for rate limiting
428
428
  3. After all issues are created, set dependency links:
429
429
  - **GitLab**: use `glab api` to set `blocks`/`is-blocked-by` relations. On HTTP 403 (non-Premium/Ultimate): `relates_to` + body-ordering-note fallback — see gitlab-ops SKILL.md § "Issue Linking (`blocks` / `is_blocked_by`)".
@@ -111,9 +111,9 @@ Source: PRD Section 3 (Acceptance Criteria).
111
111
 
112
112
  Apply this scoring:
113
113
 
114
- 1. **Technical dependencies** — Issues that block other issues get `priority:critical` or `priority:high`. DB before API, API before UI, shared before consumers.
115
- 2. **Core acceptance criteria** — Issues covering primary happy-path scenarios get `priority:high`.
116
- 3. **Edge cases / nice-to-haves** — Defensive scenarios, error handling, optional behaviors get `priority:medium` or `priority:low`.
114
+ 1. **Technical dependencies** — Issues that block other issues get `priority::critical` or `priority::high`. DB before API, API before UI, shared before consumers.
115
+ 2. **Core acceptance criteria** — Issues covering primary happy-path scenarios get `priority::high`.
116
+ 3. **Edge cases / nice-to-haves** — Defensive scenarios, error handling, optional behaviors get `priority::medium` or `priority::low`.
117
117
  4. **Risk factor — Impact × Risk 2×2 triage:** Classify by Impact (high/low) × Risk (high/low): **High-Impact + Low-Risk → Implement** (bump priority up one level); **High-Impact + High-Risk → Experiment** (smallest spike first); **Low-Impact + Low-Risk → Defer** (backlog, no issue in this set); **Low-Impact + High-Risk → Reject** (no issue — note rationale in § Risks & Dependencies). Full quadrant definitions: `SKILL.md` § 6.2 Auto-Prioritize.
118
118
 
119
119
  ### Labels
@@ -121,7 +121,7 @@ Apply this scoring:
121
121
  Apply per gitlab-ops skill label taxonomy:
122
122
 
123
123
  - **Type:** `type:feature` for new capabilities, `type:enhancement` for extensions of existing features.
124
- - **Priority:** `priority:critical` / `priority:high` / `priority:medium` / `priority:low` from auto-prioritize above.
124
+ - **Priority:** `priority::critical` / `priority::high` / `priority::medium` / `priority::low` from auto-prioritize above.
125
125
  - **Area:** Infer from affected code paths (e.g., `area:api`, `area:frontend`, `area:infra`).
126
126
  - **Appetite:** Map from Wave 1 Q5 scope answer (`appetite:1w`, `appetite:2w`, `appetite:6w`).
127
127
 
@@ -235,10 +235,10 @@ Score each issue using three factors:
235
235
 
236
236
  1. **Technical dependencies (highest weight):**
237
237
  - DB schema before API, API before frontend, shared libs before consumers.
238
- - Issues that block others → `priority:critical` or `priority:high`.
238
+ - Issues that block others → `priority::critical` or `priority::high`.
239
239
  2. **Business value (medium weight):**
240
- - Core MVP features → `priority:high`.
241
- - Nice-to-haves → `priority:medium` or `priority:low`.
240
+ - Core MVP features → `priority::high`.
241
+ - Nice-to-haves → `priority::medium` or `priority::low`.
242
242
  3. **Risk (tiebreaker):**
243
243
  - Issues with identified risks from PRD Section 7 → bump up one level.
244
244
 
@@ -249,12 +249,12 @@ Use taxonomy from `setup-gitlab-groups.sh`:
249
249
  **Priority mapping for VCS labels:**
250
250
  | Categorization | VCS Label |
251
251
  |---|---|
252
- | P0 (critical path, blocking) | `priority:critical` |
253
- | P1 (high impact, needed soon) | `priority:high` |
254
- | P2 (medium, can wait) | `priority:medium` |
255
- | P3 (nice-to-have) | `priority:low` |
252
+ | P0 (critical path, blocking) | `priority::critical` |
253
+ | P1 (high impact, needed soon) | `priority::high` |
254
+ | P2 (medium, can wait) | `priority::medium` |
255
+ | P3 (nice-to-have) | `priority::low` |
256
256
 
257
- Always use the `priority:<level>` format in VCS CLI commands, not P0/P1/P2/P3.
257
+ Always use the `priority::<level>` format in VCS CLI commands, not P0/P1/P2/P3.
258
258
 
259
259
  - **type:** feature, enhancement, bug, chore, docs
260
260
  - **status:** `status:ready`
@@ -275,11 +275,11 @@ Use AskUserQuestion to present the full issue structure:
275
275
  ```bash
276
276
  # Create epic
277
277
  glab issue create --title "$EPIC_TITLE" --description "$EPIC_DESC" \
278
- --label "type:epic,priority:$PRIORITY" --milestone "$MILESTONE"
278
+ --label "type:epic,priority::$PRIORITY" --milestone "$MILESTONE"
279
279
 
280
280
  # Create sub-issues
281
281
  glab issue create --title "$ISSUE_TITLE" --description "$ISSUE_DESC" \
282
- --label "type:feature,priority:$PRIORITY,status:ready,area:$AREA,appetite:$APPETITE"
282
+ --label "type:feature,priority::$PRIORITY,status:ready,area:$AREA,appetite:$APPETITE"
283
283
  ```
284
284
 
285
285
  ### Step 6: Set dependency links
@@ -225,7 +225,7 @@ Before Phase 3.2, satisfy the PRD Commit Gate — see `SKILL.md` § Phase 5.5 (H
225
225
 
226
226
  Create one VCS issue per agreed improvement from Wave 2:
227
227
  - **Title**: `[Retro] <action description>`
228
- - **Labels**: `type:enhancement`, `priority:<from ranking>`, `area:<inferred>`
228
+ - **Labels**: `type:enhancement`, `priority::<from ranking>`, `area:<inferred>`
229
229
  - **Description**: action, impact, effort, link to retro document
230
230
  - Create via VCS CLI (per gitlab-ops skill)
231
231
 
@@ -109,7 +109,7 @@ Commands:
109
109
  4. Check changed files for debug artifacts: `console.log`, `debugger`, `TODO: remove`.
110
110
 
111
111
  Behavior: BLOCKING. Do not commit if any check fails. Fix quick issues (<2 min) inline.
112
- For anything longer, create a `priority:high` issue and proceed without committing the
112
+ For anything longer, create a `priority::high` issue and proceed without committing the
113
113
  affected files.
114
114
 
115
115
  > **Broken-Window cross-reference (#730/H5):** a Full-Gate PASS that ships with a documented exception (echo-stub, WARN-lint, overridden finding) feeds session-end Phase 2.6 — see `skills/session-end/SKILL.md § Phase 2.6`.
@@ -35,6 +35,16 @@ any file is written. Advisory-only — rules are NEVER auto-applied.
35
35
  - **Engine never writes `.claude/rules/`.** `runReconcile` computes proposals and records
36
36
  them in the idempotency sidecar only. The only module that writes `.claude/rules/` is
37
37
  `writer.mjs`, and only AFTER the operator approves proposals via AUQ.
38
+ - **The candidate store belongs to `mergeCandidates` — nothing else writes it.**
39
+ `.orchestrator/runtime/reconcile-candidates.jsonl` is a mutable work-queue whose only
40
+ sanctioned writer is `mergeCandidates` (`scripts/lib/reconcile/idempotency.mjs`); it is
41
+ not a scratch pad, and no report, analysis run, or agent may append to it by hand. A
42
+ hand-written record there corrupts downstream readers — the session-start reconcile nudge
43
+ banner derives "last run" from `created_at`, so a foreign-shaped record makes a non-empty
44
+ store report *no reconcile run on record*. Candidate analyses and dry-run reports write
45
+ their findings to `docs/reconcile/<date>-<topic>.md`, never into the store. (Since
46
+ 2026-07-31 a read-side shape guard drops records lacking `learning_key`/`created_at` and
47
+ `mergeCandidates` reports the count as `skipped` — that guard is a backstop, not a licence.)
38
48
  - **Same pipeline as session-end Phase 3.6.8.** This skill uses the identical engine and
39
49
  writer seams as the automatic session-end reconciliation phase — operator experience is
40
50
  consistent, and any fixes to the engine benefit both paths.
@@ -76,6 +86,7 @@ CONFIDENCE_FLOOR=$(echo "$CONFIG" | jq -r '.reconcile["confidence-floor"] // 0.5
76
86
  RECONCILE_MODE=$(echo "$CONFIG" | jq -r '.reconcile.mode // "warn"')
77
87
  MIN_RULE_DAYS=$(echo "$CONFIG" | jq -r '.reconcile["min-rule-days"] // 7')
78
88
  MIN_INSIGHT_CHARS=$(echo "$CONFIG" | jq -r '.reconcile["min-insight-chars"] // 24')
89
+ MAX_PROPOSALS_PER_RUN=$(echo "$CONFIG" | jq -r '.reconcile["max-proposals-per-run"] // 10')
79
90
  ```
80
91
 
81
92
  When `RULE_EXPIRY_DAYS` is empty, pass `ruleExpiryDays: undefined` to `runReconcile` so the engine uses its per-type TTL. Defaults when the `reconcile` block is absent or a field is missing:
@@ -86,6 +97,8 @@ When `RULE_EXPIRY_DAYS` is empty, pass `ruleExpiryDays: undefined` to `runReconc
86
97
  near-dead or already-elapsed natural expiry never produces a born-dead rule (issue #741.1).
87
98
  - `min-insight-chars`: 24 — opt-in minimum insight length gating the eligibility
88
99
  placeholder-insight check (issue #741.2).
100
+ - `max-proposals-per-run`: 10 — volume brake (issue #900 D); the engine sorts eligible
101
+ learnings by confidence DESC and proposes at most this many per run.
89
102
 
90
103
  Note: `reconcile.enabled` is intentionally NOT checked — this on-demand command always runs.
91
104
 
@@ -119,6 +132,7 @@ const { proposals, rejected, summary, error } = await runReconcile({
119
132
  ruleExpiryDays: RULE_EXPIRY_DAYS, // empty → undefined → engine per-type TTL
120
133
  minRuleDays: MIN_RULE_DAYS, // default 7 — floors a near-dead expires-at
121
134
  minInsightChars: MIN_INSIGHT_CHARS, // default 24 — opt-in placeholder-insight length gate
135
+ maxProposalsPerRun: MAX_PROPOSALS_PER_RUN, // default 10 — volume brake (issue #900 D)
122
136
  now: new Date(),
123
137
  dryRun: DRY_RUN, // true → engine touches no disk (no idempotency sidecar write)
124
138
  });
@@ -163,7 +177,10 @@ List `rejected[].reason` and exit.
163
177
 
164
178
  **Only when `DRY_RUN=true`.**
165
179
 
166
- Print the proposals in a readable table. Do NOT write the sidecar, do NOT render an AUQ.
180
+ Print the proposals in a readable table. Do NOT write the sidecar, do NOT render an AUQ, and
181
+ do NOT write candidates into `.orchestrator/runtime/reconcile-candidates.jsonl` — that store
182
+ is `mergeCandidates`' alone (see Posture Contract). A dry-run write-up belongs in
183
+ `docs/reconcile/`.
167
184
 
168
185
  ```
169
186
  ## Reconcile — Dry Run (N proposals, M rejected)
@@ -307,9 +324,9 @@ If `written === 0` and `approved.length === 0`:
307
324
  silently swallow failures.
308
325
  - **ALWAYS** present proposals in batches of ≤4 via AUQ multiSelect — mirrors session-end
309
326
  3.6.3 / 3.6.8 and keeps the operator prompt readable.
310
- - **ALWAYS** honour `confidence-floor`, `rule-expiry-days`, `min-rule-days`, and
311
- `min-insight-chars` from Session Config `reconcile` block — the engine reads these, but
312
- the skill must pass them explicitly.
327
+ - **ALWAYS** honour `confidence-floor`, `rule-expiry-days`, `min-rule-days`,
328
+ `min-insight-chars`, and `max-proposals-per-run` from Session Config `reconcile` block —
329
+ the engine reads these, but the skill must pass them explicitly.
313
330
 
314
331
  ## Anti-Patterns
315
332
 
@@ -74,7 +74,7 @@ Read back the session plan that was agreed at the start. For EACH planned item:
74
74
  - Document what was completed and what remains
75
75
  - **Do NOT file the carryover issue here (#769).** Collect a carryover **candidate** instead — append it to the in-memory candidate list that the Phase 1.65 Handover Alignment Gate consumes. The issue is filed (only if the gate confirms it) in Phase 5 Step 3. Candidate record (JS keys as `routeCandidates` / `normalizeCandidate` read them — `source-phase`→`sourcePhase`, `origin-issue`→`originIssue`; see `plan-verification.md § Candidate Record Format`):
76
76
  - `{ task: '<original task description>', sourcePhase: '1.2', originIssue: <IID or null>, priority: '<original>', bucket: 'partially-done' }`
77
- - The eventual issue keeps the source-specific `[Carryover]` template — Title `[Carryover] <original task description>`, Labels `priority:<original>` + `status:ready`, Description = what's done / what's left / context for next session.
77
+ - The eventual issue keeps the source-specific `[Carryover]` template — Title `[Carryover] <original task description>`, Labels `priority::<original>` + `status:ready`, Description = what's done / what's left / context for next session.
78
78
  - Link to the original issue when applicable (record its IID as `originIssue`; a candidate with no origin issue auto-carries per the gate's routing, so nothing planned is silently forgotten).
79
79
 
80
80
  ### 1.3 Not Started Items
@@ -218,7 +218,7 @@ node scripts/emit-event.mjs --type orchestrator.handover.gated --payload \
218
218
  const { autoCarry, ask } = routeCandidates(candidates);
219
219
  ```
220
220
 
221
- `autoCarry` = `priority:critical|high` OR `bucket === 'spiral-failed'` OR `originIssue === null` — **non-deselectable** (dropping any of these would be real forgetting; consistent with the Critical Rule at `SKILL.md:853`). `ask` = the middle-band (priority `medium`/`low`/none WITH an origin issue, buckets not-started/emergent/partially-done) plus any `malformed` record. `routeCandidates` returns NORMALIZED copies for gate rendering; the coordinator retains its ORIGINAL candidate objects (with filing payloads) for Phase 5 Step 3.
221
+ `autoCarry` = `priority::critical|high` OR `bucket === 'spiral-failed'` OR `originIssue === null` — **non-deselectable** (dropping any of these would be real forgetting; consistent with the Critical Rule at `SKILL.md:853`). `ask` = the middle-band (priority `medium`/`low`/none WITH an origin issue, buckets not-started/emergent/partially-done) plus any `malformed` record. `routeCandidates` returns NORMALIZED copies for gate rendering; the coordinator retains its ORIGINAL candidate objects (with filing payloads) for Phase 5 Step 3.
222
222
 
223
223
  3. Read STATE.md contents and extract the open questions via the sibling helper:
224
224
 
@@ -313,10 +313,10 @@ Dispatch the session-reviewer agent to verify implementation quality before the
313
313
 
314
314
  | Finding class | Disposition |
315
315
  |---|---|
316
- | HIGH+ / blocking review finding | Fix inline if quick (<2 min); else create an issue (`priority:high`, `status:ready`) and note it in the Final Report |
316
+ | HIGH+ / blocking review finding | Fix inline if quick (<2 min); else create an issue (`priority::high`, `status:ready`) and note it in the Final Report |
317
317
  | MED / LOW review finding | Fold in-session if quick; else record under "Unresolved Review Findings" in the Final Report — DO NOT create an issue (#617) |
318
318
  | Planned-carryover (item was in the plan, not finished) | Route as a carryover **candidate** per Phase 1.2 → the Phase 1.65 gate files it. Never forgotten: a no-origin/critical/high item auto-carries as a `[Carryover]` issue; a middle-band item with an origin issue is preselected=carry (and its origin issue stays open even if dropped). |
319
- | SPIRAL / FAILED agent carryover | Route as an **auto-carry** candidate per Phase 1.6 → filed via `createSpiralCarryoverIssue` in Phase 5 Step 3 (non-deselectable) |
319
+ | SPIRAL / FAILED agent carryover | Route as an **auto-carry** candidate per Phase 1.6 → filed via `createSpiralCarryoverIssue` in Phase 5 Step 3 (non-deselectable; **exempt from the `issue-budget` cap** — the `[Carryover] [SPIRAL\|FAILED]` title and the `type::carryover` label bypass it, so a full budget can never swallow this filing) |
320
320
 
321
321
  **Override-ratio telemetry (#730/H5):** whenever one or more MED/LOW review findings are routed to "Unresolved Review Findings" (rather than fixed), additionally emit a single event capturing how many findings were absorbed rather than resolved — feeding the `override_ratio` metric:
322
322
 
@@ -364,7 +364,7 @@ Rules:
364
364
 
365
365
  > **Verification Reference:** See `verification-checklist.md` in this skill directory for the full quality gate checklist.
366
366
 
367
- Run ALL checks listed in the verification checklist. If any check fails: fix if quick (<2 min), otherwise create a `priority:high` issue. Do NOT commit broken code.
367
+ Run ALL checks listed in the verification checklist. If any check fails: fix if quick (<2 min), otherwise create a `priority::high` issue. Do NOT commit broken code.
368
368
 
369
369
  ### Phase 2.0a: Echo-Stub Detection (GH #42)
370
370
 
@@ -438,7 +438,7 @@ totalFindings = projectStaleness.findings.length + narrativeStaleness.findings.l
438
438
  - If `totalFindings === 0`: continue, log `Vault staleness: clean (mode=strict)`.
439
439
  - If `totalFindings > 0`: do NOT block the close. Present the findings list and surface an AskUserQuestion whose Recommended default is **warn + carryover + continue**:
440
440
  - On Claude Code: AskUserQuestion with options:
441
- 1. "Warn + carryover and close (Recommended)" — file a carryover issue (labels `carryover`, `priority:high`) titled `[Carryover] Vault staleness (strict) — <count> findings` documenting the stale projects/narratives for a follow-up session, log a Deviation entry in STATE.md `## Deviations`, then continue the close:
441
+ 1. "Warn + carryover and close (Recommended)" — file a carryover issue (labels `carryover`, `priority::high`) titled `[Carryover] Vault staleness (strict) — <count> findings` documenting the stale projects/narratives for a follow-up session, log a Deviation entry in STATE.md `## Deviations`, then continue the close:
442
442
  `- [<ISO timestamp>] Phase 2.3: Vault staleness strict-mode findings carried over. Findings: <count> (projects: <N>, narratives: <M>) → issue #<IID>.`
443
443
  2. "Override and close" — proceed without a carryover issue, log a Deviation entry in STATE.md `## Deviations`:
444
444
  `- [<ISO timestamp>] Phase 2.3: Vault staleness strict-mode findings overridden by user. Findings: <count> (projects: <N>, narratives: <M>).`
@@ -480,7 +480,7 @@ For each kept phase:
480
480
  - exit code `≠ 0` ⇒ **BLOCK the close** using the same routing pattern as Phase 2.3 strict-mode. `mode: hard` here is an operator-declared repo contract (the repo deliberately chose `mode: hard`), so the block semantics are preserved — but the AUQ now ALSO offers a warn + carryover escape hatch. Present the phase name + captured summary and offer:
481
481
  - On Claude Code: AskUserQuestion with options:
482
482
  1. "Fix and retry Phase 2.5" (Recommended) — exit close, let the user investigate.
483
- 2. "Warn + carryover and close" — file a carryover issue (labels `carryover`, `priority:high`) titled `[Carryover] custom-phase '<name>' (mode=hard) exited <code>` capturing the phase name + captured summary for a follow-up session, log the Deviation entry, then continue the close.
483
+ 2. "Warn + carryover and close" — file a carryover issue (labels `carryover`, `priority::high`) titled `[Carryover] custom-phase '<name>' (mode=hard) exited <code>` capturing the phase name + captured summary for a follow-up session, log the Deviation entry, then continue the close.
484
484
  3. "Override and close" — proceed, log a Deviation entry in STATE.md `## Deviations`:
485
485
  `- [<ISO timestamp>] Phase 2.5: custom-phase '<name>' (mode=hard) exited <code>, overridden by user.`
486
486
  In addition to the Deviation entry, emit an override-ratio event so the override feeds the `override_ratio` metric (#730/H5): `node scripts/emit-event.mjs --type orchestrator.finding.overridden --payload '{"phase":"2.5","kind":"custom-phase-hard","count":N}'`.
@@ -507,7 +507,7 @@ already-computed results — no new detection logic, only aggregation:
507
507
  4. Wave-level reviewer findings overridden without a fix task (`## Deviations` entries matching `reviewer finding overridden` — written by wave-executor §5/5a).
508
508
 
509
509
  For EACH item: file a hard-terminated closure issue via `createBrokenWindowIssue()`
510
- from `scripts/lib/spiral-carryover.mjs` — labels `broken-window` + `priority:high`,
510
+ from `scripts/lib/spiral-carryover.mjs` — labels `broken-window` + `priority::high`,
511
511
  due-date = today + `broken-window-budget.due-days` (default 7; `glab` native
512
512
  `--due-date`, `gh` fallback: `Due: <date>` as first body line — GitHub has no
513
513
  native due-date field). Idempotent per task-hash — re-running a close never
@@ -986,8 +986,8 @@ if (sweep) {
986
986
  The call is idempotent: if the issue has no `status:*` labels, no update CLI call is made. Failures from `stripStatusLabels` are non-fatal — log and proceed with close.
987
987
 
988
988
  2. **Update in-progress issues**: ensure labels reflect actual state using the issue update command
989
- 3. **Create carryover issues — from the Phase 1.65 gate's carry-list ONLY (#769):** file an issue for each item on the carry-list produced by the Handover Alignment Gate — i.e. the non-deselectable **auto-carry** class (`priority:critical|high`, SPIRAL/FAILED, or no-origin-issue candidates) PLUS the middle-band items the operator LEFT SELECTED in triage. Do NOT file anything the gate dropped, and do NOT file directly from Phase 1.2/1.3/1.4/1.6 — those phases only collected candidates.
990
- - **Template stays source-specific:** 1.2 Partially-Done → `[Carryover] <task>` (labels `priority:<original>`, `status:ready`); 1.4 unfinished Emergent → a **normal** issue (NOT the `[Carryover]` template); 1.6 SPIRAL/FAILED → fire the deferred `createSpiralCarryoverIssue({ taskDescription, kind, context, priority: 'high', vcs })` (idempotent task-hash dedup — payload comes from the candidate's `_spiral` annotation set in Phase 1.6 step 5). 1.3 files no NEW issue: a carried 1.3 candidate simply keeps its ORIGINAL issue `status:ready`.
989
+ 3. **Create carryover issues — from the Phase 1.65 gate's carry-list ONLY (#769):** file an issue for each item on the carry-list produced by the Handover Alignment Gate — i.e. the non-deselectable **auto-carry** class (`priority::critical|high`, SPIRAL/FAILED, or no-origin-issue candidates) PLUS the middle-band items the operator LEFT SELECTED in triage. Do NOT file anything the gate dropped, and do NOT file directly from Phase 1.2/1.3/1.4/1.6 — those phases only collected candidates.
990
+ - **Template stays source-specific:** 1.2 Partially-Done → `[Carryover] <task>` (labels `priority::<original>`, `status:ready`); 1.4 unfinished Emergent → a **normal** issue (NOT the `[Carryover]` template); 1.6 SPIRAL/FAILED → fire the deferred `createSpiralCarryoverIssue({ taskDescription, kind, context, priority: 'high', vcs })` (idempotent task-hash dedup — payload comes from the candidate's `_spiral` annotation set in Phase 1.6 step 5). 1.3 files no NEW issue: a carried 1.3 candidate simply keeps its ORIGINAL issue `status:ready`.
991
991
  - **Dropped middle-band items:** file NO `[Carryover]` duplicate; the origin issue stays open and unchanged. Record each drop in the Phase 6 Final Report under `### Dropped at Handover Gate` with its origin-issue reference and a reason slot.
992
992
  - **Fail-open / gate skipped:** when Phase 1.65 skipped fail-open, the carry-list is ALL candidates (status quo) and there is no drop-list.
993
993
  - **Mark answered open questions `[x]` durably — atomic with the filing above (#769):** now, on the completed side of the Quality Gate, persist each answered open question captured in-memory at Phase 1.65 Step 4 to STATE.md via the lock-guarded sibling helper (PSA-005). Co-locating this write with the carryover-issue filing is the load-bearing correctness invariant: an earlier Quality-Gate abort leaves every question `- [ ]` on disk, so it correctly re-surfaces via `readOpenQuestions().filter(!answered)` on re-close — the `[x]` mark now reflects a COMPLETED handover, never a mid-close state a later abort would invalidate. Any implied-work candidate an answered question enqueued in Phase 1.65 is filed by the carry-list step above, so the mark and its issue land together:
@@ -1002,13 +1002,32 @@ if (sweep) {
1002
1002
 
1003
1003
  Fail-open: a `markOpenQuestionAnsweredOnDisk` failure is non-fatal — log a WARN and proceed with the close; the question simply stays `- [ ]` and roundtrips to the next session.
1004
1004
 
1005
+ 3b. **Drain the issue-budget overflow — exactly ONE collector artefact (issue-budget):** when `.orchestrator/runtime/issue-budget.json` has a non-empty `overflow[]`, the session hit its `issue-budget.max-per-session` cap and every over-cap creation was PARKED rather than filed. Fold the whole list into a single artefact so nothing is silently dropped.
1006
+
1007
+ **Ordering (load-bearing):** run this as the LAST issue-creating action of Phase 5 — after step 3, after "Discovery Issue Creation", after step 4 — and re-read the counter file at that moment. Those steps can themselves push new entries into `overflow[]`; draining early would leave them unfiled.
1008
+
1009
+ ```js
1010
+ import { readBudgetState, budgetStatePath } from '${PLUGIN_ROOT}/scripts/lib/issue-budget.mjs';
1011
+ const state = readBudgetState(repoRoot, sessionId); // { sessionId, count, exempt, overflow: [...] }
1012
+ ```
1013
+
1014
+ - **`issue-budget.overflow: collect-issue` (default)** — create exactly ONE issue:
1015
+ - Title: `[Backlog-Sammel] <session-id>, <N> zurückgestellte Punkte`
1016
+ - Labels: `type::backlog`, `priority::low`
1017
+ - Body: a Markdown checklist with one `- [ ]` line per `overflow[]` entry (`title` when present, otherwise the truncated `command`, plus its `at` timestamp).
1018
+ - This collector issue is itself EXEMPT from the cap (`[Backlog-Sammel]` is in the exemption list in `scripts/lib/issue-budget.mjs`), so it always lands even at count == max.
1019
+ - **`issue-budget.overflow: vault-note`** — create NO issue. Write one Markdown file `vault/00-inbox/<session-id>-backlog-sammel.md` (path relative to `vault-integration.vault-dir`) with valid vault frontmatter and the same checklist body.
1020
+ - After the artefact exists, reset `overflow` to `[]` in the counter file and record the collector issue ID / note path in the Phase 6 Final Report under `### Zurückgestellt (issue-budget)`.
1021
+ - **Never exempt-by-accident:** the cap never applied to `priority::critical`, the carryover class (`[Carryover]`, SPIRAL/FAILED, `type::carryover`), or `broken-window` closure issues, so nothing on the Phase 1.65 carry-list can ever appear in `overflow[]`. The promises at Phase 1.8 ("SPIRAL / FAILED agent carryover … non-deselectable") and the Critical Rule "ALWAYS create issues for unfinished PLANNED work" stay intact by construction.
1022
+ - Fail-open: a missing or malformed counter file means "no overflow" — log a WARN and continue the close.
1023
+
1005
1024
  #### Discovery Issue Creation (if discovery ran in Phase 1.5)
1006
1025
 
1007
1026
  For each finding with severity `critical` or `high` from Phase 1.5:
1008
1027
  1. Create a VCS issue using the detected platform CLI:
1009
1028
  - Title: `[Discovery] <description>` (truncated to 70 chars)
1010
1029
  - Body: `**Probe:** <probe>\n**File:** <file>:<line>\n**Severity:** <severity>\n**Confidence:** <confidence>%\n**Recommendation:** <recommendation>`
1011
- - Labels: `type:discovery`, `priority:<severity>` (critical→critical, high→high)
1030
+ - Labels: `type:discovery`, `priority::<severity>` (critical→critical, high→high)
1012
1031
  2. Log each created issue ID for the Final Report
1013
1032
  3. Update `discovery_stats.issues_created` count
1014
1033
 
@@ -1050,7 +1069,7 @@ Present to the user:
1050
1069
  - Wave 1 (Discovery): [duration] — [N agents] — [K files]
1051
1070
  - Wave 2 (Impl-Core): [duration] — [N agents] — [K files]
1052
1071
  - ...
1053
- - Tests: [passing/total]
1072
+ - Tests: [passing/total] · Δ this session: +[added] / −[removed] / ~[consolidated] · tests:src LOC ratio [x.xx] (advisory ceiling 1.60)
1054
1073
  - TypeScript: 0 errors
1055
1074
  - Commits: [N] pushed to [branch]
1056
1075
  - Mirror: [synced/skipped]
@@ -1072,6 +1091,8 @@ Present to the user:
1072
1091
  - Notes: [any context for next session]
1073
1092
  ```
1074
1093
 
1094
+ > **Test-delta anchor:** the `Δ this session` figures are aggregated from the `test_delta` field of this session's agent reports (added / removed / consolidated test cases); the `tests:src LOC ratio` is `wc -l` over `tests/` divided by `wc -l` over `scripts/` + `hooks/`. A bare `passing/total` count is not a progress signal — a growing suite reads as progress even when the growth is redundant, which is exactly why the delta and the ratio are reported alongside it. When the ratio exceeds the advisory ceiling, recommend that the NEXT session's Quality wave run as a **consolidation wave**: no new test lands without a redundant one being removed in the same change. This is advisory only — it never blocks the close.
1095
+
1075
1096
  > **Documentation Coverage anchor:** If Phase 3.2 ran and produced task verification results (i.e. `docs-orchestrator.enabled: true` and `docs-tasks` were found), the results appear here as a `### Documentation Coverage (docs-orchestrator)` subsection emitted by Phase 3.2 Step 7. The content is written dynamically — it is not pre-populated in this template. When `docs-orchestrator.enabled` is `false` or `docs-tasks` were absent, this subsection is omitted entirely.
1076
1097
 
1077
1098
  ## Sub-File Reference
@@ -1110,7 +1131,7 @@ Present to the user:
1110
1131
  - **NEVER commit with TypeScript errors** — 0 errors is non-negotiable
1111
1132
  - **NEVER use `git add .`** — stage files individually to avoid capturing parallel session work
1112
1133
  - **NEVER skip issue updates** — VCS must reflect reality after every session
1113
- - **ALWAYS create issues for unfinished PLANNED work** — SPIRAL/FAILED agent carryover and partially-done plan items (Phase 1.2 / 1.6) ALWAYS get a ticket; nothing planned-but-unfinished is "remembered" without one.
1134
+ - **ALWAYS create issues for unfinished PLANNED work** — SPIRAL/FAILED agent carryover and partially-done plan items (Phase 1.2 / 1.6) ALWAYS get a ticket; nothing planned-but-unfinished is "remembered" without one. The `issue-budget` per-session cap does NOT weaken this: `priority::critical`, the carryover class (`[Carryover]`, `[SPIRAL]`/`[FAILED]`, `type::carryover`, bare `carryover`) and `broken-window` closure issues are exempt from the cap by construction (`scripts/lib/issue-budget.mjs` `EXEMPT_RULES`). Non-exempt over-cap creations are not dropped either — they are parked and folded into one `[Backlog-Sammel]` collector in Phase 5 Step 3b.
1114
1135
  - **DO NOT auto-file MED/LOW review findings as issues** — newly-surfaced reviewer findings (Phase 1.8 / W4 panel) at MED or LOW severity are folded in-session or recorded in the Final Report under "Unresolved Review Findings". Only HIGH+/blocking review findings get an issue. (Issue #617 — stops the self-referential low-priority backlog.)
1115
1136
  - **ALWAYS push to origin** — local-only work is lost work
1116
1137
  - **ALWAYS mirror to GitHub** if configured — keep mirrors in sync