forgedock 1.1.7 → 1.1.9

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 (97) hide show
  1. package/README.md +10 -33
  2. package/bin/cinema.mjs +23 -5
  3. package/bin/engine/invariants.mjs +361 -0
  4. package/bin/engine-cli.mjs +137 -11
  5. package/bin/env-detect.mjs +249 -0
  6. package/bin/forge-utils.mjs +364 -4
  7. package/bin/forgedock.mjs +1064 -135
  8. package/bin/hooks/pre-tool-use.mjs +182 -0
  9. package/bin/hooks/session-start.mjs +68 -3
  10. package/bin/init-detect.mjs +101 -12
  11. package/bin/journey.mjs +917 -16
  12. package/bin/labels.json +10 -0
  13. package/bin/recall.mjs +557 -0
  14. package/bin/registry.mjs +91 -3
  15. package/bin/runner.mjs +38 -2
  16. package/bin/tui.mjs +49 -3
  17. package/commands/adopt.md +1 -1
  18. package/commands/analytics.md +8 -8
  19. package/commands/audit.md +1 -1
  20. package/commands/autopilot.md +958 -77
  21. package/commands/ci-audit.md +1 -1
  22. package/commands/cleanup.md +139 -55
  23. package/commands/compat-audit.md +1 -1
  24. package/commands/deploy-info.md +1 -1
  25. package/commands/deploy-pr.md +1 -1
  26. package/commands/diagnose.md +1 -1
  27. package/commands/explain.md +1 -1
  28. package/commands/fix-ci.md +1 -1
  29. package/commands/forgedock-init.md +1 -1
  30. package/commands/incident-response.md +3 -3
  31. package/commands/issue.md +1 -1
  32. package/commands/optimize.md +586 -1
  33. package/commands/orchestrate/config.md +13 -3
  34. package/commands/orchestrate/phase-1-resolve.md +36 -26
  35. package/commands/orchestrate/phase-2-triage.md +1 -1
  36. package/commands/orchestrate/phase-2.5-synthesis.md +1 -1
  37. package/commands/orchestrate/phase-3-dependency.md +587 -48
  38. package/commands/orchestrate/phase-4-execution.md +1031 -57
  39. package/commands/orchestrate/phase-5-cleanup.md +1 -1
  40. package/commands/orchestrate/phase-6-report.md +248 -11
  41. package/commands/orchestrate/safety.md +2 -2
  42. package/commands/pipeline-health/phase-2-metrics.md +305 -1
  43. package/commands/pipeline-health/phase-4-proposals.md +127 -0
  44. package/commands/qa-sweep.md +1 -1
  45. package/commands/quality-gate.md +418 -1
  46. package/commands/recover-orphans.md +5 -5
  47. package/commands/replay.md +1 -1
  48. package/commands/review-pr-agents/api.md +2 -2
  49. package/commands/review-pr-agents/auth.md +2 -2
  50. package/commands/review-pr-agents/billing.md +2 -2
  51. package/commands/review-pr-agents/concurrency.md +2 -2
  52. package/commands/review-pr-agents/database.md +2 -2
  53. package/commands/review-pr-agents/frontend.md +2 -2
  54. package/commands/review-pr-agents/infra.md +2 -2
  55. package/commands/review-pr-agents/protocols.md +1 -1
  56. package/commands/review-pr-agents/scraper.md +2 -2
  57. package/commands/review-pr-agents/security.md +2 -2
  58. package/commands/review-pr-staging.md +41 -3
  59. package/commands/review-pr.md +462 -19
  60. package/commands/rollback.md +1 -1
  61. package/commands/scope.md +1 -1
  62. package/commands/security-audit.md +1 -1
  63. package/commands/spec-doctor.md +429 -0
  64. package/commands/test-gate.md +1 -1
  65. package/commands/validate.md +53 -1
  66. package/commands/work-on/build/architect.md +78 -1
  67. package/commands/work-on/build/context.md +360 -22
  68. package/commands/work-on/build/implement.md +3 -1
  69. package/commands/work-on/build/validate.md +29 -1
  70. package/commands/work-on/build.md +75 -6
  71. package/commands/work-on/close.md +624 -10
  72. package/commands/work-on/decompose.md +1 -1
  73. package/commands/work-on/investigate.md +235 -5
  74. package/commands/work-on/remediate.md +331 -0
  75. package/commands/work-on/review.md +1 -1
  76. package/commands/work-on-monolithic.md +1 -1
  77. package/commands/work-on.md +205 -66
  78. package/docs/CI.md +2 -2
  79. package/docs/CODEX.md +1 -1
  80. package/docs/CONFIG.md +186 -0
  81. package/docs/FORGE-PROTOCOL.md +101 -0
  82. package/docs/site/command-reference.md +31 -1
  83. package/docs/site/getting-started.md +9 -34
  84. package/docs/site/index.md +1 -1
  85. package/docs/spec/forge-protocol-v1.md +97 -5
  86. package/docs/spec/label-state-machine.md +6 -1
  87. package/package.json +3 -2
  88. package/packages/protocol/src/cli.js +374 -84
  89. package/packages/protocol/src/types.js +75 -1
  90. package/scripts/build-knowledge-index.mjs +1761 -0
  91. package/scripts/calibration.mjs +996 -0
  92. package/scripts/check-command-docs-drift.sh +4 -2
  93. package/scripts/danger-zones.mjs +789 -0
  94. package/scripts/doctor-pipeline-state.sh +88 -2
  95. package/scripts/select-fix-targets.sh +234 -0
  96. package/scripts/transition-label.sh +245 -6
  97. package/scripts/refresh-bot-token.sh +0 -97
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -21,26 +21,170 @@ done
21
21
 
