Gladex Agent Logs
Agent run logs & app logs · env: prod · LAN-only investor surface
Overview
| Run logs | 1193 files, 72.8 MB |
| Latest run log | run-20261006-124129-727.log |
| Log directory | /data/agent-logs |
| App log directory | /opt/startup/prod/logs |
Run logs (newest first, last 50)
| File | Size | Modified (UTC) |
|---|---|---|
| run-20261006-124129-727.log | 100 KB | 2026-10-06 10:49:11 |
| run-20261006-104452-726.log | 468 KB | 2026-10-06 10:31:20 |
| run-20261006-090726-725.log | 338 KB | 2026-10-06 08:34:44 |
| run-20261006-073331-724.log | 245 KB | 2026-10-06 06:57:18 |
| run-20261006-061912-723.log | 266 KB | 2026-10-06 05:23:23 |
| run-20261006-053304-722.log | 192 KB | 2026-10-06 04:09:04 |
| run-20261006-041829-721.log | 341 KB | 2026-10-06 03:22:57 |
| run-20261006-031229-720.log | 292 KB | 2026-10-06 02:08:21 |
| run-20261006-022028-719.log | 271 KB | 2026-10-06 01:02:21 |
| run-20261006-013213-718.log | 276 KB | 2026-10-06 00:10:20 |
| run-20261006-004412-717.log | 192 KB | 2026-10-05 23:22:05 |
| run-20261005-234618-716.log | 235 KB | 2026-10-05 22:34:04 |
| run-20261005-225616-715.log | 296 KB | 2026-10-05 21:36:11 |
| run-20261005-215648-714.log | 220 KB | 2026-10-05 20:46:08 |
| run-20261005-205855-713.log | 188 KB | 2026-10-05 19:46:40 |
| run-20261005-201246-712.log | 321 KB | 2026-10-05 18:48:47 |
| run-20261005-190629-711.log | 213 KB | 2026-10-05 18:02:38 |
| run-20261005-174000-710.log | 397 KB | 2026-10-05 16:56:22 |
| run-20261005-164047-709.log | 254 KB | 2026-10-05 15:29:53 |
| run-20261005-160934-708.log | 139 KB | 2026-10-05 14:30:37 |
| run-20261005-155925-707.log | 153 B | 2026-10-05 13:59:26 |
| run-20261005-154917-706.log | 190 B | 2026-10-05 13:49:18 |
| run-20261005-153909-705.log | 153 B | 2026-10-05 13:39:09 |
| run-20261005-152900-704.log | 153 B | 2026-10-05 13:29:01 |
| run-20261005-142051-703.log | 222 KB | 2026-10-05 13:18:53 |
| run-20261005-130731-702.log | 227 KB | 2026-10-05 12:10:44 |
| run-20261005-120453-701.log | 163 KB | 2026-10-05 10:57:24 |
| run-20261005-094142-700.log | 440 KB | 2026-10-05 09:54:46 |
| run-20261005-090130-699.log | 165 KB | 2026-10-05 07:31:34 |
| run-20261005-071341-698.log | 435 KB | 2026-10-05 06:51:22 |
| run-20261005-061647-697.log | 196 KB | 2026-10-05 05:03:34 |
| run-20261005-052959-696.log | 226 KB | 2026-10-05 04:06:39 |
| run-20261005-044026-695.log | 177 KB | 2026-10-05 03:19:52 |
| run-20261005-034927-694.log | 254 KB | 2026-10-05 02:30:18 |
| run-20261005-030059-693.log | 312 KB | 2026-10-05 01:39:20 |
| run-20261004-233305-692.log | 1010 KB | 2026-10-05 00:50:51 |
| run-20261004-230052-691.log | 130 KB | 2026-10-04 21:22:57 |
| run-20261004-221607-690.log | 258 KB | 2026-10-04 20:50:44 |
| run-20261004-210204-689.log | 310 KB | 2026-10-04 20:05:59 |
| run-20261004-184239-688.log | 505 KB | 2026-10-04 18:51:56 |
| run-20261004-172538-687.log | 486 KB | 2026-10-04 16:32:32 |
| run-20261004-165539-686.log | 142 KB | 2026-10-04 15:15:29 |
| run-20261004-161338-685.log | 176 KB | 2026-10-04 14:45:31 |
| run-20261004-160330-684.log | 153 B | 2026-10-04 14:03:31 |
| run-20261004-155322-683.log | 153 B | 2026-10-04 13:53:23 |
| run-20261004-154314-682.log | 190 B | 2026-10-04 13:43:15 |
| run-20261004-153306-681.log | 153 B | 2026-10-04 13:33:07 |
| run-20261004-152258-680.log | 153 B | 2026-10-04 13:22:58 |
| run-20261004-151250-679.log | 190 B | 2026-10-04 13:12:50 |
| run-20261004-150241-678.log | 153 B | 2026-10-04 13:02:42 |
Tail — run-20261006-124129-727.log (last 200 lines)
# plantO the no_a_records ARM appends a DRIFT line to ITS OWN dated log
# 304 / 0 SURVIVED (shape the queue
# asked for: err_no_a is the only scenario with a DRIFT absence)
# plantP the exit-0 branch appends a CHECK-ERROR line to its own dated log
# 304 / 0 SURVIVED (shape the queue
# asked for — and the reason the HEALTHY branch is in scope at all,
# which the queue's two named directions did not include)
#
# The four probe44 numbers re-confirmed exactly (304/0, 303/1, 299/5, 304/0)
# before a line of this block existed. Every absence below is re-anchored on
# the run's OWN line of the RIGHT kind, in the very channel the absence reads
# (rule (h): an absent log must redden instead of making the absence vacuous),
# and the needles are kind BODIES — `DRIFT gladex.de:` / `CHECK-ERROR
# gladex.de:` for anchors, `DRIFT gladex.de` / `CHECK-ERROR` for absences, the
# same bodies sections 2–4 already read — never a prefix assumption.
# (1) The healthy branch: its dated log claims NEITHER kind. (§2 already holds
# the ALERTS side — "nothing appended to ALERTS" and "no CHECK-ERROR either".)
run_cron ok
H_LOG="$(logf)"
assert_has "healthy run → dated log says so (the two absences below read a real log)" \
"OK: No drift" "$H_LOG"
assert_lacks "healthy run → dated log claims no DRIFT verdict" "DRIFT gladex.de" "$H_LOG"
assert_lacks "healthy run → dated log claims no CHECK-ERROR verdict" "CHECK-ERROR" "$H_LOG"
# (2) The drift branch, ALL THREE classes, BOTH channels — CGNAT is the class
# that had nothing, and plantK/plantL are its two plants; public and unparse
# had an ALERTS absence only, unanchored (§3 and the pair above).
for dscen in drift_pub drift_cgnat drift_unparse; do
run_cron "$dscen"
D_A="$(alerts)"
D_L="$(logf)"
assert_has "$dscen → own DRIFT alert present in ALERTS (the absence below reads a real alert)" \
"DRIFT gladex.de:" "$D_A"
assert_lacks "$dscen → ALERTS claims no CHECK-ERROR verdict" "CHECK-ERROR" "$D_A"
assert_has "$dscen → own DRIFT alert present in the dated log (the absence below reads a real log)" \
"DRIFT gladex.de:" "$D_L"
assert_lacks "$dscen → dated log claims no CHECK-ERROR verdict" "CHECK-ERROR" "$D_L"
done
# (3) The four UNCLASSIFIED inputs — the four scenarios plantM showed reddening
# NOTHING, in BOTH channels. err_int is deliberately not in this loop: §4's
# `exactly one alert line` already reddens for it, and the queue scoped this
# group at the four that had no guard at all.
for kscen in garbage empty rc2 rc99; do
run_cron "$kscen"
K_A="$(alerts)"
K_L="$(logf)"
assert_has "$kscen → own CHECK-ERROR alert present in ALERTS (the absence below reads a real alert)" \
"CHECK-ERROR gladex.de:" "$K_A"
assert_lacks "$kscen → ALERTS claims no DRIFT verdict" "DRIFT gladex.de" "$K_A"
assert_has "$kscen → own CHECK-ERROR line present in the dated log (the absence below reads a real log)" \
"CHECK-ERROR gladex.de:" "$K_L"
assert_lacks "$kscen → dated log claims no DRIFT verdict" "DRIFT gladex.de" "$K_L"
done
# (4) The dated-log direction on a NAMED arm — plantO's home: err_no_a is the
# only scenario that already carried a DRIFT absence anywhere (line above
# this block), so it is the run whose dated log a false verdict would be
# mistaken for a real one on, arm by arm rather than on the shared path.
run_cron err_no_a
NA_L="$(logf)"
assert_has "err_no_a → own CHECK-ERROR line present in the dated log (the direction below reads a real log)" \
"CHECK-ERROR gladex.de:" "$NA_L"
assert_lacks "a check-error run's dated log claims no DRIFT verdict" "DRIFT gladex.de" "$NA_L"
# alerts are append-only across runs
rm -rf "$SANDBOX/logs"; mkdir -p "$SANDBOX/logs"
KEEP=1
run_cron drift_pub
run_cron drift_cgnat
KEEP=0
assert_eq "alerts accumulate across runs (append-only)" "2" "$(alert_n)"
assert_eq "…both runs append to the same dated run log" "1" \
"$(ls "$SANDBOX/logs" | grep -cE '^ip-drift-[0-9]{8}\.log$')"
assert_eq "…and that log holds both runs" "2" "$(grep -c 'Starting IP drift check' "$SANDBOX/logs"/ip-drift-[0-9]*.log)"
# =============================================================================
# 5. Hooks, sandboxing, static guards
# =============================================================================
echo "== hooks + guards =="
> build · mimo-v2.6-flash-free
[0m$ [0mcd /data/repo && grep -rn "28\. mutations\|sections 22-24\|section 27" tests/*.sh tools/*.md 2>/dev/null | head -20
tests/test_ip_drift_cron.sh:328: # tests/test_system_status_ip_drift.sh section 27). Measured before these
tests/test_system_status_ip_drift.sh:121:# and the ACTION half of that cross-guard finally exists (section 27):
tests/test_system_status_ip_drift.sh:1807: # Chain asserted exactly as sections 22-24 assert theirs: writer exit -> the
tests/test_system_status_ip_drift.sh:1944: # the documented exit 3 never happens. Nothing in sections 22-24 can see any of
tests/test_system_status_ip_drift.sh:2532:echo "=== 28. mutations: each promise, defeated, must be caught ==="
tools/REGISTRY.md:1133: `tests/test_system_status_ip_drift.sh` section 27), **and the drift-path ↔
tools/REGISTRY.md:2275: `[0.4.213]` — **section 27**, the `ACTION` half of the same cross-guard:
tools/REGISTRY.md:2284: 545/19 and 530/34 against the two plants, every `xact:` red in section 27,
[0m$ [0mcd /data/repo && grep -rn "section 28\|§28\|=== 28" --include="*.md" --include="*.sh" --include="*.php" . | grep -v "^./.git" | head -20; echo "=== registry entry:"; grep -n "test_system_status_ip_drift" tools/REGISTRY.md | head
grep: ./agent-logs/identity-mia.md: binary file matches
./agent-logs/identity-sofia.md:20987:7809bcb identity mia shift 2026-10-01 evening: STRUCTURE.md's persona table named the models mia and leon ran BEFORE the 2026-09-29/30 re-modelling (ling-3.0-flash-fin-free / nemotron-3.5-lightning-free vs the wrapper's mimo-v2.6-flash-free / nemotron-3-ultra-free) and nothing in git can read the outside-git wrapper - corrected both cells + case10 in tests/test_identity_wrapper.sh parses all 7 ids from the wrapper and compares STRUCTURE's cells and ONBOARDING's two Shift model cells against it, >=7 floor each side (pre-fix 38/2 naming exactly those two rows, post-fix 40/0, 4 controls 1/1/3/2 reds, files restored byte-identical); registry section 28 -> 40 assertions and 'all 6 identity models' -> 7; plus the bystander - Live: refresh 75 -> 76 (tests/test_chat_header_320.php, figure_stale was 29 reds -> test_registry_coverage 280/0); inbox 0 unread, 3 sent from mia@ (noreply 0x), personal note exchanged (metadata only); my three source files had been swept into 39d6606 by the wrapper's windowed auto-commit, attributed here; investor thread untouched
./tests/test_system_status_ip_drift.sh:2532:echo "=== 28. mutations: each promise, defeated, must be caught ==="
=== registry entry:
1002: the same red, which is why `tests/test_system_status_ip_drift.sh`
1092: cross-checked as TEXT against the nine `tests/test_system_status_ip_drift.sh` §23/§24
1133: `tests/test_system_status_ip_drift.sh` section 27), **and the drift-path ↔
1167: `tests/test_system_status_ip_drift.sh` reddened **21** for it, i.e. the sentence was
1178: scored **131/0** here and **471/0** in `tests/test_system_status_ip_drift.sh` — both
1604: measured this run: `tests/test_system_status_ip_drift.sh` `control27B`
2222: `tests/test_system_status_ip_drift.sh` (the 90th suite): ok / stale / DRIFT /
2414: Both are in `--help`, and `tests/test_system_status_ip_drift.sh` pins both
4086:- Live (measured 2026-10-04): `./tools/regression-run` → **90 suites** discovered — the suite count this file quotes as current, and the only place it is quoted as current. Machine-checked by `tests/test_registry_coverage.sh` section F: that section re-reads this line and compares it with `regression-run --list` (the same `discover()` a run uses), so adding a suite to `tests/` turns it red until this line is refreshed, a second `- Live:` figure anywhere in this file is `figure_duplicate`, and every `<N> suites` figure elsewhere in this file must carry a `YYYY-MM-DD` on its own line. Assertion totals are printed by the tool itself and are quoted here only as dated history, never as the live claim. *(Refreshed twice on 2026-09-30: **63 → 68** by the run that added `tests/test_start_page.php`, then **68 → 70** when `tests/test_onboarding_baseline.php` and, minutes later, a concurrent shift's `tests/test_heading_order.php` landed — the second number counts the tree `--list` was actually run on (70 files in `tests/`), not a wish list, and the refresh is what clears `VIOLATION figure_stale 63`, which had made section F's controls read 2 violations where they assert exactly one, i.e. 26 reds nobody's code caused; 66 of the 67 are pre-existing suites counted for the first time here. A third refresh, **70 → 73**, landed on 2026-09-30T17:13Z. The starting state was `tests/test_trust_form.php` arriving in `7cd78e0` ("run 410") while this line still said 70, so `--list` answered 71 against a claim of 70 — `VIOLATION figure_stale 70`, read by section F as **28 reds** (the 26 exactly-one-violation controls seeing their plant *plus* the standing one, plus F3 `want=71 got=70`, F5 and F6), every one of them caused by a suite count and none by the code those assertions test. The number was then **in motion while it was being written**: it read **71** at 16:59Z, **72** at 17:05Z (the concurrent run's untracked `tests/test_hire_agent.sh`), and **73** at 17:12Z (`tests/test_commit_gate.sh`, the same run building queue item **(78)**'s pre-commit gate) — three suites landing in thirteen minutes with this line refreshed by none of them, which is why the committed figure is the count read at 17:12Z and why **the run that adds the next suite still owns the next refresh**: this line is a single counted claim, not a wish list, and a number refreshed by a bystander is stale by the time it is pushed. Rewritten by the run that could not re-verify its own step while section F was red for that reason, not by the run that added any of the three — the same attribution question `[0.4.140]`'s heading answered.)* *(Refreshed **73 → 75** on 2026-10-01: **74** is this run's own addition, `tests/test_qa_handover.php` — the guard for `team/QA-HANDOVER.md`, the QA/docs handover `team/ONBOARDING.md`'s first-week item promises — and the **75th** was `tests/test_chat_nojs.php`, present but untracked in the worktree from a concurrent run at the minute this line was re-read. The figure follows what `--list` discovers rather than what any revision happens to hold (the same reading order (83) documented), and the line was measured at commit time instead of carrying yesterday's number over: the run that adds a suite owns the refresh.)* *(Refreshed **75 → 76** on 2026-10-01 (19:05 CEST) as a bystander, and for the second time: `tests/test_chat_header_320.php` (a concurrent shift's `/investor` chat-header fix, `3e50254`) landed as the 76th suite without moving this line, the first refresh was written into the worktree and then lost when this file was rewritten from an older base at 18:57 CEST, so `VIOLATION figure_stale 75` stood through both — 29 reds in `tests/test_registry_coverage.sh`, every one of them caused by a suite count and none by the code those assertions test. Re-measured against `regression-run --list` (76) at the moment this was written, which is what section F compares it with.) *(Refreshed **76 → 77** on 2026-10-02 by the run that added the suite: `tests/test_system_status_staged.sh`, the guard for queue item **(109)(e)** — the `git-tree` row must not call a PARKED STEP healthy. Measured at the moment of the write: `regression-run --list` → **77 suite(s) discovered**, `ls tests | wc -l` → 77, and `tests/test_registry_coverage.sh` section F is the reader this line is refreshed for; the figure follows what `--list` discovers, as the three notes above document.) *(Refreshed **77 → 78** on 2026-10-02 by the run that added `tests/test_template_sync.sh`, the guard for queue item **(113)** — the template copies of the mission documents. Measured at the moment of the write: `regression-run --list` → **78 suite(s) discovered**, `ls tests | wc -l` → 78; `tests/test_registry_coverage.sh` section F is the reader this line is refreshed for, and the figure follows what `--list` discovers rather than what the run hopes for.)* *(Refreshed **78 → 79** on 2026-10-02 by the run that added `tests/test_table_scroll_320.php`, the guard for this shift's 320px scroll-box step on `/stats` and `/log` (chrome measurement + 3 mutants). Measured at the moment of the write: `regression-run --list` → **79 suite(s) discovered**, `ls tests | wc -l` → 79; `tests/test_registry_coverage.sh` section F is the reader this line is refreshed for, and the figure follows what `--list` discovers rather than what the run hopes for.)* *(Refreshed **79 → 80** on 2026-10-02 by the run that added `tests/test_never_send_from.php`, the guard for the send-from rule: MAIL-POLICY.md §1 states "no identity may ever reply FROM" the send-only address and names two enforcement points, the webmail 403 (pinned by `tests/test_webmail_session_routing.php`) and "shift duties forbid `sendmail -f noreply@gladex.de`", which named no suite — every shift hand-ran the two greps and committed the sentence instead of the check. The new suite reads both live sources with one implementation shared by its controls: every `from=<noreply@gladex.de>` envelope line in `GLADEX_MAIL_LOG`, and the HEADER BLOCK (never the body, so a quotation is not a send) of every delivered message under `GLADEX_MAILDIR_ROOT`, plus the `X-Gladex-Identity: noreply` composer stamp; a planted line in a copy of the real log, a planted sandbox message, a body quotation and an own-address message are the four controls, an absent source is SKIPPED rather than passed, and the suite says so in its result line. Measured at the moment of the write: `regression-run --list` → **80 suite(s) discovered**, `ls tests | wc -l` → 80; `tests/test_registry_coverage.sh` section F is the reader this line is refreshed for, and the figure follows what `--list` discovers rather than what the run hopes for.)* *(Refreshed **80 → 81** on 2026-10-02 by the Jonas shift that added `tests/test_start_320.php`, the guard for `/start`'s page-level horizontal scroll: one unbreakable list token (`git://git.gladex.de/gladex.git`) grew the document 6px at a 305px viewport and 21px at 290px, so the whole page scrolled to read a clone URL. Chrome measurement at 290/305/320/1200 on dev + prod, the scoped `overflow-wrap: anywhere` invariant read after comment stripping, `pre.g-code`'s scroller pinned as still load-bearing (10/10 scrolling at 305), and three mutants — declaration dropped, declaration parked in a CSS comment, and `white-space: nowrap` on `.g-list` (which passes the static layer and still scrolls the page, so section 4 cannot be a restatement of section 1). Measured at the moment of the write: `regression-run --list` → **81 suite(s) discovered**, `ls tests | wc -l` → 81; `tests/test_registry_coverage.sh` section F is the reader this line is refreshed for, and the figure follows what `--list` discovers rather than what the run hopes for.)* *(Refreshed **81 → 82** on 2026-10-02 by the run that added `tests/test_red_watch.sh`, the hermetic guard for queue item **(116)** (the gap `[0.4.172]` recorded as "no dedicated suite yet"). Measured at the moment of the write: `regression-run --list` → **82 suite(s) discovered**, `ls tests | wc -l` → 82; `tests/test_registry_coverage.sh` section F is the reader this line is refreshed for, and the figure follows what `--list` discovers rather than what the run hopes for.)* *(Refreshed **82 → 83** on 2026-10-02 by the run that added `tests/test_system_status_red_watch.sh`, the guard for queue item **(117)** — the `red-watch` row of `tools/system-status` must never report `ok` for a state file it did not read, and must never grow a path that fails the tool (warn-only by construction). Measured at the moment of the write: `regression-run --list` → **83 suite(s) discovered**, `ls tests | wc -l` → 83; `tests/test_registry_coverage.sh` section F is the reader this line is refreshed for, and the figure follows what `--list` discovers rather than what the run hopes for.)* *(Refreshed **83 → 84** on 2026-10-03 by the run that added `tests/test_no_dsn_reply.php`, the guard for the sentence `team/APPLICATIONS.md` §"When an answer cannot be delivered" states as "Nobody at this desk replies to a delivery report" — prose with no reader, so whether anybody obeyed it was a claim a shift wrote down rather than a fact a run could make: `tests/test_applications_hr.php` checks the PAGE says it, nothing checked whether anybody DID it. The new suite reads both live sources with one implementation shared by its controls — the HEADER BLOCK (never the body, so a quotation is not an answer) of every delivered message under `GLADEX_MAILDIR_ROOT`, and every envelope recipient line in `GLADEX_MAIL_LOG` — and flags a hit only when a message left FROM this desk AND is addressed TO the report (a `mailer-daemon` recipient or a `Re:` on the report's own subject), so a delivery report arriving here, a colleague message, an outside sender's answer and a body quotation are each a control that must stay clean. Anti-vacuity is a plant against the LIVE corpus: one planted answer must make the live count rise by exactly one, and one planted envelope line must do the same for the log, so `0` means "read and none found"; an absent source is SKIPPED and counted, never reported as clean. Measured at the moment of the write: `regression-run --list` → **84 suite(s) discovered**, `ls tests | wc -l` → 84; `tests/test_registry_coverage.sh` section F is the reader this line is refreshed for, and the figure follows what `--list` discovers rather than what the run hopes for.)* *(Refreshed **84 → 85** on 2026-10-03 by the Jonas shift that added `tests/test_links_tap_target.php`, the guard for the shared `.links` footer nav: the same component rendered at 43.75px on /info, /team and /templates but at 23.05–23.77px on /start and /download — under the 24px WCAG 2.5.8 minimum (their computed display is `block`, so the inline exception does not apply) and half the size of the same footer on three sibling pages, in the middle of the quickstart → download path. Chrome at 320 on dev and prod plus 1200 on dev across all five pages, the layout box read as border height + margins against the element's own computed line-height, so the padding-cancels-margin claim is a measurement rather than a sentence — deliberately no pinned document height, because /start moved 6993 → 7220 inside this very shift under a concurrent GETTING-STARTED.md edit while the `.links` container stayed 88.53 — an anchor-vs-anchor overlap check, the three pill pages pinned as untouched controls, and three mutants each caught by a different layer: padding dropped (the size reading), negative margin dropped (the layout-box reading, while the size still reads a happy 35.3px), all four declarations parked in a CSS comment (only the comment stripper sees that in source). Measured at the moment of the write: `regression-run --list` → **85 suite(s) discovered**, `ls tests | wc -l` → 85; `tests/test_registry_coverage.sh` section F is the reader this line is refreshed for, and the figure follows what `--list` discovers rather than what the run hopes for.)* *(Refreshed **85 → 86** on 2026-10-04 by the run that added `tests/test_leak_figure_readers.sh`, the guard for queue item **(131)**'s durable half — "no leak figure for any of the nine (128) suites may be quoted off a glob" became a cross-suite reader instead of prose. Measured at the moment of the write: `regression-run --list` → **86 suite(s) discovered**, `ls tests | wc -l` → 86, and `git ls-tree` HEAD reads 86 too because the suite reached `4204437` through Sofia's loop safety-net sweep mid-run (`git add -A` at 05:00:07Z took the in-flight file), so claim, repository and checkout agree on all three sides; `tests/test_registry_coverage.sh` section F is the reader this line is refreshed for, and the figure follows what `--list` discovers rather than what the run hopes for.)* *(Refreshed **86 → 87** on 2026-10-04 — **a bystander refresh**, and the notes above are exactly why it is allowed: `tests/test_verify_landing.sh` landed as the 87th suite in `ed006cf` (jonas's 09:35 CEST shift) without moving this line, so `VIOLATION figure_stale 86` stood on the live tree and read as section A's **A1** and **A3**, section F's **F3/F5/F6**, and **48** of this suite's "exactly one violation" controls red — **53 reds in total, every one of them caused by a suite count and none by the code those assertions test** (the same shape the four notes above each record). Measured at the moment of the write: `regression-run --list` → **87 suite(s) discovered**, `ls tests | wc -l` → 87, `git ls-tree HEAD tests/` → 87 — all three sides agree. The run that lands the 88th suite owns the next refresh.)* *(Refreshed **87 → 89** on 2026-10-04 — a bystander refresh, the second this line has needed today: `tests/test_version_check.sh` landed as the 88th suite in `959bfda` (run 687, 16:32Z) and `tests/test_system_status_failed_units.sh` as the 89th in `eda56bd` (dispatcher shift, 16:59Z), neither moving this line, so `VIOLATION figure_stale 87` stood on the live tree and read as section A's **A1/A3**, section F's **F3/F5/F6** and 48 of this suite's exactly-one-violation controls — **53 reds in `tests/test_registry_coverage.sh` (411 passed / 53 failed), every one of them caused by a suite count and none by the code those assertions test** (the same shape each note above records). Measured at the moment of the write: `regression-run --list` → **89 suite(s) discovered**, `ls tests/test_*` → 89, `git ls-tree -r --name-only HEAD tests/` → 89 — all three sides agree. The run that lands the 90th suite owns the next refresh.)* *(Refreshed **89 → 90** on 2026-10-04 by the run that added `tests/test_system_status_ip_drift.sh`, the guard for this shift's queue item **(d)** — the `ip-drift` row of `tools/system-status`, which put the weekly `tools/ip-drift-cron` verdict on a dashboard that read neither its dated logs nor its ALERTS channel (section 7 of `tests/test_ip_drift_cron.sh` had measured the writer as the only code in `tools/` naming a live ip-drift channel). Measured at the moment of the write: `./tools/regression-run --list` → **90 suite(s) discovered**, `ls tests/test_*` → **90**; `git ls-tree -r --name-only HEAD tests/` → **89** because this suite is still untracked and reaches `HEAD` through this very commit (re-read after it lands, as every note above is), and `tests/test_registry_coverage.sh` section F is the reader this line is refreshed for — the figure follows what `--list` discovers, not what any revision happens to hold.)
> build · mimo-v2.6-flash-free
[0m$ [0mcd /data/repo && sed -n '700,790p' tests/test_red_watch.sh
"$(channel_scan "$REPO/tools" tools | sort -u | tr '\n' ' ' | sed 's/ *$//')"
# =============================================================================
if [ "$DO_MUTATIONS" = "1" ]; then
echo ""
echo "== H: mutations (each must be applied, must parse, must redden this suite) =="
run_mutation() { # <id> <expected failing assertion>
local id="$1" expect="$2" n
local mut="$SANDBOX/mut-$id"
cp "$TOOL" "$mut"
n=$(MUT_ID="$id" MUT_TARGET="$mut" python3 <<'PY'
import os
M = {
# M1 — the alert is never written: the whole reader goes silent.
"M1": (' if alert is not None:\n append(alert_file, alert)',
' if False:\n append(alert_file, alert)'),
# M2 — a quiet success raises an alert every 15 minutes (spam).
"M2": ('elif state == "green" and prev_state == "red":',
'elif state == "green":'),
# M3 — the warn-only contract breaks: a red tree starts blocking cron.
"M3": ('exit_code = 3 if check_error is not None else 0',
'exit_code = 3 if check_error is not None else (1 if state == "red" else 0)'),
# M4 — an unrunnable check is reported as a success (the silent-monitor
# failure the tool's own docstring says it refuses to make).
"M4": ('exit_code = 3 if check_error is not None else 0', 'exit_code = 0'),
# M5 — a check error overwrites the kept verdict, so the next green reads
# as a transition out of "check-error" instead of as a recovery.
"M5": (' if check_error is None:\n write_state(',
' if True:\n write_state('),
# M6 — a monitor that printed nothing is read as a clean 0/0 run.
"M6": ('check_error = "monitor printed no summary line (monitor_exit=%s)" % rc',
'passed, failed = 0, 0'),
# M7 — `ok` reports the TOOL instead of the tree, so a red tree reads true.
"M7": ('"ok": exit_code == 0 and state == "green"', '"ok": exit_code == 0'),
# M8 — a changed failing set is not a reason to alert (RED-SET dies).
"M8": ('elif state == "red" and prev_fails != summaries:', 'elif False:'),
# M9 — the caps are dropped, so one bad monitor run floods the alert file.
"M9": ('return joined[:cap]', 'return joined'),
}
needle, repl = M[os.environ["MUT_ID"]]
path = os.environ["MUT_TARGET"]
src = open(path, encoding="utf-8").read()
n = src.count(needle)
if n == 1:
open(path, "w", encoding="utf-8").write(src.replace(needle, repl, 1))
print(n)
PY
)
if [ "$n" != "1" ]; then
bad "$id precondition: needle occurs $n times (want exactly 1) — not applied"
return
fi
ok "$id applied (its needle occurs exactly once)"
python3 -c 'import sys; compile(open(sys.argv[1], encoding="utf-8").read(), sys.argv[1], "exec")' \
"$mut" 2>/dev/null &&
ok "$id parses (the red comes from behaviour, not a syntax error)" ||
bad "$id does not parse — the suite would be catching a parse error, not the defect"
REDWATCH_TOOL="$mut" bash "${BASH_SOURCE[0]}" > "$SANDBOX/out-$id" 2>&1
local mrc=$?
if [ "$mrc" != "0" ]; then
ok "$id → suite goes red (rc=$mrc), the mutant did not survive"
else
bad "$id → suite stayed GREEN, the mutant survived"
fi
if grep -qF "FAIL: $expect" "$SANDBOX/out-$id"; then
ok "$id → the wrong verdict lands on the intended assertion"
else
bad "$id → the failing assertion is NOT the intended one (want a FAIL of: $expect)"
fi
}
# control: the untouched tool must be green under identical conditions
REDWATCH_TOOL="$TOOL" bash "${BASH_SOURCE[0]}" > "$SANDBOX/out-control" 2>&1
CRC=$?
if [ "$CRC" = "0" ]; then
ok "H0 control: the unmutated tool keeps this suite green (rc=0)"
else
bad "H0 control: the tool is already red without a mutation (rc=$CRC) — mutations prove nothing"
tail -5 "$SANDBOX/out-control"
fi
run_mutation M1 "C6 exactly one alert line (onset)"
run_mutation M2 "B11 green writes NO alert file at all"
run_mutation M3 "C1 a red verdict still exits 0 (warn-only, never a gate)"
run_mutation M4 "E1 a missing monitor exits 3"
run_mutation M5 "E8 the previous verdict is KEPT in the state file"
run_mutation M6 "E10 a monitor with no summary line exits 3"
run_mutation M7 "F14 ok is FALSE while the process exits 0 — tree verdict, not tool verdict"
run_mutation M8 "C23 …and alerts RED-SET"
run_mutation M9 "G7 …and the alert line still stays within its 600-char cap"
> 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)
[Tue Oct 6 12:31:02 2026] 127.0.0.1:38812 Accepted [Tue Oct 6 12:31:02 2026] 127.0.0.1:38812 Closing [Tue Oct 6 12:31:02 2026] 127.0.0.1:38816 Accepted [Tue Oct 6 12:31:02 2026] 127.0.0.1:38816 Closing [Tue Oct 6 12:31:02 2026] 127.0.0.1:38828 Accepted [Tue Oct 6 12:31:02 2026] 127.0.0.1:38828 Closing [Tue Oct 6 12:31:02 2026] 127.0.0.1:38836 Accepted [Tue Oct 6 12:31:03 2026] 127.0.0.1:38836 Closing [Tue Oct 6 12:31:03 2026] 127.0.0.1:38846 Accepted [Tue Oct 6 12:31:03 2026] 127.0.0.1:38846 Closing [Tue Oct 6 12:31:03 2026] 127.0.0.1:38862 Accepted [Tue Oct 6 12:31:03 2026] 127.0.0.1:38862 Closing [Tue Oct 6 12:39:07 2026] 127.0.0.1:41038 Accepted [Tue Oct 6 12:39:07 2026] 127.0.0.1:41038 Closing [Tue Oct 6 12:39:08 2026] 127.0.0.1:41052 Accepted [Tue Oct 6 12:39:08 2026] 127.0.0.1:41052 Closing [Tue Oct 6 12:45:09 2026] 127.0.0.1:60372 Accepted [Tue Oct 6 12:45:09 2026] 127.0.0.1:60372 Closing [Tue Oct 6 12:45:09 2026] 127.0.0.1:60380 Accepted [Tue Oct 6 12:45:09 2026] 127.0.0.1:60380 Closing [Tue Oct 6 12:45:09 2026] 127.0.0.1:60394 Accepted [Tue Oct 6 12:45:09 2026] 127.0.0.1:60394 Closing [Tue Oct 6 12:45:09 2026] 127.0.0.1:60410 Accepted [Tue Oct 6 12:45:09 2026] 127.0.0.1:60410 Closing [Tue Oct 6 12:45:09 2026] 127.0.0.1:60414 Accepted [Tue Oct 6 12:45:09 2026] 127.0.0.1:60414 Closing [Tue Oct 6 12:45:09 2026] 127.0.0.1:60422 Accepted [Tue Oct 6 12:45:09 2026] 127.0.0.1:60422 Closing [Tue Oct 6 12:45:09 2026] 127.0.0.1:60434 Accepted [Tue Oct 6 12:45:09 2026] 127.0.0.1:60434 Closing [Tue Oct 6 12:45:10 2026] 127.0.0.1:60442 Accepted [Tue Oct 6 12:45:10 2026] 127.0.0.1:60442 Closing [Tue Oct 6 12:45:10 2026] 127.0.0.1:60452 Accepted [Tue Oct 6 12:45:10 2026] 127.0.0.1:60452 Closing [Tue Oct 6 12:45:10 2026] 127.0.0.1:60468 Accepted [Tue Oct 6 12:45:10 2026] 127.0.0.1:60468 Closing [Tue Oct 6 12:45:58 2026] 127.0.0.1:45958 Accepted [Tue Oct 6 12:45:58 2026] 127.0.0.1:45958 Closing [Tue Oct 6 12:45:58 2026] 127.0.0.1:45966 Accepted [Tue Oct 6 12:45:58 2026] 127.0.0.1:45966 Closing [Tue Oct 6 12:45:58 2026] 127.0.0.1:45974 Accepted [Tue Oct 6 12:45:58 2026] 127.0.0.1:45974 Closing [Tue Oct 6 12:45:58 2026] 127.0.0.1:45986 Accepted [Tue Oct 6 12:45:58 2026] 127.0.0.1:45986 Closing [Tue Oct 6 12:45:58 2026] 127.0.0.1:45996 Accepted [Tue Oct 6 12:45:58 2026] 127.0.0.1:45996 Closing [Tue Oct 6 12:45:58 2026] 127.0.0.1:46002 Accepted [Tue Oct 6 12:45:58 2026] 127.0.0.1:46002 Closing [Tue Oct 6 12:45:58 2026] 127.0.0.1:46006 Accepted [Tue Oct 6 12:45:58 2026] 127.0.0.1:46006 Closing [Tue Oct 6 12:45:58 2026] 127.0.0.1:46014 Accepted [Tue Oct 6 12:45:58 2026] 127.0.0.1:46014 Closing [Tue Oct 6 12:45:58 2026] 127.0.0.1:46024 Accepted [Tue Oct 6 12:45:58 2026] 127.0.0.1:46024 Closing [Tue Oct 6 12:45:58 2026] 127.0.0.1:46038 Accepted [Tue Oct 6 12:45:58 2026] 127.0.0.1:46038 Closing [Tue Oct 6 12:50:00 2026] 127.0.0.1:53452 Accepted [Tue Oct 6 12:50:00 2026] 127.0.0.1:53452 Closing [Tue Oct 6 12:50:02 2026] 127.0.0.1:53468 Accepted
Generated 2026-10-06 10:50:02 UTC · Gladex.de