forgedock 1.29.0 → 1.29.1

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.
@@ -70,10 +70,10 @@ run() { # run <fixture> [args...] -> sets OUT, RC
70
70
  local fx="$1"; shift
71
71
  OUT=$(MOCK_GH_JSON="$fx" bash "$VERIFY" 3061 -R o/r "$@" 2>/dev/null); RC=$?
72
72
  }
73
- expect_pass() { run "$2" "${@:3}"; [ $RC -eq 0 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: PASS' && ok "$1" || bad "$1 (rc=$RC out=$OUT)"; }
73
+ expect_pass() { run "$2" "${@:3}"; [ $RC -eq 0 ] && grep -q 'PHASE_TRAIL: PASS' <<< "$OUT" && ok "$1" || bad "$1 (rc=$RC out=$OUT)"; }
74
74
  expect_fail() { # name fixture needle [args]
75
75
  run "$2" "${@:4}"
76
- [ $RC -eq 1 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: FAIL' && echo "$OUT" | grep -q "MISSING: $3" && ok "$1" || bad "$1 (rc=$RC out=$OUT)"
76
+ [ $RC -eq 1 ] && grep -q 'PHASE_TRAIL: FAIL' <<< "$OUT" && grep -q "MISSING: $3" <<< "$OUT" && ok "$1" || bad "$1 (rc=$RC out=$OUT)"
77
77
  }
78
78
 
79
79
  expect_pass "full COMPLEX trail passes" "$(mk full INV CONTRACT FP_COMPLEX CONTEXT ARCH QG_PASS)"
@@ -107,12 +107,12 @@ expect_pass "INVESTIGATION:INVALID accepted as terminal investigator sentinel" "
107
107
 
108
108
  # Missing FAST_PATH uses conservative STANDARD set, so CONTEXT/ARCHITECT also named
109
109
  run "$(mk l INV CONTRACT QG_PASS)"
110
- echo "$OUT" | grep -q 'MISSING: FAST_PATH' && echo "$OUT" | grep -q 'MISSING: CONTEXT' && echo "$OUT" | grep -q 'MISSING: ARCHITECT' \
110
+ grep -q 'MISSING: FAST_PATH' <<< "$OUT" && grep -q 'MISSING: CONTEXT' <<< "$OUT" && grep -q 'MISSING: ARCHITECT' <<< "$OUT" \
111
111
  && ok "missing FAST_PATH names every missing artifact" || bad "missing FAST_PATH names every missing artifact ($OUT)"
112
112
 
113
113
  # Every missing line names the phase to route back to
114
114
  run "$(mk m INV CONTRACT FP_COMPLEX)"
115
- echo "$OUT" | grep -q 'MISSING: CONTEXT -> re-run Skill work-on/build/context' && ok "refusal routes to the missing phase" || bad "route text ($OUT)"
115
+ grep -q 'MISSING: CONTEXT -> re-run Skill work-on/build/context' <<< "$OUT" && ok "refusal routes to the missing phase" || bad "route text ($OUT)"
116
116
 
117
117
  # Large thread (pipefail + early-exit grep SIGPIPE regression, #3099): a valid trail buried in filler
118
118
  python3 - "$TMP_FX/big.json" <<'PY'
@@ -168,12 +168,12 @@ mp="$TMP_FX/mp.json"
168
168
  { jq -nc '[{"body":"<!-- FORGE:INVESTIGATOR -->\n<!-- INVESTIGATION:COMPLETE -->"},{"body":"<!-- FORGE:FAST_PATH -->\n**COMPLEXITY_BAND**: TRIVIAL"},{"body":"<!-- FORGE:CONTRACT -->\nc"},{"body":"<!-- FORGE:BUILDER -->\nx\n<!-- FORGE:BUILDER:COMPLETE -->","updated_at":"2026-01-04T00:00:00Z"}] | map(. + {author_association:"OWNER", user:{login:"owner",type:"User"}})'
169
169
  jq -nc '[{"body":"<!-- FORGE:BUILDER -->\nx\n<!-- FORGE:BUILDER:COMPLETE -->","updated_at":"2026-10-06T00:00:00Z"}] | map(. + {author_association:"OWNER", user:{login:"owner",type:"User"}})'; } > "$mp"
170
170
  OUT=$(FORGE_TRAIL_QG_SINCE=2026-10-05T00:00:00Z MOCK_GH_JSON="$mp" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
171
- [ $RC -eq 1 ] && echo "$OUT" | grep -q 'MISSING: QUALITY_GATE' && ok "multi-page: latest build on page 2 is not waived" || bad "multi-page (rc=$RC out=$OUT)"
171
+ [ $RC -eq 1 ] && grep -q 'MISSING: QUALITY_GATE' <<< "$OUT" && ok "multi-page: latest build on page 2 is not waived" || bad "multi-page (rc=$RC out=$OUT)"
172
172
 
173
173
  # Cutoff validation (#3121): malformed or future cutoff fails closed
174
174
  for bad_since in "garbage" "2999-01-01T00:00:00Z" "2020-13-45T99:99:99Z" "2026-02-30T00:00:00Z" "2026-10-01T24:00:00Z"; do
175
175
  OUT=$(FORGE_TRAIL_QG_SINCE="$bad_since" MOCK_GH_JSON="$(lg lgv 2026-10-01T00:00:00Z)" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
176
- [ $RC -eq 2 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: ERROR' && ok "invalid cutoff '$bad_since' rejected" || bad "cutoff '$bad_since' (rc=$RC out=$OUT)"
176
+ [ $RC -eq 2 ] && grep -q 'PHASE_TRAIL: ERROR' <<< "$OUT" && ok "invalid cutoff '$bad_since' rejected" || bad "cutoff '$bad_since' (rc=$RC out=$OUT)"
177
177
  done
178
178
 
179
179
  # Runner clock behind the default cutoff: cutoff looks future-dated -> exit 2 (fails closed)
@@ -188,7 +188,7 @@ if [ "\$mode" = now ] && [ -n "\$fmt" ]; then echo 2020-01-01T00:00:00Z; else ex
188
188
  MOCKDATE
189
189
  chmod +x "$CLK/date"
190
190
  OUT=$(PATH="$CLK:$PATH" MOCK_GH_JSON="$(lg lgc 2026-10-01T00:00:00Z)" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
191
- [ $RC -eq 2 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: ERROR' && ok "clock behind default cutoff fails closed (exit 2)" || bad "clock behind cutoff (rc=$RC out=$OUT)"
191
+ [ $RC -eq 2 ] && grep -q 'PHASE_TRAIL: ERROR' <<< "$OUT" && ok "clock behind default cutoff fails closed (exit 2)" || bad "clock behind cutoff (rc=$RC out=$OUT)"
192
192
 
193
193
  # BSD/macOS date fallback (#3139): GNU `date -d` unavailable, `date -j -f` available -> valid cutoff still honoured;
194
194
  # neither parser available -> fails closed (exit 2).
@@ -207,11 +207,11 @@ chmod +x "$BSD/date"
207
207
  OUT=$(PATH="$BSD:$PATH" FORGE_TRAIL_QG_SINCE=2026-10-05T00:00:00Z MOCK_GH_JSON="$(lg lgbsd 2026-10-01T00:00:00Z)" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
208
208
  [ $RC -eq 0 ] && ok "BSD date -j -f fallback validates a real cutoff" || bad "BSD fallback (rc=$RC out=$OUT)"
209
209
  OUT=$(PATH="$BSD:$PATH" BSD_NO_PARSE=1 FORGE_TRAIL_QG_SINCE=2026-10-05T00:00:00Z MOCK_GH_JSON="$(lg lgbsd2 2026-10-01T00:00:00Z)" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
210
- [ $RC -eq 2 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: ERROR' && ok "no usable date parser fails closed (exit 2)" || bad "no date parser (rc=$RC out=$OUT)"
210
+ [ $RC -eq 2 ] && grep -q 'PHASE_TRAIL: ERROR' <<< "$OUT" && ok "no usable date parser fails closed (exit 2)" || bad "no date parser (rc=$RC out=$OUT)"
211
211
 
212
212
  # Boundary: cutoff exactly equal to BUILDER:COMPLETE time is NOT waived (strictly-before only)
213
213
  OUT=$(FORGE_TRAIL_QG_SINCE=2026-10-01T00:00:00Z MOCK_GH_JSON="$(lg lgb 2026-10-01T00:00:00Z)" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
214
- [ $RC -eq 1 ] && echo "$OUT" | grep -q 'MISSING: QUALITY_GATE' && ok "boundary: build at exactly the cutoff is not waived" || bad "boundary equal (rc=$RC out=$OUT)"
214
+ [ $RC -eq 1 ] && grep -q 'MISSING: QUALITY_GATE' <<< "$OUT" && ok "boundary: build at exactly the cutoff is not waived" || bad "boundary equal (rc=$RC out=$OUT)"
215
215
  OUT=$(FORGE_TRAIL_QG_SINCE=2026-10-01T00:00:01Z MOCK_GH_JSON="$(lg lgb2 2026-10-01T00:00:00Z)" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
216
216
  [ $RC -eq 0 ] && ok "boundary: build one second before the cutoff is waived" || bad "boundary minus 1s (rc=$RC out=$OUT)"
217
217
 
@@ -222,23 +222,23 @@ BASE='[{"body":"<!-- FORGE:INVESTIGATOR -->\n<!-- INVESTIGATION:COMPLETE -->"},{
222
222
  # empty pages around a populated one
223
223
  { echo '[]'; pg "$BASE"; echo '[]'; pg "[$BLD]"; echo '[]'; } > "$TMP_FX/empty.json"
224
224
  OUT=$(FORGE_TRAIL_QG_SINCE=2026-10-05T00:00:00Z MOCK_GH_JSON="$TMP_FX/empty.json" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
225
- [ $RC -eq 1 ] && echo "$OUT" | grep -q 'MISSING: QUALITY_GATE' && ok "empty pages interleaved are tolerated" || bad "empty pages (rc=$RC out=$OUT)"
225
+ [ $RC -eq 1 ] && grep -q 'MISSING: QUALITY_GATE' <<< "$OUT" && ok "empty pages interleaved are tolerated" || bad "empty pages (rc=$RC out=$OUT)"
226
226
  OUT=$(MOCK_GH_JSON=<(echo '[]') bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
227
- [ $RC -eq 1 ] && echo "$OUT" | grep -q 'MISSING: INVESTIGATOR' && ok "all-empty thread fails (not error)" || bad "all-empty (rc=$RC out=$OUT)"
227
+ [ $RC -eq 1 ] && grep -q 'MISSING: INVESTIGATOR' <<< "$OUT" && ok "all-empty thread fails (not error)" || bad "all-empty (rc=$RC out=$OUT)"
228
228
  # 3+ pages: latest build on the LAST page wins
229
229
  { pg "$BASE"; pg '[{"body":"<!-- FORGE:BUILDER -->\nx\n<!-- FORGE:BUILDER:COMPLETE -->","updated_at":"2026-01-01T00:00:00Z"}]'; pg "[$BLD]"; } > "$TMP_FX/p3.json"
230
230
  OUT=$(FORGE_TRAIL_QG_SINCE=2026-10-05T00:00:00Z MOCK_GH_JSON="$TMP_FX/p3.json" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
231
- [ $RC -eq 1 ] && echo "$OUT" | grep -q 'MISSING: QUALITY_GATE' && ok "3 pages: latest build on page 3 is not waived" || bad "3 pages (rc=$RC out=$OUT)"
231
+ [ $RC -eq 1 ] && grep -q 'MISSING: QUALITY_GATE' <<< "$OUT" && ok "3 pages: latest build on page 3 is not waived" || bad "3 pages (rc=$RC out=$OUT)"
232
232
  # Malformed / mixed pages fail closed with exit 2
233
233
  { pg "$BASE"; echo '{"message":"Server Error"}'; } > "$TMP_FX/mixed.json"
234
234
  OUT=$(MOCK_GH_JSON="$TMP_FX/mixed.json" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
235
- [ $RC -eq 2 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: ERROR' && ok "mixed array/object pages fail closed (exit 2)" || bad "mixed pages (rc=$RC out=$OUT)"
235
+ [ $RC -eq 2 ] && grep -q 'PHASE_TRAIL: ERROR' <<< "$OUT" && ok "mixed array/object pages fail closed (exit 2)" || bad "mixed pages (rc=$RC out=$OUT)"
236
236
  { pg "$BASE"; echo '[1,"x"]'; } > "$TMP_FX/nonobj.json"
237
237
  OUT=$(MOCK_GH_JSON="$TMP_FX/nonobj.json" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
238
- [ $RC -eq 2 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: ERROR' && ok "page with non-object elements fails closed (exit 2)" || bad "non-object elements (rc=$RC out=$OUT)"
238
+ [ $RC -eq 2 ] && grep -q 'PHASE_TRAIL: ERROR' <<< "$OUT" && ok "page with non-object elements fails closed (exit 2)" || bad "non-object elements (rc=$RC out=$OUT)"
239
239
  { pg "$BASE"; echo '[{"body":'; } > "$TMP_FX/malformed.json"
240
240
  OUT=$(MOCK_GH_JSON="$TMP_FX/malformed.json" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
241
- [ $RC -eq 2 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: ERROR' && ok "malformed page fails closed (exit 2)" || bad "malformed page (rc=$RC out=$OUT)"
241
+ [ $RC -eq 2 ] && grep -q 'PHASE_TRAIL: ERROR' <<< "$OUT" && ok "malformed page fails closed (exit 2)" || bad "malformed page (rc=$RC out=$OUT)"
242
242
 
243
243
  # SEC-4 (#3149): the grace keys on updated_at -- BUILDER:COMPLETE is PATCHed in later than the comment was created
244
244
  jq -nc '[{"body":"<!-- FORGE:INVESTIGATOR -->\n<!-- INVESTIGATION:COMPLETE -->"},{"body":"<!-- FORGE:FAST_PATH -->\n**COMPLEXITY_BAND**: TRIVIAL"},{"body":"<!-- FORGE:CONTRACT -->\nc"},{"body":"<!-- FORGE:BUILDER -->\nx\n<!-- FORGE:BUILDER:COMPLETE -->","created_at":"2026-10-01T00:00:00Z","updated_at":"2026-10-08T00:00:00Z"}] | map(. + {author_association:"OWNER", user:{login:"owner",type:"User"}})' > "$TMP_FX/patched.json"
@@ -251,7 +251,7 @@ expect_fail "INVESTIGATION band + code diff requires CONTRACT" "$(mk cd1 INV FP_
251
251
  expect_fail "INVESTIGATION band + code diff requires QUALITY_GATE" "$(mk cd2 INV CONTRACT CONTEXT ARCH FP_INVESTIGATION)" QUALITY_GATE --code-diff
252
252
  expect_pass "INVESTIGATION band + code diff passes with a full trail" "$(mk cd3 INV CONTRACT CONTEXT ARCH QG_PASS FP_INVESTIGATION)" --code-diff
253
253
  run "$(mk cd4 INV FP_INVESTIGATION)" --code-diff
254
- echo "$OUT" | grep -q 'NOTE: INVESTIGATION band ignored' && ok "ignored band is reported" || bad "band note ($OUT)"
254
+ grep -q 'NOTE: INVESTIGATION band ignored' <<< "$OUT" && ok "ignored band is reported" || bad "band note ($OUT)"
255
255
  expect_pass "INVESTIGATION band + docs-only (no code diff) still passes" "$(mk cd5 INV FP_INVESTIGATION)" --docs-only
256
256
  expect_pass "TRIVIAL band is unaffected by --code-diff" "$(mk cd6 INV CONTRACT FP_TRIVIAL QG_PASS)" --code-diff
257
257
 
@@ -282,7 +282,7 @@ expect_pass "uppercase head-tree is normalised" "$(ht h6 "$QG_T1")" --head-tree
282
282
  expect_pass "docs-only still waives the bound gate" "$(ht h7)" --docs-only --head-tree "$T1"
283
283
  for bt in "" "xyz" "${T1}0" "abc123"; do
284
284
  OUT=$(MOCK_GH_JSON="$(ht h8 "$QG_T1")" bash "$VERIFY" 3061 -R o/r --head-tree "$bt" 2>/dev/null); RC=$?
285
- [ $RC -eq 2 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: ERROR' && ok "invalid --head-tree '$bt' fails closed (exit 2)" || bad "head-tree '$bt' (rc=$RC out=$OUT)"
285
+ [ $RC -eq 2 ] && grep -q 'PHASE_TRAIL: ERROR' <<< "$OUT" && ok "invalid --head-tree '$bt' fails closed (exit 2)" || bad "head-tree '$bt' (rc=$RC out=$OUT)"
286
286
  done
287
287
  # A 64-hex marker tree that merely starts with the 40-hex head tree must not match
288
288
  QG_LONG=$'<!-- FORGE:QUALITY_GATE -->\n**Result**: PASS\n**Tree**: '"${T1}1111111111111111111111"
@@ -292,26 +292,26 @@ OUT=$(MOCK_GH_JSON="$(ht h9 "$QG_T1")" bash "$VERIFY" 3061 -R o/r --head-tree 2>
292
292
 
293
293
  # -h prints the full header
294
294
  HOUT=$(bash "$VERIFY" -h)
295
- echo "$HOUT" | grep -q 'TRUSTED ENVIRONMENT ONLY' && echo "$HOUT" | grep -q 'COLLABORATOR includes read-level' && echo "$HOUT" | grep -q '^# Usage:' && ok "-h prints the full multi-line header" || bad "-h truncated"
295
+ grep -q 'TRUSTED ENVIRONMENT ONLY' <<< "$HOUT" && grep -q 'COLLABORATOR includes read-level' <<< "$HOUT" && grep -q '^# Usage:' <<< "$HOUT" && ok "-h prints the full multi-line header" || bad "-h truncated"
296
296
 
297
297
  # Untrusted-author FORGE markers are ignored AND diagnosed with a NOTE (#3123)
298
298
  UT="$TMP_FX/untrusted.json"
299
299
  jq -c 'map(. + {author_association:"CONTRIBUTOR", user:{login:"ext",type:"User"}})' "$(mk ut_src INV CONTRACT FP_TRIVIAL QG_PASS)" > "$UT"
300
300
  OUT=$(MOCK_GH_JSON="$UT" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
301
- [ $RC -eq 1 ] && echo "$OUT" | grep -q 'MISSING: INVESTIGATOR' && echo "$OUT" | grep -q 'NOTE: .*untrusted authors were ignored' && ok "CONTRIBUTOR markers ignored with NOTE" || bad "contributor note (rc=$RC out=$OUT)"
301
+ [ $RC -eq 1 ] && grep -q 'MISSING: INVESTIGATOR' <<< "$OUT" && grep -q 'NOTE: .*untrusted authors were ignored' <<< "$OUT" && ok "CONTRIBUTOR markers ignored with NOTE" || bad "contributor note (rc=$RC out=$OUT)"
302
302
  OUT=$(FORGE_TRAIL_TRUSTED_LOGINS=ext MOCK_GH_JSON="$UT" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
303
303
  [ $RC -eq 0 ] && ok "FORGE_TRAIL_TRUSTED_LOGINS accepts a CONTRIBUTOR login" || bad "trusted login override (rc=$RC out=$OUT)"
304
304
  # forge#3350: widening only the override list must not make CONTRIBUTOR trail markers count
305
305
  OUT=$(FORGE_TRAIL_OVERRIDE_ASSOCIATIONS="OWNER,MEMBER,COLLABORATOR,CONTRIBUTOR" MOCK_GH_JSON="$UT" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
306
- [ $RC -eq 1 ] && echo "$OUT" | grep -q 'MISSING: INVESTIGATOR' && ok "override list widened only: CONTRIBUTOR trail markers still ignored (#3350)" || bad "override-only widen trail (rc=$RC out=$OUT)"
306
+ [ $RC -eq 1 ] && grep -q 'MISSING: INVESTIGATOR' <<< "$OUT" && ok "override list widened only: CONTRIBUTOR trail markers still ignored (#3350)" || bad "override-only widen trail (rc=$RC out=$OUT)"
307
307
  expect_pass "marker-only CONTEXT satisfies STANDARD" "$(mk minctx INV FP_STANDARD CONTRACT CONTEXT ARCH QG_PASS)"
308
308
 
309
309
  # -h is bounded by the END-HELP sentinel, not a hardcoded line range (#3147)
310
- echo "$HOUT" | grep -q 'Callers route on the exit code' && ! echo "$HOUT" | grep -q 'END-HELP' && ! echo "$HOUT" | grep -q 'set -uo pipefail' && ok "-h stops at the END-HELP sentinel" || bad "-h sentinel"
310
+ grep -q 'Callers route on the exit code' <<< "$HOUT" && ! grep -q 'END-HELP' <<< "$HOUT" && ! grep -q 'set -uo pipefail' <<< "$HOUT" && ok "-h stops at the END-HELP sentinel" || bad "-h sentinel"
311
311
 
312
312
  # Outage fails closed (exit 2, never PASS)
313
313
  OUT=$(MOCK_GH_FAIL=1 MOCK_GH_JSON=/dev/null bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
314
- [ $RC -eq 2 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: ERROR' && ok "gh outage fails closed" || bad "gh outage (rc=$RC out=$OUT)"
314
+ [ $RC -eq 2 ] && grep -q 'PHASE_TRAIL: ERROR' <<< "$OUT" && ok "gh outage fails closed" || bad "gh outage (rc=$RC out=$OUT)"
315
315
 
316
316
  # -R with no value must error out, not hang
317
317
  # macOS has no `timeout`; fall back to gtimeout, then perl alarm.
@@ -353,14 +353,14 @@ ovf() { # ovf <name> <override comment json...> -> fixture = base trail + commen
353
353
  }
354
354
  GOODC() { ovc alice User "$AFTER" "$AFTER" "$HS" "$CUR_MISSING" "gate misfire, verified by hand"; }
355
355
  orun() { OUT=$(MOCK_GH_JSON="$1" bash "$VERIFY" 3061 -R o/r --head-sha "$HS" "${@:2}" 2>/dev/null); RC=$?; }
356
- expect_override() { orun "$2" "${@:3}"; [ $RC -eq 0 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: OVERRIDDEN' && echo "$OUT" | grep -q '^OVERRIDE: approver=' && ok "$1" || bad "$1 (rc=$RC out=$OUT)"; }
357
- expect_blocked() { orun "$2" "${@:3}"; [ $RC -eq 1 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: FAIL' && ! echo "$OUT" | grep -q 'OVERRIDDEN' && ok "$1" || bad "$1 (rc=$RC out=$OUT)"; }
356
+ expect_override() { orun "$2" "${@:3}"; [ $RC -eq 0 ] && grep -q 'PHASE_TRAIL: OVERRIDDEN' <<< "$OUT" && grep -q '^OVERRIDE: approver=' <<< "$OUT" && ok "$1" || bad "$1 (rc=$RC out=$OUT)"; }
357
+ expect_blocked() { orun "$2" "${@:3}"; [ $RC -eq 1 ] && grep -q 'PHASE_TRAIL: FAIL' <<< "$OUT" && ! grep -q 'OVERRIDDEN' <<< "$OUT" && ok "$1" || bad "$1 (rc=$RC out=$OUT)"; }
358
358
 
359
359
  expect_blocked "no override comment stays blocked" "$(ovf o0)"
360
360
  expect_override "valid human override is accepted" "$(ovf o1 "$(GOODC)")"
361
361
  orun "$(ovf o1b "$(GOODC)")"
362
- echo "$OUT" | grep -q "^OVERRIDE: approver=alice head=$HS missing=$CUR_MISSING reason=gate misfire, verified by hand$" && ok "OVERRIDE line carries approver, head, missing set and reason" || bad "OVERRIDE line ($OUT)"
363
- echo "$OUT" | grep -q '^MISSING:' && bad "accepted override must not emit MISSING lines" || ok "accepted override emits no MISSING lines"
362
+ grep -q "^OVERRIDE: approver=alice head=$HS missing=$CUR_MISSING reason=gate misfire, verified by hand$" <<< "$OUT" && ok "OVERRIDE line carries approver, head, missing set and reason" || bad "OVERRIDE line ($OUT)"
363
+ grep -q '^MISSING:' <<< "$OUT" && bad "accepted override must not emit MISSING lines" || ok "accepted override emits no MISSING lines"
364
364
  expect_override "admin permission is accepted" "$(ovf o2 "$(ovc carol User "$AFTER" "$AFTER" "$HS" "$CUR_MISSING" "ok")")"
365
365
  expect_override "uppercase head and shuffled/spaced missing list still bind to the same set" \
366
366
  "$(ovf o3 "$(ovc alice User "$AFTER" "$AFTER" "$(echo $HS | tr a-f A-F)" "QUALITY_GATE , ARCHITECT,CONTEXT -> x" "ok")")"
@@ -376,7 +376,7 @@ expect_blocked "rejects: missing timestamps" "$(ovf r5 "$(GOODC | jq -c 'del(.up
376
376
  expect_blocked "rejects: read-only collaborator" "$(ovf r6 "$(ovc reader User "$AFTER" "$AFTER" "$HS" "$CUR_MISSING" "r")")"
377
377
  expect_blocked "rejects: author with no permission record" "$(ovf r7 "$(ovc stranger User "$AFTER" "$AFTER" "$HS" "$CUR_MISSING" "r")")"
378
378
  orun "$(ovf r8 "$(GOODC)")"; OUT=$(MOCK_PERM_FAIL=1 MOCK_GH_JSON="$TMP_FX/r8.json" bash "$VERIFY" 3061 -R o/r --head-sha "$HS" 2>/dev/null); RC=$?
379
- [ $RC -eq 1 ] && ! echo "$OUT" | grep -q OVERRIDDEN && ok "rejects: permission API error fails closed (exit 1)" || bad "permission api error (rc=$RC out=$OUT)"
379
+ [ $RC -eq 1 ] && ! grep -q OVERRIDDEN <<< "$OUT" && ok "rejects: permission API error fails closed (exit 1)" || bad "permission api error (rc=$RC out=$OUT)"
380
380
  expect_blocked "rejects: wrong head sha" "$(ovf r9 "$(ovc alice User "$AFTER" "$AFTER" "$HS2" "$CUR_MISSING" "r")")"
381
381
  expect_blocked "rejects: truncated head sha" "$(ovf r10 "$(ovc alice User "$AFTER" "$AFTER" "${HS:0:12}" "$CUR_MISSING" "r")")"
382
382
  expect_blocked "rejects: override for a different MISSING set" "$(ovf r11 "$(ovc alice User "$AFTER" "$AFTER" "$HS" "ARCHITECT,CONTEXT" "r")")"
@@ -396,21 +396,21 @@ PIPE_C=$(jq -nc '{body:"<!-- FORGE:QUALITY_GATE -->\n**Result**: FAIL",user:{log
396
396
  expect_blocked "rejects: author of any trusted FORGE marker (QUALITY_GATE) is pipeline" "$(ovf p5 "$PIPE_C" "$(ovc pipeuser User "$AFTER" "$AFTER" "$HS" "$CUR_MISSING" "r")")"
397
397
  # Override cannot be evaluated without --head-sha
398
398
  OUT=$(MOCK_GH_JSON="$(ovf n1 "$(GOODC)")" bash "$VERIFY" 3061 -R o/r 2>/dev/null); RC=$?
399
- [ $RC -eq 1 ] && ! echo "$OUT" | grep -q OVERRIDDEN && ok "rejects: no --head-sha means the override is never evaluated" || bad "no head-sha (rc=$RC out=$OUT)"
399
+ [ $RC -eq 1 ] && ! grep -q OVERRIDDEN <<< "$OUT" && ok "rejects: no --head-sha means the override is never evaluated" || bad "no head-sha (rc=$RC out=$OUT)"
400
400
  for bs in "" "xyz" "${HS}0"; do
401
401
  OUT=$(MOCK_GH_JSON="$(ovf n2 "$(GOODC)")" bash "$VERIFY" 3061 -R o/r --head-sha "$bs" 2>/dev/null); RC=$?
402
- [ $RC -eq 2 ] && echo "$OUT" | grep -q 'PHASE_TRAIL: ERROR' && ok "invalid --head-sha '$bs' fails closed (exit 2)" || bad "head-sha '$bs' (rc=$RC out=$OUT)"
402
+ [ $RC -eq 2 ] && grep -q 'PHASE_TRAIL: ERROR' <<< "$OUT" && ok "invalid --head-sha '$bs' fails closed (exit 2)" || bad "head-sha '$bs' (rc=$RC out=$OUT)"
403
403
  done
404
404
  OUT=$(MOCK_GH_JSON="$TMP_FX/n2.json" bash "$VERIFY" 3061 -R o/r --head-sha 2>/dev/null); RC=$?
405
405
  [ $RC -eq 2 ] && ok "--head-sha without a value exits 2" || bad "head-sha no value (rc=$RC)"
406
406
  # Exit 2 is never overridable
407
407
  OUT=$(MOCK_GH_FAIL=1 MOCK_GH_JSON="$TMP_FX/o1.json" bash "$VERIFY" 3061 -R o/r --head-sha "$HS" 2>/dev/null); RC=$?
408
- [ $RC -eq 2 ] && ! echo "$OUT" | grep -q OVERRIDDEN && ok "exit 2 (unreadable trail) is not overridable" || bad "exit 2 override (rc=$RC out=$OUT)"
408
+ [ $RC -eq 2 ] && ! grep -q OVERRIDDEN <<< "$OUT" && ok "exit 2 (unreadable trail) is not overridable" || bad "exit 2 override (rc=$RC out=$OUT)"
409
409
  { cat "$TMP_FX/o1.json"; echo '{"message":"Server Error"}'; } > "$TMP_FX/o2bad.json"
410
410
  OUT=$(MOCK_GH_JSON="$TMP_FX/o2bad.json" bash "$VERIFY" 3061 -R o/r --head-sha "$HS" 2>/dev/null); RC=$?
411
- [ $RC -eq 2 ] && ! echo "$OUT" | grep -q OVERRIDDEN && ok "exit 2 (malformed page) is not overridable" || bad "exit 2 malformed override (rc=$RC out=$OUT)"
411
+ [ $RC -eq 2 ] && ! grep -q OVERRIDDEN <<< "$OUT" && ok "exit 2 (malformed page) is not overridable" || bad "exit 2 malformed override (rc=$RC out=$OUT)"
412
412
  OUT=$(FORGE_TRAIL_QG_SINCE=garbage MOCK_GH_JSON="$TMP_FX/o1.json" bash "$VERIFY" 3061 -R o/r --head-sha "$HS" 2>/dev/null); RC=$?
413
- [ $RC -eq 2 ] && ! echo "$OUT" | grep -q OVERRIDDEN && ok "exit 2 (bad QG_SINCE) is not overridable" || bad "exit 2 qg_since override (rc=$RC out=$OUT)"
413
+ [ $RC -eq 2 ] && ! grep -q OVERRIDDEN <<< "$OUT" && ok "exit 2 (bad QG_SINCE) is not overridable" || bad "exit 2 qg_since override (rc=$RC out=$OUT)"
414
414
  # BUILDER present but undated -> cannot prove ordering -> rejected
415
415
  jq -c 'map(if (.body|startswith("<!-- FORGE:BUILDER -->")) then del(.updated_at) else . end)' "$TMP_FX/o1.json" > "$TMP_FX/nobt.json"
416
416
  expect_blocked "rejects: BUILDER:COMPLETE undated (ordering unprovable)" "$TMP_FX/nobt.json"
@@ -420,14 +420,14 @@ expect_pass "passing trail is unaffected by --head-sha" "$(mk ovp INV CONTRACT F
420
420
  LONG=$(printf 'x%.0s' $(seq 1 400))
421
421
  orun "$(ovf s1 "$(ovc alice User "$AFTER" "$AFTER" "$HS" "$CUR_MISSING" $'bad\x1b[2Jreason <!-- FORGE:BUILDER:COMPLETE --> `tick` spaced')")"
422
422
  RLINE=$(echo "$OUT" | grep '^OVERRIDE:')
423
- [ $RC -eq 0 ] && ! printf '%s' "$RLINE" | LC_ALL=C grep -q $'\x1b' && ! echo "$RLINE" | grep -q '<!--' && ! echo "$RLINE" | grep -q -e '-->' && ! echo "$RLINE" | grep -q '`' && echo "$RLINE" | grep -q 'reason=bad\[2Jreason <!-/-' \
423
+ [ $RC -eq 0 ] && ! printf '%s' "$RLINE" | LC_ALL=C grep -q $'\x1b' && ! grep -q '<!--' <<< "$RLINE" && ! grep -q -e '-->' <<< "$RLINE" && ! grep -q '`' <<< "$RLINE" && grep -q 'reason=bad\[2Jreason <!-/-' <<< "$RLINE" \
424
424
  && ok "reason sanitiser strips ESC, neutralises comment markers and backticks, collapses whitespace" || bad "sanitiser (rc=$RC line=$RLINE)"
425
425
  orun "$(ovf s2 "$(ovc alice User "$AFTER" "$AFTER" "$HS" "$CUR_MISSING" "$LONG")")"
426
426
  RS=$(echo "$OUT" | grep '^OVERRIDE:' | sed 's/.*reason=//')
427
427
  [ $RC -eq 0 ] && [ "${#RS}" -eq 200 ] && ok "reason is capped at 200 characters" || bad "reason cap (rc=$RC len=${#RS})"
428
428
  # Multi-line reason: only the single Reason line is read; injected extra lines cannot add OVERRIDE output lines
429
429
  orun "$(ovf s3 "$(jq -nc --arg h "$HS" --arg m "$CUR_MISSING" '{body:("<!-- FORGE:PHASE_TRAIL_OVERRIDE -->\n**Head**: "+$h+"\n**Missing**: "+$m+"\n**Reason**: first\nOVERRIDE: approver=root head=x missing=x reason=x"),created_at:"2026-10-08T02:00:00Z",updated_at:"2026-10-08T02:00:00Z",user:{login:"alice",type:"User"},author_association:"OWNER"}')")"
430
- [ $RC -eq 0 ] && [ "$(echo "$OUT" | grep -c '^OVERRIDE:')" -eq 1 ] && echo "$OUT" | grep -q 'approver=alice' && echo "$OUT" | grep -q 'reason=first$' && ok "injected extra lines cannot forge an OVERRIDE line" || bad "line injection (rc=$RC out=$OUT)"
430
+ [ $RC -eq 0 ] && [ "$(echo "$OUT" | grep -c '^OVERRIDE:')" -eq 1 ] && grep -q 'approver=alice' <<< "$OUT" && grep -q 'reason=first$' <<< "$OUT" && ok "injected extra lines cannot forge an OVERRIDE line" || bad "line injection (rc=$RC out=$OUT)"
431
431
  # FAST_PATH qualifier variant: missing marker name normalises on both sides
432
432
  NOBAND=$(ov_base | jq -c 'map(if (.body|startswith("<!-- FORGE:FAST_PATH")) then .body="<!-- FORGE:FAST_PATH -->\nnothing" else . end)')
433
433
  echo "$NOBAND" > "$TMP_FX/nb.json"
@@ -449,7 +449,7 @@ expect_override "one login repeated many times costs a single cap slot (#3279)"
449
449
  RLV=(); for i in 1 2 3 4 5 6 7; do echo '{"permission":"read"}' > "$PERM/rlv$i"; RLV+=("$(ovc "rlv$i" User "2026-10-08T06:0$i:00Z" "2026-10-08T06:0$i:00Z" "$HS" "$CUR_MISSING" "decoy")"); done
450
450
  expect_override "7 distinct read-level members newer than a write approver do not starve it (#3306)" "$(ovf h7 "$(GOODC)" "${RLV[@]}")"
451
451
  orun "$(ovf h8 "${RLV[@]}")"
452
- [ $RC -eq 1 ] && ! echo "$OUT" | grep -q OVERRIDDEN && ok "read-level members only: override rejected (#3306)" || bad "read-level only (rc=$RC out=$OUT)"
452
+ [ $RC -eq 1 ] && ! grep -q OVERRIDDEN <<< "$OUT" && ok "read-level members only: override rejected (#3306)" || bad "read-level only (rc=$RC out=$OUT)"
453
453
  ULV=(); for i in 1 2 3 4 5 6 7; do ULV+=("$(ovc "ulv$i" User "2026-10-08T07:0$i:00Z" "2026-10-08T07:0$i:00Z" "$HS" "$CUR_MISSING" "unreadable")"); done
454
454
  expect_blocked "unreadable lookups stay bounded by the error cap and fail closed (#3306)" "$(ovf h9 "$(GOODC)" "${ULV[@]}")"
455
455
  # forge#3307: association prefilter reuses the configured list; each association value is pinned
@@ -471,12 +471,12 @@ echo '{"permission":"read"}' > "$PERM/cvx"
471
471
  CVV=(); i=0; for l in Cvx cvx CVX cvX Cvx CVx; do i=$((i+1)); CVV+=("$(ovc "$l" User "2026-10-08T09:0$i:00Z" "2026-10-08T09:0$i:00Z" "$HS" "$CUR_MISSING" "dup")"); done
472
472
  PL="$TMP_FX/permlog-cv"; : > "$PL"
473
473
  OUT=$(MOCK_PERM_LOG="$PL" MOCK_GH_JSON="$(ovf a7 "$(GOODC)" "${CVV[@]}")" bash "$VERIFY" 3061 -R o/r --head-sha "$HS" 2>/dev/null); RC=$?
474
- [ $RC -eq 0 ] && echo "$OUT" | grep -q OVERRIDDEN && [ "$(grep -ic '^cvx$' "$PL")" -eq 1 ] && ok "case-variant duplicate logins cost a single lookup (#3307)" || bad "case-variant dup (rc=$RC lookups=$(grep -ic '^cvx$' "$PL"))"
474
+ [ $RC -eq 0 ] && grep -q OVERRIDDEN <<< "$OUT" && [ "$(grep -ic '^cvx$' "$PL")" -eq 1 ] && ok "case-variant duplicate logins cost a single lookup (#3307)" || bad "case-variant dup (rc=$RC lookups=$(grep -ic '^cvx$' "$PL"))"
475
475
  # Decoys (NONE/CONTRIBUTOR) never reach the permission API; the NOTE reports the filtered count without logins
476
476
  MIXD=("${PDEC[@]}" "${CDEC[@]}")
477
477
  PL="$TMP_FX/permlog-decoy"; : > "$PL"
478
478
  ERR=$(MOCK_PERM_LOG="$PL" MOCK_GH_JSON="$(ovf a8 "${MIXD[@]}")" bash "$VERIFY" 3061 -R o/r --head-sha "$HS" 2>&1 >/dev/null);
479
- [ ! -s "$PL" ] && echo "$ERR" | grep -q 'NOTE: 14 override candidate(s) ignored: author_association not in override-approver set' && ! echo "$ERR" | grep -q 'pdecoy\|cdecoy' \
479
+ [ ! -s "$PL" ] && grep -q 'NOTE: 14 override candidate(s) ignored: author_association not in override-approver set' <<< "$ERR" && ! grep -q 'pdecoy\|cdecoy' <<< "$ERR" \
480
480
  && ok "decoys trigger no permission lookups; NOTE carries the count and no logins (#3307)" || bad "decoy no-call (log=$(cat "$PL") err=$ERR)"
481
481
  # forge#3351: the NOTE count shares one predicate with the candidates: only otherwise-eligible, association-excluded
482
482
  # comments are counted (edited, bot, and pre-floor ones are not)
@@ -486,20 +486,20 @@ EXP=$(ovc xold User "2000-01-01T00:00:00Z" "2000-01-01T00:00:00Z" "$HS" "$CUR_MI
486
486
  EX1=$(ovc xok1 User "2026-10-08T11:04:00Z" "2026-10-08T11:04:00Z" "$HS" "$CUR_MISSING" "dropped" | assoc NONE)
487
487
  EX2=$(ovc xok2 User "2026-10-08T11:05:00Z" "2026-10-08T11:05:00Z" "$HS" "$CUR_MISSING" "dropped" | assoc CONTRIBUTOR)
488
488
  ERR=$(MOCK_GH_JSON="$(ovf a10 "$(GOODC)" "$EXC" "$EXB" "$EXP" "$EX1" "$EX2")" bash "$VERIFY" 3061 -R o/r --head-sha "$HS" 2>&1 >/dev/null)
489
- echo "$ERR" | grep -q 'NOTE: 2 override candidate(s) ignored: author_association not in override-approver set' \
489
+ grep -q 'NOTE: 2 override candidate(s) ignored: author_association not in override-approver set' <<< "$ERR" \
490
490
  && ok "NOTE count equals only eligible association-excluded candidates (#3351)" || bad "NOTE count shared predicate (err=$ERR)"
491
491
  # 6th distinct unreadable login is cut off: the cap stops the scan before an older real approver
492
492
  UNR=(); for i in 1 2 3 4 5 6 7 8; do UNR+=("$(ovc "unr$i" User "2026-10-08T10:0$i:00Z" "2026-10-08T10:0$i:00Z" "$HS" "$CUR_MISSING" "unreadable")"); done
493
493
  PL="$TMP_FX/permlog-unr"; : > "$PL"
494
494
  OUT=$(MOCK_PERM_LOG="$PL" MOCK_GH_JSON="$(ovf a9 "$(GOODC)" "${UNR[@]}")" bash "$VERIFY" 3061 -R o/r --head-sha "$HS" 2>/dev/null); RC=$?
495
- [ $RC -eq 1 ] && ! echo "$OUT" | grep -q OVERRIDDEN && [ "$(wc -l < "$PL" | tr -d ' ')" -eq 6 ] && ! grep -qx alice "$PL" && ok "unreadable lookups stop at the 6th login; older approver never queried (#3307)" || bad "unreadable cutoff (rc=$RC lookups=$(wc -l < "$PL"))"
495
+ [ $RC -eq 1 ] && ! grep -q OVERRIDDEN <<< "$OUT" && [ "$(wc -l < "$PL" | tr -d ' ')" -eq 6 ] && ! grep -qx alice "$PL" && ok "unreadable lookups stop at the 6th login; older approver never queried (#3307)" || bad "unreadable cutoff (rc=$RC lookups=$(wc -l < "$PL"))"
496
496
  MOCK_SELF_LOGIN=alice orun "$(ovf h3 "$(GOODC)")"
497
- [ $RC -eq 1 ] && ! echo "$OUT" | grep -q OVERRIDDEN && ok "verifier's own login cannot approve an override (#3269)" || bad "self-login exclusion (rc=$RC out=$OUT)"
497
+ [ $RC -eq 1 ] && ! grep -q OVERRIDDEN <<< "$OUT" && ok "verifier's own login cannot approve an override (#3269)" || bad "self-login exclusion (rc=$RC out=$OUT)"
498
498
  MOCK_SELF_LOGIN=someoneelse orun "$(ovf h3b "$(GOODC)")"
499
- [ $RC -eq 0 ] && echo "$OUT" | grep -q OVERRIDDEN && ok "an unrelated self-login does not block a valid override (#3269)" || bad "self-login unrelated (rc=$RC out=$OUT)"
499
+ [ $RC -eq 0 ] && grep -q OVERRIDDEN <<< "$OUT" && ok "an unrelated self-login does not block a valid override (#3269)" || bad "self-login unrelated (rc=$RC out=$OUT)"
500
500
  orun "$(ovf h4 "$(ovc alice User "$AFTER" "$AFTER" "$HS" "$CUR_MISSING" $'ping @octocat\u2028next\u2029line')")"
501
501
  RS=$(echo "$OUT" | grep '^OVERRIDE:' | sed 's/.*reason=//')
502
- [ $RC -eq 0 ] && ! printf '%s' "$RS" | grep -q '@' && ! printf '%s' "$RS" | grep -q $'\xe2\x80\xa8' && ok "reason sanitiser neutralises @mentions and U+2028/2029 (#3274)" || bad "sanitiser unicode (rc=$RC rs=$RS)"
502
+ [ $RC -eq 0 ] && ! grep -q '@' <<< "$RS" && ! grep -q $'\xe2\x80\xa8' <<< "$RS" && ok "reason sanitiser neutralises @mentions and U+2028/2029 (#3274)" || bad "sanitiser unicode (rc=$RC rs=$RS)"
503
503
  # No BUILDER at all: floor falls back to the newest trusted FORGE marker (#3271)
504
504
  ov_base | jq -c '[.[] | select((.body|startswith("<!-- FORGE:BUILDER -->"))|not)] | map(if (.body|startswith("<!-- FORGE:CONTRACT -->")) then .created_at="2026-10-08T01:30:00Z" else . end)' > "$TMP_FX/nobuilder.json"
505
505
  orun "$TMP_FX/nobuilder.json"; NB_OUT="$OUT"
@@ -512,11 +512,11 @@ if [ -n "$NB_MISS" ]; then
512
512
  else bad "no-BUILDER fixture produced no MISSING set ($NB_OUT)"; fi
513
513
  # Rejected permission is explained on stderr (#3272)
514
514
  ERRTXT=$(MOCK_GH_JSON="$(ovf h5 "$(ovc reader User "$AFTER" "$AFTER" "$HS" "$CUR_MISSING" "no perm")")" bash "$VERIFY" 3061 -R o/r --head-sha "$HS" 2>&1 >/dev/null)
515
- echo "$ERRTXT" | grep -q "rejected: permission='read'" && ok "rejected permission prints a diagnostic to stderr (#3272)" || bad "permission diagnostic ($ERRTXT)"
515
+ grep -q "rejected: permission='read'" <<< "$ERRTXT" && ok "rejected permission prints a diagnostic to stderr (#3272)" || bad "permission diagnostic ($ERRTXT)"
516
516
 
517
517
  # -h documents the override
518
518
  HOUT2=$(bash "$VERIFY" -h)
519
- echo "$HOUT2" | grep -q 'FORGE_TRAIL_PIPELINE_LOGINS' && echo "$HOUT2" | grep -q -- '--head-sha' && ok "-h documents the override and --head-sha" || bad "-h override docs"
519
+ grep -q 'FORGE_TRAIL_PIPELINE_LOGINS' <<< "$HOUT2" && grep -q -- '--head-sha' <<< "$HOUT2" && ok "-h documents the override and --head-sha" || bad "-h override docs"
520
520
 
521
521
  echo "passed=$PASS failed=$FAILN"
522
522
  [ "$FAILN" -eq 0 ]
@@ -55,7 +55,7 @@ while [ $# -gt 0 ]; do
55
55
  --timeout) [ $# -ge 2 ] || usage; TIMEOUT="$2"; shift 2 ;;
56
56
  --interval) [ $# -ge 2 ] || usage; INTERVAL="$2"; shift 2 ;;
57
57
  --ignore-check) [ $# -ge 2 ] || usage; IGNORE="$(jq -c --arg n "$2" '. + [$n]' <<<"$IGNORE")"; shift 2 ;;
58
- -h|--help) sed -n '5,37p' "$0"; exit 0 ;;
58
+ -h|--help) sed -n '5,41p' "$0"; exit 0 ;;
59
59
  *) [ -z "$PR" ] || usage; PR="$1"; shift ;;
60
60
  esac
61
61
  done
@@ -142,5 +142,9 @@ for f in commands/work-on/review.md; do
142
142
  grep -q 'stale review' "$HERE/../$f" && PASS=$((PASS+1)) || { FAILN=$((FAILN+1)); echo "FAIL: $f does not route stale review"; }
143
143
  done
144
144
 
145
+ # forge#4304: --help prints the whole header, through the rate-limit and "No checks" lines.
146
+ HELP=$(bash "$GATE" --help); HRC=$?
147
+ expect "--help exits 0" "0" "$HRC"
148
+ grep -qF 'Rate-limit and transient gh failures' <<<"$HELP" && grep -qF 'the gate keeps waiting.' <<<"$HELP" && PASS=$((PASS+1)) || { FAILN=$((FAILN+1)); echo "FAIL: --help cuts off the header"; }
145
149
  echo "wait-ci-green tests: pass=$PASS fail=$FAILN"
146
150
  [ "$FAILN" -eq 0 ]