forgedock 1.1.8 → 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 (84) hide show
  1. package/README.md +10 -33
  2. package/bin/cinema.mjs +23 -5
  3. package/bin/engine-cli.mjs +137 -11
  4. package/bin/env-detect.mjs +249 -0
  5. package/bin/forge-utils.mjs +364 -4
  6. package/bin/forgedock.mjs +1064 -135
  7. package/bin/hooks/session-start.mjs +68 -3
  8. package/bin/init-detect.mjs +101 -12
  9. package/bin/journey.mjs +917 -16
  10. package/bin/recall.mjs +24 -7
  11. package/bin/registry.mjs +91 -3
  12. package/bin/runner.mjs +38 -2
  13. package/bin/tui.mjs +49 -3
  14. package/commands/adopt.md +1 -1
  15. package/commands/analytics.md +8 -8
  16. package/commands/audit.md +1 -1
  17. package/commands/autopilot.md +1 -1
  18. package/commands/ci-audit.md +1 -1
  19. package/commands/cleanup.md +87 -52
  20. package/commands/compat-audit.md +1 -1
  21. package/commands/deploy-info.md +1 -1
  22. package/commands/deploy-pr.md +1 -1
  23. package/commands/diagnose.md +1 -1
  24. package/commands/explain.md +1 -1
  25. package/commands/fix-ci.md +1 -1
  26. package/commands/forgedock-init.md +1 -1
  27. package/commands/incident-response.md +3 -3
  28. package/commands/issue.md +1 -1
  29. package/commands/optimize.md +1 -1
  30. package/commands/orchestrate/config.md +13 -3
  31. package/commands/orchestrate/phase-1-resolve.md +1 -1
  32. package/commands/orchestrate/phase-2-triage.md +1 -1
  33. package/commands/orchestrate/phase-2.5-synthesis.md +1 -1
  34. package/commands/orchestrate/phase-3-dependency.md +128 -20
  35. package/commands/orchestrate/phase-4-execution.md +598 -43
  36. package/commands/orchestrate/phase-5-cleanup.md +1 -1
  37. package/commands/orchestrate/phase-6-report.md +51 -3
  38. package/commands/orchestrate/safety.md +2 -2
  39. package/commands/qa-sweep.md +1 -1
  40. package/commands/quality-gate.md +1 -1
  41. package/commands/recover-orphans.md +5 -5
  42. package/commands/replay.md +1 -1
  43. package/commands/review-pr-agents/api.md +2 -2
  44. package/commands/review-pr-agents/auth.md +2 -2
  45. package/commands/review-pr-agents/billing.md +2 -2
  46. package/commands/review-pr-agents/concurrency.md +2 -2
  47. package/commands/review-pr-agents/database.md +2 -2
  48. package/commands/review-pr-agents/frontend.md +2 -2
  49. package/commands/review-pr-agents/infra.md +2 -2
  50. package/commands/review-pr-agents/protocols.md +1 -1
  51. package/commands/review-pr-agents/scraper.md +2 -2
  52. package/commands/review-pr-agents/security.md +2 -2
  53. package/commands/review-pr-staging.md +41 -3
  54. package/commands/review-pr.md +71 -6
  55. package/commands/rollback.md +1 -1
  56. package/commands/scope.md +1 -1
  57. package/commands/security-audit.md +1 -1
  58. package/commands/test-gate.md +1 -1
  59. package/commands/validate.md +1 -1
  60. package/commands/work-on/build/architect.md +1 -1
  61. package/commands/work-on/build/context.md +9 -6
  62. package/commands/work-on/build/implement.md +1 -1
  63. package/commands/work-on/build/validate.md +1 -1
  64. package/commands/work-on/build.md +31 -6
  65. package/commands/work-on/close.md +3 -2
  66. package/commands/work-on/decompose.md +1 -1
  67. package/commands/work-on/investigate.md +14 -5
  68. package/commands/work-on/remediate.md +331 -0
  69. package/commands/work-on/review.md +1 -1
  70. package/commands/work-on-monolithic.md +1 -1
  71. package/commands/work-on.md +194 -56
  72. package/docs/CI.md +2 -2
  73. package/docs/CODEX.md +1 -1
  74. package/docs/CONFIG.md +186 -0
  75. package/docs/site/command-reference.md +31 -1
  76. package/docs/site/getting-started.md +9 -34
  77. package/docs/site/index.md +1 -1
  78. package/docs/spec/label-state-machine.md +1 -1
  79. package/package.json +2 -1
  80. package/packages/protocol/src/types.js +1 -1
  81. package/scripts/check-command-docs-drift.sh +4 -2
  82. package/scripts/danger-zones.mjs +19 -12
  83. package/scripts/transition-label.sh +101 -13
  84. package/scripts/refresh-bot-token.sh +0 -97
@@ -11,7 +11,7 @@ argument-hint: [issue number or "next" to pick highest priority]
11
11
 
12
12
  Orchestrator for the full issue lifecycle: investigate → decompose (if needed) → build → review → merge → close. GitHub issues are the persistent context layer — read existing comments before starting, write structured reports back, use `workflow:*` labels to track state.
13
13
 
14
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
14
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
15
15
  **NEVER use plan mode (EnterPlanMode).**
16
16
  **NEVER use the Agent tool** — this spec uses `Skill(...)` for sub-phase dispatch. The Agent tool spawns opaque subprocesses that bypass phase protocols, skip FORGE annotations, and cannot be constrained by allowed-tools. Always use `Skill(skill="...", args="...")` for sub-phase invocations.
17
17
 
@@ -30,10 +30,16 @@ Orchestrator for the full issue lifecycle: investigate → decompose (if needed)
30
30
  ### Compaction Resilience
31
31
 
32
32
  1. Write state to GitHub after EVERY significant step
