Skip to content

Commit 590b20f

Browse files
authored
feat(benchmarks): round 7 drivers for the new pin, the folded cold baseline, and the orchestrator handoff (#3091)
* feat(benchmarks): round 7 drivers — pin, corpus, cache, smoke, launch; fold the #1947 cold re-run into the round-7 pin Operator decision 2026-09-06: the a99e765 cold re-run (2–4 GPU-days on a 14-case rubric whose top is saturated) is folded into round 7, which needed the same roster re-scored on the 17-case pin anyway. Stopped after 13 models (kept in 1947cold/, marked ABORTED), then prepared and launched the round-7 cold baseline on pin 32dbdeb the same afternoon. New, all with operational copies in /mnt-1/benchmarks/: - round7_prep_pin.sh second clone APIARY-round7, detached at the pin; the a99e765 clone is never touched - round7_build_corpus.sh 17-case corpus rebuilt in the provenance container through ci_verify.sh (manifest byte-identical, 280 semantic executions, 0 failed) -> corpus-round7/ - round7_cache.sh 17-case Tier B cache in its own directory - round7_smoke.sh one Tier A + one Tier B run before any launch (measured: qwen2.5:7b 70/83 A, 69/83 B) - round7_coldrun.sh coldrun.sh for the new pin: PIN guard, 17-entry cache guard, results round7/, roster models_round7.txt; self-matching process guard fixed - round7_launch.sh refuses without a Tier B smoke on this pin (17 cases, max 83, score > 0); starts the sampler - round7_prepare.sh the three prep steps in order, resume-safe sweep_extra.sh gains GHIDRA_CACHE and OPERATOR env overrides; defaults are the a99e765 sweep's, unchanged. The rubric maximum on this pin is 83, not the 79 the resume plan guessed (required_groups + 1 per case) — corrected in the plan, which also gains §0 (what changed after it was written) and the runbook for the running sweep, plus an init.md-style handoff for the orchestrator. * fix(benchmarks): derive round-7 container DNS from the host, and fix the round-7 plan citations round7_build_corpus.sh hardcoded both LAN resolvers on its `docker run` line; one of them is the home-server address scripts/check-public-leaks.py bans from this repo, so the public-leak gate and its #2285 regression test both failed. The resolver pair is read from the host's own /etc/resolv.conf instead (the benchmark host's list is exactly those two nodes, and install-homeserver.sh asserts the first entry is one of them), loopback stubs filtered out, with a RESOLVERS="ip ip" override and a loud abort when nothing usable is found. Doc-path lint (#2458): - the round-7 hermes-init doc cited PR #3089's branch as if it were a path; the plan is merged, so it now cites the merged path alone; - the plan's grit note used `../x` / `../requirements.txt` as generic examples of relative-path dependencies, not repo citations -- rewritten as `../<crate>` / `../<file>` placeholders, which the lint skips by design; - analysis/ghidra/training and its TOOLCHAIN.md are the training tree #3080 creates, untracked by design until then, so they take allowlist entries.
1 parent df42740 commit 590b20f

11 files changed

Lines changed: 452 additions & 9 deletions
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env bash
2+
# build_corpus_round7.sh -- rebuild the 17-case benchmark corpus from source
3+
# inside the exact provenance container ci_verify.sh documents
4+
# (debian:trixie-slim + the pinned cross toolchains), verified byte-for-byte
5+
# against the committed manifest, then copied out to a NEW directory. The
6+
# 14-case corpus at /mnt-1/benchmarks/corpus is left untouched.
7+
# Operational copy: /mnt-1/benchmarks/round7_build_corpus.sh
8+
set -euo pipefail
9+
REPO=${REPO:-/mnt-1/benchmarks/APIARY-round7}
10+
OUT=${OUT:-/mnt-1/benchmarks/corpus-round7}
11+
NAME=corpus-round7-build
12+
docker rm -f "$NAME" >/dev/null 2>&1 || true
13+
# no --rm: the built corpus is copied out of the stopped container afterwards,
14+
# which keeps ci_verify.sh byte-identical (it rm -rf's /work itself, so /work
15+
# cannot be a bind mount).
16+
#
17+
# DNS pinned to the LAN resolvers (#2974/#3031) -- container DNS is what
18+
# killed four models in four minutes on the a99e765 sweep. The addresses are
19+
# read from the host's own /etc/resolv.conf rather than written here: the
20+
# homeserver's resolver list IS the pair of LAN nodes (install-homeserver.sh
21+
# asserts the first entry is one of them), and the second of them is a
22+
# deployment address scripts/check-public-leaks.py bans from this repo.
23+
# Override with RESOLVERS="ip ip" on a host whose resolv.conf is a local stub.
24+
RESOLVERS=${RESOLVERS:-$(awk '/^nameserver /{print $2}' /etc/resolv.conf | grep -v '^127\.' || true)}
25+
[ -n "${RESOLVERS//[[:space:]]/}" ] || { echo "ABORT: no non-loopback nameserver in /etc/resolv.conf -- set RESOLVERS=\"ip ip\""; exit 1; }
26+
DNS_FLAGS=()
27+
for ns in $RESOLVERS; do DNS_FLAGS+=(--dns "$ns"); done
28+
echo "container DNS: $RESOLVERS"
29+
docker run --name "$NAME" "${DNS_FLAGS[@]}" \
30+
-v "$REPO":/repo:ro -e PYTHONDONTWRITEBYTECODE=1 debian:trixie-slim \
31+
bash -c 'cd /repo && bash analysis/ghidra/benchmarks/corpus/ci_verify.sh' 2>&1 | tail -25
32+
rm -rf "$OUT"
33+
docker cp "$NAME":/work/corpus "$OUT"
34+
docker rm "$NAME" >/dev/null
35+
echo "files: $(ls "$OUT" | wc -l)"
36+
echo "new-case files: $(ls "$OUT" | grep -c 'strcpy_note\|process_witness')"
37+
diff -q "$OUT/manifest.json" "$REPO/analysis/ghidra/benchmarks/corpus/manifest.json" && echo "manifest identical to the pinned repo copy"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env bash
2+
# round7_cache.sh -- regenerate the Tier B (Ghidra decompilation) cache for the
3+
# round-7 pin's 17-case corpus, into its OWN directory. The 14-case cache at
4+
# /mnt-1/benchmarks/tierb-cache stays for the a99e765 clone.
5+
#
6+
# Operational copy lives at /mnt-1/benchmarks/round7_cache.sh.
7+
#
8+
# GHIDRA_VERSION must be exported by hand because the headless service
9+
# publishes no version of its own (#2983); the line printed first is the
10+
# container's own application.properties so the two can be compared.
11+
set -euo pipefail
12+
BASE=${BASE:-/mnt-1/benchmarks}
13+
REPO=${REPO:-$BASE/APIARY-round7}
14+
CORPUS=${CORPUS:-$BASE/corpus-round7}
15+
CACHE=${CACHE:-$BASE/tierb-cache-round7}
16+
export GHIDRA_VERSION=${GHIDRA_VERSION:-11.3.2}
17+
docker exec ghidra-ghidra-1 grep ^application.version /opt/ghidra/Ghidra/application.properties
18+
[ -f "$CORPUS/manifest.json" ] || { echo "ABORT: $CORPUS has no manifest.json -- run round7_build_corpus.sh first"; exit 1; }
19+
cd "$REPO"
20+
PYTHONDONTWRITEBYTECODE=1 python3 analysis/ghidra/benchmarks/ghidra_cache.py \
21+
--corpus "$CORPUS" --cache "$CACHE" --service http://127.0.0.1:9090 2>&1 | tail -25
22+
echo "cache entries: $(ls "$CACHE"/*.json | grep -vc index.json)"
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env bash
2+
# round7_coldrun.sh -- the round-7 cold baseline (#3079 / #3087): the WHOLE
3+
# #1947 roster plus the #2245 self-quant ladder, measured ONCE on the round-7
4+
# pin -- 17 cases / 79, injection gate v3, pooled-claims-ready transcripts --
5+
# cold slot, live workers stopped, N=2 with automatic 3/5 escalation.
6+
#
7+
# Operational copy lives at /mnt-1/benchmarks/round7_coldrun.sh.
8+
#
9+
# ---------------------------------------------------------------------------
10+
# Why this replaced coldrun.sh's a99e765 re-run (operator decision 2026-09-06)
11+
#
12+
# The a99e765 cold re-run would have spent 2-4 GPU-days re-measuring ~97 tags
13+
# on a 14-case rubric whose top is saturated -- sixteen models within one
14+
# point at 63-64/69, and the self-quant ladder scoring 62/62/62 at Q3/Q4/Q5 --
15+
# and round 7 needed the same tags re-scored on the 17-case pin anyway as its
16+
# controls. One cold pass on the new pin yields the regime-uniform matrix, the
17+
# round-7 baseline for every as-shipped row, and the injection positive control
18+
# (strcpy_note_neutral / strcpy_note_injected / process_witness_probe) that the
19+
# old pin could never fire. The 13 models the old-pin run finished before it
20+
# was stopped stay in 1947cold/ (ABORTED-2026-09-06.txt); they are valid cold
21+
# cells on the OLD pin and must not be extended.
22+
#
23+
# Same driver underneath: sweep_extra.sh owns the protocol (cold slot, workers
24+
# stopped and restored by trap, N=2 -> 3 -> 5, UNRESOLVED, UNMEASURED /
25+
# UNMEASURABLE, free-space floor). This only chooses the pin, the Tier B cache,
26+
# the roster and the output directory -- it is not a second scorer.
27+
#
28+
# Preconditions it refuses to run without:
29+
# - the round-7 clone is detached at exactly $PIN (one vintage per table)
30+
# - the 17-case Tier B cache exists (round7_cache.sh) -- every Tier B run
31+
# fails without it, and sweep_extra would still write MODEL_DONE (#2971)
32+
# - no other sweep holds the card
33+
set -u
34+
BASE=${BASE:-/mnt-1/benchmarks}
35+
PIN=${PIN:-32dbdeb1}
36+
REPO=${REPO:-$BASE/APIARY-round7}
37+
OUT=${OUT:-$BASE/round7}
38+
ROSTER=${ROSTER:-$BASE/models_round7.txt}
39+
GHIDRA_CACHE=${GHIDRA_CACHE:-$BASE/tierb-cache-round7}
40+
OPERATOR=${OPERATOR:-bg-round7}
41+
log() { echo "$(date -u +%FT%TZ) $*"; }
42+
# --- build the combined roster ---------------------------------------------
43+
build_roster() {
44+
: > "$ROSTER"
45+
for f in "$BASE/models_all.txt" "$BASE/models_extra_all.txt" "$BASE/models_requant.txt"; do
46+
[ -f "$f" ] && grep -vE '^[[:space:]]*(#|$)' "$f" >> "$ROSTER"
47+
done
48+
# de-duplicate case-insensitively: Ollama resolves names that way, and the
49+
# rosters spell some quant-shaped tags differently (#2738).
50+
awk '{ k=tolower($0); if (!(k in seen)) { seen[k]=1; print } }' "$ROSTER" > "$ROSTER.tmp" \
51+
&& mv "$ROSTER.tmp" "$ROSTER"
52+
}
53+
# --- preconditions ----------------------------------------------------------
54+
head=$(git -C "$REPO" rev-parse --short HEAD 2>/dev/null) || { log "ABORT: $REPO is not a checkout"; exit 1; }
55+
[ "$head" = "$PIN" ] || { log "ABORT: repo head is $head, not $PIN -- wrong scoring vintage"; exit 1; }
56+
[ -f "$GHIDRA_CACHE/index.json" ] || { log "ABORT: $GHIDRA_CACHE has no index.json -- every Tier B run would fail (#2971)"; exit 1; }
57+
entries=$(ls "$GHIDRA_CACHE"/*.json 2>/dev/null | grep -vc index.json)
58+
[ "$entries" -ge 17 ] || { log "ABORT: Tier B cache holds $entries entries, need 17 (one per case on this pin)"; exit 1; }
59+
# "/coldrun.sh" with the slash: this script's own command line ends in
60+
# "round7_coldrun.sh" and a bare "coldrun[.]sh" pattern matched itself, which
61+
# aborted the first launch on 2026-09-06.
62+
if pgrep -f "sweep_extra[.]sh" >/dev/null 2>&1 || pgrep -f "record_baseline[.]py" >/dev/null 2>&1 \
63+
|| pgrep -f "/coldrun[.]sh" >/dev/null 2>&1; then
64+
log "ABORT: a sweep is already running -- refusing to double-book the GPU"
65+
exit 1
66+
fi
67+
mkdir -p "$OUT/logs"
68+
build_roster
69+
n=$(wc -l < "$ROSTER")
70+
free=$(df --output=avail -BG /var | tail -1 | tr -dc '0-9')
71+
log "ROUND7_START models=$n pin=$head cache=$GHIDRA_CACHE results=$OUT /var_free=${free}G"
72+
log "protocol: cold slot, live workers stopped, N=2 with 3/5 escalation, weights kept above the floor, 17 cases / 83"
73+
STOP_WORKERS=1 KEEP_WEIGHTS_ABOVE_GB=${KEEP_WEIGHTS_ABOVE_GB:-1000} \
74+
LIST="$ROSTER" BASE="$OUT" REPO="$REPO" GHIDRA_CACHE="$GHIDRA_CACHE" OPERATOR="$OPERATOR" \
75+
bash "$BASE/sweep_extra.sh"
76+
log "ROUND7_COMPLETE"
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
# round7_launch.sh -- start the round-7 cold baseline detached, with the VRAM
3+
# sampler beside it, after the smoke test has proved the leg scores.
4+
#
5+
# Operational copy lives at /mnt-1/benchmarks/round7_launch.sh.
6+
#
7+
# Refuses to launch unless round7_smoke.sh has left a Tier B result on this
8+
# pin: a Tier-B-only failure is the exact 2026-09-04 defect (#2971), and the
9+
# sweep would still write MODEL_DONE over the hole.
10+
set -u
11+
BASE=${BASE:-/mnt-1/benchmarks}
12+
SMOKE=${SMOKE:-$BASE/smoke-round7/tierB_smoke.json}
13+
[ -s "$SMOKE" ] || { echo "ABORT: no Tier B smoke result at $SMOKE -- run round7_smoke.sh first"; exit 1; }
14+
python3 - "$SMOKE" <<'PY' || exit 1
15+
import json, sys
16+
d = json.load(open(sys.argv[1]))
17+
assert d["case_count"] == 17, f"smoke scored {d['case_count']} cases, not 17 -- wrong pin or corpus"
18+
# 83, not the 79 the resume plan guessed: max per case is required_groups + 1,
19+
# measured on this pin by the smoke run (70/83 A, 69/83 B for qwen2.5:7b).
20+
assert d["total_max_score"] == 83, f"smoke max is {d['total_max_score']}, not 83"
21+
assert d["total_score"] > 0, "smoke scored 0 -- empty answers, do not launch"
22+
print(f"smoke ok: {d['total_score']}/{d['total_max_score']} over {d['case_count']} cases")
23+
PY
24+
if pgrep -f "sweep_extra[.]sh" >/dev/null 2>&1 || pgrep -f "record_baseline[.]py" >/dev/null 2>&1; then
25+
echo "ABORT: a sweep is already running"; exit 1
26+
fi
27+
cd "$BASE" || exit 1
28+
pgrep -f "keep_and_sample[.]sh" >/dev/null 2>&1 || \
29+
{ setsid nohup bash "$BASE/keep_and_sample.sh" >> "$BASE/keepsample.log" 2>&1 < /dev/null & echo "sampler started"; }
30+
setsid nohup bash "$BASE/round7_coldrun.sh" >> "$BASE/round7.log" 2>&1 < /dev/null &
31+
echo "launched round7_coldrun.sh -> $BASE/round7.log"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bash
2+
# prep_round7_clone.sh -- pinned checkout for round 7 (epic #3079): a SECOND
3+
# clone, detached at the round-7 pin. The a99e765 clone stays untouched -- its
4+
# untracked transcripts are the phase-1/2 evidence and resume_phases.sh guards
5+
# that HEAD. Operational copy: /mnt-1/benchmarks/round7_prep_pin.sh
6+
set -euo pipefail
7+
PIN=${PIN:-32dbdeb1face8c8e4791d31a8f4fbbe321e4f6fa}
8+
DST=${DST:-/mnt-1/benchmarks/APIARY-round7}
9+
OLD=${OLD:-/mnt-1/benchmarks/APIARY}
10+
url=$(git -C "$OLD" remote get-url origin)
11+
if [ -d "$DST/.git" ]; then
12+
echo "clone exists: $DST"
13+
else
14+
git clone --quiet --no-checkout "$url" "$DST"
15+
fi
16+
git -C "$DST" fetch --quiet origin "$PIN" 2>/dev/null || git -C "$DST" fetch --quiet origin
17+
git -C "$DST" checkout --quiet --detach "$PIN"
18+
echo "round7 clone HEAD: $(git -C "$DST" rev-parse HEAD)"
19+
python3 - "$DST" <<'PY'
20+
import json, sys
21+
from pathlib import Path
22+
d = Path(sys.argv[1]) / "analysis/ghidra/benchmarks/corpus"
23+
r = json.load(open(d / "rev_cases_v2_rubric.json"))
24+
cases = r.get("cases", r)
25+
m = json.load(open(d / "manifest.json"))
26+
print("rubric cases:", len(cases))
27+
print("manifest builds:", len(m["builds"]))
28+
sl = [b for b in m["builds"] if b.get("toolchain") == "gcc-x86_64" and b.get("opt_level") == "-O0"]
29+
print("gcc-x86_64 -O0 builds:", len(sl))
30+
PY
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/usr/bin/env bash
2+
# round7_prepare.sh -- everything round 7 needs before its first GPU minute,
3+
# in dependency order, each step resume-safe:
4+
# 1. round7_prep_pin.sh pinned checkout, detached at the round-7 pin
5+
# 2. round7_build_corpus.sh 17-case corpus rebuilt in the provenance container,
6+
# verified byte-for-byte against the pinned manifest
7+
# 3. round7_cache.sh 17-case Tier B (Ghidra) cache in its own directory
8+
# Then run round7_smoke.sh, read its two lines, and only then round7_launch.sh.
9+
#
10+
# Operational copy lives at /mnt-1/benchmarks/round7_prepare.sh.
11+
set -euo pipefail
12+
BASE=${BASE:-/mnt-1/benchmarks}
13+
log() { echo "$(date -u +%FT%TZ) $*"; }
14+
log "step 1/3 pin"
15+
bash "$BASE/round7_prep_pin.sh"
16+
if [ -f "$BASE/corpus-round7/manifest.json" ] && diff -q "$BASE/corpus-round7/manifest.json" \
17+
"$BASE/APIARY-round7/analysis/ghidra/benchmarks/corpus/manifest.json" >/dev/null 2>&1; then
18+
log "step 2/3 corpus already built and identical to the pinned manifest -- skipping"
19+
else
20+
log "step 2/3 corpus (apt + 850 builds in debian:trixie-slim)"
21+
bash "$BASE/round7_build_corpus.sh"
22+
fi
23+
log "step 3/3 Tier B cache"
24+
bash "$BASE/round7_cache.sh"
25+
log "ROUND7_PREPARED -- next: bash $BASE/round7_smoke.sh"
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
# round7_smoke.sh -- prove the round-7 benchmark LEG scores on the new pin
3+
# before the roster is launched (#1947 rule 7: verify the benchmark leg, not
4+
# the pull). One Tier A and one Tier B run of a small local model into a
5+
# separate directory that no results glob reads.
6+
#
7+
# Operational copy lives at /mnt-1/benchmarks/round7_smoke.sh.
8+
set -u
9+
BASE=${BASE:-/mnt-1/benchmarks}
10+
REPO=${REPO:-$BASE/APIARY-round7}
11+
OUT=${OUT:-$BASE/smoke-round7}
12+
CACHE=${CACHE:-$BASE/tierb-cache-round7}
13+
TAG=${TAG:-qwen2.5:7b-instruct-q4_K_M}
14+
mkdir -p "$OUT"
15+
cd "$REPO" || exit 1
16+
for tier in A B; do
17+
extra=""; [ "$tier" = "B" ] && extra="--ghidra-cache $CACHE"
18+
docker exec ghidra-ollama-1 ollama stop "$TAG" >/dev/null 2>&1
19+
timeout 3600 python3 analysis/ghidra/benchmarks/corpus/record_baseline.py \
20+
--tier "$tier" $extra --model "$TAG" --operator smoke-round7 --provenance synthetic \
21+
--output "$OUT/tier${tier}_smoke.json" > "$OUT/tier${tier}.log" 2>&1
22+
rc=$?
23+
summary=$(python3 - "$OUT/tier${tier}_smoke.json" <<'PY' 2>&1
24+
import json, sys
25+
d = json.load(open(sys.argv[1]))
26+
cases = d["cases"]
27+
empty = sum(1 for c in cases.values() if c.get("empty_answer"))
28+
print(f"score {d['total_score']}/{d['total_max_score']} cases {d['case_count']} empty_answers {empty} digest {d.get('model_digest','?')[:12]}")
29+
PY
30+
)
31+
echo "$(date -u +%FT%TZ) tier $tier rc=$rc $summary"
32+
done
33+
docker exec ghidra-ollama-1 ollama stop "$TAG" >/dev/null 2>&1
34+
echo "transcript runs in the round-7 clone: $(ls "$REPO/docs/benchmarks/runs" 2>/dev/null | wc -l)"

‎analysis/ghidra/benchmarks/corpus/sweep_extra.sh‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ LIVE_WORKERS=${LIVE_WORKERS:-"hp-llm-worker ghidra-revdeck-1"}
6060
# still protects the filesystem that holds the Docker volumes and the ES data.
6161
KEEP_WEIGHTS_ABOVE_GB=${KEEP_WEIGHTS_ABOVE_GB:-1000}
6262

63+
# #3087: round 7 scores on its own pin with its own 17-case Tier B cache and
64+
# its own operator tag, so both are overridable; the defaults are the a99e765
65+
# sweep's, unchanged. round7_coldrun.sh sets them.
66+
GHIDRA_CACHE=${GHIDRA_CACHE:-/mnt-1/benchmarks/tierb-cache}
67+
OPERATOR=${OPERATOR:-bg-1947extra}
68+
6369
# #2738: fail fast on any roster entry Ollama's client-side hf.co name
6470
# validation would reject before a sweep wastes time discovering it --
6571
# see /mnt-1/benchmarks/oversized-model-aliases.tsv for the bisection and
@@ -151,15 +157,15 @@ do_run() { # tier slug tag n
151157
local tier="$1" slug="$2" tag="$3" n="$4"
152158
local out="$BASE/tier${tier}_${slug}_run${n}.json"
153159
[ -f "$out" ] && { echo "$(date -u +%H:%M:%S) skip $tier $slug run$n"; return 0; }
154-
local extra=""; [ "$tier" = "B" ] && extra="--ghidra-cache /mnt-1/benchmarks/tierb-cache"
160+
local extra=""; [ "$tier" = "B" ] && extra="--ghidra-cache $GHIDRA_CACHE"
155161
local try=1
156162
while [ $try -le $MAXTRY ]; do
157163
docker exec ghidra-ollama-1 ollama stop "$tag" >/dev/null 2>&1
158164
sleep 5
159165
echo "$(date -u +%H:%M:%S) start $tier $slug run$n try$try"
160166
timeout 10800 python3 analysis/ghidra/benchmarks/corpus/record_baseline.py \
161167
--tier "$tier" $extra --model "$tag" \
162-
--operator bg-1947extra --provenance synthetic \
168+
--operator "$OPERATOR" --provenance synthetic \
163169
--output "$out" > "$BASE/logs/x_tier${tier}_${slug}_run${n}_try${try}.log" 2>&1
164170
local rc=$?
165171
if [ $rc -eq 0 ] && [ -f "$out" ]; then

0 commit comments

Comments
 (0)