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.
- package/bin/engine/admission.mjs +57 -1
- package/bin/engine/admission.test.mjs +60 -0
- package/commands/analytics.md +3 -1
- package/commands/audit-agents.md +3 -1
- package/commands/audit.md +10 -8
- package/commands/autopilot.md +16 -14
- package/commands/ci-audit.md +4 -2
- package/commands/cleanup.md +1 -1
- package/commands/deploy-pr.md +31 -11
- package/commands/design-bench.md +1 -0
- package/commands/design-render-critique-loop.md +1 -0
- package/commands/design.md +1 -0
- package/commands/forge-stats.md +1 -0
- package/commands/geo-audit.md +3 -1
- package/commands/incident-response.md +3 -1
- package/commands/issue.md +4 -2
- package/commands/milestone.md +8 -6
- package/commands/orchestrate/config.md +3 -2
- package/commands/orchestrate/phase-1-resolve.md +57 -12
- package/commands/orchestrate/phase-2-triage.md +1 -0
- package/commands/orchestrate/phase-2.5-synthesis.md +1 -0
- package/commands/orchestrate/phase-3-dependency.md +17 -5
- package/commands/orchestrate/phase-4-execution.md +625 -78
- package/commands/orchestrate/phase-5-cleanup.md +5 -2
- package/commands/orchestrate/phase-6-report.md +8 -3
- package/commands/orchestrate/safety.md +1 -0
- package/commands/orchestrate.md +13 -9
- package/commands/pipeline-health/config.md +1 -0
- package/commands/pipeline-health/phase-1-context.md +1 -0
- package/commands/pipeline-health/phase-2-metrics.md +1 -0
- package/commands/pipeline-health/phase-3-analyze.md +1 -0
- package/commands/pipeline-health/phase-4-proposals.md +7 -4
- package/commands/pipeline-health/phase-5-report.md +5 -2
- package/commands/pipeline-health/phase-6-summary.md +1 -0
- package/commands/pipeline-health.md +9 -7
- package/commands/pipeline-resume.md +4 -2
- package/commands/qa-sweep.md +3 -1
- package/commands/quality-gate.md +4 -3
- package/commands/recover-orphans.md +219 -13
- package/commands/review-pr-agents/api.md +1 -0
- package/commands/review-pr-agents/auth.md +1 -0
- package/commands/review-pr-agents/billing.md +1 -0
- package/commands/review-pr-agents/concurrency.md +1 -0
- package/commands/review-pr-agents/database.md +1 -0
- package/commands/review-pr-agents/frontend.md +1 -0
- package/commands/review-pr-agents/infra.md +1 -0
- package/commands/review-pr-agents/protocols.md +1 -0
- package/commands/review-pr-agents/scraper.md +1 -0
- package/commands/review-pr-agents/security.md +1 -0
- package/commands/review-pr-agents/spec-cli.md +1 -0
- package/commands/review-pr-agents.md +10 -4
- package/commands/review-pr-staging.md +14 -5
- package/commands/review-pr.md +187 -19
- package/commands/rollback.md +3 -1
- package/commands/security-audit.md +3 -1
- package/commands/signal-planner.md +6 -4
- package/commands/sync-ecosystem.md +4 -2
- package/commands/test-gate.md +4 -2
- package/commands/upgrade-deps.md +3 -1
- package/commands/validate.md +3 -1
- package/commands/work-on/build/architect.md +82 -19
- package/commands/work-on/build/context.md +62 -18
- package/commands/work-on/build/implement.md +63 -18
- package/commands/work-on/build/validate.md +241 -60
- package/commands/work-on/build.md +345 -146
- package/commands/work-on/close.md +618 -246
- package/commands/work-on/decompose.md +240 -41
- package/commands/work-on/investigate.md +378 -61
- package/commands/work-on/remediate.md +43 -9
- package/commands/work-on/review.md +241 -29
- package/commands/work-on.md +180 -2014
- package/docs/CODEX.md +2 -0
- package/docs/CONFIG.md +11 -0
- package/package.json +1 -1
- package/scripts/build-spec-graph.mjs +11 -4
- package/scripts/check-spec-markers.sh +21 -1
- package/scripts/checkbox-sections.test.sh +8 -23
- package/scripts/extract-affected-files.sh +5 -2
- package/scripts/extract-affected-files.test.sh +27 -0
- package/scripts/forge-root.test.sh +176 -0
- package/scripts/is-docs-only.sh +31 -0
- package/scripts/is-docs-only.test.sh +64 -0
- package/scripts/lint-dispatch-prompt.sh +217 -0
- package/scripts/lint-dispatch-prompt.test.sh +307 -0
- package/scripts/validate-spec-graph.sh +3 -2
- package/scripts/verify-phase-trail.sh +282 -0
- package/scripts/verify-phase-trail.test.sh +298 -0
- package/scripts/wait-ci-green.sh +98 -0
- package/scripts/wait-ci-green.test.sh +99 -0
- 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
|