forgedock 1.14.0 → 1.16.0
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/phases-args.test.mjs +11 -0
- package/bin/engine/phases.mjs +133 -15
- package/bin/engine/phases.test.mjs +24 -0
- package/bin/engine/runlog.mjs +10 -1
- package/bin/engine.mjs +43 -7
- package/bin/forgedock.mjs +1 -1
- package/commands/forge-stats.md +10 -0
- package/commands/issue.md +17 -2
- package/commands/orchestrate/phase-4-execution.md +10 -3
- package/commands/review-pr-staging.md +290 -30
- package/commands/review-pr.md +71 -22
- package/commands/work-on/build/implement.md +15 -1
- package/commands/work-on/build.md +7 -4
- package/commands/work-on/investigate.md +5 -3
- package/commands/work-on/remediate.md +215 -35
- package/docs/CONFIG.md +2 -2
- package/docs/RELEASE-NOTES.md +12 -0
- package/package.json +1 -1
- package/packages/protocol/src/phases.js +6 -0
- package/scripts/check-spec-markers.sh +2 -0
- package/scripts/check-spec-size.sh +102 -0
- package/scripts/check-spec-size.test.sh +85 -0
- package/scripts/classify-finding.sh +14 -7
- package/scripts/classify-finding.test.sh +9 -0
- package/scripts/forge-root.test.sh +41 -4
- package/scripts/remediate-approved-head.test.sh +43 -0
- package/scripts/remediation-marker-match.test.sh +6 -0
- package/scripts/resolve-deploy-gate.sh +51 -0
- package/scripts/resolve-deploy-gate.test.sh +59 -0
- package/scripts/review-pr-delta-wiring.test.sh +8 -0
- package/scripts/review-pr-domain-map.test.sh +10 -0
- package/scripts/review-pr-staging-scope.test.sh +127 -0
|
@@ -99,6 +99,48 @@ done
|
|
|
99
99
|
awk '/^[^ `#].* # FENCE_BOOTSTRAP/ && !seen[$0]++' "$SPEC" > "$TMP/boot.sh"
|
|
100
100
|
[ "$(wc -l < "$TMP/boot.sh")" -eq 8 ] && ok || bad "FENCE_BOOTSTRAP block expected 8 distinct lines (got $(wc -l < "$TMP/boot.sh"))"
|
|
101
101
|
[ "$(grep -c '^# Fence bootstrap' "$SPEC")" -ge 3 ] && ok || bad "bootstrap must be carried by 0B.1 and both 7B.5 fences"
|
|
102
|
+
# forge#3681: EVERY bash fence after Phase 0B that reads cross-fence state must carry the bootstrap (a fresh Bash call loses it)
|
|
103
|
+
UNCOVERED=$(awk '
|
|
104
|
+
/^```bash/ { infence=1; body=""; start=NR; next }
|
|
105
|
+
/^```/ && infence { infence=0
|
|
106
|
+
if (start > 400 && body ~ /PR_NUMBER|ALL_PR_NUMBERS|GH_REPO|DEFAULT_BRANCH|STAGING_BRANCH/ && body !~ /# FENCE_BOOTSTRAP/) printf "%d ", start
|
|
107
|
+
next }
|
|
108
|
+
infence { body = body "\n" $0 }' "$SPEC")
|
|
109
|
+
[ -z "$UNCOVERED" ] && ok || bad "bash fences starting at line(s) ${UNCOVERED}consume cross-fence state without the FENCE_BOOTSTRAP block"
|
|
110
|
+
# the persisted Phase 6.5 / 6.75 values: producers write them, consumers reload them validated
|
|
111
|
+
grep -q 'test_gate_verdict' "$SPEC" && grep -q 'launched_review_agents' "$SPEC" && ok || bad "TEST_GATE_VERDICT and LAUNCHED_REVIEW_AGENTS must be persisted to the scratchpad"
|
|
112
|
+
# PR-keyed (concurrent runs in one cwd must not share state) and reset by an executable command, not prose
|
|
113
|
+
if grep -qE '\$_SD/(test_gate_verdict|test_gate_reason|launched_review_agents)|\.forge-scratch\}/launched_review_agents' "$SPEC"; then bad "gate/launched-agent scratch files must be keyed by \${PR_NUMBER}_"; else ok; fi
|
|
114
|
+
grep -qF ': > "$_SD/${PR_NUMBER}_launched_review_agents"' "$SPEC" && grep -qF 'rm -f "$_SD/${PR_NUMBER}_test_gate_verdict" "$_SD/${PR_NUMBER}_test_gate_reason"' "$SPEC" && ok || bad "Phase 2 must truncate and Phase 6.5 must rm -f the PR-keyed scratch files in executable commands"
|
|
115
|
+
if awk '/^### 7E:/,/^### 7F:/' "$SPEC" | grep -qF '{GH_REPO}'; then bad "7E must not use the {GH_REPO} template token"; else ok; fi
|
|
116
|
+
# (8b) forge#3733: Phase 6.75 fails closed on an empty/absent launched-agent list or empty PR_NUMBER; Phase 6 fails closed on a bad SELECTED_AGENT_COUNT
|
|
117
|
+
F675=$(awk '/^### Phase 6.75/,/^### 7A/' "$SPEC" | awk '/^```bash/{f=1;next} /^```/{if(f)exit} f')
|
|
118
|
+
GUARD_LN=$(printf '%s\n' "$F675" | grep -n 'launched-agent list is empty or unreadable' | head -1 | cut -d: -f1)
|
|
119
|
+
LOOP_LN=$(printf '%s\n' "$F675" | grep -n '^for AGENT_DOMAIN in' | head -1 | cut -d: -f1)
|
|
120
|
+
[ -n "$GUARD_LN" ] && [ -n "$LOOP_LN" ] && [ "$GUARD_LN" -lt "$LOOP_LN" ] && ok || bad "6.75 empty-list guard must precede the for AGENT_DOMAIN loop"
|
|
121
|
+
printf '%s\n' "$F675" | sed -n "1,$((LOOP_LN-1))p" | grep -v '^[[:space:]]*gh ' > "$TMP/f675.sh"
|
|
122
|
+
mkdir -p "$TMP/sd9/empty" "$TMP/sd9/full" "$TMP/bin9"
|
|
123
|
+
printf '#!/bin/sh\necho stub-value\n' > "$TMP/bin9/yq"; printf '#!/bin/sh\nexit 0\n' > "$TMP/bin9/gh"; chmod +x "$TMP/bin9/yq" "$TMP/bin9/gh"
|
|
124
|
+
: > "$TMP/sd9/empty/42_launched_review_agents"
|
|
125
|
+
printf 'security\n' > "$TMP/sd9/full/42_launched_review_agents"
|
|
126
|
+
run675() { ( unset LAUNCHED_REVIEW_AGENTS REVIEW_SHA_FULL; export PATH="$TMP/bin9:$PATH" FORGE_SCRATCHPAD="$1" HOME="$TMP/nohome"; [ -n "$2" ] && export PR_NUMBER="$2"; bash "$TMP/f675.sh" 2>&1; echo "rc=$?" ); }
|
|
127
|
+
for _case in "$TMP/sd9/empty|42" "$TMP/sd9/none|42" "$TMP/sd9/full|"; do
|
|
128
|
+
OUT9=$(run675 "${_case%%|*}" "${_case##*|}")
|
|
129
|
+
case "$OUT9" in *"REVIEW DELIVERY FAILURE: launched-agent list is empty or unreadable"*"rc=1"*) ok ;; *) bad "6.75 must exit 1 for case '$_case' (got '$OUT9')" ;; esac
|
|
130
|
+
done
|
|
131
|
+
OUT9=$(run675 "$TMP/sd9/full" 42)
|
|
132
|
+
case "$OUT9" in *"launched-agent list is empty"*) bad "6.75 guard must not fire for a non-empty list (got '$OUT9')" ;; *) ok ;; esac
|
|
133
|
+
# Phase 6 SELECTED_AGENT_COUNT validation: static order + behaviour of the reload/validate lines
|
|
134
|
+
F6=$(awk '/Selected isolated reviewers/{exit} {print}' "$SPEC" | awk '/^```bash/{b=""; f=1; next} /^```/{f=0; next} f{b=b $0 "\n"; last=b} END{printf "%s", last}')
|
|
135
|
+
printf '%s' "$F6" | grep -qF 'SELECTED_COUNT_INVALID' && ok || bad "Phase 6 fence must validate SELECTED_AGENT_COUNT (SELECTED_COUNT_INVALID)"
|
|
136
|
+
printf '%s' "$F6" | grep -F 'SELECTED_AGENT_COUNT=' | grep -F 'launched_review_agents' > "$TMP/sel.sh"
|
|
137
|
+
printf '%s\n' 'SELECTED_COUNT_INVALID=false' "$(printf '%s' "$F6" | grep -F 'case "${SELECTED_AGENT_COUNT:-}"')" > "$TMP/selcase.sh"
|
|
138
|
+
for _c in "$TMP/sd9/empty|42" "$TMP/sd9/none|42" "$TMP/sd9/full|42"; do
|
|
139
|
+
OUT9=$( ( unset SELECTED_AGENT_COUNT; _SD="${_c%%|*}"; PR_NUMBER="${_c##*|}"; . "$TMP/sel.sh"; . "$TMP/selcase.sh"; echo "$SELECTED_COUNT_INVALID|$SELECTED_AGENT_COUNT" ) 2>&1 )
|
|
140
|
+
case "${_c%%|*}" in */full) [ "$OUT9" = "false|1" ] && ok || bad "Phase 6: one launched agent must give SELECTED_AGENT_COUNT=1 (got '$OUT9')" ;; *) [ "$OUT9" = "true|0" ] && ok || bad "Phase 6: empty/absent list must be invalid (got '$OUT9')" ;; esac
|
|
141
|
+
done
|
|
142
|
+
OUT9=$( ( SELECTED_AGENT_COUNT="abc"; . "$TMP/selcase.sh"; echo "$SELECTED_COUNT_INVALID" ) 2>&1 )
|
|
143
|
+
[ "$OUT9" = "true" ] && ok || bad "Phase 6: non-integer SELECTED_AGENT_COUNT must be invalid (got '$OUT9')"
|
|
102
144
|
mkdir -p "$TMP/sd8/full" "$TMP/sd8/empty" "$TMP/sd8/bad" "$TMP/bin"
|
|
103
145
|
printf '#!/bin/sh\necho stub-value\n' > "$TMP/bin/yq"; chmod +x "$TMP/bin/yq"
|
|
104
146
|
printf '42\n' > "$TMP/sd8/full/staging_pr_number"; printf '7\n8\n' > "$TMP/sd8/full/42_bundle-prs.txt"
|
|
@@ -155,5 +197,90 @@ run_dup() { # finding-file finding-line -> "DISPOSITION|FILED|REFLAG|LISTED"
|
|
|
155
197
|
[ "$(run_dup src/other.py:12)" = "ISSUE MEDIUM-CONFIRMED|1|0|0" ] && ok || bad "a non-duplicate on another file must classify normally (got '$(run_dup src/other.py:12)')"
|
|
156
198
|
run_dup src/api.py:12 >/dev/null
|
|
157
199
|
grep -q 'reflag-filed #123' "$TMP/dup-notes.md" && ok || bad "a re-flag NOTE must be listed, not dropped"
|
|
200
|
+
# (9) stale-head delta credit (forge#3638): the spec's bundle_delta_credit runs against real git fixtures and the REAL review-delta.sh / trusted-comments.sh.
|
|
201
|
+
# Fail-closed cases first: every FULL / error / untrusted path must leave the PR's commits UNREVIEWED (empty credit).
|
|
202
|
+
if command -v git >/dev/null 2>&1; then
|
|
203
|
+
awk '/^# BEGIN bundle-delta-credit$/{f=1;next} /^# END bundle-delta-credit$/{f=0} f' "$SPEC" > "$TMP/delta-fn.sh"
|
|
204
|
+
[ -s "$TMP/delta-fn.sh" ] && ok || bad "bundle-delta-credit block not found in the spec"
|
|
205
|
+
RDS="$ROOT/scripts/review-delta.sh"
|
|
206
|
+
export GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null GIT_CONFIG_NOSYSTEM=1
|
|
207
|
+
export GIT_AUTHOR_NAME=t GIT_AUTHOR_EMAIL=t@example.invalid GIT_COMMITTER_NAME=t GIT_COMMITTER_EMAIL=t@example.invalid
|
|
208
|
+
g9() { git -C "$W9" -c commit.gpgsign=false -c core.hooksPath=/dev/null "$@"; }
|
|
209
|
+
commit9() { mkdir -p "$W9/$(dirname "$1")"; printf '%s\n' "$2" > "$W9/$1"; g9 add -A >/dev/null; g9 commit -q -m "$3" >/dev/null; }
|
|
210
|
+
C9=0
|
|
211
|
+
mkrepo9() { # W9 = clone with origin/main, branch feat with one PR commit
|
|
212
|
+
C9=$((C9+1)); local o="$TMP/do$C9"; W9="$TMP/dw$C9"
|
|
213
|
+
git init -q --bare -b main "$o"; git clone -q "$o" "$W9" 2>/dev/null
|
|
214
|
+
g9 checkout -q -b main 2>/dev/null; commit9 a.txt base base; g9 push -q origin main; g9 checkout -q feat 2>/dev/null || g9 checkout -q -b feat; commit9 pr.txt pr1 pr1
|
|
215
|
+
}
|
|
216
|
+
agent9() { printf '<!-- FORGE:REVIEW-AGENT:%s -->\nReviewed-SHA: %s\n\n<!-- REVIEW-FINDINGS-START -->\nNo findings.\n<!-- REVIEW-FINDINGS-END -->' "$1" "$2"; }
|
|
217
|
+
synth9() { printf '<!-- REVIEW-FINDINGS-SYNTHESIZED-START -->\nReviewed-SHA: %s\n\nsynth' "$1"; }
|
|
218
|
+
carried9() { printf '<!-- FORGE:REVIEW-CARRIED -->\nReviewed-SHA: %s\nCarried-From-SHA: %s\n' "$1" "$2"; }
|
|
219
|
+
cm9() { jq -n --arg b "$1" --arg t "${2:-Bot}" '{"body":$b,"author_association":"NONE","user":{"type":$t,"login":"x"}}'; }
|
|
220
|
+
panel9() { CM9=$(printf '%s\n' "$(cm9 "$(agent9 security "$1")")" "$(cm9 "$(agent9 api "$1")")" "$(cm9 "$(synth9 "$1")")" | jq -s '.'); }
|
|
221
|
+
credit9() { # args: head, labels-csv (default none); needs W9 CM9; prints "<DELTA_FULL>|<credited oids, short>"
|
|
222
|
+
local head="$1" labels="${2:-}" J
|
|
223
|
+
J=$(git -C "$W9" log --format=%H origin/main..HEAD | jq -R . | jq -s --arg h "$head" --arg l "$labels" '{headRefOid:$h,baseRefName:"main",mergeCommit:null,labels:($l|split(",")|map(select(.!="")|{name:.})),commits:(map({oid:.})|reverse)}')
|
|
224
|
+
( cd "$W9"; pr=7; PR_NUMBER=9; GH_REPO=o/r; _SD="$TMP"; _HEAD="$head"; _J="$J"; _CM="$CM9"; TRUSTED_SCRIPT="${T9_TRUSTED:-$TC}"; REVIEW_DELTA_SCRIPT="${T9_RD-$RDS}"
|
|
225
|
+
. "$TMP/delta-fn.sh"; bundle_delta_credit; echo "$DELTA_FULL|$(for o in $DELTA_COMMITS; do printf '%s ' "${o:0:7}"; done)" )
|
|
226
|
+
}
|
|
227
|
+
short9() { git -C "$W9" rev-parse --short=7 "$1"; }
|
|
228
|
+
# stale-SHA PR, but no trusted panel: stays UNREVIEWED
|
|
229
|
+
mkrepo9; R=$(git -C "$W9" rev-parse HEAD); commit9 pr.txt pr2 pr2; H=$(git -C "$W9" rev-parse HEAD); CM9='[]'
|
|
230
|
+
[ "$(credit9 "$H")" = "0|" ] && ok || bad "stale head with no trusted panel must credit nothing (got '$(credit9 "$H")')"
|
|
231
|
+
# untrusted reviewer comments (User type): stays UNREVIEWED
|
|
232
|
+
CM9=$(printf '%s\n' "$(cm9 "$(agent9 security "$R")" User)" "$(cm9 "$(agent9 api "$R")" User)" "$(cm9 "$(synth9 "$R")" User)" | jq -s '.')
|
|
233
|
+
[ "$(credit9 "$H")" = "0|" ] && ok || bad "untrusted panel comments must credit nothing (got '$(credit9 "$H")')"
|
|
234
|
+
# resolver script missing / garbage / error: UNREVIEWED
|
|
235
|
+
panel9 "$R"
|
|
236
|
+
[ "$(T9_RD="" credit9 "$H")" = "0|" ] && ok || bad "unresolved review-delta.sh must credit nothing"
|
|
237
|
+
printf '#!/bin/sh\necho "DELTA garbage"\necho FULL_ROUNDS=1\n' > "$TMP/rd-garbage.sh"
|
|
238
|
+
[ "$(T9_RD="$TMP/rd-garbage.sh" credit9 "$H")" = "0|" ] && ok || bad "malformed resolver output must credit nothing"
|
|
239
|
+
printf '#!/bin/sh\nexit 3\n' > "$TMP/rd-error.sh"
|
|
240
|
+
[ "$(T9_RD="$TMP/rd-error.sh" credit9 "$H")" = "0|" ] && ok || bad "a failing resolver must credit nothing"
|
|
241
|
+
printf '#!/bin/sh\necho FULL\necho FULL_ROUNDS=0\n' > "$TMP/rd-full.sh"
|
|
242
|
+
[ "$(T9_RD="$TMP/rd-full.sh" credit9 "$H")" = "0|" ] && ok || bad "resolver FULL must credit nothing"
|
|
243
|
+
printf '#!/bin/sh\necho "DELTA %s..%s"\necho FULL_ROUNDS=1\n' "$H" "$H" > "$TMP/rd-same.sh"
|
|
244
|
+
[ "$(T9_RD="$TMP/rd-same.sh" credit9 "$H")" = "0|" ] && ok || bad "a resolver DELTA whose from equals head must credit nothing"
|
|
245
|
+
printf '#!/bin/sh\necho "DELTA %s..%s"\necho FULL_ROUNDS=1\n' "$R" "$R" > "$TMP/rd-wrongto.sh"
|
|
246
|
+
[ "$(T9_RD="$TMP/rd-wrongto.sh" credit9 "$H")" = "0|" ] && ok || bad "a resolver DELTA whose to is not the PR head must credit nothing"
|
|
247
|
+
# degraded label: the resolver forces FULL
|
|
248
|
+
[ "$(credit9 "$H" review-degraded)" = "0|" ] && ok || bad "a review-degraded PR must credit nothing"
|
|
249
|
+
# force-pushed PR (reviewed sha no longer an ancestor): all UNREVIEWED
|
|
250
|
+
mkrepo9; R=$(git -C "$W9" rev-parse HEAD); panel9 "$R"; g9 reset -q --hard HEAD~1; commit9 pr.txt rewritten rewritten; H=$(git -C "$W9" rev-parse HEAD)
|
|
251
|
+
[ "$(credit9 "$H")" = "0|" ] && ok || bad "force-pushed PR must keep every commit UNREVIEWED (got '$(credit9 "$H")')"
|
|
252
|
+
# resolver says DELTA from a sha that is not an ancestor of the PR commits (stub): per-commit ancestor test credits nothing
|
|
253
|
+
mkrepo9; R=$(git -C "$W9" rev-parse HEAD); commit9 pr.txt pr2 pr2; H=$(git -C "$W9" rev-parse HEAD); panel9 "$R"
|
|
254
|
+
g9 checkout -q -b other main; commit9 o.txt o other; O=$(git -C "$W9" rev-parse HEAD); g9 checkout -q feat
|
|
255
|
+
printf '#!/bin/sh\necho "DELTA %s..%s"\necho FULL_ROUNDS=1\n' "$O" "$H" > "$TMP/rd-unrelated.sh"
|
|
256
|
+
[ "$(T9_RD="$TMP/rd-unrelated.sh" credit9 "$H")" = "0|" ] && ok || bad "a DELTA from an unrelated sha must credit no PR commit"
|
|
257
|
+
# carried base-sync: untrusted / absent record stays UNREVIEWED
|
|
258
|
+
mkrepo9; R=$(git -C "$W9" rev-parse HEAD); panel9 "$R"; g9 checkout -q main; commit9 b.txt b1 b1; g9 push -q origin main; g9 checkout -q feat; g9 fetch -q origin
|
|
259
|
+
g9 merge -q --no-ff -m "merge main" origin/main >/dev/null 2>&1; H=$(git -C "$W9" rev-parse HEAD)
|
|
260
|
+
PANEL9=$CM9
|
|
261
|
+
[ "$(credit9 "$H")" = "0|" ] && ok || bad "BASE_SYNC_ONLY with no carried record must credit nothing (got '$(credit9 "$H")')"
|
|
262
|
+
CM9=$(printf '%s\n%s\n' "$(printf '%s' "$PANEL9" | jq -c '.[]')" "$(cm9 "$(carried9 "$H" "$R")" User)" | jq -s '.')
|
|
263
|
+
[ "$(credit9 "$H")" = "0|" ] && ok || bad "an untrusted REVIEW-CARRIED record must credit nothing (got '$(credit9 "$H")')"
|
|
264
|
+
CM9=$(printf '%s\n%s\n' "$(printf '%s' "$PANEL9" | jq -c '.[]')" "$(cm9 "$(carried9 "$H" "$(printf 'c%.0s' $(seq 1 40))")")" | jq -s '.')
|
|
265
|
+
[ "$(credit9 "$H")" = "0|" ] && ok || bad "a carried record naming another reviewed sha must credit nothing"
|
|
266
|
+
# --- happy paths
|
|
267
|
+
CM9=$(printf '%s\n%s\n' "$(printf '%s' "$PANEL9" | jq -c '.[]')" "$(cm9 "$(carried9 "$H" "$R")")" | jq -s '.')
|
|
268
|
+
[ "$(credit9 "$H")" = "1|" ] && ok || bad "carried base-sync with a trusted record must credit the whole PR (got '$(credit9 "$H")')"
|
|
269
|
+
# stale-SHA PR: older trusted complete panel at R, one real new commit -> only the old commit is credited
|
|
270
|
+
mkrepo9; R=$(git -C "$W9" rev-parse HEAD); R7=$(short9 "$R"); commit9 pr.txt pr2 pr2; H=$(git -C "$W9" rev-parse HEAD); H7=$(short9 "$H"); panel9 "$R"
|
|
271
|
+
OUT9=$(credit9 "$H")
|
|
272
|
+
[ "$OUT9" = "0|$R7 " ] && ok || bad "stale-SHA PR must credit only commits up to the reviewed sha, not the new one (got '$OUT9', new commit $H7)"
|
|
273
|
+
# partially reviewed PR: two commits reviewed, two new -> only the reviewed two credited
|
|
274
|
+
mkrepo9; commit9 pr.txt pr1b pr1b; R=$(git -C "$W9" rev-parse HEAD); panel9 "$R"; commit9 pr.txt pr3 pr3; commit9 pr.txt pr4 pr4; H=$(git -C "$W9" rev-parse HEAD)
|
|
275
|
+
OUT9=$(credit9 "$H"); N9=$(printf '%s' "${OUT9#*|}" | wc -w)
|
|
276
|
+
[ "${OUT9%%|*}" = 0 ] && [ "$N9" -eq 2 ] && case "$OUT9" in *"$(short9 HEAD)"*|*"$(short9 HEAD~1)"*) false ;; *) true ;; esac && ok || bad "partial review must credit exactly the 2 reviewed commits (got '$OUT9')"
|
|
277
|
+
# spec text guards: the resolver is reused, never copied; carried record accepted; the unchanged cross-PR and merge-resolution paths are still present
|
|
278
|
+
grep -q 'review-delta.sh' "$SPEC" && grep -q 'REVIEW-CARRIED' "$SPEC" && ok || bad "spec must call review-delta.sh and accept the REVIEW-CARRIED record"
|
|
279
|
+
if grep -qE 'merge-base --is-ancestor.*REVIEWED_SHA_STALE|diff_shape\(\)' "$SPEC"; then bad "spec must not carry a second copy of the resolver logic"; else ok; fi
|
|
280
|
+
grep -qF 'printf '"'"'%s\n'"'"' "$_FILES" | sort -u | sed "s|^|$pr |" >> "$_PF"' "$SPEC" && grep -qF 'git diff-tree --no-commit-id --name-only -r --cc "$c"' "$SPEC" && ok || bad "CROSS_PR_FILES accumulation and --cc merge scoping must stay intact"
|
|
281
|
+
[ "$(grep -c '# FENCE_BOOTSTRAP' "$SPEC" | head -1)" -ge 8 ] && ok || bad "FENCE_BOOTSTRAP lines must remain"
|
|
282
|
+
# a stale-head PR must not credit its own merge commit (it contains the post-review commits): the DELTA branch only appends DELTA_COMMITS
|
|
283
|
+
if awk '/^bundle_delta_credit\(\)/,/^\}/' "$SPEC" | grep -q 'mergeCommit'; then bad "the DELTA path must not credit the PR merge commit"; else ok; fi
|
|
284
|
+
else echo "review-pr-staging-scope.test.sh: git missing, delta section skipped"; fi
|
|
158
285
|
echo "review-pr-staging-scope.test.sh: passed=$PASS failed=$FAILN"
|
|
159
286
|
[ "$FAILN" -eq 0 ]
|