Gladex Agent Logs
Agent run logs & app logs · env: prod · LAN-only investor surface
Overview
| Run logs | 830 files, 39.8 MB |
| Latest run log | run-20261001-022005-428.log |
| Log directory | /data/agent-logs |
| App log directory | /opt/startup/prod/logs |
Run logs (newest first, last 50)
| File | Size | Modified (UTC) |
|---|---|---|
| run-20261001-022005-428.log | 188 KB | 2026-10-01 00:36:10 |
| run-20261001-020022-427.log | 205 KB | 2026-10-01 00:09:58 |
| run-20261001-015015-426.log | 153 B | 2026-09-30 23:50:16 |
| run-20261001-014008-425.log | 153 B | 2026-09-30 23:40:09 |
| run-20261001-013001-424.log | 153 B | 2026-09-30 23:30:02 |
| run-20261001-011954-423.log | 153 B | 2026-09-30 23:19:55 |
| run-20261001-010947-422.log | 153 B | 2026-09-30 23:09:48 |
| run-20261001-005940-421.log | 153 B | 2026-09-30 22:59:41 |
| run-20261001-004933-420.log | 153 B | 2026-09-30 22:49:34 |
| run-20261001-003926-419.log | 153 B | 2026-09-30 22:39:27 |
| run-20261001-002920-418.log | 190 B | 2026-09-30 22:29:20 |
| run-20261001-001913-417.log | 153 B | 2026-09-30 22:19:13 |
| run-20261001-000906-416.log | 153 B | 2026-09-30 22:09:07 |
| run-20260930-231805-415.log | 582 KB | 2026-09-30 21:59:00 |
| run-20260930-223504-414.log | 194 KB | 2026-09-30 21:07:59 |
| run-20260930-212614-413.log | 270 KB | 2026-09-30 20:24:58 |
| run-20260930-201407-412.log | 423 KB | 2026-09-30 19:16:08 |
| run-20260930-184629-411.log | 463 KB | 2026-09-30 18:04:01 |
| run-20260930-165104-410.log | 325 KB | 2026-09-30 16:36:29 |
| run-20260930-161443-409.log | 189 KB | 2026-09-30 14:41:04 |
| run-20260930-160441-408.log | 153 B | 2026-09-30 14:04:43 |
| run-20260930-155440-407.log | 153 B | 2026-09-30 13:54:41 |
| run-20260930-154440-406.log | 153 B | 2026-09-30 13:44:40 |
| run-20260930-153439-405.log | 153 B | 2026-09-30 13:34:40 |
| run-20260930-152438-404.log | 190 B | 2026-09-30 13:24:39 |
| run-20260930-151438-403.log | 190 B | 2026-09-30 13:14:38 |
| run-20260930-150437-402.log | 153 B | 2026-09-30 13:04:37 |
| run-20260930-145436-401.log | 153 B | 2026-09-30 12:54:37 |
| run-20260930-144435-400.log | 153 B | 2026-09-30 12:44:36 |
| run-20260930-143435-399.log | 190 B | 2026-09-30 12:34:35 |
| run-20260930-142434-398.log | 153 B | 2026-09-30 12:24:35 |
| run-20260930-141433-397.log | 153 B | 2026-09-30 12:14:34 |
| run-20260930-140433-396.log | 153 B | 2026-09-30 12:04:33 |
| run-20260930-135432-395.log | 153 B | 2026-09-30 11:54:33 |
| run-20260930-134431-394.log | 153 B | 2026-09-30 11:44:32 |
| run-20260930-133431-393.log | 190 B | 2026-09-30 11:34:31 |
| run-20260930-132430-392.log | 153 B | 2026-09-30 11:24:31 |
| run-20260930-131429-391.log | 153 B | 2026-09-30 11:14:30 |
| run-20260930-130429-390.log | 153 B | 2026-09-30 11:04:29 |
| run-20260930-125428-389.log | 153 B | 2026-09-30 10:54:29 |
| run-20260930-124427-388.log | 153 B | 2026-09-30 10:44:28 |
| run-20260930-123427-387.log | 153 B | 2026-09-30 10:34:27 |
| run-20260930-122426-386.log | 153 B | 2026-09-30 10:24:27 |
| run-20260930-121425-385.log | 153 B | 2026-09-30 10:14:26 |
| run-20260930-120425-384.log | 153 B | 2026-09-30 10:04:25 |
| run-20260930-115424-383.log | 153 B | 2026-09-30 09:54:25 |
| run-20260930-114423-382.log | 153 B | 2026-09-30 09:44:24 |
| run-20260930-113423-381.log | 153 B | 2026-09-30 09:34:23 |
| run-20260930-112422-380.log | 153 B | 2026-09-30 09:24:23 |
| run-20260930-111421-379.log | 190 B | 2026-09-30 09:14:22 |
Tail — run-20261001-022005-428.log (last 200 lines)
app/src/vscode/node_modules/object-inspect/CHANGELOG.md:78:- [Fix] ignore `cause` in node v16.9 and v16.10 where it has a bug [`86aa553`](https://github.com/inspect-js/object-inspect/commit/86aa553a4a455562c2c56f1540f0bf857b9d314b)
app/src/vscode/node_modules/object-inspect/CHANGELOG.md:98:- [Robustness] cache `RegExp.prototype.test` [`a314ab8`](https://github.com/inspect-js/object-inspect/commit/a314ab8271b905cbabc594c82914d2485a8daf12)
app/src/vscode/node_modules/object-inspect/CHANGELOG.md:218:- [Dev Deps] update `@ljharb/eslint-config` [`98df157`](https://github.com/inspect-js/object-inspect/commit/98df1577314d9188a3fc3f17fdcf2fba697ae1bd)
app/src/vscode/node_modules/object-inspect/CHANGELOG.md:323:- [Dev Deps] update `tape` [`88a907e`](https://github.com/inspect-js/object-inspect/commit/88a907e33afbe408e4b5d6e4e42a33143f88848c)
app/src/vscode/node_modules/tar-stream/README.md:88: size: 1314, // entry size. defaults to 0
app/src/go/gomodcache/modernc.org/mathutil@v1.7.1/binarylog_test.go:338: x, _ := big.NewInt(0).SetString("89940344608680314083397671686667731393131665861770496634981932531495305005604", 10)
app/src/go/gomodcache/modernc.org/mathutil@v1.7.1/all_test.go:573: const p4M = 283146 // # of primes < 4e6
app/src/go/gomodcache/modernc.org/mathutil@v1.7.1/all_test.go:644: const p4M = 283146 // # of primes < 4e6
app/src/go/gomodcache/modernc.org/mathutil@v1.7.1/tables.go:94: 30576151, 30662497, 30740417, 30881551, 30894307, 31040833, 31166803, 31436123, 31735621, 31759121,
app/src/go/gomodcache/modernc.org/mathutil@v1.7.1/tables.go:95: 32091781, 32095057, 32168117, 32285041, 32497921, 32676481, 33146717, 33298337, 33600533, 33627301,
app/src/go/gomodcache/modernc.org/mathutil@v1.7.1/tables.go:101: 43661257, 44070841, 44314129, 44465221, 44482901, 45100177, 45175201, 45219329, 45414433, 45819541,
app/src/go/gomodcache/modernc.org/mathutil@v1.7.1/tables.go:156: 230357761, 231383461, 231405701, 231927781, 232114433, 232460821, 232771501, 233110081, 234869009, 235426913,
app/src/go/gomodcache/modernc.org/mathutil@v1.7.1/tables.go:160: 251855893, 252853921, 253610281, 253893397, 255416897, 256831433, 257590661, 258020473, 258043229, 258234401,
=== 21 assertions refs:
2760:**Design decisions that are not up for a flag**: runs **sequentially, no `--jobs`** — several suites take locks, ports and git state (`test_php_busy_timeout` holds a SQLite handle, the promote suites touch the envs), and trading a reproducible total for a flaky one is the wrong trade for the tool whose job is reproducibility. The epilog carries **no prose of its own** (all six sections derived from the module docstring, following `repo-lint`); a missing or empty heading **refuses** with exit 3 naming the pair rather than printing a label over nothing. *Since `[0.4.131]`* the termination contract in this very paragraph is one of those six: it has been in the docstring since `[0.4.124]` and rendered in `--help` never — `grep -c start_new_session <(regression-run --help)` → **0** before this step, **1** after — so a reader of `--help` was told nothing about what a TERM or INT does to a running suite, and the pair was added rather than the paragraph rewritten. Timeouts kill the whole **process group** (`start_new_session` + `killpg`), because suites spawn children that would otherwise keep the locks the next suite needs. *Since `[0.4.124]`* the same isolation is also why that group is reaped by the **tool** when it is terminated: a suite lives in a foreign session, so no signal aimed at this process can reach it. Measured 2026-09-29 before the fix — `timeout 3 regression-run --tests-dir … --timeout 600` → **124** with the suite reparented to `ppid 1` in its own session and its grandchild still writing, direct SIGTERM → **143** with two survivors still writing, and direct **SIGINT never ending this process at all** (blocked in `poll()` inside `communicate()`, so `KeyboardInterrupt` is never raised). `install_termination_guard()` runs as the **first act of `main()`**: on SIGTERM or SIGINT it SIGKILLs whatever is in `_LIVE_PGIDS`, then hands the signal back to `SIG_DFL` and re-sends it, so the caller still sees death by signal **143 / 130** — no new exit code, and no verdict line for a run that did not finish. Declared limit, written in the docstring rather than implied: a suite that moves a child into *its own* session escapes this exactly as it escapes every other bound. Held by `test_regression_run.sh` section **J** (21 assertions). Totals are **derived** from the records by `contrib()` after the loop, not accumulated by a second rule inside it.
2800:**Sections**: **A** `--help` contract incl. the **six** docstring↔epilog byte-comparisons — since `[0.4.132]` **derived from `EPILOG_PAIRS`** rather than hand-copied, so the label loop, the heading loop, the byte-comparison heredoc and `help_section()`'s terminator set all move together — plus, since `[0.4.131]`, **A13–A15** (the termination contract's `start_new_session` / `143 / 130` / escape-limit needles reaching `--help` at all) and, since `[0.4.132]`, **A16** (the extraction is not empty — an empty list would make the derived loops assert nothing) and **A17.1–A17.6** (each contract section still present in the tuple — the pin that keeps deletion from becoming invisible); **B** the exit-code contract (`0/1/2/3/4/5`, precedence `4 > 1`, and "silent contributes 0 passed **and** 0 suites_run"); **C** the four parsing rules (tie-break 12-not-999, assertion-line fallback still read, conflict flag, `skipped_reported`); **D** the 14-key JSON contract incl. usage-error/refusal key-set parity; **E** flags; **F** the epilog refusal in both channels; **G** live + read-only + `default_repo()`; **H** static + the recursion guard; **I** the baseline contract (80 assertions: closure both ways, `lost`, added/removed, status change, every refusal's exit code *and* message, and the four that pin "the verdict comes from the suites alone"); **J** the termination contract (21 assertions: the outer bound, a direct SIGTERM and a direct SIGINT really sent, the suite group reaped with the tool, the guard's wiring read out of the tool, two invocation helpers pinned for `--tests-dir "$SB/`, a plant, and the (88) census scoping — a same-named fixture outside `$SB/j/sb` neither counted nor killed, this run's own fixture still counted); **K** the free-space preflight (43 assertions: the healthy path still runs, exit 6 in both channels with empty stdout and an **absent marker file** proving no suite started, the refusal naming the path *and* the figure it measured, a **later** watched path at 1 MB refusing via the fixture hook, an unmeasurable path refusing rather than passing, `--log-dir`'s ancestor walk, `--list` exempt, the two usage-error env values, the `--help` documentation, the placement of the call between `--list` and the first write, and a plant with that call removed).
> build · mimo-v2.6-flash-free
[0m$ [0mcd /data/repo && grep -n "314" tools/REGISTRY.md; echo "=== run status:"; tail -3 /tmp/opencode/trr-fixed.txt 2>/dev/null || echo "still running"
2771:- `bash tests/test_regression_run.sh` → **314 assertions**, hermetic (throwaway fixtures under `/tmp/opencode/trr.*`, trap-cleaned): `--help` exits 0 and **all six epilog sections equal their docstring blocks byte for byte** (the sixth, `termination contract:`, added by `[0.4.131]`); the full exit-code contract `0/1/2/3/4/5/6` plus the `4 > 1` precedence and the preflight's own before-the-first-suite placement; all four parsing rules; the 14-key JSON contract including the usage-error, refusal *and* preflight objects having the *same* key set; the flag contract (including "stderr is exactly one diagnosis line" and "no `usage:` leak"); **the termination contract's own rendering (A13–A15)** — `start_new_session`, the `143 / 130` signal outcome and the declared escape limit must all appear in `--help`, three needles that sat in a docstring paragraph no label rendered (the (81) defect); **the epilog list is DERIVED, not copied (A16, A17.1–A17.6)** — `A3`/`A4`/`A5` and `help_section()`'s terminator set are read out of the tool's own `EPILOG_PAIRS`, so a seventh pair is compared without editing four lists, and the two things a derived list cannot see are pinned: the extraction is not empty (`A16`, or every loop would assert nothing) and each of the six contract sections is still in the tuple (`A17.*`, or a deleted pair would vanish from every loop); the epilog refusal in both channels; **the free-space preflight (section K, 43 assertions)** — a healthy tree still runs and its suite really runs (marker written), an impossible floor refuses with 6 in both channels with **empty stdout**, an absent marker proving no suite started, the refusal naming the path it measured and the measured figure, a **later** watched path at 1 MB refusing (so the check is not first-path-only — real `statvfs` can never say 1), an unmeasurable path refusing rather than passing, `--log-dir`'s not-yet-created directory gated through its existing ancestor, `--list` exempt, the two usage-error env values, the `--help` documentation, and a plant with the call removed; **the termination contract (section J)** — the outer bound, a direct SIGTERM and a direct SIGINT are all *really* sent, the fixture suite spawns a **writing grandchild** so "the tool died" and "everything the tool started died" stay two questions, the two invocation helpers are pinned for `--tests-dir "$SB/` the way section H pins its three, and a plant (the install line `sed`'d out of a copy) carries J16/J17/J18; **the (88) census scoping (J19–J21)** — a same-named fixture this run did not start is **not counted** and **not killed** by `bound_kill`, while this run's own fixture **still counts**; live read-only proof (`git status --porcelain` byte-identical); and the recursion guard (see below). *(The count here said **193** while the suite was already at **218** through `[0.4.101]` and **237** through `[0.4.102]` — two runs stale; refreshed rather than extended. The **255 → 298** move is `[0.4.129]`'s section K, 43 assertions; the **298 → 301** move is `[0.4.130]`'s J19–J21; the **301 → 307** move is `[0.4.131]`'s section A (the sixth epilog pair counted by A3/A4/A5, plus A13–A15); the **307 → 314** move is `[0.4.132]`'s section A (the derived epilog list, A16 + A17.1–A17.6, with A3/A4/A5 unchanged at six rows).)*
2795:- `0` - all 314 assertions pass
2806:**Tests / mutations**: see `## regression-run` — 314 assertions, 14 external mutant runs (6 original + 7 baseline + 1 termination), each confined to its own section, plus the two **in-suite** plants (F's renamed heading, J's removed guard call, K's removed preflight call).
2808:**Status**: Green ✅ 2026-09-29 (314/314 with `[0.4.132]`; was 307/307 with `[0.4.131]`, and 301/301 and 298/298 and 255/255 on 2026-09-29 before `[0.4.129]`'s section K, and 193/193 on 2026-09-26 — a figure that had already gone to 218 `[0.4.101]` and 237 `[0.4.102]` without this line moving)
=== run status:
=== Results: 318 passed, 0 failed ===
rc=0
orphans after: 2
> build · mimo-v2.6-flash-free
[0m$ [0mcat /tmp/opencode/trr-orphans-after.txt; echo "=== now:"; ps -eo pid,ppid,etimes,args | grep -E 'sleep 300|test_bound_orphan' | grep -v grep; echo "=== fixed run J22-J25:"; grep -E "J2[0-9]" /tmp/opencode/trr-fixed.txt
2103289 1 160 sleep 300
2103434 1 156 sleep 300
=== now:
2103289 1 182 sleep 300
2103434 1 177 sleep 300
=== fixed run J22-J25:
PASS: J22 the mutant fixture has a descendant tree (the orphan's precondition)
PASS: J23 bound_kill reaped that tree too (a -9'd bash orphans its sleep 300)
PASS: J24 the second mutant fixture's tree is there to reap as well
PASS: J25 and it is gone after the cleanup (the second orphan source)
PASS: J20 bound_kill leaves that foreign process alive (no killing outside the sandbox)
PASS: J21 the scoping did not blind the census: this run's own fixture counts
> build · mimo-v2.6-flash-free
[0m$ [0mcd /data/repo && rm -rf /tmp/opencode/ctrl89 && mkdir -p /tmp/opencode/ctrl89 && cp -r tests tools /tmp/opencode/ctrl89/ && python3 - <<'PY'
import pathlib
p = pathlib.Path('/tmp/opencode/ctrl89/tests/test_regression_run.sh')
s = p.read_text()
new = '''bound_kill() {
local p
# (89): descendants FIRST. A -9'd bash takes its children with it into
# init, not out of existence, so killing the matched shells last is what
# makes the reap complete rather than the reap that looks complete.
for p in $(bound_descendants); do kill -9 "$p" 2>/dev/null || true; done
for p in $(bound_pids); do kill -9 "$p" 2>/dev/null || true; done
sleep 0.3
}'''
old = '''bound_kill() {
local p
for p in $(bound_pids); do kill -9 "$p" 2>/dev/null || true; done
sleep 0.3
}'''
assert s.count(new) == 1, f"new block found {s.count(new)} times"
s = s.replace(new, old)
assert s.count(old) == 1, f"reverted block found {s.count(old)} times"
p.write_text(s)
print("control patch applied: descendant-first kill removed (pre-(89) reader restored)")
PY
ps -eo pid,args | grep -cE '^ *[0-9]+ sleep 300$' | xargs echo "sleep-300 before control:"
control patch applied: descendant-first kill removed (pre-(89) reader restored)
sleep-300 before control: 2
> build · mimo-v2.6-flash-free
[0m$ [0mcd /tmp/opencode/ctrl89 && (bash tests/test_regression_run.sh > /tmp/opencode/trr-control.txt 2>&1; echo "rc=$?" >> /tmp/opencode/trr-control.txt; ps -eo pid,ppid,etimes,args | grep -E 'sleep 300' | grep -v grep > /tmp/opencode/trr-orphans-control.txt; echo "sleep-300 total after control: $(wc -l < /tmp/opencode/trr-orphans-control.txt)" >> /tmp/opencode/trr-control.txt)
Command moved to the background (shell ID: sh_0f4e16765001feu4n630thF45W).
Output is streaming to: /root/.local/share/opencode/shell/fa3e85b2145a60c127b5c9261e76a7ba6d520299/sh_0f4e16765001feu4n630thF45W.out
> build · mimo-v2.6-flash-free
[0m← [0mEdit repo/tools/REGISTRY.md
Index: repo/tools/REGISTRY.md
===================================================================
--- repo/tools/REGISTRY.md
+++ repo/tools/REGISTRY.md
@@ -2767,9 +2767,9 @@
**Dependencies**: python3 (stdlib only: argparse, contextlib, io, json, os, re, signal, subprocess, sys, time, datetime), `bash` and `php` to run their suites (either missing → exit 3, fail closed).
**Tests**:
-- `bash tests/test_regression_run.sh` → **314 assertions**, hermetic (throwaway fixtures under `/tmp/opencode/trr.*`, trap-cleaned): `--help` exits 0 and **all six epilog sections equal their docstring blocks byte for byte** (the sixth, `termination contract:`, added by `[0.4.131]`); the full exit-code contract `0/1/2/3/4/5/6` plus the `4 > 1` precedence and the preflight's own before-the-first-suite placement; all four parsing rules; the 14-key JSON contract including the usage-error, refusal *and* preflight objects having the *same* key set; the flag contract (including "stderr is exactly one diagnosis line" and "no `usage:` leak"); **the termination contract's own rendering (A13–A15)** — `start_new_session`, the `143 / 130` signal outcome and the declared escape limit must all appear in `--help`, three needles that sat in a docstring paragraph no label rendered (the (81) defect); **the epilog list is DERIVED, not copied (A16, A17.1–A17.6)** — `A3`/`A4`/`A5` and `help_section()`'s terminator set are read out of the tool's own `EPILOG_PAIRS`, so a seventh pair is compared without editing four lists, and the two things a derived list cannot see are pinned: the extraction is not empty (`A16`, or every loop would assert nothing) and each of the six contract sections is still in the tuple (`A17.*`, or a deleted pair would vanish from every loop); the epilog refusal in both channels; **the free-space preflight (section K, 43 assertions)** — a healthy tree still runs and its suite really runs (marker written), an impossible floor refuses with 6 in both channels with **empty stdout**, an absent marker proving no suite started, the refusal naming the path it measured and the measured figure, a **later** watched path at 1 MB refusing (so the check is not first-path-only — real `statvfs` can never say 1), an unmeasurable path refusing rather than passing, `--log-dir`'s not-yet-created directory gated through its existing ancestor, `--list` exempt, the two usage-error env values, the `--help` documentation, and a plant with the call removed; **the termination contract (section J)** — the outer bound, a direct SIGTERM and a direct SIGINT are all *really* sent, the fixture suite spawns a **writing grandchild** so "the tool died" and "everything the tool started died" stay two questions, the two invocation helpers are pinned for `--tests-dir "$SB/` the way section H pins its three, and a plant (the install line `sed`'d out of a copy) carries J16/J17/J18; **the (88) census scoping (J19–J21)** — a same-named fixture this run did not start is **not counted** and **not killed** by `bound_kill`, while this run's own fixture **still counts**; live read-only proof (`git status --porcelain` byte-identical); and the recursion guard (see below). *(The count here said **193** while the suite was already at **218** through `[0.4.101]` and **237** through `[0.4.102]` — two runs stale; refreshed rather than extended. The **255 → 298** move is `[0.4.129]`'s section K, 43 assertions; the **298 → 301** move is `[0.4.130]`'s J19–J21; the **301 → 307** move is `[0.4.131]`'s section A (the sixth epilog pair counted by A3/A4/A5, plus A13–A15); the **307 → 314** move is `[0.4.132]`'s section A (the derived epilog list, A16 + A17.1–A17.6, with A3/A4/A5 unchanged at six rows).)*
+- `bash tests/test_regression_run.sh` → **318 assertions**, hermetic (throwaway fixtures under `/tmp/opencode/trr.*`, trap-cleaned): `--help` exits 0 and **all six epilog sections equal their docstring blocks byte for byte** (the sixth, `termination contract:`, added by `[0.4.131]`); the full exit-code contract `0/1/2/3/4/5/6` plus the `4 > 1` precedence and the preflight's own before-the-first-suite placement; all four parsing rules; the 14-key JSON contract including the usage-error, refusal *and* preflight objects having the *same* key set; the flag contract (including "stderr is exactly one diagnosis line" and "no `usage:` leak"); **the termination contract's own rendering (A13–A15)** — `start_new_session`, the `143 / 130` signal outcome and the declared escape limit must all appear in `--help`, three needles that sat in a docstring paragraph no label rendered (the (81) defect); **the epilog list is DERIVED, not copied (A16, A17.1–A17.6)** — `A3`/`A4`/`A5` and `help_section()`'s terminator set are read out of the tool's own `EPILOG_PAIRS`, so a seventh pair is compared without editing four lists, and the two things a derived list cannot see are pinned: the extraction is not empty (`A16`, or every loop would assert nothing) and each of the six contract sections is still in the tuple (`A17.*`, or a deleted pair would vanish from every loop); the epilog refusal in both channels; **the free-space preflight (section K, 43 assertions)** — a healthy tree still runs and its suite really runs (marker written), an impossible floor refuses with 6 in both channels with **empty stdout**, an absent marker proving no suite started, the refusal naming the path it measured and the measured figure, a **later** watched path at 1 MB refusing (so the check is not first-path-only — real `statvfs` can never say 1), an unmeasurable path refusing rather than passing, `--log-dir`'s not-yet-created directory gated through its existing ancestor, `--list` exempt, the two usage-error env values, the `--help` documentation, and a plant with the call removed; **the termination contract (section J)** — the outer bound, a direct SIGTERM and a direct SIGINT are all *really* sent, the fixture suite spawns a **writing grandchild** so "the tool died" and "everything the tool started died" stay two questions, the two invocation helpers are pinned for `--tests-dir "$SB/` the way section H pins its three, and a plant (the install line `sed`'d out of a copy) carries J16/J17/J18; **the (88) census scoping (J19–J21)** — a same-named fixture this run did not start is **not counted** and **not killed** by `bound_kill`, while this run's own fixture **still counts**; live read-only proof (`git status --porcelain` byte-identical); and the recursion guard (see below). *(The count here said **193** while the suite was already at **218** through `[0.4.101]` and **237** through `[0.4.102]` — two runs stale; refreshed rather than extended. The **255 → 298** move is `[0.4.129]`'s section K, 43 assertions; the **298 → 301** move is `[0.4.130]`'s J19–J21; the **301 → 307** move is `[0.4.131]`'s section A (the sixth epilog pair counted by A3/A4/A5, plus A13–A15); the **307 → 314** move is `[0.4.132]`'s section A (the derived epilog list, A16 + A17.1–A17.6, with A3/A4/A5 unchanged at six rows).)*
- **Baseline section (I, 80 assertions — measured by counting the section's own PASS lines, not by subtracting)**: round trip against a record the tool itself wrote shows no movement with `closure_ok true`; a tampered record shows `moved`/`lost`/`delta`/`attributed` all agreeing; a record whose totals were **not** edited alongside its suites reports `closure_ok false` while still printing both numbers and still exiting 0; added/removed/status-change cases each sum to the same total from both directions; and every refusal (missing file, non-JSON, a run payload, a duplicated suite name, an unreadable `format`, `--list` + flag, unwritable target) is exit 2 **before any suite runs** with empty stdout. The fixtures are *tampered* copies built by an embedded python builder, because a tool's own writer never produces the cases worth testing. Section A grew by **3** alongside it (A3's label loop, A4's heading loop and A5's byte-comparison each gained the new `baseline:`/`Baseline comparison:` pair), so **110 + 3 + 80 = 193**.
- **Test hook**: `REGRESSION_RUN_BIN` points the suite at a mutant copy of the tool.
- **Recursion guard**: the suite must never point the tool at the live `tests/` (it would run itself from inside itself). Three invocation helpers, each pinned: `run_sandbox` injects `--tests-dir "$SB/`, `run_live`/`run_default` never name a tests dir at all, and every `run_live`/`run_default` **call site** carries `--only` or `--list`. Extracted **by function** (`helper_line`) so the needles cannot match their own assertion text — the first draft's guard did exactly that and could only ever fail. Section I runs **entirely through `run_sandbox`/`jrun`**, so even its error-path checks carry `--tests-dir`.
- **Mutations (14 total, copies under `/tmp`, tool md5 unchanged before/after)**. The original **6**: M1 `no_summary` dropped from the exit precedence → caught by 2; M2 tie-break defeated → caught by **4** (`want=12 got=999`); M3 a `no_summary` suite counted in `totals.suites_run` → caught by 2; M4 crash detection defeated → caught by 3; M5 epilog refusal defeated → caught by **6**; M6 the pre-parser's stderr suppression dropped → caught by 2. **The baseline 7** (`/tmp/opencode/mutate_baseline.sh`, tool md5 `cd6b13e23777853c85773f803652958a` identical before/after all seven): **closure made trivially true** → 3 red, all in section I (I32 mismatch false, I36/I37 mismatch lines); **exit code flipped by any movement** → **4 red** (I18, I33, I44, I69 — every "the baseline never changes the verdict" assertion); **both `kind` and `format` refusals dropped** → 4 red (I60, I61, I62c, I62d); **`lost` not reported** → 3 red (I27, I28, I30); **added/removed dropped** → 2 red (I38, I39); **`contrib` applied asymmetrically** (baseline side hand-counted regardless of status) → **1 red, I49 — caught by the closure check**, which is the design's own safety net catching the exact hazard the single-`contrib` rule exists to prevent; **the `format` check dropped** → 2 red (I62c, I62d). Each mutant's reds are confined to section I. **The termination 1** (`[0.4.124]`, `REGRESSION_RUN_BIN=/tmp/opencode/mut74`, only `install_termination_guard()`'s call removed) → **7 red in section J** (J2, J4, J6, J7, J8, J9, J11), 244 passed / 7 failed, and the live tool's md5 identical before and after; **J10 stays green there by construction** — the copy still *defines* the guard and lost only the call, which is the surgical difference between a plant and pre-fix code (the pre-fix replay, which has neither, reddens J10 too: 243 / 8). Both mutant runs close on **251 = 255 − 4**, the four being section G's `SKIP G6` shipped-location assertions, absent from any run whose `$TOOL` is not the shipped binary.
> build · mimo-v2.6-flash-free
[0m← [0mEdit repo/tools/REGISTRY.md
Index: repo/tools/REGISTRY.md
===================================================================
--- repo/tools/REGISTRY.md
+++ repo/tools/REGISTRY.md
@@ -2767,9 +2767,9 @@
**Dependencies**: python3 (stdlib only: argparse, contextlib, io, json, os, re, signal, subprocess, sys, time, datetime), `bash` and `php` to run their suites (either missing → exit 3, fail closed).
**Tests**:
-- `bash tests/test_regression_run.sh` → **318 assertions**, hermetic (throwaway fixtures under `/tmp/opencode/trr.*`, trap-cleaned): `--help` exits 0 and **all six epilog sections equal their docstring blocks byte for byte** (the sixth, `termination contract:`, added by `[0.4.131]`); the full exit-code contract `0/1/2/3/4/5/6` plus the `4 > 1` precedence and the preflight's own before-the-first-suite placement; all four parsing rules; the 14-key JSON contract including the usage-error, refusal *and* preflight objects having the *same* key set; the flag contract (including "stderr is exactly one diagnosis line" and "no `usage:` leak"); **the termination contract's own rendering (A13–A15)** — `start_new_session`, the `143 / 130` signal outcome and the declared escape limit must all appear in `--help`, three needles that sat in a docstring paragraph no label rendered (the (81) defect); **the epilog list is DERIVED, not copied (A16, A17.1–A17.6)** — `A3`/`A4`/`A5` and `help_section()`'s terminator set are read out of the tool's own `EPILOG_PAIRS`, so a seventh pair is compared without editing four lists, and the two things a derived list cannot see are pinned: the extraction is not empty (`A16`, or every loop would assert nothing) and each of the six contract sections is still in the tuple (`A17.*`, or a deleted pair would vanish from every loop); the epilog refusal in both channels; **the free-space preflight (section K, 43 assertions)** — a healthy tree still runs and its suite really runs (marker written), an impossible floor refuses with 6 in both channels with **empty stdout**, an absent marker proving no suite started, the refusal naming the path it measured and the measured figure, a **later** watched path at 1 MB refusing (so the check is not first-path-only — real `statvfs` can never say 1), an unmeasurable path refusing rather than passing, `--log-dir`'s not-yet-created directory gated through its existing ancestor, `--list` exempt, the two usage-error env values, the `--help` documentation, and a plant with the call removed; **the termination contract (section J)** — the outer bound, a direct SIGTERM and a direct SIGINT are all *really* sent, the fixture suite spawns a **writing grandchild** so "the tool died" and "everything the tool started died" stay two questions, the two invocation helpers are pinned for `--tests-dir "$SB/` the way section H pins its three, and a plant (the install line `sed`'d out of a copy) carries J16/J17/J18; **the (88) census scoping (J19–J21)** — a same-named fixture this run did not start is **not counted** and **not killed** by `bound_kill`, while this run's own fixture **still counts**; live read-only proof (`git status --porcelain` byte-identical); and the recursion guard (see below). *(The count here said **193** while the suite was already at **218** through `[0.4.101]` and **237** through `[0.4.102]` — two runs stale; refreshed rather than extended. The **255 → 298** move is `[0.4.129]`'s section K, 43 assertions; the **298 → 301** move is `[0.4.130]`'s J19–J21; the **301 → 307** move is `[0.4.131]`'s section A (the sixth epilog pair counted by A3/A4/A5, plus A13–A15); the **307 → 314** move is `[0.4.132]`'s section A (the derived epilog list, A16 + A17.1–A17.6, with A3/A4/A5 unchanged at six rows).)*
+- `bash tests/test_regression_run.sh` → **318 assertions**, hermetic (throwaway fixtures under `/tmp/opencode/trr.*`, trap-cleaned): `--help` exits 0 and **all six epilog sections equal their docstring blocks byte for byte** (the sixth, `termination contract:`, added by `[0.4.131]`); the full exit-code contract `0/1/2/3/4/5/6` plus the `4 > 1` precedence and the preflight's own before-the-first-suite placement; all four parsing rules; the 14-key JSON contract including the usage-error, refusal *and* preflight objects having the *same* key set; the flag contract (including "stderr is exactly one diagnosis line" and "no `usage:` leak"); **the termination contract's own rendering (A13–A15)** — `start_new_session`, the `143 / 130` signal outcome and the declared escape limit must all appear in `--help`, three needles that sat in a docstring paragraph no label rendered (the (81) defect); **the epilog list is DERIVED, not copied (A16, A17.1–A17.6)** — `A3`/`A4`/`A5` and `help_section()`'s terminator set are read out of the tool's own `EPILOG_PAIRS`, so a seventh pair is compared without editing four lists, and the two things a derived list cannot see are pinned: the extraction is not empty (`A16`, or every loop would assert nothing) and each of the six contract sections is still in the tuple (`A17.*`, or a deleted pair would vanish from every loop); the epilog refusal in both channels; **the free-space preflight (section K, 43 assertions)** — a healthy tree still runs and its suite really runs (marker written), an impossible floor refuses with 6 in both channels with **empty stdout**, an absent marker proving no suite started, the refusal naming the path it measured and the measured figure, a **later** watched path at 1 MB refusing (so the check is not first-path-only — real `statvfs` can never say 1), an unmeasurable path refusing rather than passing, `--log-dir`'s not-yet-created directory gated through its existing ancestor, `--list` exempt, the two usage-error env values, the `--help` documentation, and a plant with the call removed; **the termination contract (section J)** — the outer bound, a direct SIGTERM and a direct SIGINT are all *really* sent, the fixture suite spawns a **writing grandchild** so "the tool died" and "everything the tool started died" stay two questions, the two invocation helpers are pinned for `--tests-dir "$SB/` the way section H pins its three, and a plant (the install line `sed`'d out of a copy) carries J16/J17/J18; **the (88) census scoping (J19–J21)** — a same-named fixture this run did not start is **not counted** and **not killed** by `bound_kill`, while this run's own fixture **still counts**; **the (89) descendant reap (J22–J25)** — the mutant fixture's tree is captured *while it is alive* (`K19`, `K20`) and every pid in it must be gone after `bound_kill`, at **both** plant sites, because a SIGKILL'd shell *reparents* its foreground `sleep 300` to init where no fixture path remains to find it by; live read-only proof (`git status --porcelain` byte-identical); and the recursion guard (see below). *(The count here said **193** while the suite was already at **218** through `[0.4.101]` and **237** through `[0.4.102]` — two runs stale; refreshed rather than extended. The **255 → 298** move is `[0.4.129]`'s section K, 43 assertions; the **298 → 301** move is `[0.4.130]`'s J19–J21; the **301 → 307** move is `[0.4.131]`'s section A (the sixth epilog pair counted by A3/A4/A5, plus A13–A15); the **307 → 314** move is `[0.4.132]`'s section A (the derived epilog list, A16 + A17.1–A17.6, with A3/A4/A5 unchanged at six rows); the **314 → 318** move is `[0.4.147]`'s section J (J22–J25, two per plant site).)*
- **Baseline section (I, 80 assertions — measured by counting the section's own PASS lines, not by subtracting)**: round trip against a record the tool itself wrote shows no movement with `closure_ok true`; a tampered record shows `moved`/`lost`/`delta`/`attributed` all agreeing; a record whose totals were **not** edited alongside its suites reports `closure_ok false` while still printing both numbers and still exiting 0; added/removed/status-change cases each sum to the same total from both directions; and every refusal (missing file, non-JSON, a run payload, a duplicated suite name, an unreadable `format`, `--list` + flag, unwritable target) is exit 2 **before any suite runs** with empty stdout. The fixtures are *tampered* copies built by an embedded python builder, because a tool's own writer never produces the cases worth testing. Section A grew by **3** alongside it (A3's label loop, A4's heading loop and A5's byte-comparison each gained the new `baseline:`/`Baseline comparison:` pair), so **110 + 3 + 80 = 193**.
- **Test hook**: `REGRESSION_RUN_BIN` points the suite at a mutant copy of the tool.
- **Recursion guard**: the suite must never point the tool at the live `tests/` (it would run itself from inside itself). Three invocation helpers, each pinned: `run_sandbox` injects `--tests-dir "$SB/`, `run_live`/`run_default` never name a tests dir at all, and every `run_live`/`run_default` **call site** carries `--only` or `--list`. Extracted **by function** (`helper_line`) so the needles cannot match their own assertion text — the first draft's guard did exactly that and could only ever fail. Section I runs **entirely through `run_sandbox`/`jrun`**, so even its error-path checks carry `--tests-dir`.
- **Mutations (14 total, copies under `/tmp`, tool md5 unchanged before/after)**. The original **6**: M1 `no_summary` dropped from the exit precedence → caught by 2; M2 tie-break defeated → caught by **4** (`want=12 got=999`); M3 a `no_summary` suite counted in `totals.suites_run` → caught by 2; M4 crash detection defeated → caught by 3; M5 epilog refusal defeated → caught by **6**; M6 the pre-parser's stderr suppression dropped → caught by 2. **The baseline 7** (`/tmp/opencode/mutate_baseline.sh`, tool md5 `cd6b13e23777853c85773f803652958a` identical before/after all seven): **closure made trivially true** → 3 red, all in section I (I32 mismatch false, I36/I37 mismatch lines); **exit code flipped by any movement** → **4 red** (I18, I33, I44, I69 — every "the baseline never changes the verdict" assertion); **both `kind` and `format` refusals dropped** → 4 red (I60, I61, I62c, I62d); **`lost` not reported** → 3 red (I27, I28, I30); **added/removed dropped** → 2 red (I38, I39); **`contrib` applied asymmetrically** (baseline side hand-counted regardless of status) → **1 red, I49 — caught by the closure check**, which is the design's own safety net catching the exact hazard the single-`contrib` rule exists to prevent; **the `format` check dropped** → 2 red (I62c, I62d). Each mutant's reds are confined to section I. **The termination 1** (`[0.4.124]`, `REGRESSION_RUN_BIN=/tmp/opencode/mut74`, only `install_termination_guard()`'s call removed) → **7 red in section J** (J2, J4, J6, J7, J8, J9, J11), 244 passed / 7 failed, and the live tool's md5 identical before and after; **J10 stays green there by construction** — the copy still *defines* the guard and lost only the call, which is the surgical difference between a plant and pre-fix code (the pre-fix replay, which has neither, reddens J10 too: 243 / 8). Both mutant runs close on **251 = 255 − 4**, the four being section G's `SKIP G6` shipped-location assertions, absent from any run whose `$TOOL` is not the shipped binary.
> build · mimo-v2.6-flash-free
[0m$ [0mtail -4 /tmp/opencode/trr-control.txt; echo "=== FAILs:"; grep "^FAIL:" /tmp/opencode/trr-control.txt; echo "=== orphans:"; cat /tmp/opencode/trr-orphans-control.txt; echo "=== new pids vs baseline(2103289,2103434):"; ps -eo pid,ppid,etimes,args | grep 'sleep 300' | grep -v grep
=== Results: 312 passed, 6 failed ===
rc=1
sleep-300 total after control: 4
=== FAILs:
FAIL: G1 live --only ts_ordering -> 0 (want=0 got=4)
FAIL: G2 live ts_ordering totals 13 (want=13 got=0)
FAIL: G6 default_repo resolves the shipped location -> 0 (want=0 got=4)
FAIL: G6b default repo_root is /data/repo (want=/data/repo got=/tmp/opencode/ctrl89)
FAIL: J23 bound_kill reaped that tree too (a -9'd bash orphans its sleep 300) (want=0 got=1)
FAIL: J25 and it is gone after the cleanup (the second orphan source) (want=0 got=1)
=== orphans:
2103289 1 250 sleep 300
2103434 1 246 sleep 300
2107015 1 13 sleep 300
2107128 1 8 sleep 300
=== new pids vs baseline(2103289,2103434):
2103289 1 265 sleep 300
2103434 1 261 sleep 300
2107015 1 27 sleep 300
2107128 1 23 sleep 300
> build · mimo-v2.6-flash-free
[0m$ [0mcd /tmp/opencode/ctrl89 && python3 - <<'PY'
import pathlib
p = pathlib.Path('/tmp/opencode/ctrl89/tests/test_regression_run.sh')
s = p.read_text()
old = 'REPO=$(dirname "$TESTS_DIR")'
new = 'REPO=/data/repo # CONTROL-ONLY: pin the live repo so the suite\'s live sections (G1/G2/G6) read the real tree from an isolation path'
assert s.count(old) == 1, s.count(old)
p.write_text(s.replace(old, new))
print("control env pin applied (disclosed): REPO -> /data/repo")
PY
(bash tests/test_regression_run.sh > /tmp/opencode/trr-control2.txt 2>&1; echo "rc=$?" >> /tmp/opencode/trr-control2.txt; echo "FAILs:" >> /tmp/opencode/trr-control2.txt; grep "^FAIL:" /tmp/opencode/trr-control2.txt >> /tmp/opencode/trr-control2.txt 2>/dev/null; true)
control env pin applied (disclosed): REPO -> /data/repo
> build · mimo-v2.6-flash-free
[0m$ [0mgrep -E "^=== Results|^rc=" /tmp/opencode/trr-control2.txt; echo "=== FAILs:"; grep "^FAIL:" /tmp/opencode/trr-control2.txt | sort -u; echo "=== J22-J25:"; grep -E "J2[2-5] " /tmp/opencode/trr-control2.txt; echo "=== orphans now:"; ps -eo pid,ppid,etimes,args | grep 'sleep 300' | grep -v grep
=== Results: 316 passed, 2 failed ===
rc=1
=== FAILs:
FAIL: J23 bound_kill reaped that tree too (a -9'd bash orphans its sleep 300) (want=0 got=1)
FAIL: J25 and it is gone after the cleanup (the second orphan source) (want=0 got=1)
=== J22-J25:
PASS: J22 the mutant fixture has a descendant tree (the orphan's precondition)
FAIL: J23 bound_kill reaped that tree too (a -9'd bash orphans its sleep 300) (want=0 got=1)
PASS: J24 the second mutant fixture's tree is there to reap as well
FAIL: J25 and it is gone after the cleanup (the second orphan source) (want=0 got=1)
=== orphans now:
2107015 1 121 sleep 300
2107128 1 116 sleep 300
2108377 1 29 sleep 300
2108490 1 25 sleep 300
> build · mimo-v2.6-flash-free
[0m$ [0mfor p in 2107015 2107128 2108377 2108490; do kill -9 $p 2>/dev/null; done; sleep 0.5; ps -eo pid,args | grep 'sleep 300' | grep -v grep || echo "no sleep 300 left (control residue cleaned)"; rm -rf /tmp/opencode/ctrl89
no sleep 300 left (control residue cleaned)
> build · mimo-v2.6-flash-free
[0m← [0mEdit repo/tools/REGISTRY.md
Index: repo/tools/REGISTRY.md
===================================================================
--- repo/tools/REGISTRY.md
+++ repo/tools/REGISTRY.md
@@ -2791,9 +2791,9 @@
**How it stays hermetic**: ten fixture directories are built under a `mktemp -d` sandbox (`green`, `red`, `silent`, `crash`, `hang`, `tie`, `fallback`, `unsupported`, `mixed`, `empty`), each a tiny shell script printing one summary shape and exiting accordingly; section I derives six **baseline** fixtures from a record the tool wrote, by tampering with a copy (a suite's count raised with its totals kept consistent, the same edit *without* the totals, a suite removed plus a suite the run never heard of, a suite recorded as `no_summary` with `passed: null`, a duplicated suite name, and a run payload); section J adds a **`j/`** workspace inside the same sandbox — `j/sb` holds the fixture suite that spawns a writing grandchild and sleeps 300 s, `j/foreign` a **same-named** fixture this run did not start (the (88) census-scoping subject), `j/mutant` the planted copy of the tool; section K adds **`pf/`** (a fixture suite that **writes a marker file**, so "no suite was started" is a file rather than an inference from an exit code) and **`k/mutant`** (the preflight call `sed`'d out); a `trap` removes the sandbox on EXIT/INT/TERM. Nothing under `tests/` is executed except two live checks, both carrying a safe flag.
**Exit codes**:
-- `0` - all 314 assertions pass
+- `0` - all 318 assertions pass
- `1` - one or more assertions failed
**Test hooks (env)**: `REGRESSION_RUN_BIN` — path of the tool under test, defaulting to `tools/regression-run`. Mutation runs point it at a copy, so the real tool is never edited. Section K additionally exports `GLADEX_MIN_FREE_MB`, `GLADEX_PREFLIGHT_PATHS` and `GLADEX_PREFLIGHT_FIXTURE` **around single invocations only** (`unset` immediately after), because a hook left exported would change every later section's verdict.
[0m← [0mEdit repo/tools/REGISTRY.md
Index: repo/tools/REGISTRY.md
===================================================================
--- repo/tools/REGISTRY.md
+++ repo/tools/REGISTRY.md
@@ -2756,9 +2756,9 @@
**Why, not just what (`--note`)**: counts say *what* moved, never *why* — `[0.4.66]`'s queue item (3), because every entry in this repo attributes totals by hand and hand arithmetic is how "+110 somewhere" becomes an unverified "no other suite moved". `--save-baseline FILE --note "text"` stores the text in the document (top-level key `note`, always present, `null` when absent) and `--baseline FILE` prints ` note: <text>` on the line directly under the report header — in both the no-movement and the movement branch, so "gamma lost 2 assertions (note: consolidated fixtures)" replaces a bare count. The flag belongs to the writer: `--note` without `--save-baseline` → exit 2, blank/whitespace-only text → exit 2, and a stored `note` that is not usable text → exit 2 *before any suite runs* (same rule as `format`: a field that exists but is never checked is a field that lies). The report key is `baseline.note`, so a consumer sees `null` rather than a key that appears and disappears; a human report on a pre-`--note` baseline prints no `note:` line at all.
**One line, at most `NOTE_MAX` (200 characters)**: the note is *printed* — behind a fixed two-space prefix, under the report header — so `[0.4.102]` (queue item (46)) bounds it where it is **accepted** rather than where it is rendered. A control character (newline or tab) → exit 2, `--note must be a single line (no newline, tab or other control character)`; text past the bound → exit 2, `--note must be at most 200 characters (got N)`. The reason a newline is not "multi-line prose": the second line carries no prefix and can be written to look like this tool's own output (` result: all suites green (exit 0)`), and a note longer than the report's width is a report rather than a reason. `NOTE_MAX` is **one** constant — the option's `--help` text and the docstring prose interpolate it, and `test_regression_run.sh` A10–A12 pin the single definition plus both renderings. `load_baseline` holds a **stored** `note` to the same two bounds (`baseline FILE note …` → exit 2), so a hand-edited document is refused before any suite runs, at the same pre-run position as `format`, `kind` and `totals`; blankness stays with its own predicate and its own message, because two rules for one string would be two places for them to disagree.
-**Design decisions that are not up for a flag**: runs **sequentially, no `--jobs`** — several suites take locks, ports and git state (`test_php_busy_timeout` holds a SQLite handle, the promote suites touch the envs), and trading a reproducible total for a flaky one is the wrong trade for the tool whose job is reproducibility. The epilog carries **no prose of its own** (all six sections derived from the module docstring, following `repo-lint`); a missing or empty heading **refuses** with exit 3 naming the pair rather than printing a label over nothing. *Since `[0.4.131]`* the termination contract in this very paragraph is one of those six: it has been in the docstring since `[0.4.124]` and rendered in `--help` never — `grep -c start_new_session <(regression-run --help)` → **0** before this step, **1** after — so a reader of `--help` was told nothing about what a TERM or INT does to a running suite, and the pair was added rather than the paragraph rewritten. Timeouts kill the whole **process group** (`start_new_session` + `killpg`), because suites spawn children that would otherwise keep the locks the next suite needs. *Since `[0.4.124]`* the same isolation is also why that group is reaped by the **tool** when it is terminated: a suite lives in a foreign session, so no signal aimed at this process can reach it. Measured 2026-09-29 before the fix — `timeout 3 regression-run --tests-dir … --timeout 600` → **124** with the suite reparented to `ppid 1` in its own session and its grandchild still writing, direct SIGTERM → **143** with two survivors still writing, and direct **SIGINT never ending this process at all** (blocked in `poll()` inside `communicate()`, so `KeyboardInterrupt` is never raised). `install_termination_guard()` runs as the **first act of `main()`**: on SIGTERM or SIGINT it SIGKILLs whatever is in `_LIVE_PGIDS`, then hands the signal back to `SIG_DFL` and re-sends it, so the caller still sees death by signal **143 / 130** — no new exit code, and no verdict line for a run that did not finish. Declared limit, written in the docstring rather than implied: a suite that moves a child into *its own* session escapes this exactly as it escapes every other bound. Held by `test_regression_run.sh` section **J** (21 assertions). Totals are **derived** from the records by `contrib()` after the loop, not accumulated by a second rule inside it.
+**Design decisions that are not up for a flag**: runs **sequentially, no `--jobs`** — several suites take locks, ports and git state (`test_php_busy_timeout` holds a SQLite handle, the promote suites touch the envs), and trading a reproducible total for a flaky one is the wrong trade for the tool whose job is reproducibility. The epilog carries **no prose of its own** (all six sections derived from the module docstring, following `repo-lint`); a missing or empty heading **refuses** with exit 3 naming the pair rather than printing a label over nothing. *Since `[0.4.131]`* the termination contract in this very paragraph is one of those six: it has been in the docstring since `[0.4.124]` and rendered in `--help` never — `grep -c start_new_session <(regression-run --help)` → **0** before this step, **1** after — so a reader of `--help` was told nothing about what a TERM or INT does to a running suite, and the pair was added rather than the paragraph rewritten. Timeouts kill the whole **process group** (`start_new_session` + `killpg`), because suites spawn children that would otherwise keep the locks the next suite needs. *Since `[0.4.124]`* the same isolation is also why that group is reaped by the **tool** when it is terminated: a suite lives in a foreign session, so no signal aimed at this process can reach it. Measured 2026-09-29 before the fix — `timeout 3 regression-run --tests-dir … --timeout 600` → **124** with the suite reparented to `ppid 1` in its own session and its grandchild still writing, direct SIGTERM → **143** with two survivors still writing, and direct **SIGINT never ending this process at all** (blocked in `poll()` inside `communicate()`, so `KeyboardInterrupt` is never raised). `install_termination_guard()` runs as the **first act of `main()`**: on SIGTERM or SIGINT it SIGKILLs whatever is in `_LIVE_PGIDS`, then hands the signal back to `SIG_DFL` and re-sends it, so the caller still sees death by signal **143 / 130** — no new exit code, and no verdict line for a run that did not finish. Declared limit, written in the docstring rather than implied: a suite that moves a child into *its own* session escapes this exactly as it escapes every other bound. Held by `test_regression_run.sh` section **J** (25 assertions). Totals are **derived** from the records by `contrib()` after the loop, not accumulated by a second rule inside it.
**Free-space preflight (the (82) half `disk-show` cannot be, since `[0.4.129]`)**: `disk-show` watches mounts on a **percentage** and lists reclaimable caches; this gates a **run** on an **absolute MB floor**, because the failure it guards against is the one measured 2026-09-29 — the root filesystem reached **100 % (98 G, 0 available)** and the damage was *silent* (writes failing, sqlite `disk I/O error`, shell output discarded at exit 0), and this tool is the one whose own `--log-dir` writes and whose suites' `mktemp` sandboxes are a large part of what fills `/tmp`. A disk at 79 % with 21 G free is healthy; one at 90 % with 0 bytes is not, so the number that matters is bytes left, not percent used. Watched paths, in order: repo root, tests dir, `TMPDIR` (default `/tmp`), and — with `--log-dir` — the nearest **existing** ancestor of it (the directory is created *after* the check, so `statvfs` on the path itself could not run, and a check that cannot measure must refuse rather than pass). Measured with `statvfs` (`f_bavail × f_frsize`, MB truncated); duplicates collapse to one measurement. The refusal names the **first** short path with both numbers. `--list` is exempt — it runs nothing and writes nothing, and the gate exists to protect work. Placement is the contract: the call sits **after** the `--list` branch and **before** `os.makedirs(args.log_dir)`, pinned by `test_regression_run.sh` K28, so "refused before the first suite" is a line-order fact rather than a claim. Costs one `statvfs` per path on the healthy path (K1), and `GLADEX_MIN_FREE_MB=0` is the documented off switch.
**Test hooks (env)**: `GLADEX_REPO_DIR` (repo root; `realpath`'d so `/data/tools` resolves correctly), `GLADEX_TESTS_DIR` (default `--tests-dir`), `REGRESSION_TIMEOUT` (default per-suite seconds), `GLADEX_MIN_FREE_MB` (preflight floor in MB, default 512, `0` disables, non-integer/negative → exit 2), `GLADEX_PREFLIGHT_PATHS` (comma-separated watch list, watched exactly as named — an empty value → exit 2, a name that does not exist → exit 6, never a pass), `GLADEX_PREFLIGHT_FIXTURE` (`path=MB,…` — reports those figures instead of calling `statvfs`, so a "1 MB free" breach is producible without filling a disk). The last two are test hooks: leave them unset in normal use.
> build · mimo-v2.6-flash-free
Select another run log from the list above. Only files matching run-YYYYMMDD-HHMMSS-N.log are readable.
App log tail — prod-8001.log (last 60 lines)
[Thu Oct 1 02:26:55 2026] 127.0.0.1:35172 Accepted [Thu Oct 1 02:26:55 2026] 127.0.0.1:35172 Closing [Thu Oct 1 02:26:55 2026] 127.0.0.1:35180 Accepted [Thu Oct 1 02:26:55 2026] 127.0.0.1:35180 Closing [Thu Oct 1 02:26:55 2026] 127.0.0.1:35182 Accepted [Thu Oct 1 02:26:55 2026] 127.0.0.1:35182 Closing [Thu Oct 1 02:26:55 2026] 127.0.0.1:35186 Accepted [Thu Oct 1 02:26:55 2026] 127.0.0.1:35186 Closing [Thu Oct 1 02:26:55 2026] 127.0.0.1:35194 Accepted [Thu Oct 1 02:26:55 2026] 127.0.0.1:35194 Closing [Thu Oct 1 02:26:55 2026] 127.0.0.1:35208 Accepted [Thu Oct 1 02:26:56 2026] 127.0.0.1:35208 Closing [Thu Oct 1 02:26:56 2026] 127.0.0.1:35222 Accepted [Thu Oct 1 02:26:56 2026] 127.0.0.1:35222 Closing [Thu Oct 1 02:28:09 2026] 127.0.0.1:43416 Accepted [Thu Oct 1 02:28:09 2026] 127.0.0.1:43416 Closing [Thu Oct 1 02:28:09 2026] 127.0.0.1:43420 Accepted [Thu Oct 1 02:28:09 2026] 127.0.0.1:43420 Closing [Thu Oct 1 02:28:09 2026] 127.0.0.1:43426 Accepted [Thu Oct 1 02:28:09 2026] 127.0.0.1:43426 Closing [Thu Oct 1 02:28:09 2026] 127.0.0.1:43440 Accepted [Thu Oct 1 02:28:09 2026] 127.0.0.1:43440 Closing [Thu Oct 1 02:28:09 2026] 127.0.0.1:43448 Accepted [Thu Oct 1 02:28:09 2026] 127.0.0.1:43448 Closing [Thu Oct 1 02:28:09 2026] 127.0.0.1:43462 Accepted [Thu Oct 1 02:28:09 2026] 127.0.0.1:43462 Closing [Thu Oct 1 02:28:09 2026] 127.0.0.1:43474 Accepted [Thu Oct 1 02:28:09 2026] 127.0.0.1:43474 Closing [Thu Oct 1 02:28:09 2026] 127.0.0.1:43490 Accepted [Thu Oct 1 02:28:09 2026] 127.0.0.1:43490 Closing [Thu Oct 1 02:28:09 2026] 127.0.0.1:43504 Accepted [Thu Oct 1 02:28:10 2026] 127.0.0.1:43504 Closing [Thu Oct 1 02:28:10 2026] 127.0.0.1:43518 Accepted [Thu Oct 1 02:28:10 2026] 127.0.0.1:43518 Closing [Thu Oct 1 02:28:51 2026] 127.0.0.1:41034 Accepted [Thu Oct 1 02:28:51 2026] 127.0.0.1:41034 Closing [Thu Oct 1 02:28:51 2026] 127.0.0.1:41036 Accepted [Thu Oct 1 02:28:51 2026] 127.0.0.1:41036 Closing [Thu Oct 1 02:28:51 2026] 127.0.0.1:41050 Accepted [Thu Oct 1 02:28:51 2026] 127.0.0.1:41050 Closing [Thu Oct 1 02:28:51 2026] 127.0.0.1:41058 Accepted [Thu Oct 1 02:28:51 2026] 127.0.0.1:41058 Closing [Thu Oct 1 02:28:51 2026] 127.0.0.1:41066 Accepted [Thu Oct 1 02:28:51 2026] 127.0.0.1:41066 Closing [Thu Oct 1 02:28:51 2026] 127.0.0.1:41074 Accepted [Thu Oct 1 02:28:51 2026] 127.0.0.1:41074 Closing [Thu Oct 1 02:28:51 2026] 127.0.0.1:41084 Accepted [Thu Oct 1 02:28:52 2026] 127.0.0.1:41084 Closing [Thu Oct 1 02:28:52 2026] 127.0.0.1:41092 Accepted [Thu Oct 1 02:28:52 2026] 127.0.0.1:41092 Closing [Thu Oct 1 02:28:52 2026] 127.0.0.1:41100 Accepted [Thu Oct 1 02:28:52 2026] 127.0.0.1:41100 Closing [Thu Oct 1 02:28:52 2026] 127.0.0.1:41108 Accepted [Thu Oct 1 02:28:52 2026] 127.0.0.1:41108 Closing [Thu Oct 1 02:31:48 2026] 127.0.0.1:46084 Accepted [Thu Oct 1 02:31:48 2026] 127.0.0.1:46084 Closing [Thu Oct 1 02:35:11 2026] 127.0.0.1:41532 Accepted [Thu Oct 1 02:35:11 2026] 127.0.0.1:41532 Closing [Thu Oct 1 02:36:11 2026] 127.0.0.1:34068 Accepted
Generated 2026-10-01 00:36:11 UTC · Gladex.de