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.
- package/README.md +10 -33
- package/bin/cinema.mjs +23 -5
- package/bin/engine/invariants.mjs +361 -0
- package/bin/engine-cli.mjs +137 -11
- package/bin/env-detect.mjs +249 -0
- package/bin/forge-utils.mjs +364 -4
- package/bin/forgedock.mjs +1064 -135
- package/bin/hooks/pre-tool-use.mjs +182 -0
- package/bin/hooks/session-start.mjs +68 -3
- package/bin/init-detect.mjs +101 -12
- package/bin/journey.mjs +917 -16
- package/bin/labels.json +10 -0
- package/bin/recall.mjs +557 -0
- package/bin/registry.mjs +91 -3
- package/bin/runner.mjs +38 -2
- package/bin/tui.mjs +49 -3
- package/commands/adopt.md +1 -1
- package/commands/analytics.md +8 -8
- package/commands/audit.md +1 -1
- package/commands/autopilot.md +958 -77
- package/commands/ci-audit.md +1 -1
- package/commands/cleanup.md +139 -55
- package/commands/compat-audit.md +1 -1
- package/commands/deploy-info.md +1 -1
- package/commands/deploy-pr.md +1 -1
- package/commands/diagnose.md +1 -1
- package/commands/explain.md +1 -1
- package/commands/fix-ci.md +1 -1
- package/commands/forgedock-init.md +1 -1
- package/commands/incident-response.md +3 -3
- package/commands/issue.md +1 -1
- package/commands/optimize.md +586 -1
- package/commands/orchestrate/config.md +13 -3
- package/commands/orchestrate/phase-1-resolve.md +36 -26
- package/commands/orchestrate/phase-2-triage.md +1 -1
- package/commands/orchestrate/phase-2.5-synthesis.md +1 -1
- package/commands/orchestrate/phase-3-dependency.md +587 -48
- package/commands/orchestrate/phase-4-execution.md +1031 -57
- package/commands/orchestrate/phase-5-cleanup.md +1 -1
- package/commands/orchestrate/phase-6-report.md +248 -11
- package/commands/orchestrate/safety.md +2 -2
- package/commands/pipeline-health/phase-2-metrics.md +305 -1
- package/commands/pipeline-health/phase-4-proposals.md +127 -0
- package/commands/qa-sweep.md +1 -1
- package/commands/quality-gate.md +418 -1
- package/commands/recover-orphans.md +5 -5
- package/commands/replay.md +1 -1
- package/commands/review-pr-agents/api.md +2 -2
- package/commands/review-pr-agents/auth.md +2 -2
- package/commands/review-pr-agents/billing.md +2 -2
- package/commands/review-pr-agents/concurrency.md +2 -2
- package/commands/review-pr-agents/database.md +2 -2
- package/commands/review-pr-agents/frontend.md +2 -2
- package/commands/review-pr-agents/infra.md +2 -2
- package/commands/review-pr-agents/protocols.md +1 -1
- package/commands/review-pr-agents/scraper.md +2 -2
- package/commands/review-pr-agents/security.md +2 -2
- package/commands/review-pr-staging.md +41 -3
- package/commands/review-pr.md +462 -19
- package/commands/rollback.md +1 -1
- package/commands/scope.md +1 -1
- package/commands/security-audit.md +1 -1
- package/commands/spec-doctor.md +429 -0
- package/commands/test-gate.md +1 -1
- package/commands/validate.md +53 -1
- package/commands/work-on/build/architect.md +78 -1
- package/commands/work-on/build/context.md +360 -22
- package/commands/work-on/build/implement.md +3 -1
- package/commands/work-on/build/validate.md +29 -1
- package/commands/work-on/build.md +75 -6
- package/commands/work-on/close.md +624 -10
- package/commands/work-on/decompose.md +1 -1
- package/commands/work-on/investigate.md +235 -5
- package/commands/work-on/remediate.md +331 -0
- package/commands/work-on/review.md +1 -1
- package/commands/work-on-monolithic.md +1 -1
- package/commands/work-on.md +205 -66
- package/docs/CI.md +2 -2
- package/docs/CODEX.md +1 -1
- package/docs/CONFIG.md +186 -0
- package/docs/FORGE-PROTOCOL.md +101 -0
- package/docs/site/command-reference.md +31 -1
- package/docs/site/getting-started.md +9 -34
- package/docs/site/index.md +1 -1
- package/docs/spec/forge-protocol-v1.md +97 -5
- package/docs/spec/label-state-machine.md +6 -1
- package/package.json +3 -2
- package/packages/protocol/src/cli.js +374 -84
- package/packages/protocol/src/types.js +75 -1
- package/scripts/build-knowledge-index.mjs +1761 -0
- package/scripts/calibration.mjs +996 -0
- package/scripts/check-command-docs-drift.sh +4 -2
- package/scripts/danger-zones.mjs +789 -0
- package/scripts/doctor-pipeline-state.sh +88 -2
- package/scripts/select-fix-targets.sh +234 -0
- package/scripts/transition-label.sh +245 -6
- package/scripts/refresh-bot-token.sh +0 -97
|
@@ -12,7 +12,7 @@ install: internal
|
|
|
12
12
|
|
|
13
13
|
Orchestrator for the full issue lifecycle: investigate → 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.
|
|
14
14
|
|
|
15
|
-
**Agent model policy**: `model: "
|
|
15
|
+
**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.
|
|
16
16
|
**NEVER use plan mode (EnterPlanMode).**
|
|
17
17
|
|
|
18
18
|
> **Relationship to `/work-on` (canonical path)**: This file is a `[BENCHMARK]` reference variant. It documents the same inline-execution model that `/work-on` Phase 3 and `work-on/build.md` use as their **default** for STANDARD/fast-lane issues — all build sub-phases (context → architect → implement → validate) run inline in the current context window with no `Skill()` sub-agent spawns. The difference is that this file omits the worktree lifecycle, `FORGE:CONTRACT` posting, and the Spawn-Decision exception path for large builds. Use `/work-on` for production runs; use this file for baseline benchmarking or when comparing single-prompt token cost against the modular path. See `work-on/build.md` (Canonical Build Path section) for the reconciled build topology. <!-- Added: forge#1276 -->
|
package/commands/work-on.md
CHANGED
|
@@ -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: "
|
|
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.
|
|
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. -->
|
|
@@ -62,6 +68,7 @@ Orchestrator for the full issue lifecycle: investigate → decompose (if needed)
|
|
|
62
68
|
- `workflow:merged` label is set
|
|
63
69
|
- `workflow:invalid` label is set
|
|
64
70
|
- `needs-human` label is set
|
|
71
|
+
- `workflow:awaiting-merge` label is set (remediated + re-reviewed, awaiting a human merge decision — see #1810) <!-- Added: forge#1810 -->
|
|
65
72
|
- `workflow:decomposed` label is set (sub-issues spawned)
|
|
66
73
|
- Issue state is CLOSED with terminal label
|
|
67
74
|
|
|
@@ -75,7 +82,7 @@ Orchestrator for the full issue lifecycle: investigate → decompose (if needed)
|
|
|
75
82
|
|
|
76
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. -->
|
|
77
84
|
|
|
78
|
-
**Default: run inline.** Every skill, phase, and sub-agent runs inline in the current context unless one of the
|
|
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.
|
|
79
86
|
|
|
80
87
|
### Spawn-Decision Table
|
|
81
88
|
|
|
@@ -84,8 +91,9 @@ Orchestrator for the full issue lifecycle: investigate → decompose (if needed)
|
|
|
84
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 |
|
|
85
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) |
|
|
86
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 --> |
|
|
87
95
|
|
|
88
|
-
**If none of the
|
|
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.
|
|
89
97
|
|
|
90
98
|
### Depth Budget
|
|
91
99
|
|
|
@@ -97,15 +105,30 @@ Orchestrator for the full issue lifecycle: investigate → decompose (if needed)
|
|
|
97
105
|
|-------|-------|-------|
|
|
98
106
|
| 1 | `/orchestrate` | Top-level dispatcher — never implements directly |
|
|
99
107
|
| 2 | `/work-on` | Issue pipeline — runs build phases inline |
|
|
100
|
-
| 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 |
|
|
101
109
|
|
|
102
|
-
**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 -->
|
|
103
111
|
|
|
104
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.
|
|
105
113
|
|
|
106
114
|
### Phase 5C Cross-Reference
|
|
107
115
|
|
|
108
|
-
The
|
|
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 -->
|
|
109
132
|
|
|
110
133
|
---
|
|
111
134
|
|
|
@@ -216,13 +239,49 @@ fi
|
|
|
216
239
|
Worktree/branch-already-exists and stale-label conditions are surfaced later (Phase 3E worktree creation and the `## Error Handling` section) with their own recovery guidance in `docs/site/troubleshooting.md`.
|
|
217
240
|
|
|
218
241
|
### 0A: Parse input
|
|
219
|
-
Extract project prefix and issue number. If `next`/`pick`: list open issues sorted by priority, skip `needs-human` and `workflow:
|
|
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.
|
|
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.
|
|
220
245
|
|
|
221
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.
|
|
222
247
|
|
|
223
|
-
### 0A.
|
|
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).
|
|
224
277
|
|
|
225
|
-
|
|
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.
|
|
226
285
|
|
|
227
286
|
```bash
|
|
228
287
|
PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
@@ -232,9 +291,9 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
|
|
|
232
291
|
**Issue**: #{NUMBER}"
|
|
233
292
|
```
|
|
234
293
|
|
|
235
|
-
**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 -->
|
|
236
295
|
|
|
237
|
-
**Skip if**: Issue already has a terminal label (`workflow:merged`, `workflow:invalid`, `needs-human`) — 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.)
|
|
238
297
|
|
|
239
298
|
### 0B: Load issue + existing context
|
|
240
299
|
```bash
|
|
@@ -242,7 +301,7 @@ gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state,comments,
|
|
|
242
301
|
gh api repos/{GH_REPO}/issues/{NUMBER}/comments --jq '.[] | {id: .id, author: .user.login, body: .body}'
|
|
243
302
|
```
|
|
244
303
|
|
|
245
|
-
**Check**: state (closed → STOP), terminal labels (`workflow:merged`/`workflow:invalid` → STOP), existing agent comments (`FORGE:INVESTIGATOR`, `FORGE:DECOMPOSED`, `FORGE:CONTRACT`, `FORGE:BUILDER`, `FORGE:TRAJECTORY`, `FORGE:DECISION_RECORD`), parent tracker status, sub-issue status.
|
|
304
|
+
**Check**: state (closed → STOP), terminal labels (`workflow:merged`/`workflow:invalid`/`workflow:awaiting-merge` → STOP), existing agent comments (`FORGE:INVESTIGATOR`, `FORGE:DECOMPOSED`, `FORGE:CONTRACT`, `FORGE:BUILDER`, `FORGE:TRAJECTORY`, `FORGE:DECISION_RECORD`), parent tracker status, sub-issue status.
|
|
246
305
|
|
|
247
306
|
**Determine resume point**: No comments → Phase 1. Investigation exists + ready-to-build → Phase 3. Builder:COMPLETE + no PR → Phase 4. Builder without :COMPLETE (partial/interrupted build) + no PR → Phase 3 (partial-build cleanup). Builder + PR open → Phase 5. PR merged + issue open → Phase 6.
|
|
248
307
|
|
|
@@ -272,6 +331,8 @@ fi
|
|
|
272
331
|
| `CLOSE` | Phase 6 (skip Phase 1–5) |
|
|
273
332
|
| *(absent or unrecognized)* | Fall back to prose heuristics above |
|
|
274
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
|
+
|
|
275
336
|
**If no checkpoint exists**: fall back to prose resume heuristics in Phase 0B above — treat as fresh start at Phase 1.
|
|
276
337
|
|
|
277
338
|
**Classify lane**: Milestone → feature lane (`milestone/{slug}`). No milestone → fast lane (`staging`).
|
|
@@ -471,12 +532,12 @@ case "$TIER" in
|
|
|
471
532
|
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} investigating ;;
|
|
472
533
|
prose)
|
|
473
534
|
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:investigating" \
|
|
474
|
-
--remove-label "workflow:ready-to-build,workflow:building,workflow:in-review,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
535
|
+
--remove-label "workflow:ready-to-build,workflow:building,workflow:in-review,workflow:awaiting-merge,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
475
536
|
;;
|
|
476
537
|
esac
|
|
477
538
|
```
|
|
478
539
|
|
|
479
|
-
**Post Phase 1 heartbeat** (skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`):
|
|
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`):
|
|
480
541
|
```bash
|
|
481
542
|
PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
482
543
|
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
|
|
@@ -491,6 +552,8 @@ Before investigation begins, verify the issue body contains the four mandatory p
|
|
|
491
552
|
|
|
492
553
|
**Skip if**: All four sections (`## Problem`, `## Affected Files`, `## Expected Behavior`, `## Acceptance Criteria`) are already present.
|
|
493
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
|
+
|
|
494
557
|
```bash
|
|
495
558
|
ISSUE_BODY=$(gh issue view {NUMBER} {GH_FLAG} --json body --jq '.body')
|
|
496
559
|
|
|
@@ -748,7 +811,7 @@ case "$TIER" in
|
|
|
748
811
|
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} ready-to-build ;;
|
|
749
812
|
prose)
|
|
750
813
|
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:ready-to-build" \
|
|
751
|
-
--remove-label "workflow:investigating,workflow:building,workflow:in-review,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
814
|
+
--remove-label "workflow:investigating,workflow:building,workflow:in-review,workflow:awaiting-merge,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
752
815
|
;;
|
|
753
816
|
esac
|
|
754
817
|
```
|
|
@@ -778,14 +841,7 @@ Inspect the subcommand output above for errors. <!-- forge#1418 -->"
|
|
|
778
841
|
fi
|
|
779
842
|
```
|
|
780
843
|
|
|
781
|
-
|
|
782
|
-
```bash
|
|
783
|
-
CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
784
|
-
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
|
|
785
|
-
\`\`\`json
|
|
786
|
-
{\"phase\": \"INVESTIGATION\", \"status\": \"COMPLETE\", \"next_phase\": \"BUILD\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
|
|
787
|
-
\`\`\`"
|
|
788
|
-
```
|
|
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 -->
|
|
789
845
|
<!-- FORGE:PHASE_COMPLETE — Investigation routed to build. See Universal Phase Dispatcher: next phase is Phase 3. Not terminal — continue immediately. -->
|
|
790
846
|
→ Continue to Phase 3.
|
|
791
847
|
|
|
@@ -797,19 +853,12 @@ case "$TIER" in
|
|
|
797
853
|
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} decomposed ;;
|
|
798
854
|
prose)
|
|
799
855
|
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:decomposed" \
|
|
800
|
-
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:merged,workflow:invalid" 2>/dev/null || true
|
|
856
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:awaiting-merge,workflow:merged,workflow:invalid" 2>/dev/null || true
|
|
801
857
|
;;
|
|
802
858
|
esac
|
|
803
859
|
```
|
|
804
860
|
|
|
805
|
-
|
|
806
|
-
```bash
|
|
807
|
-
CHECKPOINT_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
808
|
-
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
|
|
809
|
-
\`\`\`json
|
|
810
|
-
{\"phase\": \"INVESTIGATION\", \"status\": \"COMPLETE\", \"next_phase\": \"DECOMPOSE\", \"timestamp\": \"${CHECKPOINT_TIMESTAMP}\"}
|
|
811
|
-
\`\`\`"
|
|
812
|
-
```
|
|
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 -->
|
|
813
862
|
<!-- FORGE:PHASE_COMPLETE — Investigation routed to decomposition. See Universal Phase Dispatcher: next phase is Phase 2. Not terminal — continue immediately. -->
|
|
814
863
|
→ Continue to Phase 2 (Decomposition).
|
|
815
864
|
|
|
@@ -821,7 +870,7 @@ case "$TIER" in
|
|
|
821
870
|
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} invalid ;;
|
|
822
871
|
prose)
|
|
823
872
|
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:invalid" \
|
|
824
|
-
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:merged,workflow:decomposed" 2>/dev/null || true
|
|
873
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:awaiting-merge,workflow:merged,workflow:decomposed" 2>/dev/null || true
|
|
825
874
|
;;
|
|
826
875
|
esac
|
|
827
876
|
gh issue close {NUMBER} {GH_FLAG} --comment "Closing as invalid: {reason from investigation}"
|
|
@@ -927,7 +976,7 @@ case "$TIER" in
|
|
|
927
976
|
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} decomposed ;;
|
|
928
977
|
prose)
|
|
929
978
|
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:decomposed" \
|
|
930
|
-
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:merged,workflow:invalid" 2>/dev/null || true
|
|
979
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:awaiting-merge,workflow:merged,workflow:invalid" 2>/dev/null || true
|
|
931
980
|
;;
|
|
932
981
|
esac
|
|
933
982
|
```
|
|
@@ -940,7 +989,7 @@ esac
|
|
|
940
989
|
|
|
941
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. -->
|
|
942
991
|
|
|
943
|
-
**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
|
|
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 -->
|
|
944
993
|
|
|
945
994
|
**Skip if**: `<!-- FORGE:BUILDER:COMPLETE -->` is present in a BUILDER comment. <!-- Added: forge#1305 — require completion marker, not mere presence of BUILDER annotation -->
|
|
946
995
|
|
|
@@ -956,9 +1005,9 @@ fi
|
|
|
956
1005
|
|
|
957
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.**
|
|
958
1007
|
|
|
959
|
-
### 3A:
|
|
1008
|
+
### 3A: Confirm state (reuse in-context; re-read only on compaction)
|
|
960
1009
|
|
|
961
|
-
**Post Phase 3 heartbeat** (skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`):
|
|
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`):
|
|
962
1011
|
```bash
|
|
963
1012
|
PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
964
1013
|
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
|
|
@@ -967,6 +1016,8 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
|
|
|
967
1016
|
**Issue**: #{NUMBER}"
|
|
968
1017
|
```
|
|
969
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
|
+
|
|
970
1021
|
```bash
|
|
971
1022
|
gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state,milestone
|
|
972
1023
|
|
|
@@ -1093,7 +1144,9 @@ FUNCTION_NAMES=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
|
1093
1144
|
|
|
1094
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.
|
|
1095
1146
|
|
|
1096
|
-
|
|
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):
|
|
1097
1150
|
|
|
1098
1151
|
**C1: Past Review Findings on These Files**
|
|
1099
1152
|
```bash
|
|
@@ -1262,7 +1315,7 @@ case "$TIER" in
|
|
|
1262
1315
|
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} building ;;
|
|
1263
1316
|
prose)
|
|
1264
1317
|
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:building" \
|
|
1265
|
-
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:in-review,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
1318
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:in-review,workflow:awaiting-merge,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
1266
1319
|
;;
|
|
1267
1320
|
esac
|
|
1268
1321
|
```
|
|
@@ -1385,6 +1438,41 @@ This advisory is informational — it does NOT block the commit. But the impleme
|
|
|
1385
1438
|
|
|
1386
1439
|
Skip for 1-file config/docs edits.
|
|
1387
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
|
+
|
|
1388
1476
|
```
|
|
1389
1477
|
iteration = 0
|
|
1390
1478
|
max_iterations = 3
|
|
@@ -1404,15 +1492,17 @@ if iteration == max_iterations AND not PASS:
|
|
|
1404
1492
|
Add needs-human label → STOP
|
|
1405
1493
|
```
|
|
1406
1494
|
|
|
1495
|
+
— and note in the builder comment that context isolation was degraded for this run.
|
|
1496
|
+
|
|
1407
1497
|
# MUST CONTINUE to sub-phase 3H (Format and verify) — quality gate PASS is intermediate, NOT terminal. <!-- Added: forge#220 -->
|
|
1408
1498
|
|
|
1409
|
-
**After
|
|
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.**
|
|
1410
1500
|
|
|
1411
|
-
**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 -->
|
|
1412
1502
|
|
|
1413
1503
|
### 3H: Format and verify
|
|
1414
1504
|
|
|
1415
|
-
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 -->
|
|
1416
1506
|
|
|
1417
1507
|
**Track skipped checks** — initialize before any check runs:
|
|
1418
1508
|
```bash
|
|
@@ -1465,7 +1555,47 @@ fi
|
|
|
1465
1555
|
```
|
|
1466
1556
|
Typecheck or build failures are BLOCKING.
|
|
1467
1557
|
|
|
1468
|
-
**
|
|
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:
|
|
1469
1599
|
|
|
1470
1600
|
```bash
|
|
1471
1601
|
# LEARNED_TEST_COMMANDS was set in Phase 0B.1 from forge.yaml → learned.test_commands
|
|
@@ -1475,11 +1605,15 @@ if [ -n "$LEARNED_TEST_COMMANDS" ] && [ "$LEARNED_TEST_COMMANDS" != "[]" ]; then
|
|
|
1475
1605
|
# yq outputs each entry on its own line with -r flag
|
|
1476
1606
|
echo "$LEARNED_TEST_COMMANDS" | yq '.[]' | while IFS= read -r cmd; do
|
|
1477
1607
|
[ -z "$cmd" ] && continue
|
|
1478
|
-
|
|
1479
|
-
|
|
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
|
|
1480
1614
|
CMD_EXIT=$?
|
|
1481
1615
|
if [ $CMD_EXIT -ne 0 ]; then
|
|
1482
|
-
echo "FAILED (exit $CMD_EXIT): $
|
|
1616
|
+
echo "FAILED (exit $CMD_EXIT): $FILTERED_CMD"
|
|
1483
1617
|
exit $CMD_EXIT
|
|
1484
1618
|
fi
|
|
1485
1619
|
done
|
|
@@ -1487,7 +1621,7 @@ else
|
|
|
1487
1621
|
echo "No learned test commands configured — skipping"
|
|
1488
1622
|
fi
|
|
1489
1623
|
```
|
|
1490
|
-
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 -->
|
|
1491
1625
|
|
|
1492
1626
|
### 3I: Frontend proxy wiring check (MANDATORY)
|
|
1493
1627
|
|
|
@@ -1739,7 +1873,7 @@ gh issue edit {NUMBER} {GH_FLAG} --add-label "needs-human"
|
|
|
1739
1873
|
|
|
1740
1874
|
### 4D: Create PR
|
|
1741
1875
|
```bash
|
|
1742
|
-
gh pr create {GH_FLAG} --base {PR_BASE} --head {BRANCH} \
|
|
1876
|
+
PR_URL=$(gh pr create {GH_FLAG} --base {PR_BASE} --head {BRANCH} \
|
|
1743
1877
|
--title "{Fix|Feat|Refactor}: {description}" \
|
|
1744
1878
|
--body "## Summary
|
|
1745
1879
|
{BRIEF_DESCRIPTION}
|
|
@@ -1753,10 +1887,11 @@ gh pr create {GH_FLAG} --base {PR_BASE} --head {BRANCH} \
|
|
|
1753
1887
|
---
|
|
1754
1888
|
Closes #{NUMBER}
|
|
1755
1889
|
**Implementation branch**: \`{BRANCH}\`
|
|
1756
|
-
**Base**: \`{PR_BASE}\`"
|
|
1890
|
+
**Base**: \`{PR_BASE}\`")
|
|
1891
|
+
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
|
|
1757
1892
|
```
|
|
1758
1893
|
|
|
1759
|
-
`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`.
|
|
1760
1895
|
|
|
1761
1896
|
If PR already exists for this branch, use the existing PR number.
|
|
1762
1897
|
|
|
@@ -1768,7 +1903,7 @@ case "$TIER" in
|
|
|
1768
1903
|
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} in-review ;;
|
|
1769
1904
|
prose)
|
|
1770
1905
|
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:in-review" \
|
|
1771
|
-
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
1906
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:awaiting-merge,workflow:merged,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
1772
1907
|
;;
|
|
1773
1908
|
esac
|
|
1774
1909
|
```
|
|
@@ -1777,9 +1912,9 @@ esac
|
|
|
1777
1912
|
|
|
1778
1913
|
## Phase 5: Auto-Review
|
|
1779
1914
|
|
|
1780
|
-
### 5A:
|
|
1915
|
+
### 5A: Confirm state (reuse in-context; re-read only on compaction)
|
|
1781
1916
|
|
|
1782
|
-
**Post Phase 5 heartbeat** (skip if issue already has a terminal label — `workflow:merged`, `workflow:invalid`, `needs-human`):
|
|
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`):
|
|
1783
1918
|
```bash
|
|
1784
1919
|
PHASE_START_TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
1785
1920
|
gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
|
|
@@ -1788,6 +1923,7 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:HEARTBEAT -->
|
|
|
1788
1923
|
**Issue**: #{NUMBER}"
|
|
1789
1924
|
```
|
|
1790
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):
|
|
1791
1927
|
```bash
|
|
1792
1928
|
gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state
|
|
1793
1929
|
PR_NUMBER=$(gh pr list {GH_FLAG} --head {BRANCH} --json number --jq '.[0].number')
|
|
@@ -1804,20 +1940,14 @@ PR #${PR_NUMBER} created targeting \`{PR_BASE}\`. Invoking /review-pr with --aut
|
|
|
1804
1940
|
|
|
1805
1941
|
### 5C: Invoke /review-pr with --auto-merge
|
|
1806
1942
|
|
|
1807
|
-
**
|
|
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) -->
|
|
1808
1944
|
|
|
1809
|
-
|
|
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.
|
|
1810
1946
|
|
|
1811
|
-
-
|
|
1812
|
-
-
|
|
1813
|
-
- **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.
|
|
1814
1949
|
|
|
1815
|
-
**
|
|
1816
|
-
```
|
|
1817
|
-
Skill(skill="review-pr", args="{PR_NUMBER} --auto-merge --issue {NUMBER} --base {PR_BASE} --gh-flag {GH_FLAG}")
|
|
1818
|
-
```
|
|
1819
|
-
|
|
1820
|
-
**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):
|
|
1821
1951
|
|
|
1822
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 -->
|
|
1823
1953
|
|
|
@@ -1852,6 +1982,11 @@ Skill(skill="work-on/review", args="{NUMBER} --repo {GH_REPO} --gh-flag {GH_FLAG
|
|
|
1852
1982
|
|
|
1853
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.
|
|
1854
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
|
+
|
|
1855
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.
|
|
1856
1991
|
|
|
1857
1992
|
### 5D: Verify merge and close (recovery)
|
|
@@ -1888,6 +2023,8 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CHECKPOINT -->
|
|
|
1888
2023
|
|
|
1889
2024
|
**Multi-phase guard**: Detect whether the issue has multiple phases. Only check off items belonging to the current completed phase.
|
|
1890
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
|
+
|
|
1891
2028
|
```bash
|
|
1892
2029
|
BODY=$(gh issue view {NUMBER} {GH_FLAG} --json body --jq '.body')
|
|
1893
2030
|
REMAINING_BEFORE=$(echo "$BODY" | grep -c '^- \[ \]' || true)
|
|
@@ -1924,7 +2061,7 @@ case "$TIER" in
|
|
|
1924
2061
|
adaptive|universal) bash "$SCRIPT_PATH" {NUMBER} {GH_FLAG} merged ;;
|
|
1925
2062
|
prose)
|
|
1926
2063
|
gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:merged" \
|
|
1927
|
-
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
2064
|
+
--remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:awaiting-merge,workflow:invalid,workflow:decomposed" 2>/dev/null || true
|
|
1928
2065
|
;;
|
|
1929
2066
|
esac
|
|
1930
2067
|
```
|
|
@@ -1933,8 +2070,10 @@ esac
|
|
|
1933
2070
|
|
|
1934
2071
|
**Skip if**: Not a sub-issue (no parent reference in body).
|
|
1935
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:
|
|
1936
2074
|
```bash
|
|
1937
2075
|
PARENT_REF=$(gh issue view {NUMBER} {GH_FLAG} --json body --jq '.body' \
|
|
2076
|
+
| sed -E 's/[*_]+//g' \
|
|
1938
2077
|
| grep -iE '(part of|spawned from|sub-issue of|parent issue:?|parent:)\s*#[0-9]+' \
|
|
1939
2078
|
| sed -n 's/.*#\([0-9][0-9]*\).*/\1/p' | head -1)
|
|
1940
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
|
|
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
|
|
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
|
|
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`
|