forgedock 1.8.1 → 1.8.3

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 (90) hide show
  1. package/bin/engine/admission.mjs +57 -1
  2. package/bin/engine/admission.test.mjs +60 -0
  3. package/commands/analytics.md +3 -1
  4. package/commands/audit-agents.md +3 -1
  5. package/commands/audit.md +10 -8
  6. package/commands/autopilot.md +16 -14
  7. package/commands/ci-audit.md +4 -2
  8. package/commands/cleanup.md +1 -1
  9. package/commands/deploy-pr.md +31 -11
  10. package/commands/design-bench.md +1 -0
  11. package/commands/design-render-critique-loop.md +1 -0
  12. package/commands/design.md +1 -0
  13. package/commands/forge-stats.md +1 -0
  14. package/commands/geo-audit.md +3 -1
  15. package/commands/incident-response.md +3 -1
  16. package/commands/issue.md +4 -2
  17. package/commands/milestone.md +8 -6
  18. package/commands/orchestrate/config.md +3 -2
  19. package/commands/orchestrate/phase-1-resolve.md +57 -12
  20. package/commands/orchestrate/phase-2-triage.md +1 -0
  21. package/commands/orchestrate/phase-2.5-synthesis.md +1 -0
  22. package/commands/orchestrate/phase-3-dependency.md +17 -5
  23. package/commands/orchestrate/phase-4-execution.md +625 -78
  24. package/commands/orchestrate/phase-5-cleanup.md +5 -2
  25. package/commands/orchestrate/phase-6-report.md +8 -3
  26. package/commands/orchestrate/safety.md +1 -0
  27. package/commands/orchestrate.md +13 -9
  28. package/commands/pipeline-health/config.md +1 -0
  29. package/commands/pipeline-health/phase-1-context.md +1 -0
  30. package/commands/pipeline-health/phase-2-metrics.md +1 -0
  31. package/commands/pipeline-health/phase-3-analyze.md +1 -0
  32. package/commands/pipeline-health/phase-4-proposals.md +7 -4
  33. package/commands/pipeline-health/phase-5-report.md +5 -2
  34. package/commands/pipeline-health/phase-6-summary.md +1 -0
  35. package/commands/pipeline-health.md +9 -7
  36. package/commands/pipeline-resume.md +4 -2
  37. package/commands/qa-sweep.md +3 -1
  38. package/commands/quality-gate.md +4 -3
  39. package/commands/recover-orphans.md +219 -13
  40. package/commands/review-pr-agents/api.md +1 -0
  41. package/commands/review-pr-agents/auth.md +1 -0
  42. package/commands/review-pr-agents/billing.md +1 -0
  43. package/commands/review-pr-agents/concurrency.md +1 -0
  44. package/commands/review-pr-agents/database.md +1 -0
  45. package/commands/review-pr-agents/frontend.md +1 -0
  46. package/commands/review-pr-agents/infra.md +1 -0
  47. package/commands/review-pr-agents/protocols.md +1 -0
  48. package/commands/review-pr-agents/scraper.md +1 -0
  49. package/commands/review-pr-agents/security.md +1 -0
  50. package/commands/review-pr-agents/spec-cli.md +1 -0
  51. package/commands/review-pr-agents.md +10 -4
  52. package/commands/review-pr-staging.md +14 -5
  53. package/commands/review-pr.md +187 -19
  54. package/commands/rollback.md +3 -1
  55. package/commands/security-audit.md +3 -1
  56. package/commands/signal-planner.md +6 -4
  57. package/commands/sync-ecosystem.md +4 -2
  58. package/commands/test-gate.md +4 -2
  59. package/commands/upgrade-deps.md +3 -1
  60. package/commands/validate.md +3 -1
  61. package/commands/work-on/build/architect.md +82 -19
  62. package/commands/work-on/build/context.md +62 -18
  63. package/commands/work-on/build/implement.md +63 -18
  64. package/commands/work-on/build/validate.md +241 -60
  65. package/commands/work-on/build.md +345 -146
  66. package/commands/work-on/close.md +618 -246
  67. package/commands/work-on/decompose.md +240 -41
  68. package/commands/work-on/investigate.md +378 -61
  69. package/commands/work-on/remediate.md +43 -9
  70. package/commands/work-on/review.md +241 -29
  71. package/commands/work-on.md +180 -2014
  72. package/docs/CODEX.md +2 -0
  73. package/docs/CONFIG.md +11 -0
  74. package/package.json +1 -1
  75. package/scripts/build-spec-graph.mjs +11 -4
  76. package/scripts/check-spec-markers.sh +21 -1
  77. package/scripts/checkbox-sections.test.sh +8 -23
  78. package/scripts/extract-affected-files.sh +5 -2
  79. package/scripts/extract-affected-files.test.sh +27 -0
  80. package/scripts/forge-root.test.sh +176 -0
  81. package/scripts/is-docs-only.sh +31 -0
  82. package/scripts/is-docs-only.test.sh +64 -0
  83. package/scripts/lint-dispatch-prompt.sh +217 -0
  84. package/scripts/lint-dispatch-prompt.test.sh +307 -0
  85. package/scripts/validate-spec-graph.sh +3 -2
  86. package/scripts/verify-phase-trail.sh +282 -0
  87. package/scripts/verify-phase-trail.test.sh +298 -0
  88. package/scripts/wait-ci-green.sh +98 -0
  89. package/scripts/wait-ci-green.test.sh +99 -0
  90. package/commands/work-on-monolithic.md +0 -326
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env bash
2
+ # SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ #
5
+ # wait-ci-green.sh — CI gate every autonomous merge must pass first.
6
+ #
7
+ # Usage: wait-ci-green.sh <pr-number> -R <owner/repo> [--timeout SECONDS] [--interval SECONDS]
8
+ #
9
+ # Waits until every check on the PR has finished, then decides. Fails closed: a merge may
10
+ # proceed ONLY on exit 0. Field test: pipeline agents merged PRs to staging while checks were
11
+ # pending or red (#3165 merged with the side-effect gate failing), because neither the specs nor
12
+ # branch protection required green CI, and `gh pr merge --auto` only waits for *required* checks.
13
+ #
14
+ # Exit codes (one "CI_GATE: <RESULT>" line on stdout, details after it):
15
+ # 0 PASS every check is pass/skipping (or the repo reports no checks after the grace period
16
+ # and FORGE_CI_REQUIRE_CHECKS is not 1)
17
+ # 1 FAIL at least one check failed or was cancelled (listed)
18
+ # 2 ERROR bad usage, gh unreadable for too long, the PR head moved during the wait, or
19
+ # FORGE_CI_REQUIRE_CHECKS=1 and no checks were reported
20
+ # 3 TIMEOUT checks still pending when the timeout expired (listed)
21
+ #
22
+ # Every result also prints "CI_GATE_HEAD: <sha>" — the exact PR head the decision applies to. Callers
23
+ # MUST merge with `--match-head-commit <that sha>` so a push after the gate cannot land unchecked.
24
+ #
25
+ # Env: FORGE_CI_TIMEOUT (default 540 — stays under the 10-minute Bash tool limit; on TIMEOUT the
26
+ # caller re-runs the gate, it does not raise the timeout), FORGE_CI_INTERVAL (default 20),
27
+ # FORGE_CI_NO_CHECKS_GRACE (default 120), FORGE_CI_SETTLE (default 45 — never PASS earlier than
28
+ # this after the wait starts, so workflows that register late are not missed),
29
+ # FORGE_CI_REQUIRE_CHECKS (default 0).
30
+ # "No checks" passes only when GitHub Actions also reports no workflow runs for the head commit;
31
+ # if runs exist but their checks have not appeared yet, the gate keeps waiting.
32
+ set -uo pipefail
33
+
34
+ PR=""; REPO=""
35
+ TIMEOUT="${FORGE_CI_TIMEOUT:-540}"; INTERVAL="${FORGE_CI_INTERVAL:-20}"; SETTLE="${FORGE_CI_SETTLE:-45}"
36
+ GRACE="${FORGE_CI_NO_CHECKS_GRACE:-120}"; REQUIRE="${FORGE_CI_REQUIRE_CHECKS:-0}"
37
+ usage() { echo "CI_GATE: ERROR"; echo "CI_GATE_HEAD: unknown"; echo "usage: wait-ci-green.sh <pr> -R <owner/repo> [--timeout S] [--interval S]" >&2; exit 2; }
38
+ while [ $# -gt 0 ]; do
39
+ case "$1" in
40
+ -R) [ $# -ge 2 ] || usage; REPO="$2"; shift 2 ;;
41
+ -R*) REPO="${1#-R}"; REPO="${REPO# }"; shift ;;
42
+ --timeout) [ $# -ge 2 ] || usage; TIMEOUT="$2"; shift 2 ;;
43
+ --interval) [ $# -ge 2 ] || usage; INTERVAL="$2"; shift 2 ;;
44
+ -h|--help) sed -n '5,25p' "$0"; exit 0 ;;
45
+ *) [ -z "$PR" ] || usage; PR="$1"; shift ;;
46
+ esac
47
+ done
48
+ case "$PR" in ''|*[!0-9]*) usage ;; esac
49
+ case "$REPO" in */*) ;; *) usage ;; esac
50
+ for v in "$TIMEOUT" "$INTERVAL" "$GRACE" "$SETTLE"; do case "$v" in ''|*[!0-9]*) usage ;; esac; done
51
+ [ "$INTERVAL" -ge 1 ] || INTERVAL=1
52
+
53
+ emit() { echo "CI_GATE: $1"; echo "CI_GATE_HEAD: ${START_SHA:-unknown}"; }
54
+ head_sha() { gh pr view "$PR" -R "$REPO" --json headRefOid --jq '.headRefOid' 2>/dev/null; }
55
+ START_SHA="$(head_sha)"
56
+ if [ -z "$START_SHA" ]; then START_SHA=""; emit ERROR; echo "cannot read PR #$PR head in $REPO"; exit 2; fi
57
+
58
+ start=$(date +%s); errors=0
59
+ while :; do
60
+ now=$(date +%s); elapsed=$((now - start))
61
+ if ! JSON="$(gh pr checks "$PR" -R "$REPO" --json name,bucket 2>/dev/null)" || ! printf '%s' "$JSON" | jq -e 'type=="array"' >/dev/null 2>&1; then
62
+ # gh pr checks exits non-zero both on outage and on "no checks reported"; tell them apart by output.
63
+ if printf '%s' "${JSON:-}" | jq -e 'type=="array"' >/dev/null 2>&1; then :; else
64
+ # Capture first: under pipefail, `gh ... | grep -q` fails whenever gh exits 1, which it
65
+ # does precisely in the "no checks reported" case this branch exists to detect.
66
+ NC_OUT="$(gh pr checks "$PR" -R "$REPO" 2>&1 || true)"
67
+ if printf '%s' "$NC_OUT" | grep -qi 'no checks reported'; then JSON='[]'
68
+ else
69
+ errors=$((errors + 1))
70
+ if [ "$errors" -ge 5 ]; then emit ERROR; echo "gh pr checks unreadable 5 times in a row"; exit 2; fi
71
+ sleep "$INTERVAL"; continue
72
+ fi
73
+ fi
74
+ fi
75
+ errors=0
76
+ cur="$(head_sha)"
77
+ if [ -n "$cur" ] && [ "$cur" != "$START_SHA" ]; then emit ERROR; echo "PR head moved during the wait ($START_SHA -> $cur); re-review the new head"; exit 2; fi
78
+ total=$(printf '%s' "$JSON" | jq 'length')
79
+ failed=$(printf '%s' "$JSON" | jq -r '.[] | select(.bucket=="fail" or .bucket=="cancel") | " - \(.name) (\(.bucket))"')
80
+ pending=$(printf '%s' "$JSON" | jq -r '.[] | select(.bucket!="pass" and .bucket!="skipping" and .bucket!="fail" and .bucket!="cancel") | " - \(.name) (\(.bucket))"')
81
+ if [ -n "$failed" ]; then emit FAIL; echo "PR #$PR head ${START_SHA:0:7}: failing checks:"; echo "$failed"; exit 1; fi
82
+ if [ "$total" -eq 0 ]; then
83
+ if [ "$elapsed" -ge "$GRACE" ]; then
84
+ RUNS=$(gh api "repos/$REPO/actions/runs?head_sha=$START_SHA&per_page=1" --jq '.total_count' 2>/dev/null || echo "")
85
+ if [ -n "$RUNS" ] && [ "$RUNS" != "0" ]; then
86
+ : # workflow runs exist for this head but no checks are reported yet — keep waiting
87
+ elif [ "$REQUIRE" = "1" ]; then
88
+ emit ERROR; echo "no checks reported after ${GRACE}s and FORGE_CI_REQUIRE_CHECKS=1"; exit 2
89
+ else
90
+ emit PASS; echo "no checks reported for PR #$PR after ${GRACE}s and no workflow runs for its head (no CI on this branch)"; exit 0
91
+ fi
92
+ fi
93
+ elif [ -z "$pending" ] && [ "$elapsed" -ge "$SETTLE" ]; then
94
+ emit PASS; echo "PR #$PR head ${START_SHA:0:7}: $total checks passed or skipped"; exit 0
95
+ fi
96
+ if [ "$elapsed" -ge "$TIMEOUT" ]; then emit TIMEOUT; echo "PR #$PR: still pending after ${TIMEOUT}s:"; echo "$pending"; exit 3; fi
97
+ sleep "$INTERVAL"
98
+ done
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env bash
2
+ # SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ # wait-ci-green.test.sh — offline tests for scripts/wait-ci-green.sh (fake gh on PATH).
5
+ # Usage: bash scripts/wait-ci-green.test.sh
6
+ set -uo pipefail
7
+ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; GATE="$HERE/wait-ci-green.sh"
8
+ T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
9
+ PASS=0; FAILN=0
10
+ expect() { [ "$2" = "$3" ] && PASS=$((PASS+1)) || { FAILN=$((FAILN+1)); echo "FAIL: $1 (got '$3' want '$2')"; }; }
11
+
12
+ # Fake gh: `pr view --json headRefOid` prints line N of $T/sha (N = call count, last line repeats);
13
+ # `pr checks --json` prints the N-th fixture $T/checks.N (last one repeats); "ERR" fixture = outage.
14
+ mkdir -p "$T/bin"
15
+ cat > "$T/bin/gh" <<'GH'
16
+ #!/usr/bin/env bash
17
+ D="$FAKE_DIR"
18
+ if [ "$1 $2" = "pr view" ]; then
19
+ n=$(( $(cat "$D/sha.n" 2>/dev/null || echo 0) + 1 )); echo "$n" > "$D/sha.n"
20
+ total=$(wc -l < "$D/sha"); [ "$n" -gt "$total" ] && n=$total; sed -n "${n}p" "$D/sha"; exit 0
21
+ fi
22
+ if [ "$1" = "api" ]; then
23
+ case "$2" in *actions/runs*) cat "$D/runs" 2>/dev/null || echo 0; exit 0 ;; esac
24
+ exit 1
25
+ fi
26
+ if [ "$1 $2" = "pr checks" ]; then
27
+ case " $* " in *" --json "*) ;; *) [ -f "$D/nochecks" ] && { echo "no checks reported on the 'x' branch" >&2; exit 1; }; exit 1 ;; esac
28
+ n=$(( $(cat "$D/chk.n" 2>/dev/null || echo 0) + 1 )); echo "$n" > "$D/chk.n"
29
+ f="$D/checks.$n"; while [ ! -f "$f" ] && [ "$n" -gt 1 ]; do n=$((n-1)); f="$D/checks.$n"; done
30
+ c=$(cat "$f"); [ "$c" = "ERR" ] && { echo "HTTP 502" >&2; exit 1; }
31
+ [ "$c" = "NONE" ] && { echo "no checks reported on the 'x' branch" >&2; exit 1; }
32
+ echo "$c"; exit 0
33
+ fi
34
+ exit 1
35
+ GH
36
+ chmod +x "$T/bin/gh"
37
+
38
+ run() { # run <case-dir> [gate args...] -> prints "rc|first line"
39
+ local d="$1"; shift
40
+ local out; out=$(FAKE_DIR="$d" PATH="$T/bin:$PATH" FORGE_CI_INTERVAL=1 FORGE_CI_NO_CHECKS_GRACE=1 FORGE_CI_SETTLE="${SETTLE:-0}" bash "$GATE" 7 -R o/r "$@" 2>/dev/null); local rc=$?
41
+ printf '%s|%s' "$rc" "$(printf '%s\n' "$out" | head -1)"
42
+ }
43
+ mk() { local d="$T/$1"; mkdir -p "$d"; printf 'abc1234\n' > "$d/sha"; shift; local i=1; for c in "$@"; do printf '%s' "$c" > "$d/checks.$i"; i=$((i+1)); done; echo "$d"; }
44
+
45
+ P='[{"name":"a","bucket":"pass"},{"name":"b","bucket":"skipping"}]'
46
+ expect "all pass/skipping" "0|CI_GATE: PASS" "$(run "$(mk pass "$P")")"
47
+ expect "one failure" "1|CI_GATE: FAIL" "$(run "$(mk fail '[{"name":"a","bucket":"pass"},{"name":"b","bucket":"fail"}]')")"
48
+ expect "cancelled counts as failure" "1|CI_GATE: FAIL" "$(run "$(mk cancel '[{"name":"a","bucket":"cancel"}]')")"
49
+ expect "pending then pass" "0|CI_GATE: PASS" "$(run "$(mk pend '[{"name":"a","bucket":"pending"}]' "$P")")"
50
+ expect "pending then fail" "1|CI_GATE: FAIL" "$(run "$(mk pendfail '[{"name":"a","bucket":"pending"}]' '[{"name":"a","bucket":"fail"}]')")"
51
+ expect "pending past timeout" "3|CI_GATE: TIMEOUT" "$(run "$(mk slow '[{"name":"a","bucket":"pending"}]')" --timeout 2)"
52
+ expect "fail wins over pending" "1|CI_GATE: FAIL" "$(run "$(mk mixed '[{"name":"a","bucket":"pending"},{"name":"b","bucket":"fail"}]')")"
53
+ expect "transient outage then pass" "0|CI_GATE: PASS" "$(run "$(mk blip ERR "$P")")"
54
+ expect "persistent outage" "2|CI_GATE: ERROR" "$(run "$(mk down ERR)" --timeout 60)"
55
+ d=$(mk none NONE); touch "$d/nochecks"
56
+ expect "no checks after grace passes" "0|CI_GATE: PASS" "$(run "$d")"
57
+ expect "no checks + REQUIRE fails closed" "2|CI_GATE: ERROR" "$(FORGE_CI_REQUIRE_CHECKS=1 run "$d")"
58
+ d=$(mk moved '[{"name":"a","bucket":"pending"}]' "$P"); printf 'abc1234\nabc1234\ndef5678\n' > "$d/sha"
59
+ expect "head moved during wait" "2|CI_GATE: ERROR" "$(run "$d")"
60
+ expect "bad pr number" "2|CI_GATE: ERROR" "$(FAKE_DIR="$T/pass" PATH="$T/bin:$PATH" bash "$GATE" abc -R o/r 2>/dev/null | head -1 | sed 's/^/2|/')"
61
+ expect "missing repo" "2" "$(FAKE_DIR="$T/pass" PATH="$T/bin:$PATH" bash "$GATE" 7 >/dev/null 2>&1; echo $?)"
62
+
63
+ d=$(mk runsnochecks NONE); touch "$d/nochecks"; echo 3 > "$d/runs"
64
+ expect "no checks but workflow runs exist keeps waiting (timeout)" "3|CI_GATE: TIMEOUT" "$(run "$d" --timeout 3)"
65
+ expect "pass is held until the settle period" "0|CI_GATE: PASS" "$(SETTLE=2 run "$(mk settle "$P")")"
66
+ out=$(FAKE_DIR="$(mk headline "$P")" PATH="$T/bin:$PATH" FORGE_CI_INTERVAL=1 FORGE_CI_SETTLE=0 bash "$GATE" 7 -R o/r 2>/dev/null)
67
+ expect "every result names the gated head" "CI_GATE_HEAD: abc1234" "$(printf '%s\n' "$out" | grep '^CI_GATE_HEAD:')"
68
+
69
+ # Static guard: every autonomous `gh pr merge` command line in the pipeline specs must be preceded
70
+ # (within 25 lines) by a CI gate result check. Prose/manual instructions are not command lines.
71
+ for spec in commands/review-pr.md commands/work-on/review.md commands/work-on/remediate.md commands/recover-orphans.md commands/deploy-pr.md; do
72
+ bad=$(awk '
73
+ { line[NR]=$0 }
74
+ /^[[:space:]]*(MERGE_RESULT=\$\()?gh pr merge / {
75
+ ok=0; for (i=NR-1; i>0 && i>=NR-25; i--) if (line[i] ~ /CI_GATE_RC/) { ok=1; break }
76
+ if (!ok) print FILENAME ":" NR
77
+ }' "$HERE/../$spec")
78
+ expect "every autonomous merge in $spec is CI-gated" "" "$bad"
79
+ done
80
+ # Every autonomous merge pins the gated head (forge#3187): no merge line may omit --match-head-commit.
81
+ for spec in commands/review-pr.md commands/work-on/review.md commands/work-on/remediate.md commands/recover-orphans.md commands/deploy-pr.md; do
82
+ unpinned=$(grep -nE '^[[:space:]]*(MERGE_RESULT=\$\(|MERGE_OUTPUT=\$\()?gh pr merge ' "$HERE/../$spec" | grep -v -- '--match-head-commit')
83
+ expect "every autonomous merge in $spec pins the gated head" "" "$unpinned"
84
+ done
85
+ # The gate's own default timeout stays under the 10-minute Bash tool limit (forge#3189).
86
+ expect "default gate timeout fits one Bash call" "540" "$(sed -n 's/.*TIMEOUT="\${FORGE_CI_TIMEOUT:-\([0-9]*\)}".*/\1/p' "$GATE")"
87
+
88
+ # The /work-on router never merges: every merge lives in a CI-gated phase.
89
+ if grep -qE '^[[:space:]]*(MERGE_RESULT=\$\()?gh pr merge ' "$HERE/../commands/work-on.md"; then FAILN=$((FAILN+1)); echo "FAIL: work-on.md router contains a merge command"; else PASS=$((PASS+1)); fi
90
+
91
+ # Reviewed-head guard: review-pr Phase 8 must refuse to merge a head other than the reviewed commit,
92
+ # and the work-on callers must route "stale review" to a re-review, never a manual merge.
93
+ grep -qF '${GATED_HEAD#"$REVIEWED_SHORT"}' "$HERE/../commands/review-pr.md" && PASS=$((PASS+1)) || { FAILN=$((FAILN+1)); echo "FAIL: review-pr Phase 8 lacks reviewed-head guard"; }
94
+ for f in commands/work-on/review.md; do
95
+ grep -q 'stale review' "$HERE/../$f" && PASS=$((PASS+1)) || { FAILN=$((FAILN+1)); echo "FAIL: $f does not route stale review"; }
96
+ done
97
+
98
+ echo "wait-ci-green tests: pass=$PASS fail=$FAILN"
99
+ [ "$FAILN" -eq 0 ]
@@ -1,326 +0,0 @@
1
- ---
2
- description: "[BENCHMARK] Monolithic work-on — single-prompt pipeline with no Skill sub-phase boundaries"
3
- argument-hint: "[issue number]"
4
- install: internal
5
- ---
6
- <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
7
- <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
8
-
9
- # /work-on-monolithic — Full Issue Pipeline (Single Prompt)
10
-
11
- **Input**: $ARGUMENTS
12
-
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
-
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
- **NEVER use plan mode (EnterPlanMode).**
17
-
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 -->
19
- **NEVER use the Agent tool** — this spec executes all phases inline as a single monolithic prompt. The Agent tool would spawn a subprocess outside this context, breaking the single-prompt contract and losing all accumulated pipeline state.
20
-
21
- <!-- FORGE:SPEC_LOADED — work-on-monolithic.md loaded and active. Agent is bound by this spec. -->
22
-
23
- ### Compaction Resilience
24
-
25
- 1. Write state to GitHub after EVERY significant step
26
- 2. Re-read GitHub state at the START of each phase (don't rely on in-memory context)
27
- 3. After compaction: re-read issue (body + comments + labels) to reconstruct state
28
- 4. Key principle: A NEW session running this command should pick up where the last left off by reading GitHub state alone
29
-
30
- ---
31
-
32
- ## Pipeline Rules
33
-
34
- - **NEVER merge to main.** PRs target `staging` (fast lane) or `milestone/{slug}` (feature lane).
35
- - **`Closes #N` does not auto-close for non-default-branch PRs.** You MUST explicitly `gh issue close`.
36
- - **Review findings are NOT merge blockers.** They become separate issues.
37
-
38
- ---
39
-
40
- ## Project Configuration
41
-
42
- Read `forge.yaml` from the repository root. If missing, tell the user to run `npx forgedock init` and stop.
43
-
44
- Resolve the following variables from `forge.yaml` before running any pipeline phase:
45
-
46
- | Variable | Source in forge.yaml | Description |
47
- |----------|----------------------|-------------|
48
- | `GH_REPO` | `project.owner` + `project.repo` | Full GitHub repo identifier (`owner/repo`) |
49
- | `REPO_PATH` | `paths.root` | Absolute path to the local repository |
50
- | `STAGING_BRANCH` | `branches.staging` | Branch for fast-lane PRs (no milestone) |
51
- | `WORKTREE_BASE` | `paths.worktree_base` | Directory where git worktrees are created |
52
- | `GH_FLAG` | _(empty for default repo; `-R owner/repo` for satellites)_ | Extra flag for `gh` CLI satellite repo calls |
53
-
54
- **Multi-repo routing** (optional): If `forge.yaml` contains a `repos.satellites` list, build the prefix routing table from it. Each satellite entry has `prefix`, `repo`, `staging_branch`, and `local_path`. Issues without a prefix route to the default `GH_REPO`. Satellite repos have no staging — their fast-lane PRs go to their configured `staging_branch` (typically `main`).
55
-
56
- If no `repos.satellites` section exists, only the primary repo is available — all issues route to `GH_REPO`.
57
-
58
- ---
59
-
60
- ## Phase 0: Resolve Issue & Load Context
61
-
62
- ### 0A: Parse input
63
- Extract project prefix and issue number.
64
-
65
- ### 0B: Load issue + existing context
66
- ```bash
67
- gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state,comments,milestone
68
- gh api repos/{GH_REPO}/issues/{NUMBER}/comments --jq '.[] | {id: .id, author: .user.login, body: .body}'
69
- ```
70
-
71
- **Check**: state (closed → STOP), terminal labels (`workflow:merged`/`workflow:invalid` → STOP), existing agent comments (`FORGE:INVESTIGATOR`, `FORGE:CONTRACT`, `FORGE:BUILDER`, `FORGE:TRAJECTORY`), parent tracker status.
72
-
73
- **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 → close issue.
74
-
75
- **Classify lane**: Milestone → feature lane (`milestone/{slug}`). No milestone → fast lane (`staging`).
76
-
77
- **Source branch for review-findings**: Parse `**Code branch**: \`{branch}\`` from body. Branch from there, not main.
78
-
79
- ### 0C: Sync to Project board
80
- Add issue to project, set Status=In Progress, Lane, Component, Priority, Workflow=Investigating.
81
-
82
- ---
83
-
84
- ## Phase 1: Investigation
85
-
86
- **Skip if**: `<!-- FORGE:INVESTIGATOR -->` exists with `<!-- INVESTIGATION:COMPLETE -->`.
87
-
88
- ### 1A: Set label
89
- ```bash
90
- gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:investigating"
91
- gh issue edit {NUMBER} {GH_FLAG} --remove-label "workflow:ready-to-build,workflow:building,workflow:in-review" 2>/dev/null || true
92
- ```
93
-
94
- ### 1B: Investigate the issue
95
-
96
- Mission: Validate whether the issue is real. Assume description is wrong until proven otherwise.
97
-
98
- **Domain file hints** (start search here):
99
-
100
- Configure domain-to-file mappings for your project in `forge.yaml → review.key_paths`. The table below shows the standard layout — override any entry that doesn't match your codebase:
101
-
102
- | Domain | Default key files (override via forge.yaml) |
103
- |--------|---------------------------------------------|
104
- | BILLING | `routers/billing.py`, `core/pricing.py`, `services/payment_service.py` |
105
- | WORKER | `worker/main.py`, `worker/queues.py`, `worker/tasks.py` |
106
- | AUTH | `core/auth.py`, `routers/auth.py`, `dependencies.py` |
107
- | DATABASE | `infra/migrations/`, `models/`, `db/` |
108
- | FRONTEND | `web/src/app/`, `web/src/components/`, `web/src/lib/` |
109
- | AI | `services/ai_client.py`, `routers/ai.py` |
110
- | INFRA | `.github/workflows/`, `docker-compose.yml`, `infra/traefik/` |
111
-
112
- If `forge.yaml → review.key_paths` is present, use those mappings instead of the defaults above.
113
-
114
- **Steps**: Check right branch → read domain files → verify claims → git blame → determine root cause → identify affected files → validate proposed fixes against full system stack.
115
-
116
- **Fix-Approach Validation**: If issue proposes a fix, don't adopt it as spec. Trace through target system's middleware, auth, routing.
117
-
118
- **Domain Context Discovery** (narrow scope, max 5 files):
119
- ```bash
120
- git log --oneline --all -30 -- {affected_files} | grep -oP '#\d+' | sort -u
121
- ```
122
-
123
- **Output format**: Verdict (CONFIRMED/PARTIAL/INVALID), Confidence, Severity, Task Type, What Was Claimed, What We Found, Root Cause, Affected Files, Evidence, Recommendation.
124
-
125
- ### 1C: Post investigation comment
126
- ```bash
127
- gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:INVESTIGATOR -->
128
- <!-- INVESTIGATION:COMPLETE -->
129
- ## Investigation Report
130
- {report}"
131
- ```
132
-
133
- ### 1D: Update labels
134
- CONFIRMED/PARTIAL → add `workflow:ready-to-build`, continue to Phase 3.
135
- INVALID → close issue, add `workflow:invalid`, STOP.
136
-
137
- ---
138
-
139
- ## Phase 3: Build
140
-
141
- **Skip if**: `<!-- FORGE:BUILDER -->` exists.
142
-
143
- ### 3A: Re-read state from GitHub (MANDATORY)
144
- ```bash
145
- gh issue view {NUMBER} {GH_FLAG} --json body,labels
146
- gh api repos/{GH_REPO}/issues/{NUMBER}/comments --jq '.[] | select(.body | contains("FORGE:INVESTIGATOR")) | .body'
147
- ```
148
-
149
- ### 3B: Classify task type
150
-
151
- | Signal | Type | Approach |
152
- |--------|------|----------|
153
- | UI/UX, feature + web/ files | UI/UX | `frontend-design` skill |
154
- | Feature + services/ | Backend Feature | Implement directly |
155
- | Feature + both | Full-Stack | Backend first, then frontend-design |
156
- | Bug + web/ | Frontend Fix | Direct |
157
- | Bug + services/ | Backend Fix | Direct |
158
- | Refactor/docs | Maintenance | Direct |
159
-
160
- ### 3C: Builder Contract (MANDATORY)
161
- Post `<!-- FORGE:CONTRACT -->` comment with: task type, proposed approach, deliverables table (file/change/why), acceptance criteria, quality considerations.
162
-
163
- ### 3C.5: Context Gathering (max 2 minutes)
164
-
165
- Surface institutional memory before writing code. For each affected file:
166
- ```bash
167
- # Recent agent work on these files
168
- git log --oneline --all -30 -- {affected_files} | grep -oP '#\d+' | sort -u | head -5
169
- # Check for review findings on same files
170
- for ISSUE_NUM in {related_issue_numbers}; do
171
- gh api repos/{GH_REPO}/issues/${ISSUE_NUM}/comments --jq '.[] | select(.body | contains("FORGE:")) | .body' 2>/dev/null | head -20
172
- done
173
- ```
174
-
175
- Post `<!-- FORGE:CONTEXT -->` comment with findings (or skip if no relevant history).
176
-
177
- ### 3C.6: Architecture Plan
178
-
179
- For multi-file changes, trace ALL affected code paths before writing code:
180
- - Map the call chain: entry point → middleware → handler → service → model → response
181
- - Identify every file that needs to change
182
- - Order implementation steps to avoid broken intermediate states
183
-
184
- Post `<!-- FORGE:ARCHITECT -->` comment with the implementation plan.
185
-
186
- For single-file or trivial changes, skip this step.
187
-
188
- ### 3D: Set building label
189
- ```bash
190
- gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:building" --remove-label "workflow:ready-to-build,workflow:investigating"
191
- ```
192
-
193
- ### 3E: Create worktree
194
- Branch slug from title (lowercase, hyphenated, max 40 chars). Prefix: `fix/` (bugs) or `feat/` (features).
195
- - Review-finding → branch from `origin/{SOURCE_BRANCH}`
196
- - Fast lane → branch from `origin/{STAGING_BRANCH}`
197
- - Feature lane → branch from `origin/milestone/{slug}`
198
-
199
- ```bash
200
- cd {REPO_PATH}
201
- git fetch origin
202
- BRANCH="fix/{slug}-{NUMBER}"
203
- git worktree add {WORKTREE_BASE}/{BRANCH} -b {BRANCH} origin/{PR_BASE}
204
- ```
205
-
206
- ### 3F: Implement
207
- Route by task type. Read the investigation report and contract. Implement the fix/feature.
208
-
209
- ### 3F.5: Quality Gate
210
- Invoke quality-gate on changed files:
211
- ```
212
- Skill(skill="quality-gate", args="{changed_files} --worktree {WORKTREE_PATH}")
213
- ```
214
- Fix HIGH/MEDIUM findings. Max 2 iterations. Skip for 1-file config/docs edits.
215
-
216
- ### 3G: Format and verify
217
- - Python: `black` + `isort` + `py_compile`
218
- - TypeScript (primary repo): `prettier --write` + `tsc --noEmit` (BLOCKING if fails)
219
- - TypeScript (satellite): `npm run build` + `prettier --write`
220
-
221
- ### 3H: Frontend proxy wiring check (MANDATORY)
222
- All client-side fetch/useSWR/apiFetch must use `/api/...` proxy routes, NEVER `/api/v1/...` directly.
223
-
224
- ### 3I: Deployment completeness check (MANDATORY)
225
- Skip if no new env vars introduced.
226
-
227
- For each new env var, verify present in ALL required locations:
228
-
229
- | Location | Required for |
230
- |----------|-------------|
231
- | `.env.example` | All new vars |
232
- | Secrets backend (see `deploy.secrets_backend`) | Secret vars — skip if backend is `none` or unset |
233
- | `app/env_validation.py` | API service vars (if project has one) |
234
-
235
- **Secrets backend check** *(trigger: `deploy.secrets_backend == "sops"`)*:
236
-
237
- If the project uses SOPS, verify the new var is present in all SOPS chain locations:
238
- - `infra/secrets/prod.enc.yaml` — SOPS-encrypted secret store
239
- - `infra/decrypt-secrets.sh` ENV_MAPPING — maps SOPS key to env var name
240
-
241
- If `deploy.secrets_backend` is absent or not `sops`, skip these checks and log:
242
- > `SKIP: SOPS chain check — deploy.secrets_backend is not "sops". Configure deploy.secrets_backend in forge.yaml to enable.`
243
-
244
- ### 3J: Commit
245
- Conventional prefix (fix/feat/refactor/docs). Reference #{NUMBER} in the commit message.
246
-
247
- ### 3K: Post implementation comment
248
- Post `<!-- FORGE:BUILDER -->` with: branch, commit, files changed, approach, changes list, testing checklist.
249
-
250
- ---
251
-
252
- ## Phase 4: PR Creation
253
-
254
- ### 4A: Push branch
255
- ```bash
256
- cd {WORKTREE_PATH} && git push -u origin {BRANCH}
257
- ```
258
- If fails: try `--force-with-lease`. If still fails: post comment, add `needs-human`, STOP.
259
-
260
- ### 4B: Determine PR target
261
- No milestone → `staging`. Has milestone → `milestone/{slug}`. NEVER `main`.
262
-
263
- ### 4C: Create PR
264
- ```bash
265
- gh pr create {GH_FLAG} --base {PR_BASE} --title "{Fix|Feat}: {description} (#{NUMBER})" --body "..."
266
- ```
267
- Include `Closes #{NUMBER}`.
268
-
269
- ### 4D: Update labels
270
- ```bash
271
- gh issue edit {NUMBER} {GH_FLAG} --add-label "workflow:in-review" --remove-label "workflow:building"
272
- ```
273
-
274
- ---
275
-
276
- ## Phase 5: Auto-Review
277
-
278
- ### 5A: Re-read state from GitHub (MANDATORY)
279
- ### 5B: Invoke /review-pr with --auto-merge
280
- ```
281
- Skill(skill="review-pr", args="{PR_NUMBER} --auto-merge --issue {NUMBER} --base {PR_BASE} --gh-flag {GH_FLAG}")
282
- ```
283
-
284
- ### 5C: Verify merge and close (recovery)
285
- - PR MERGED + issue CLOSED → proceed to Phase 6/7
286
- - PR MERGED + issue OPEN → close issue manually
287
- - PR NOT MERGED → `gh pr merge --merge {GH_FLAG}`, close issue. If merge fails → post comment, add `needs-human`, STOP.
288
-
289
- ### 5D: Project board update (Workflow=Merged, Status=Done)
290
-
291
- ### 5E: Final issue body update
292
- Check off remaining items.
293
-
294
- ---
295
-
296
- ## Phase 6: Parent Tracker Update (Sub-Issues Only)
297
-
298
- **Skip if**: Not a sub-issue.
299
-
300
- Check off this sub-issue in parent body. If ALL sub-issues closed → close parent with `workflow:merged`.
301
-
302
- ---
303
-
304
- ## Phase 7: Summary & Trajectory
305
-
306
- ### 7A: Report
307
- ```
308
- ## Done: #{NUMBER} — {TITLE}
309
- - Investigation: {verdict} ({confidence})
310
- - Lane: {FAST/FEATURE}
311
- - Fix: {branch} → PR #{PR_NUMBER} → merged to `{PR_BASE}`
312
- - Files changed: {count}
313
- ```
314
-
315
- ### 7B: Trajectory Log (MANDATORY)
316
- Post `<!-- FORGE:TRAJECTORY -->` comment with phase-by-phase results table, decisions, anomalies.
317
-
318
- ---
319
-
320
- ## Error Handling
321
-
322
- - Worktree exists: reuse or clean up
323
- - PR creation fails: check if branch pushed, if PR already exists
324
- - Merge conflicts: report to user, do NOT auto-resolve
325
- - gh CLI fails: check `gh auth status`
326
- - Label missing: create it