33
- 2. Re-read GitHub state at the START of each phase (don't rely on in-memory context)
33
+ 2. Read full GitHub state (issue body + comments + labels) ONCE, in Phase 0B. Carry those values in-context for the rest of the run — do NOT re-fetch them at later phase boundaries. The only trigger for a fresh re-read of something already read this session is a **detected compaction**: if a later phase needs a value (issue body, a specific FORGE:* comment, labels) that this session does not actually have in its visible context — because compaction dropped the earlier tool output, or this is a brand-new session resuming mid-pipeline — re-fetch ONLY that missing value, not the full state.
34
34
  3. After compaction: re-read issue (body + comments + labels) to reconstruct state
35
35
  4. Key principle: A NEW session running `/work-on {number}` should pick up where the last left off by reading GitHub state alone
36
36
 
37
+ **Session state cache convention**: Phases 1A.5, 3A, and 5A previously re-ran `gh issue view`/`gh api .../comments` unconditionally "for safety." That default is now inverted: reuse the value if this session already produced it (e.g., you already have `ISSUE_BODY` from Phase 0B, or already read the FORGE:INVESTIGATOR comment during Phase 1). Only issue the `gh` call if the value is genuinely absent from context. This is what makes re-reads free for a normal single-turn run while remaining exactly as resilient across a real compaction or resume. (Phase 6A's body read is a deliberate exception — see its note — because it immediately precedes a body-mutating write and Phase 5 can involve an external `/review-pr` process.)
38
+
39
+ ### Orchestration Flag
40
+
41
+ `UNDER_ORCHESTRATION` — resolved once in Phase 0A. Defaults to `false` (solo run). Set to `true` when the invocation args include `--under-orchestration` (this is how `/orchestrate` dispatches `/work-on`; see `commands/orchestrate/phase-4-execution.md`). This flag gates the 4 heartbeat comments below — they exist solely to feed `/orchestrate`'s Step 4B.5 time-based stall detector (which reads the last comment's `updated_at`) and have no consumer in a solo run.
42
+
37
43
  ### Universal Phase Dispatcher
38
44
 
39
45
  <!-- FORGE:DISPATCHER — This is the SINGLE source of truth for phase transitions. Every phase boundary references this section. -->
@@ -76,7 +82,7 @@ Orchestrator for the full issue lifecycle: investigate → decompose (if needed)
76
82
 
77
83
  <!-- FORGE:SPAWN_POLICY — Canonical spawn-decision table. Sibling specs (orchestrate.md, review-pr.md) link to this section. Sub-issues #1276–#1279 reference this table. -->
78
84
 
79
- **Default: run inline.** Every skill, phase, and sub-agent runs inline in the current context unless one of the three criteria below explicitly applies. A sub-agent buys exactly two things — parallelism and context isolation. If neither is needed, forking is waste.
85
+ **Default: run inline.** Every skill, phase, and sub-agent runs inline in the current context unless one of the four criteria below explicitly applies. A sub-agent buys exactly three things — parallelism, context isolation, and (below) prompt-cache preservation. If none is needed, forking is waste.
80
86
 
81
87
  ### Spawn-Decision Table
82
88
 
@@ -85,8 +91,9 @@ Orchestrator for the full issue lifecycle: investigate → decompose (if needed)
85
91
  | a | **Parallel fan-out** — two or more independent work units can execute concurrently and the total wall time saving justifies the fork overhead | YES — spawn one sub-agent per work unit | `/orchestrate` dispatching multiple `/work-on` agents; `review-pr` spawning domain-specific reviewers in parallel |
86
92
  | b | **Fresh-context isolation** — the work unit is a structured review or audit whose value depends on seeing the artefact without the builder's accumulated context bias, AND the review result is load-bearing for the merge decision | YES — spawn a dedicated sub-agent | Phase 5C review-fork when build context is large (see Row c for the quantitative threshold) |
87
93
  | c | **Parent context near overflow** — the parent agent has made ≥20 Skill invocations OR the build changed ≥10 files, meaning delegating review inline risks a mid-review token overflow | YES — spawn a fresh sub-agent for review | Phase 5C: `Skill(skill="work-on/review", …)` instead of direct `review-pr` invocation |
94
+ | d | **Prompt-cache TTL** — the sub-operation (multi-domain review, multi-iteration quality-gate) is expected to run for several minutes, longer than Anthropic's ~5-minute prompt-cache TTL. Leaving it inline lets the parent's already-large accumulated context (investigation/contract/context/architect/implement annotations) sit idle past the TTL; the parent's next turn then re-hydrates that entire context **uncached**, roughly doubling effective token cost for that turn. This is independent of build size — a 1-file fix idles the parent exactly as long as a 20-file one once the sub-operation starts running | YES — spawn a fresh sub-agent, **unconditionally**, regardless of file count or Skill-invocation count | Phase 5C review (always forks — Row d supersedes Row c's threshold as the controlling reason); Phase 3G quality-gate loop (forks under Row d even though it never qualified under Row c) <!-- Added: forge#1825 --> |
88
95
 
89
- **If none of the three rows match: run inline.** Do not fork for convenience, narrative clarity, or to avoid reading a large file. Context cost of a fork (spawning, context reconstruction, result aggregation) is paid every time, even when parallelism or isolation adds no value.
96
+ **If none of the four rows match: run inline.** Do not fork for convenience, narrative clarity, or to avoid reading a large file. Context cost of a fork (spawning, context reconstruction, result aggregation) is paid every time, even when parallelism, isolation, or cache preservation adds no value.
90
97
 
91
98
  ### Depth Budget
92
99
 
@@ -98,15 +105,30 @@ Orchestrator for the full issue lifecycle: investigate → decompose (if needed)
98
105
  |-------|-------|-------|
99
106
  | 1 | `/orchestrate` | Top-level dispatcher — never implements directly |
100
107
  | 2 | `/work-on` | Issue pipeline — runs build phases inline |
101
- | 3 | Parallel reviewers (Row a/b/c fork) | Domain review agents spawned by Phase 5C |
108
+ | 3 | Parallel reviewers (Row a/b/c/d fork); quality-gate loop (Row d fork) | Domain review agents spawned by Phase 5C; quality-gate sub-agent spawned by Phase 3G |
102
109
 
103
- **Build phases (3A–3M) run inline at depth 2** — they are sequential sub-phases of `/work-on`, not independent agents. Forking the build into a sub-agent (depth 3) is a violation of Row a/b/c unless the build itself fans out independently scoped work units.
110
+ **Build phases (3A–3M) run inline at depth 2** — they are sequential sub-phases of `/work-on`, not independent agents. Forking the build into a sub-agent (depth 3) is a violation of Row a/b/c/d unless the build itself fans out independently scoped work units, **except** the Phase 3G quality-gate loop, which forks unconditionally under Row d (cache-TTL economics) regardless of build size. <!-- Updated: forge#1825 -->
104
111
 
105
112
  **Depth 4–5 are reserved** for exceptional cases (e.g. an orchestrate agent that itself spawns an orchestrate agent for a sub-milestone). Agents that reach depth 4 MUST log a justification comment on the relevant issue.
106
113
 
107
114
  ### Phase 5C Cross-Reference
108
115
 