22
22
  Aggregate into the batch-level analytics for Step 6B.
23
23
 
24
- **Also collect the machine-readable summary cards** (`<!-- FORGE:CARD {json} -->`, embedded in
25
- each issue's `FORGE:TRAJECTORY` comment by `/work-on` close phase). These power the per-issue and
26
- batch cards in Step 6C:
24
+ **Also collect the machine-readable summary cards** (embedded in each issue's `FORGE:TRAJECTORY`
25
+ comment by `/work-on` close phase as a `<!-- FORGE:CARD: v1 sha:... b64:... -->` line). These
26
+ power the per-issue and batch cards in Step 6C.
27
+
28
+ **CODEC PATH (forge#1727)**: Cards are now Base64url-encoded. Use the protocol CLI's `parse`
29
+ subcommand to decode them — do NOT use the old `sed -n 's/.*<!-- FORGE:CARD \(.*\) -->.*/\1/p'`
30
+ regex extraction, which only worked for the deprecated inline-JSON form:
27
31
 
28
32
  ```bash
29
33
  CARDS=""
30
34
  for NUM in {all_completed_issue_numbers}; do
31
- CARD=$(gh api repos/{GH_REPO}/issues/${NUM}/comments \
32
- --jq '.[] | select(.body | contains("FORGE:CARD")) | .body' 2>/dev/null \
33
- | sed -n 's/.*<!-- FORGE:CARD \(.*\) -->.*/\1/p' | head -1)
34
- [ -n "$CARD" ] && CARDS="${CARDS}${CARD}"$'\n'
35
+ # Fetch the TRAJECTORY comment body for this issue
36
+ TRAJ_BODY=$(gh api repos/{GH_REPO}/issues/${NUM}/comments \
37
+ --jq '[.[] | select(.body | contains("FORGE:CARD:"))] | last | .body // ""' 2>/dev/null || true)
38
+ if [ -n "$TRAJ_BODY" ]; then
39
+ # Decode via protocol CLI — handles both Base64url form (forge#1727) and gracefully
40
+ # exits 1 (skips) for pre-migration inline-JSON entries.
41
+ CARD=$(echo "$TRAJ_BODY" | node packages/protocol/src/cli.js parse --type CARD 2>/dev/null || true)
42
+ [ -n "$CARD" ] && CARDS="${CARDS}${CARD}"$'\n'
43
+ fi
44
+ done
45
+ # CARDS is now a newline-delimited list of per-issue JSON objects decoded from FORGE:CARD payloads.
46
+ # Skip any issue whose card is absent — pre-card runs or non-merged terminal states degrade gracefully.
47
+ ```
48
+
49
+ ### Step 6A.5: Collect merge-ready PRs (`workflow:awaiting-merge`) <!-- Added: forge#1811 -->
50
+
51
+ **Why a separate collection pass**: `workflow:awaiting-merge` issues are open (their PR hasn't
52
+ merged yet), so they are excluded from `{all_completed_issue_numbers}` above — that set only
53
+ contains issues that reached a closed/merged terminal state. Iterate the full batch issue set
54
+ (`{all_batch_issue_numbers}` — same prose-variable used by the batch-wide loops in
55
+ `phase-4-execution.md`) instead, and filter to the awaiting-merge label.
56
+
57
+ For each awaiting-merge issue, resolve its open PR using the `"Closes #N"`-anchored search
58
+ established by forge#1634/#1646 — **do not** use a bare-number `search "${NUM} in:body"`, which
59
+ can false-positive on changelogs or cross-references and resolve the wrong PR:
60
+
61
+ ```bash
62
+ MERGE_READY_PRS=() # each entry: "NUM|PR_NUMBER|TITLE"
63
+
64
+ for NUM in {all_batch_issue_numbers}; do
65
+ IS_AWAITING=$(gh issue view "$NUM" -R {GH_REPO} --json labels \
66
+ --jq '[.labels[].name | select(. == "workflow:awaiting-merge")] | length' 2>/dev/null || echo "0")
67
+ [ "$IS_AWAITING" -gt 0 ] || continue
68
+
69
+ TITLE=$(gh issue view "$NUM" -R {GH_REPO} --json title --jq '.title' 2>/dev/null || echo "")
70
+
71
+ # Anchor on "Closes #N" first (forge#1634/#1646 precedent).
72
+ PR_NUM=$(gh pr list -R {GH_REPO} --state open --search "\"Closes #${NUM}\" in:body" \
73
+ --json number,updatedAt --jq 'sort_by(.updatedAt) | last | .number' 2>/dev/null || echo "")
74
+ if [ -z "$PR_NUM" ]; then
75
+ # Fallback: a PR may close the issue via "Fixes #N"/"Resolves #N" rather than
76
+ # "Closes #N". Use the bare number only to generate candidates, then RE-VALIDATE
77
+ # each candidate's body against an anchored (Closes|Fixes|Resolves) #N word-boundary
78
+ # regex before accepting it — never accept a bare mention (changelog / cross-reference),
79
+ # which could resolve the wrong PR into the one-shot batch-merge command. This mirrors
80
+ # the recover-orphans.md re-validation precedent. <!-- forge#1822 -->
81
+ PR_NUM=$(gh pr list -R {GH_REPO} --state open --search "${NUM} in:body" \
82
+ --json number,updatedAt,body \
83
+ --jq --arg n "${NUM}" '[ .[] | select(.body | test("(?i)(close[sd]?|fix(e[sd])?|resolve[sd]?)\\s+#" + $n + "\\b")) ] | sort_by(.updatedAt) | last | .number // empty' 2>/dev/null || echo "")
84
+ fi
85
+
86
+ [ -n "$PR_NUM" ] && MERGE_READY_PRS+=("${NUM}|${PR_NUM}|${TITLE}")
87
+ done
88
+ # MERGE_READY_PRS now holds one entry per merge-ready PR in the batch (empty array if none).
89
+ # Feeds the "### Merge-Ready" report block and the Implementation Results table row (Step 6B).
90
+
91
+ # Pre-build the batch-merge command (rendered verbatim as {MERGE_READY_CMD} in Step 6B —
92
+ # computed here, not inside the report template, so Step 6B stays plain template text with no
93
+ # nested code fences of its own). `gh pr merge` accepts only ONE PR selector per invocation,
94
+ # so chain one `gh pr merge` per PR with && rather than passing a space-separated list (which
95
+ # `gh` rejects). <!-- forge#1838 review: BUG-3 -->
96
+ MERGE_READY_CMD=""
97
+ if [ "${#MERGE_READY_PRS[@]}" -gt 0 ]; then
98
+ for _entry in "${MERGE_READY_PRS[@]}"; do
99
+ _pr=$(printf '%s' "$_entry" | cut -d'|' -f2)
100
+ [ -z "$_pr" ] && continue
101
+ [ -n "$MERGE_READY_CMD" ] && MERGE_READY_CMD="${MERGE_READY_CMD} && "
102
+ MERGE_READY_CMD="${MERGE_READY_CMD}gh pr merge ${_pr} --merge -R {GH_REPO}"
103
+ done
104
+ fi
105
+ ```
106
+
107
+ ### Step 6A.6: Collect blocked-on-human-merge dependents (`blocked-on-human-merge`) <!-- Added: forge#1812 -->
108
+
109
+ **Why a separate collection pass**: A `blocked-on-human-merge` issue is a *dependent* of a `GATED`
110
+ predecessor (`needs-human` or `workflow:awaiting-merge` — see `phase-4-execution.md` Step 4B's
111
+ Predecessor Classification), not itself blocked or failed. It has no PR of its own yet — it hasn't
112
+ even been dispatched. It is excluded from both `{all_completed_issue_numbers}` (never ran) and
113
+ `MERGE_READY_PRS` (Step 6A.5, which only covers issues that themselves reached `workflow:awaiting-merge`).
114
+ Reporting it as `⏭ Skipped (dep)` would be wrong — that label means the predecessor `FAILED` and the
115
+ work is abandoned; this state means the work is queued and will auto-dispatch on merge, no manual
116
+ re-run required.
117
+
118
+ ```bash
119
+ BLOCKED_ON_MERGE=() # each entry: "NUM|GATING_PRED|GATING_PR|TITLE"
120
+
121
+ for NUM in {all_batch_issue_numbers}; do
122
+ IS_BLOCKED=$(gh issue view "$NUM" -R {GH_REPO} --json labels \
123
+ --jq '[.labels[].name | select(. == "blocked-on-human-merge")] | length' 2>/dev/null || echo "0")
124
+ [ "$IS_BLOCKED" -gt 0 ] || continue
125
+
126
+ TITLE=$(gh issue view "$NUM" -R {GH_REPO} --json title --jq '.title' 2>/dev/null || echo "")
127
+ GATING_PRED=$(gh api repos/{GH_REPO}/issues/${NUM}/comments \
128
+ --jq '[.[] | select(.body | contains("FORGE:BLOCKED_ON_HUMAN_MERGE"))] | last | (.body | capture("Gating predecessor\\*\\*: #(?<p>[0-9]+)").p) // ""' 2>/dev/null || echo "")
129
+ GATING_PR=""
130
+ if [ -n "$GATING_PRED" ]; then
131
+ GATING_PR=$(gh pr list -R {GH_REPO} --state open --search "\"Closes #${GATING_PRED}\" in:body" \
132
+ --json number --jq '.[0].number // empty' 2>/dev/null || echo "")
133
+ fi
134
+
135
+ BLOCKED_ON_MERGE+=("${NUM}|${GATING_PRED}|${GATING_PR}|${TITLE}")
136
+ done
137
+ # BLOCKED_ON_MERGE now holds one entry per dependent queued behind a human-gated predecessor
138
+ # (empty array if none). Feeds the "Blocked-on-Merge" report block and the Implementation
139
+ # Results table row (Step 6B).
140
+ ```
141
+
142
+ ### Step 6A.7: Determine idle-policy end state <!-- Added: forge#1814 -->
143
+
144
+ **Why**: `phase-4-execution.md` Step 4B item 6.7 can end the live monitoring loop in two visibly
145
+ different ways: a **clean/paused drain** (every original-batch issue reached `DONE`/`FAILED`, or
146
+ is `blocked-on-human-merge` with no cascade activity left to suppress), or an **idle-policy
147
+ pause** (`BATCH_FULLY_GATED` was true at the moment the batch stopped, meaning Step 4C actively
148
+ suppressed one or more newly-spawned review-finding dispatches). Both are legitimate stopping
149
+ points, but rendering an idle-policy pause under the same "Orchestration Complete" header as a
150
+ genuine full drain misrepresents a paused run as finished — the whole point of #1814 is to make
151
+ this pause visible, not silent.
152
+
153
+ ```bash
154
+ # BATCH_FULLY_GATED is the value computed in phase-4-execution.md Step 4B item 6.7 at the point
155
+ # the live loop stopped — threaded into Phase 6 as a plain boolean, not recomputed here (Phase 6
156
+ # runs after the live loop has already ended, so re-running item 6.7's per-issue classification
157
+ # would be redundant; carry the value forward instead).
158
+ ORCHESTRATION_ENDED_IDLE="${BATCH_FULLY_GATED:-false}"
159
+
160
+ # Count of findings deferred this run for the idle-policy reason specifically (distinct from
161
+ # generation>=2 and comment/typo/P3-same-file deferrals, which are unaffected by this policy).
162
+ IDLE_POLICY_DEFERRED_COUNT=0
163
+ for FINDING_NUM in "${DEFERRED_FINDINGS[@]:-}"; do
164
+ [ -z "$FINDING_NUM" ] && continue
165
+ echo "${DEFERRED_REASONS[$FINDING_NUM]:-}" | grep -qi "batch fully human-gated" && \
166
+ IDLE_POLICY_DEFERRED_COUNT=$((IDLE_POLICY_DEFERRED_COUNT + 1))
35
167
  done
36
- # CARDS is now a newline-delimited list of per-issue JSON objects (skip any issue whose
37
- # card is absent — pre-card runs or non-merged terminal states degrade gracefully).
38
168
  ```
39
169
 
40
170
  ### Step 6B: Present consolidated report
41
171
 
172
+ Always rendered in full, once, regardless of `{NARRATION_MODE}` — `pipeline.narration` (see `config.md`) only gates the per-completion recap in Step 4B item 8. This is the one place full tables are guaranteed to appear.
173
+
42
174
  ```
175
+ {IF ORCHESTRATION_ENDED_IDLE == "true":}
176
+ ## Orchestration Paused — Idle (waiting on {#MERGE_READY_PRS[@]:-0} + {#BLOCKED_ON_MERGE[@]:-0} merge(s))
177
+
178
+ This run stopped because the remaining batch is fully human-gated — every original issue is
179
+ merged/invalid, or blocked on a human decision/merge. {IDLE_POLICY_DEFERRED_COUNT} newly-spawned
180
+ review-finding issue(s) were deferred (not dispatched) rather than added to the open-issue count.
181
+ See "Merge-Ready" and "Blocked-on-Merge" below for the exact action needed to resume. Re-run
182
+ `/orchestrate` (or let a still-running session's live wake pick it up) once you've merged the
183
+ PR(s) listed there — deferred findings are re-evaluated automatically by the next Completion
184
+ Sweep (Step 4F.2.5) once the batch is no longer fully gated.
185
+ {ELSE:}
43
186
  ## Orchestration Complete
187
+ {END IF}
44
188
 
45
189
  **Scope**: {milestone / issue list}
46
190
  **Duration**: {approximate time}
@@ -61,7 +205,62 @@ done
61
205
  | #{N1} | {title} | from #{INV1} | ✓ Merged | #{PR} | staging |
62
206
  | #{C} | {title} | original | ✗ Invalid | — | — |
63
207
  | #{D} | {title} | original | ⚠ Blocked | — | — |
208
+ | #{F} | {title} | original | ⏸ Awaiting Merge | #{PR} | staging |
64
209
  | #{E} | {title} | original | ⏭ Skipped (dep) | — | — |
210
+ | #{G} | {title} | original | 🔗 Blocked-on-Merge (gated by #{PRED}) | — | — |
211
+
212
+ `⏸ Awaiting Merge` (`workflow:awaiting-merge`) is structurally distinct from `⚠ Blocked`
213
+ (`needs-human`): a Blocked row means the pipeline hit something it cannot resolve on its own
214
+ and a human must diagnose it; an Awaiting Merge row means the PR was already remediated and
215
+ re-reviewed to a clean APPROVED verdict — a human only needs to click merge. Never render an
216
+ awaiting-merge PR as `⚠ Blocked`.
217
+
218
+ `🔗 Blocked-on-Merge` (`blocked-on-human-merge`, forge#1812) is distinct from all three of the
219
+ above: it is a *dependent* of a GATED predecessor (one currently `⚠ Blocked` or `⏸ Awaiting
220
+ Merge`) — the dependent issue itself was never dispatched and has no problem of its own. It is
221
+ also distinct from `⏭ Skipped (dep)`, which means the predecessor `FAILED` outright and the work
222
+ was abandoned. A Blocked-on-Merge row means the work is queued and will auto-dispatch the instant
223
+ the gating predecessor's PR merges — no manual `/orchestrate` re-run required. Never render a
224
+ blocked-on-human-merge dependent as `⏭ Skipped (dep)`.
225
+
226
+ {IF `MERGE_READY_PRS` (Step 6A.5) is non-empty:}
227
+
228
+ ### Merge-Ready — {N} PR(s) awaiting only a human merge
229
+
230
+ These PRs were remediated and re-reviewed to a clean `APPROVED` verdict after an earlier
231
+ `needs-human` escalation. They do not need further diagnosis — merge them to land the batch.
232
+
233
+ | # | Issue | PR | Title |
234
+ |---|-------|----|----|
235
+ {one row per `MERGE_READY_PRS` entry: | {n} | #{NUM} | #{PR} | {title} |}
236
+
237
+ **One action to land all {N}:** `{MERGE_READY_CMD}`
238
+
239
+ (`{MERGE_READY_CMD}` is pre-built in Step 6A.5 by chaining one `gh pr merge <pr> --merge` per PR
240
+ in `MERGE_READY_PRS` with `&&` — `gh pr merge` takes a single PR selector, so a space-separated
241
+ list would be rejected. Render it verbatim here as plain text, no code fence.)
242
+
243
+ {ELSE: omit this section entirely — do not print an empty "Merge-Ready" heading.}
244
+
245
+ {IF `BLOCKED_ON_MERGE` (Step 6A.6) is non-empty:}
246
+
247
+ ### Blocked-on-Merge — {N} issue(s) queued behind a human-gated predecessor
248
+
249
+ These issues are dependents of a predecessor that is currently `⚠ Blocked` or `⏸ Awaiting Merge`.
250
+ They were never dispatched — no work has started on them — and they need no action right now.
251
+ Each will auto-dispatch the instant its gating predecessor's PR merges (live-session wake via
252
+ `phase-4-execution.md` Step 4B item 6.6, or next-`/orchestrate`-invocation wake via
253
+ `phase-3-dependency.md`'s wake/compaction reconstruction). This batch is a **paused drain**, not
254
+ a complete one — re-running `/orchestrate` after merging the gating PR(s) below will pick these up.
255
+
256
+ | # | Issue | Gated By | Gating PR | Title |
257
+ |---|-------|----------|-----------|-------|
258
+ {one row per `BLOCKED_ON_MERGE` entry: | {n} | #{NUM} | #{GATING_PRED} | #{GATING_PR} | {title} |}
259
+
260
+ **To unblock**: merge the gating PR(s) referenced above, then re-run `/orchestrate` (or let the
261
+ current session's live wake pick them up automatically if it is still running).
262
+
263
+ {ELSE: omit this section entirely — do not print an empty "Blocked-on-Merge" heading.}
65
264
 
66
265
  ### Review-Spawned Issues
67
266
 
@@ -88,9 +287,11 @@ done
88
287
 
89
288
  ### Summary
90
289
  - **Investigations**: {N} completed, spawned {M} new issues
91
- - **Review findings**: {N} spawned, {M} resolved in-batch, {K} swept, {J} deferred (cosmetic), {L} deferred (gen2)
290
+ - **Review findings**: {N} spawned, {M} resolved in-batch, {K} swept, {J} deferred (cosmetic), {L} deferred (gen2), {IDLE_POLICY_DEFERRED_COUNT} deferred (idle policy, forge#1814)
92
291
  - **Succeeded**: {N} issues resolved (implementation + sweep)
93
292
  - **Failed**: {N} issues need attention
293
+ - **Merge-ready**: {#MERGE_READY_PRS[@]:-0} PRs awaiting only a human merge (see "Merge-Ready" section above)
294
+ - **Blocked-on-merge**: {#BLOCKED_ON_MERGE[@]:-0} issues queued behind a human-gated predecessor, will auto-dispatch on merge (see "Blocked-on-Merge" section above)
94
295
  - **Skipped**: {N} issues (dependency failures)
95
296
 
96
297
  ### Post-Batch Cleanup
@@ -120,10 +321,44 @@ done
120
321
  | Contract→code divergences | {N} (agents that updated contract mid-build) |
121
322
  | Review findings created | {N_total} |
122
323
  | Findings queued after cascade control | {N_queued}/{N_total} ({N_deferred} deferred — see Step 4C) |
324
+ | Findings deferred — idle policy (forge#1814) | {IDLE_POLICY_DEFERRED_COUNT} (batch fully human-gated at defer time — see Step 4B item 6.7) |
325
+ | P3 findings clubbed (surface-area batching, forge#1818) | {SURFACE_BATCH_COUNT:-0} batch(es) covering {#SURFACE_BATCHED_FINDINGS[@]:-0} finding(s) — see Step 4C |
326
+ | Findings deferred — token budget (forge#1858) | {#TOKEN_DEFERRED[@]:-0} (batch spend {BATCH_TOKEN_SPEND:-0}/{TOKEN_BUDGET:-uncapped} tokens — see Step 4C) |
123
327
  | Competing recommendations reconciled (Phase 2.5) | {RECONCILED_COUNT} (investigation plans arbitrated in place + serialized) |
124
328
  | Findings validated | {N} |
125
329
  | False positives | {N} ({%}) |
126
330
  | Anomalies flagged | {N} |
331
+ | Budget limit ($) | ${BUDGET_LIMIT:-uncapped} |
332
+ | Projected spend (dispatched issues) | ${PROJECTED_SPEND:-—} |
333
+ | Actual spend (best-effort telemetry) | ${ACTUAL_SPEND:-—} |
334
+ | Issues deferred (budget ceiling) | {#DEFERRED_BUDGET_ISSUES[@]:-0} |
335
+ | ε-reserve issued (no-prior dispatch) | {EPSILON_DISPATCHED:-no} |
336
+ | **Value-weighted throughput** | **{VALUE_THROUGHPUT:-—} value-pts/USD** |
337
+
338
+ `value-weighted throughput` = Σ(priority_weight × danger_zone_weight) for **merged** issues ÷ actual spend (USD). A higher value means more high-priority issues were resolved per dollar. Trend this across runs via `/pipeline-health` to measure the economic scheduling ROI. <!-- Added: forge#1743 -->
339
+
340
+ `P3 findings clubbed` and `Findings deferred — token budget` are a DIFFERENT mechanism from the `$`-denominated `Budget limit`/`Projected spend`/`Actual spend`/`Issues deferred (budget ceiling)` rows above (forge#1743, opt-in `--budget N` flag, gates the *original* issue dispatch order). The token-budget row reads the always-on, token-denominated ceiling that scopes ONLY to Step 4C's review-finding cascade dispatch (forge#1858); `SURFACE_BATCH_COUNT` and `SURFACE_BATCHED_FINDINGS` are populated by the same Step 4C surface-area-batching block (forge#1818) that clubs P3 findings sharing a file/leaf-directory into one dispatched pipeline. Both degrade gracefully to `0`/`uncapped` when the batch had no review findings or ran with defaults. <!-- Added: forge#1858 -->
341
+
342
+ **Compute value-weighted throughput** (populate before rendering the table):
343
+
344
+ ```bash
345
+ # Collect value scores for merged issues (ISSUE_VALUE[] populated in Step 3E.5)
346
+ MERGED_VALUE_SUM="0"
347
+ for NUM in {all_completed_issue_numbers}; do
348
+ IS_MERGED=$(gh issue view "$NUM" -R {GH_REPO} --json labels \
349
+ --jq '[.labels[].name | select(. == "workflow:merged")] | length' 2>/dev/null || echo "0")
350
+ if [ "$IS_MERGED" -gt 0 ] && [ -n "${ISSUE_VALUE[$NUM]:-}" ]; then
351
+ MERGED_VALUE_SUM=$(echo "scale=4; $MERGED_VALUE_SUM + ${ISSUE_VALUE[$NUM]}" | bc 2>/dev/null \
352
+ || echo "$MERGED_VALUE_SUM")
353
+ fi
354
+ done
355
+
356
+ if [ "${ACTUAL_SPEND:-0}" != "0" ] && echo "${ACTUAL_SPEND:-0}" | grep -qP '^\d+(\.\d+)?$'; then
357
+ VALUE_THROUGHPUT=$(echo "scale=2; $MERGED_VALUE_SUM / $ACTUAL_SPEND" | bc 2>/dev/null || echo "—")
358
+ else
359
+ VALUE_THROUGHPUT="—" # no spend telemetry available
360
+ fi
361
+ ```
127
362
 
128
363
  **Domain breakdown**: {N} scraping, {N} frontend, {N} billing, ...
129
364
  **Routing**: {N} fast-lane, {N} feature-lane
@@ -137,8 +372,10 @@ done
137
372
  - Contract divergences > 20% → investigation quality may need improvement
138
373
  - **Idle% > 50%** → agents stalling at phase boundaries; check work-on routing loop
139
374
  - **Avg resumes > 1** → orchestrator having to compensate for agent stops
375
+ - **Value-weighted throughput trending down** → high-value issues deferred or blocked; check Step 3E.5 score distribution
140
376
 
141
377
  ### Next Steps
378
+ {If ORCHESTRATION_ENDED_IDLE == "true": "This run paused on the human-gated idle policy (forge#1814) — merge the PR(s) listed in the Merge-Ready section above, then re-run `/orchestrate` (or let a still-running session's live wake pick it up automatically) to resume dispatch of blocked-on-merge dependents and re-evaluate the {IDLE_POLICY_DEFERRED_COUNT} deferred finding(s)."}
142
379
  {If milestone and all issues done: "Milestone ready to ship. Run `/milestone ship {slug}` when ready. The ship command includes a pre-merge hunk-loss audit (Step 2.5) that detects staging-only hunks in milestone-modified files and rebases the milestone branch to absorb them before creating the PR — protecting against squash-merge regressions."}
143
380
  {If some failed: "Issues #{X}, #{Y} need manual attention. Re-run with `/work-on #{X}` after resolving blockers."}
144
381
  {If fast-lane: "All fixes merged to staging. Merge staging → main via GitHub web UI when ready to deploy."}
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -10,7 +10,7 @@ install: internal
10
10
 
11
11
  1. **Every agent MUST invoke `/work-on` via the Skill tool.** No custom prompts. No manual implementation. Copy the Phase 4A template, fill in variables, done. (See HARD RULES at top of file.)
12
12
  2. **NEVER merge anything to `main`** — agents merge to `staging` or `milestone/{slug}` only
13
- 3. **No artificial concurrency limit by default** — spawn as many agents as there are independent issues. Only file overlap and dependencies require sequencing. Set `forge.yaml → orchestration.max_concurrent` to cap in-flight workers and prevent rate-limit storms on large batches (see Engine mode § Concurrency model).
13
+ 3. **Concurrency is capped by default (12 in-flight agents)** <!-- Updated: forge#1912 --> — file overlap and dependencies still determine which issues are *eligible* to run in parallel, but Phase 4 never dispatches more than `MAX_CONCURRENT` at once (default 12); the rest queue and dispatch as running agents complete. Set `forge.yaml → orchestration.max_concurrent` to raise or lower the cap (see Engine mode § Concurrency model, and `phase-4-execution.md` Step 4A-pre.0.2).
14
14
  4. **Always confirm with user before launching** — Step 3E is the mandatory checkpoint
15
15
  5. **No retries** — if an agent fails, report it and move on
16
16
  6. **Respect existing work** — skip issues already being worked on (`workflow:building`, `workflow:in-review`)