109
- The existing Phase 5C context-budget check (≥10 changed files OR ≥20 Skill invocations → spawn `work-on/review` sub-agent) is an instance of **Row (c)** above. The quantitative thresholds are not changed — only their framing: they are now a specific application of the spawn-decision table, not a standalone ad-hoc rule.
116
+ The Phase 5C review fork is now unconditional, controlled by **Row (d)** (prompt-cache TTL economics — independent of build size). It was previously gated by **Row (c)** alone (≥10 changed files OR ≥20 Skill invocations, i.e. parent-context-overflow risk); that threshold is preserved as a *documented, non-gating* reason the fork is also correct for large builds, but no longer determines *whether* the fork happens — Row (d) means it always does. <!-- Updated: forge#1825 (previously: "quantitative thresholds are not changed") -->
117
+
118
+ ### Phase 3G Cross-Reference
119
+
120
+ The Phase 3G quality-gate loop forks unconditionally under **Row (d)**: it scans 14+ domains across up to 3 iterations, long enough to idle the parent's accumulated context past the prompt-cache TTL regardless of how many files changed. Unlike Phase 5C, quality-gate never had a Row (c) (file-count) exception — Row (d) is the sole justification for forking it. <!-- Added: forge#1825 -->
121
+
122
+ ### Model and Effort Tiering — What Actually Applies
123
+
124
+ <!-- FORGE:MODEL_TIER_NOTE — Canonical explanation of the real vs. aspirational tiering mechanism. Every work-on/*.md "Agent model policy" line cross-references this section instead of restating it. -->
125
+
126
+ Every `work-on/*.md` sub-phase file carries an "Agent model policy" line naming a `model` and an `effort`. These are two different mechanisms with two different scopes, and only one of them changes anything for an in-process `Skill()` call:
127
+
128
+ - **`effort` is real and applies per `Skill()` invocation.** It is genuine reasoning-depth tuning on the model already running the session, gated correctly on Claude Code >= 2.1.154. Setting `effort: low` on a sub-phase file that is mechanical end-to-end (deterministic label edits, annotation posting, board sync — no root-cause analysis, no architecture planning) is a real, no-fork-required cost reduction.
129
+ - **`model` overrides are NOT functional for `Skill()`-dispatched sub-phases.** The `Skill` tool executes "within the main conversation" — it has no model parameter and does not fork a new agent/session, so a sub-phase file cannot switch the model that's already generating the current run. The only tool with real model-override semantics is `Agent(model=...)` (see `tool_param_value_permission_rules`, Claude Code >= 2.1.178) — and HARD RULE #2 above explicitly forbids using the `Agent` tool for `/work-on` sub-phase dispatch, to keep the FORGE-annotation paper trail and phase protocol intact. In practice, the only place a `/work-on` run's model is genuinely chosen is `/orchestrate`'s single `Agent(model=..., ...)` spawn for the entire run (see `commands/orchestrate/phase-4-execution.md`) — every internal `Skill()` call inside that run inherits that same model, with no per-sub-phase override.
130
+
131
+ **What this means concretely**: a sub-phase file's "Agent model policy" line documents *effort* tiering that genuinely takes effect, plus a *model* value that is aspirational/no-op unless that file is one day dispatched via `Agent(...)` instead of `Skill(...)`. Because mechanical bits (label transitions, `FORGE:CHECKPOINT` writes, heartbeat posts, task-type classification) are interleaved with reasoning-heavy content within the same `Skill()`-invoked file in most sub-phases, they cannot be selectively downtiered without either degrading the reasoning phases sharing that file, or extracting them into a brand-new fork — which the Spawn-Decision Policy above correctly discourages for operations this small (a single `gh issue edit` call does not clear Row a/b/c/d). Do not add a `model: "haiku"` claim to a sub-phase file expecting it to have effect; only add `effort: low`, and only when the whole file is mechanical end-to-end. <!-- Added: forge#1827 -->
110
132
 
111
133
  ---
112
134
 
@@ -219,11 +241,47 @@ Worktree/branch-already-exists and stale-label conditions are surfaced later (Ph
219
241
  ### 0A: Parse input
220
242
  Extract project prefix and issue number. If `next`/`pick`: list open issues sorted by priority, skip `needs-human`, `workflow:decomposed`, and `workflow:awaiting-merge`, pick highest priority.
221
243
 
244
+ **Resolve `UNDER_ORCHESTRATION`**: `true` if the invocation args contain `--under-orchestration`, else `false`. This is a single parse done once, here — every later gated block (heartbeats) just checks this variable, no re-parsing.
245
+
222
246
  **Optional pre-flight**: Before committing to the full pipeline, run `/scope {NUMBER}` to get a complexity estimate (affected files, blast radius, risk flags, and decomposition recommendation). Especially useful for large or ambiguous issues.
223
247
 
224
- ### 0A.5: Post Heartbeat Annotation
248
+ ### 0A.1: Remediation Mode Detection (`--remediate`) <!-- Added: forge#1813 -->
249
+
250
+ **Check first, before any other Phase 0 routing** — if `$ARGUMENTS` contains `--remediate`, this is NOT a normal issue-pipeline invocation. The first positional argument is a **PR number**, not an issue number:
251
+
252
+ ```bash
253
+ if echo "$ARGUMENTS" | grep -qE -- '--remediate\b'; then
254
+ REMEDIATE_PR_NUMBER=$(echo "$ARGUMENTS" | grep -oP '^\s*\K[0-9]+' | head -1)
255
+ REMEDIATE_ISSUE_FLAG=""
256
+ REMEDIATE_ISSUE_NUMBER=$(echo "$ARGUMENTS" | grep -oP -- '--issue\s+\K[0-9]+' | head -1)
257
+ [ -n "$REMEDIATE_ISSUE_NUMBER" ] && REMEDIATE_ISSUE_FLAG="--issue ${REMEDIATE_ISSUE_NUMBER}"
258
+
259
+ if [ -z "$REMEDIATE_PR_NUMBER" ]; then
260
+ echo "ERROR: --remediate requires a PR number as the first argument, e.g. /work-on 1234 --remediate"
261
+ exit 1
262
+ fi
263
+
264
+ echo "Remediation mode: routing PR #${REMEDIATE_PR_NUMBER} to work-on/remediate (issue flag: ${REMEDIATE_ISSUE_FLAG:-<resolved from PR body>})"
265
+ fi
266
+ ```
267
+
268
+ If detected, dispatch immediately and STOP — do NOT fall through to Phase 0B's normal issue-number resume logic (an issue number is not even known yet; `work-on/remediate.md` Phase M0 resolves it):
269
+
270
+ ```
271
+ Skill(skill="work-on/remediate", args="${REMEDIATE_PR_NUMBER} ${REMEDIATE_ISSUE_FLAG} --repo {GH_REPO} --gh-flag {GH_FLAG}")
272
+ ```
273
+
274
+ **After `REMEDIATE_RESULT` returns, STOP unconditionally** — do not run any further Phase 0–7 logic in this file. `work-on/remediate.md` is self-contained: when `re_gate_outcome: AUTO-LANDED`, it drives its own close phase internally (Phase M8 invokes `Skill("work-on:close", ...)` directly) before returning. For every other outcome (`HELD-AWAITING-MERGE`, `RE-ESCALATED`, `UNFIXABLE`, `BLOCKED`, `ALREADY_DONE`), the issue is already at a terminal state (`workflow:awaiting-merge` or `needs-human`, or already closed) per the Universal Phase Dispatcher — nothing further to do.
275
+
276
+ This mode is reachable both standalone (a human or script running `/work-on <pr> --remediate` directly) and via the orchestrator (`commands/orchestrate/phase-4-execution.md` item 6.4 auto-dispatches the identical `Skill(skill='work-on', args='{PR} --remediate --issue {N} ...')` invocation against a `needs-human`-gated predecessor's own PR).
225
277
 
226
- Post a lightweight activity signal immediately after resolving the issue number. This gives the stall detector (orchestrate Step 4B.5) a fresh timestamp to compare against `STALL_TIMEOUT`. Without this, the stall detector can only see the last structured comment (INVESTIGATOR, BUILDER, etc.) which may be hours old during a valid long-running phase.
278
+ **Skip this entire section if `--remediate` is absent from `$ARGUMENTS`** — proceed to the normal parse below.
279
+
280
+ ### 0A.5: Post Heartbeat Annotation (orchestration-only)
281
+
282
+ **Skip entirely if `UNDER_ORCHESTRATION` is `false`** — a solo run has no stall detector polling comment timestamps, so this write has zero consumer. Do not post it "just in case."
283
+
284
+ When `UNDER_ORCHESTRATION` is `true`: post a lightweight activity signal immediately after resolving the issue number. This gives the stall detector (orchestrate Step 4B.5) a fresh timestamp to compare against `STALL_TIMEOUT`. Without this, the stall detector can only see the last structured comment (INVESTIGATOR, BUILDER, etc.) which may be hours old during a valid long-running phase.
227
285
 
228
286
  ```bash
229
287
  PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
@@ -233,9 +291,9 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
233
291
  **Issue**: #{NUMBER}"
234
292
  ```
235
293
 
236
- **Also post at major phase entry points** (Phases 1, 3, and 5) — replace `Phase 0` with the correct phase name in each case. These mid-pipeline heartbeats ensure the stall detector sees recent activity during long phases (e.g., a build phase running for 20 minutes is not falsely classified as stalled). Inline snippets are embedded at Phase 1A, Phase 3A, and Phase 5A — agents resuming mid-pipeline encounter them without reading this section. <!-- Added: forge#740 -->
294
+ **Also post at major phase entry points** (Phases 1, 3, and 5) — replace `Phase 0` with the correct phase name in each case, and same `UNDER_ORCHESTRATION` gate. These mid-pipeline heartbeats ensure the stall detector sees recent activity during long phases (e.g., a build phase running for 20 minutes is not falsely classified as stalled). Inline snippets are embedded at Phase 1A, Phase 3A, and Phase 5A — agents resuming mid-pipeline encounter them without reading this section. <!-- Added: forge#740 -->
237
295
 
238
- **Skip if**: Issue already has a terminal label (`workflow:merged`, `workflow:invalid`, `needs-human`, `workflow:awaiting-merge`) — no heartbeat needed on a completed issue.
296
+ **Skip if**: Issue already has a terminal label (`workflow:merged`, `workflow:invalid`, `needs-human`, `workflow:awaiting-merge`) — no heartbeat needed on a completed issue. (This is in addition to, not instead of, the `UNDER_ORCHESTRATION` gate above.)
239
297
 
240
298
  ### 0B: Load issue + existing context
241
299
  ```bash
@@ -273,6 +331,8 @@ fi
273
331
  | `CLOSE` | Phase 6 (skip Phase 1–5) |
274
332
  | *(absent or unrecognized)* | Fall back to prose heuristics above |
275
333
 
334
+ Note: Phase 1D no longer writes `next_phase: BUILD`/`DECOMPOSE` CHECKPOINT comments (removed as redundant with the `workflow:ready-to-build`/`workflow:decomposed` label transition — see Phase 1D). Those two rows remain here only to route older, pre-existing CHECKPOINT comments correctly; new runs land on the prose-heuristic fallback for those two cases instead, which is equally precise. `REVIEW` and `CLOSE` are still written (Phase 3M and Phase 5D) because each covers a real gap before the corresponding label transition.
335
+
276
336
  **If no checkpoint exists**: fall back to prose resume heuristics in Phase 0B above — treat as fresh start at Phase 1.
277
337
 
278
338
  **Classify lane**: Milestone → feature lane (`milestone/{slug}`). No milestone → fast lane (`staging`).
@@ -477,7 +537,7 @@ case "$TIER" in
477
537
  esac
478
538
  ```
479
539
 
480
- **Post Phase 1 heartbeat** (skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`, `workflow:awaiting-merge`):
540
+ **Post Phase 1 heartbeat** (skip unless `UNDER_ORCHESTRATION` is `true`; also skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`, `workflow:awaiting-merge`):
481
541
  ```bash
482
542
  PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
483
543
  gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
@@ -492,6 +552,8 @@ Before investigation begins, verify the issue body contains the four mandatory p
492
552
 
493
553
  **Skip if**: All four sections (`## Problem`, `## Affected Files`, `## Expected Behavior`, `## Acceptance Criteria`) are already present.
494
554
 
555
+ **Reuse `ISSUE_BODY` from Phase 0B** — it was already read in full there. Only re-fetch with the command below if `ISSUE_BODY` is not actually present in this session's context (compaction or a fresh resume).
556
+
495
557
  ```bash
496
558
  ISSUE_BODY=$(gh issue view {NUMBER} {GH_FLAG} --json body --jq '.body')
497
559
 
@@ -779,14 +841,7 @@ Inspect the subcommand output above for errors. <!-- forge#1418 -->"
779
841
  fi
780
842
  ```
781
843
 
782
- Write machine-readable phase checkpoint (MUST execute immediately after label transition, before continuing):
783
- ```bash
784
- CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
785
- gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
786
- \`\`\`json
787
- {\"phase\": \"INVESTIGATION\", \"status\": \"COMPLETE\", \"next_phase\": \"BUILD\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
788
- \`\`\`"
789
- ```
844
+ **No separate CHECKPOINT write here** — the `workflow:ready-to-build` label set above already fully disambiguates the resume point ("Investigation exists + ready-to-build → Phase 3" per Phase 0B's prose heuristics); a `next_phase: BUILD` CHECKPOINT comment would duplicate that signal with an extra write. <!-- Removed redundant FORGE:CHECKPOINT write: forge#1826 -->
790
845
  <!-- FORGE:PHASE_COMPLETE — Investigation routed to build. See Universal Phase Dispatcher: next phase is Phase 3. Not terminal — continue immediately. -->
791
846
  → Continue to Phase 3.
792
847
 
@@ -803,14 +858,7 @@ case "$TIER" in
803
858
  esac
804
859
  ```
805
860
 
806
- Write machine-readable phase checkpoint (MUST execute immediately after label transition, before continuing):
807
- ```bash
808
- CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
809
- gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
810
- \`\`\`json
811
- {\"phase\": \"INVESTIGATION\", \"status\": \"COMPLETE\", \"next_phase\": \"DECOMPOSE\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
812
- \`\`\`"
813
- ```
861
+ **No separate CHECKPOINT write here** — the `workflow:decomposed` label set above is itself a terminal state for this run; nothing needs to resume past it, so a `next_phase: DECOMPOSE` CHECKPOINT comment adds no information. <!-- Removed redundant FORGE:CHECKPOINT write: forge#1826 -->
814
862
  <!-- FORGE:PHASE_COMPLETE — Investigation routed to decomposition. See Universal Phase Dispatcher: next phase is Phase 2. Not terminal — continue immediately. -->
815
863
  → Continue to Phase 2 (Decomposition).
816
864
 
@@ -941,7 +989,7 @@ esac
941
989
 
942
990
  <!-- FORGE:PHASE_COMPLETE — Entering Phase 3 (Build). See Universal Phase Dispatcher: sub-phases 3A–3M execute in sequence. No sub-phase completion is terminal. -->
943
991
 
944
- **Canonical path**: Sub-phases 3A–3M run **inline** in the current context window for STANDARD and fast-lane issues. This is the single authoritative build topology. `work-on/build.md` and `work-on-monolithic.md` ([BENCHMARK]) describe the same inline model with different levels of detail; they are not separate competing paths. `Skill()` sub-agent spawns for build sub-phases are only permitted under the Spawn-Decision Table Row (c) exception (≥20 Skill invocations or ≥10 files changed before the build). <!-- Added: forge#1276 -->
992
+ **Canonical path**: Sub-phases 3A–3M run **inline** in the current context window for STANDARD and fast-lane issues, with one standing exception: 3G (quality gate) always forks to a sub-agent under Row (d). This is the single authoritative build topology. `work-on/build.md` and `work-on-monolithic.md` ([BENCHMARK]) describe the same inline model with different levels of detail; they are not separate competing paths. `Skill()`/`Agent()` sub-agent spawns for build sub-phases are only permitted under a Spawn-Decision Table Row (c) or Row (d) exception — Row (c): ≥20 Skill invocations or ≥10 files changed before the build; Row (d): the sub-phase's own runtime risks exceeding the prompt-cache TTL regardless of build size (3G's standing exception). <!-- Added: forge#1276, updated: forge#1825 -->
945
993
 
946
994
  **Skip if**: `<!-- FORGE:BUILDER:COMPLETE -->` is present in a BUILDER comment. <!-- Added: forge#1305 — require completion marker, not mere presence of BUILDER annotation -->
947
995
 
@@ -957,9 +1005,9 @@ fi
957
1005
 
958
1006
  **CRITICAL: You MUST execute ALL sub-phases 3A–3M in order. Sub-phases 3C.5 (context) and 3C.6 (architect) are skipped ONLY for TRIVIAL tasks and Investigation tasks — see Phase 3B for classification. For STANDARD and COMPLEX tasks they post mandatory `FORGE:CONTEXT` and `FORGE:ARCHITECT` comments that Phase 3F reads as its primary input. Skipping them without a TRIVIAL/Investigation classification degrades build quality and causes review findings. After each sub-phase, continue to the next — no sub-phase is terminal.**
959
1007
 
960
- ### 3A: Re-read state from GitHub (MANDATORY)
1008
+ ### 3A: Confirm state (reuse in-context; re-read only on compaction)
961
1009
 
962
- **Post Phase 3 heartbeat** (skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`, `workflow:awaiting-merge`):
1010
+ **Post Phase 3 heartbeat** (skip unless `UNDER_ORCHESTRATION` is `true`; also skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`, `workflow:awaiting-merge`):
963
1011
  ```bash
964
1012
  PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
965
1013
  gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
@@ -968,6 +1016,8 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
968
1016
  **Issue**: #{NUMBER}"
969
1017
  ```
970
1018
 
1019
+ If this is a continuous run (Phase 1 already executed in this same session), you already have the issue view, the FORGE:INVESTIGATOR body, and know whether FORGE:BUILDER:COMPLETE/FORGE:FAST_PATH exist from having posted or checked for them earlier — reuse those values, do not re-fetch. Only run the block below if one of those values is genuinely missing from this session's context (fresh/resumed session after compaction, or checkpoint routing jumped straight here):
1020
+
971
1021
  ```bash
972
1022
  gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state,milestone
973
1023
 
@@ -1094,7 +1144,9 @@ FUNCTION_NAMES=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
1094
1144
 
1095
1145
  **The ONLY acceptable skip conditions** (all must be true): Issue is a 1-file config/docs edit with no code logic AND affected files have zero git history. In all other cases, run context gathering.
1096
1146
 
1097
- Run these queries (20s timeout each, 2 min total budget):
1147
+ **Batch execution (MANDATORY)**: C1, C2 (including the direct commit-body read and the pickaxe pass), C3, and C4 below are mutually independent — none consumes another's output. Issue ALL of their underlying `gh`/`git`/`grep` calls as a single batch of parallel tool calls in one message, not as four sequential steps. This is the same rule as the "independent tool calls → same message" convention used elsewhere in the pipeline; it turns a ~4x serial round-trip chain (each `gh api` call is 0.5-2s) into one wall-clock round-trip. The per-file loop inside C1 and the per-function loop inside C3 are themselves independent across iterations — include every iteration's call in the same batch rather than looping turn-by-turn. Total budget is still 20s per query / 2 min overall; batching only removes serialization, it does not change the timeout.
1148
+
1149
+ Queries to batch (20s timeout each, 2 min total budget):
1098
1150
 
1099
1151
  **C1: Past Review Findings on These Files**
1100
1152
  ```bash
@@ -1386,6 +1438,41 @@ This advisory is informational — it does NOT block the commit. But the impleme
1386
1438
 
1387
1439
  Skip for 1-file config/docs edits.
1388
1440
 
1441
+ **Fork the loop unconditionally under Spawn-Decision Table Row (d)** (see Phase 3G Cross-Reference above): quality-gate scans 14+ domains across up to 3 iterations — long enough to idle the parent's already-large accumulated context past the prompt-cache's ~5-minute TTL, forcing an uncached re-hydration on the parent's next turn. This applies regardless of changed-file count; there is no small-build exception. Dispatch with the `Agent` tool, not a nested `Skill()` call — a nested `Skill()` call still executes inside the parent's own context and would not stop the parent from idling past the cache TTL while the loop runs. The sub-agent needs nothing from the parent's context: it works directly against files on disk at `{WORKTREE_PATH}`. <!-- Added: forge#1825 -->
1442
+
1443
+ ```
1444
+ Agent(
1445
+ subagent_type="general-purpose",
1446
+ model="{SUBAGENT_MODEL}",
1447
+ description="Quality gate for #{NUMBER}",
1448
+ run_in_background=false,
1449
+ prompt="Run the quality gate loop for issue #{NUMBER} in worktree {WORKTREE_PATH}.
1450
+ NEVER use plan mode (EnterPlanMode).
1451
+ Changed files: {CHANGED_FILES}
1452
+
1453
+ iteration = 0
1454
+ max_iterations = 3
1455
+ while iteration < max_iterations:
1456
+ iteration += 1
1457
+ Skill('quality-gate', args='{CHANGED_FILES} --worktree {WORKTREE_PATH}')
1458
+ if result == 'QUALITY GATE: PASS':
1459
+ GATE_PASSED = true
1460
+ break
1461
+ else:
1462
+ Fix each HIGH and MEDIUM finding directly in the files at {WORKTREE_PATH}
1463
+ Re-stage the fixes (do not commit, do not push)
1464
+
1465
+ Do not touch GitHub issue state — the caller handles labels/comments.
1466
+ Return exactly one line: 'GATE_RESULT: passed={true|false} iterations={N} summary={one-line summary of remaining findings if failed, else \"clean\"}'"
1467
+ )
1468
+ ```
1469
+
1470
+ Parse the returned `GATE_RESULT` line:
1471
+ - `passed=true` → `GATE_PASSED = true` → continue to sub-phase 3H.
1472
+ - `passed=false` (loop exhausted 3 iterations) → post "Quality Gate Failed After 3 Iterations" comment using the returned `summary` → add `needs-human` label → STOP.
1473
+
1474
+ **Fallback**: if the `Agent` tool is unavailable in the current runtime (partial install), run the loop inline as before —
1475
+
1389
1476
  ```
1390
1477
  iteration = 0
1391
1478
  max_iterations = 3
@@ -1405,15 +1492,17 @@ if iteration == max_iterations AND not PASS:
1405
1492
  Add needs-human label → STOP
1406
1493
  ```
1407
1494
 
1495
+ — and note in the builder comment that context isolation was degraded for this run.
1496
+
1408
1497
  # MUST CONTINUE to sub-phase 3H (Format and verify) — quality gate PASS is intermediate, NOT terminal. <!-- Added: forge#220 -->
1409
1498
 
1410
- **After quality gate completes (PASS or fixes applied): proceed immediately to sub-phase 3H below. Quality gate is an intermediate check — "PASS" means the code is clean, NOT that the build is done. Do NOT stop.**
1499
+ **After the sub-agent returns `passed=true`: proceed immediately to sub-phase 3H below. Quality gate is an intermediate check — "PASS" means the code is clean, NOT that the build is done. Do NOT stop.**
1411
1500
 
1412
- **After PASS: Do NOT re-read GitHub state, issue body, labels, or any file. Do NOT run any gh commands. Do NOT check PR status. Proceed directly to Phase 3H (Format and verify) below.** <!-- Added: forge#93 -->
1501
+ **After PASS: Do NOT re-read GitHub state, issue body, labels, or any file beyond what the sub-agent already changed on disk. Do NOT run any gh commands. Do NOT check PR status. Proceed directly to Phase 3H (Format and verify) below.** <!-- Added: forge#93 -->
1413
1502
 
1414
1503
  ### 3H: Format and verify
1415
1504
 
1416
- All tool commands are read from `forge.yaml → verification.commands`. When a key is absent, the step logs `SKIPPED — not configured in verification.commands` and continues rather than silently passing.
1505
+ All tool commands are read from `forge.yaml → verification.commands`. When a key is absent, the step logs `SKIPPED — not configured in verification.commands` and continues rather than silently passing. Before any test command executes (`learned.test_commands`, or a future `verification.commands.*.test` invocation), it is first checked against `verification.known_slow_tests` — a repo-declared list of patterns for suites known to hang or make live network/LLM calls — and skipped or narrowed to a safe subset on a match. When that config is absent, this gate is a no-op and behavior is unchanged. <!-- Added: forge#1861 -->
1417
1506
 
1418
1507
  **Track skipped checks** — initialize before any check runs:
1419
1508
  ```bash
@@ -1466,7 +1555,47 @@ fi
1466
1555
  ```
1467
1556
  Typecheck or build failures are BLOCKING.
1468
1557
 
1469
- **Learned test commands** — After all `verification.commands` steps complete, run any commands from `learned.test_commands` (captured from owner corrections in Phase 1D or set manually in forge.yaml):
1558
+ **Known-slow test gate** — Before running any test command below, check it against `verification.known_slow_tests` (a repo-declared list of test patterns known to hang or make live network/LLM calls). When absent or empty, this is a no-op and behavior is unchanged. <!-- Added: forge#1861 -->
1559
+
1560
+ ```bash
1561
+ # KNOWN_SLOW_TESTS read directly from verification.known_slow_tests (a static,
1562
+ # operator-declared config — unlike learned.test_commands, it is NOT part of the
1563
+ # agent-writable `learned:` section, so it is read inline here rather than
1564
+ # pre-loaded in Phase 0B.1).
1565
+ KNOWN_SLOW_TESTS=$(yq -o=json -I=0 '.verification.known_slow_tests // []' forge.yaml 2>/dev/null || echo '[]')
1566
+
1567
+ # apply_known_slow_filter <cmd> — echoes the command to actually run, or "" to
1568
+ # skip it entirely. Matching is substring match of `pattern` against the full
1569
+ # command text. Exactly one of skip/subset is expected per matched entry.
1570
+ apply_known_slow_filter() {
1571
+ local cmd="$1"
1572
+ local out="$cmd"
1573
+ if [ -n "$KNOWN_SLOW_TESTS" ] && [ "$KNOWN_SLOW_TESTS" != "[]" ] && [ "$KNOWN_SLOW_TESTS" != "null" ]; then
1574
+ while IFS= read -r entry; do
1575
+ [ -z "$entry" ] && continue
1576
+ pattern=$(echo "$entry" | yq '.pattern // ""')
1577
+ skip=$(echo "$entry" | yq '.skip // false')
1578
+ subset=$(echo "$entry" | yq '.subset // ""')
1579
+ reason=$(echo "$entry" | yq '.reason // "no reason given"')
1580
+ [ -z "$pattern" ] && continue
1581
+ case "$cmd" in
1582
+ *"$pattern"*)
1583
+ if [ "$skip" = "true" ]; then
1584
+ echo "SKIPPED — known-slow test matched pattern '$pattern' ($reason)" >&2
1585
+ out=""
1586
+ elif [ -n "$subset" ]; then
1587
+ echo "SUBSTITUTED — known-slow test matched pattern '$pattern' ($reason); running safe subset instead" >&2
1588
+ out="$subset"
1589
+ fi
1590
+ ;;
1591
+ esac
1592
+ done < <(echo "$KNOWN_SLOW_TESTS" | yq -o=json -I=0 '.[]' 2>/dev/null)
1593
+ fi
1594
+ echo "$out"
1595
+ }
1596
+ ```
1597
+
1598
+ **Learned test commands** — After all `verification.commands` steps complete, run any commands from `learned.test_commands` (captured from owner corrections in Phase 1D or set manually in forge.yaml), filtered through the known-slow gate above:
1470
1599
 
1471
1600
  ```bash
1472
1601
  # LEARNED_TEST_COMMANDS was set in Phase 0B.1 from forge.yaml → learned.test_commands
@@ -1476,11 +1605,15 @@ if [ -n "$LEARNED_TEST_COMMANDS" ] && [ "$LEARNED_TEST_COMMANDS" != "[]" ]; then
1476
1605
  # yq outputs each entry on its own line with -r flag
1477
1606
  echo "$LEARNED_TEST_COMMANDS" | yq '.[]' | while IFS= read -r cmd; do
1478
1607
  [ -z "$cmd" ] && continue
1479
- echo "Running learned command: $cmd"
1480
- eval "$cmd" 2>&1 | tail -30
1608
+ FILTERED_CMD=$(apply_known_slow_filter "$cmd")
1609
+ if [ -z "$FILTERED_CMD" ]; then
1610
+ continue
1611
+ fi
1612
+ echo "Running learned command: $FILTERED_CMD"
1613
+ eval "$FILTERED_CMD" 2>&1 | tail -30
1481
1614
  CMD_EXIT=$?
1482
1615
  if [ $CMD_EXIT -ne 0 ]; then
1483
- echo "FAILED (exit $CMD_EXIT): $cmd"
1616
+ echo "FAILED (exit $CMD_EXIT): $FILTERED_CMD"
1484
1617
  exit $CMD_EXIT
1485
1618
  fi
1486
1619
  done
@@ -1488,7 +1621,7 @@ else
1488
1621
  echo "No learned test commands configured — skipping"
1489
1622
  fi
1490
1623
  ```
1491
- Learned test command failures are BLOCKING (same as verification.commands failures). <!-- Added: forge#667 -->
1624
+ Learned test command failures are BLOCKING (same as verification.commands failures). A command matched and skipped by the known-slow gate is never executed and never counted as a failure. <!-- Added: forge#667, forge#1861 -->
1492
1625
 
1493
1626
  ### 3I: Frontend proxy wiring check (MANDATORY)
1494
1627
 
@@ -1740,7 +1873,7 @@ gh issue edit {NUMBER} {GH_FLAG} --add-label "needs-human"
1740
1873
 
1741
1874
  ### 4D: Create PR
1742
1875
  ```bash
1743
- gh pr create {GH_FLAG} --base {PR_BASE} --head {BRANCH} \
1876
+ PR_URL=$(gh pr create {GH_FLAG} --base {PR_BASE} --head {BRANCH} \
1744
1877
  --title "{Fix|Feat|Refactor}: {description}" \
1745
1878
  --body "## Summary
1746
1879
  {BRIEF_DESCRIPTION}
@@ -1754,10 +1887,11 @@ gh pr create {GH_FLAG} --base {PR_BASE} --head {BRANCH} \
1754
1887
  ---
1755
1888
  Closes #{NUMBER}
1756
1889
  **Implementation branch**: \`{BRANCH}\`
1757
- **Base**: \`{PR_BASE}\`"
1890
+ **Base**: \`{PR_BASE}\`")
1891
+ PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
1758
1892
  ```
1759
1893
 
1760
- `Closes #{NUMBER}` documents intent but does NOT auto-close for non-default-branch PRs.
1894
+ `Closes #{NUMBER}` documents intent but does NOT auto-close for non-default-branch PRs. Capture `PR_NUMBER` here — Phase 5A reuses it instead of re-querying `gh pr list`.
1761
1895
 
1762
1896
  If PR already exists for this branch, use the existing PR number.
1763
1897
 
@@ -1778,9 +1912,9 @@ esac
1778
1912
 
1779
1913
  ## Phase 5: Auto-Review
1780
1914
 
1781
- ### 5A: Re-read state from GitHub (MANDATORY)
1915
+ ### 5A: Confirm state (reuse in-context; re-read only on compaction)
1782
1916
 
1783
- **Post Phase 5 heartbeat** (skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`, `workflow:awaiting-merge`):
1917
+ **Post Phase 5 heartbeat** (skip unless `UNDER_ORCHESTRATION` is `true`; also skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`, `workflow:awaiting-merge`):
1784
1918
  ```bash
1785
1919
  PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
1786
1920
  gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
@@ -1789,6 +1923,7 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
1789
1923
  **Issue**: #{NUMBER}"
1790
1924
  ```
1791
1925
 
1926
+ `PR_NUMBER` was already captured from `gh pr create`'s output in Phase 4D — reuse it. Only run the lookup below if `PR_NUMBER` is genuinely unset (resumed session after compaction, or checkpoint routing jumped straight to Phase 4/5):
1792
1927
  ```bash
1793
1928
  gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state
1794
1929
  PR_NUMBER=$(gh pr list {GH_FLAG} --head {BRANCH} --json number --jq '.[0].number')
@@ -1805,20 +1940,14 @@ PR #${PR_NUMBER} created targeting \`{PR_BASE}\`. Invoking /review-pr with --aut
1805
1940
 
1806
1941
  ### 5C: Invoke /review-pr with --auto-merge
1807
1942
 
1808
- **Context budget check** (run before invoking review-pr): <!-- Added: forge#93 -->
1943
+ **Always fork — Row (d) supersedes Row (c) as the controlling reason** (run before invoking review-pr): <!-- Updated: forge#1825 (previously threshold-gated by Row (c) alone — forge#93) -->
1809
1944
 
1810
- Large-context sessions that accumulated significant build history cause review-pr to hit the token limit mid-review. This check is an instance of **Spawn-Decision Policy Row (c)** (parent context near overflow) — see the [Spawn-Decision Table](#spawn-decision-table) for the general policy. Check the accumulated context before delegating:
1945
+ `/review-pr` spawns domain review agent(s) (observed at `effort: xhigh`) that run for minutes — long enough to idle the parent's accumulated context past the prompt cache's ~5-minute TTL under **Spawn-Decision Policy Row (d)**, regardless of build size — see the [Spawn-Decision Table](#spawn-decision-table). There is no small-build exception: review ALWAYS forks.
1811
1946
 
1812
- - If the build changed **≥10 files** OR this agent has made **≥20 Skill invocations** since it started: invoke `work-on/review` as a fresh sub-agent (via `Skill(skill="work-on/review", args="...")`) rather than calling review-pr directly. The sub-agent starts with a clean context window.
1813
- - Otherwise (small build, few skill calls): invoke review-pr directly as below.
1814
- - **Fallback**: if `work-on/review` is not available (partial install), invoke review-pr directly regardless of file count and add a note that context may be large.
1947
+ - ALWAYS invoke `work-on/review` as a fresh sub-agent (via `Skill(skill="work-on/review", args="...")`) rather than calling review-pr directly. The sub-agent starts with a clean context window and re-reads all needed state from GitHub (Phase R0) — it does not depend on anything the parent accumulated.
1948
+ - **Fallback only**: if `work-on/review` is not available (partial install), invoke review-pr directly and add a note in the progress comment that context isolation was degraded for this run.
1815
1949
 
1816
- **Direct invocation** (small builds — <10 changed files AND <20 Skill invocations):
1817
- ```
1818
- Skill(skill="review-pr", args="{PR_NUMBER} --auto-merge --issue {NUMBER} --base {PR_BASE} --gh-flag {GH_FLAG}")
1819
- ```
1820
-
1821
- **Sub-agent invocation** (large builds — ≥10 changed files OR ≥20 Skill invocations):
1950
+ **Sub-agent invocation** (always, regardless of changed-file count or Skill-invocation count):
1822
1951
 
1823
1952
  Before spawning, build a distilled hot-copy of the key annotations the review sub-agent would otherwise re-fetch from GitHub. This reduces gh round-trips in the child without replacing the durable FORGE annotation record. <!-- Added: forge#1277 -->
1824
1953
 
@@ -1853,6 +1982,11 @@ Skill(skill="work-on/review", args="{NUMBER} --repo {GH_REPO} --gh-flag {GH_FLAG
1853
1982
 
1854
1983
  The `{HOT_COPY_BLOCK}` is an optimization that avoids the child re-discovering context already held by the parent. The FORGE annotations on GitHub remain the durable, compaction-safe record. If the hot-copy block is empty (annotations not yet posted), the sub-agent falls back to reading them from GitHub as before.
1855
1984
 
1985
+ **Fallback invocation** (only when `work-on/review` is unavailable):
1986
+ ```
1987
+ Skill(skill="review-pr", args="{PR_NUMBER} --auto-merge --issue {NUMBER} --base {PR_BASE} --gh-flag {GH_FLAG}")
1988
+ ```
1989
+
1856
1990
  Review-pr handles: full domain-agent review → post findings as separate issues → merge PR. It does NOT close the issue or clean up the worktree — those run in Phase 6.
1857
1991
 
1858
1992
  ### 5D: Verify merge and close (recovery)
@@ -1889,6 +2023,8 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
1889
2023
 
1890
2024
  **Multi-phase guard**: Detect whether the issue has multiple phases. Only check off items belonging to the current completed phase.
1891
2025
 
2026
+ **This read is intentionally NOT covered by the session-state-cache rule** — the body is about to be rewritten below, and Phase 5's `/review-pr` invocation is an external process that can post comments/edits between the last read and here. Writing back a stale cached body would silently revert any concurrent change, so always fetch fresh immediately before a body mutation.
2027
+
1892
2028
  ```bash
1893
2029
  BODY=$(gh issue view {NUMBER} {GH_FLAG} --json body --jq '.body')
1894
2030
  REMAINING_BEFORE=$(echo "$BODY" | grep -c '^- \[ \]' || true)
@@ -1934,8 +2070,10 @@ esac
1934
2070
 
1935
2071
  **Skip if**: Not a sub-issue (no parent reference in body).
1936
2072
 
2073
+ Markdown emphasis markers (`**bold**`, `__bold__`, `*italic*`) are stripped before matching, since sub-issue bodies commonly render the label as `**Parent**: #NNN` and the bare label alternation below would otherwise fail to match past the emphasis characters:
1937
2074
  ```bash
1938
2075
  PARENT_REF=$(gh issue view {NUMBER} {GH_FLAG} --json body --jq '.body' \
2076
+ | sed -E 's/[*_]+//g' \
1939
2077
  | grep -iE '(part of|spawned from|sub-issue of|parent issue:?|parent:)\s*#[0-9]+' \
1940
2078
  | sed -n 's/.*#\([0-9][0-9]*\).*/\1/p' | head -1)
1941
2079
  ```
package/docs/CI.md CHANGED
@@ -20,7 +20,7 @@ The `forgedock-review.yml` workflow template triggers on every pull request (ope
20
20
  ## Prerequisites
21
21
 
22
22
  1. **Anthropic API key** — Claude Code runs against the Anthropic API. Get one at [console.anthropic.com](https://console.anthropic.com).
23
- 2. **ForgeDock installed** — The workflow installs ForgeDock automatically via `npx forgedock install --global` (CI uses global install so commands are available system-wide in the runner).
23
+ 2. **ForgeDock installed** — The workflow installs ForgeDock automatically via `npx forgedock install` (install is always global, so commands are available system-wide in the runner).
24
24
  3. **GitHub Actions enabled** — Standard for all public repos; enabled by default for private repos.
25
25
 
26
26
  ---
@@ -61,7 +61,7 @@ mkdir -p .github/workflows
61
61
  cp "$(npx forgedock which-dir)/templates/workflows/forgedock-review.yml" .github/workflows/forgedock-review.yml
62
62
  ```
63
63
 
64
- Or copy it manually — the file is in `~/.claude/commands/../templates/workflows/forgedock-review.yml` after running `npx forgedock install --global`.
64
+ Or copy it manually — the file is in `~/.claude/commands/../templates/workflows/forgedock-review.yml` after running `npx forgedock install`.
65
65
 
66
66
  ### 3. Commit and push
67
67
 
package/docs/CODEX.md CHANGED
@@ -8,7 +8,7 @@ Shared source:
8
8
  - `commands/**/*.md` remains the workflow spec for both runtimes
9
9
 
10
10
  Claude wrapper:
11
- - `install.sh` symlinks command files into the current project directory by default; pass `--global` to install into `~/.claude/commands/` for all projects
11
+ - `install.sh` symlinks command files into `~/.claude/commands/` for all projects (always global)
12
12
 
13
13
  Codex wrapper:
14
14
  - `install-codex.sh` generates namespaced skills in `~/.codex/skills`