Gladex Agent Logs

Agent run logs & app logs · env: prod · LAN-only investor surface

Overview
Run logs455 files, 11.9 MB
Latest run logrun-20260925-022932-53.log
Log directory/data/agent-logs
App log directory/opt/startup/prod/logs
Run logs (newest first, last 50)
FileSizeModified (UTC)
run-20260925-022932-53.log 166 KB 2026-09-25 00:53:34
run-20260925-012106-52.log 297 KB 2026-09-25 00:19:32
run-20260925-003542-51.log 153 KB 2026-09-24 23:11:06
run-20260924-234828-50.log 204 KB 2026-09-24 22:25:42
run-20260924-230237-49.log 303 KB 2026-09-24 21:38:28
run-20260924-222340-48.log 206 KB 2026-09-24 20:52:37
run-20260924-215353-47.log 146 KB 2026-09-24 20:13:40
run-20260924-210315-46.log 182 KB 2026-09-24 19:43:53
run-20260924-200755-45.log 181 KB 2026-09-24 18:53:15
run-20260924-192844-44.log 133 KB 2026-09-24 17:57:55
run-20260924-182059-43.log 227 KB 2026-09-24 17:18:44
run-20260924-164658-42.log 181 KB 2026-09-24 16:10:59
run-20260924-160206-41.log 101 KB 2026-09-24 14:36:58
run-20260924-153643-40.log 127 KB 2026-09-24 13:52:05
run-20260924-151001-39.log 130 KB 2026-09-24 13:26:43
run-20260924-144921-38.log 90 KB 2026-09-24 13:00:01
run-20260924-143001-37.log 63 KB 2026-09-24 12:39:21
run-20260924-141012-36.log 106 KB 2026-09-24 12:20:01
run-20260924-135151-35.log 75 KB 2026-09-24 12:00:12
run-20260924-133211-34.log 116 KB 2026-09-24 11:41:51
run-20260924-130932-33.log 67 KB 2026-09-24 11:22:11
run-20260924-115831-32.log 260 KB 2026-09-24 10:59:32
run-20260924-111405-31.log 117 KB 2026-09-24 09:48:31
run-20260924-102752-30.log 106 KB 2026-09-24 09:04:05
run-20260924-100538-29.log 81 KB 2026-09-24 08:17:52
run-20260924-092904-28.log 101 KB 2026-09-24 07:55:38
run-20260924-083526-27.log 116 KB 2026-09-24 07:19:04
run-20260924-080136-26.log 75 KB 2026-09-24 06:25:26
run-20260924-074910-25.log 23 KB 2026-09-24 05:51:36
run-20260924-072601-24.log 52 KB 2026-09-24 05:39:10
run-20260924-065657-23.log 156 KB 2026-09-24 05:16:01
run-20260924-063310-22.log 76 KB 2026-09-24 04:46:57
run-20260924-055309-21.log 107 KB 2026-09-24 04:23:10
run-20260924-052831-20.log 133 KB 2026-09-24 03:43:09
run-20260924-050107-19.log 69 KB 2026-09-24 03:18:31
run-20260924-044831-18.log 34 KB 2026-09-24 02:51:07
run-20260924-041948-17.log 156 KB 2026-09-24 02:38:31
run-20260924-035438-16.log 185 KB 2026-09-24 02:09:48
run-20260924-033002-15.log 245 KB 2026-09-24 01:44:38
run-20260924-025910-14.log 120 KB 2026-09-24 01:20:02
run-20260924-023430-13.log 97 KB 2026-09-24 00:49:10
run-20260924-015458-12.log 255 KB 2026-09-24 00:24:30
run-20260924-012751-11.log 179 KB 2026-09-23 23:44:58
run-20260924-005036-10.log 162 KB 2026-09-23 23:17:51
run-20260924-000545-9.log 217 KB 2026-09-23 22:40:36
run-20260923-235334-8.log 29 KB 2026-09-23 21:55:45
run-20260923-233751-7.log 97 KB 2026-09-23 21:43:34
run-20260923-231451-6.log 165 KB 2026-09-23 21:27:51
run-20260923-225120-5.log 58 KB 2026-09-23 21:04:51
run-20260923-222610-4.log 230 KB 2026-09-23 20:41:20
Tail — run-20260924-164658-42.log (last 200 lines)
- **Ledger corrected** (`BUDGET.md`): spent-line now reads `registered + DNS live since
  2026-09-20`; **log history kept verbatim** (dated entries are history, not lies) with
  two appended entries recording the same-day completion evidence and this correction;
  **numbers unchanged: 5.00 / 1.50 / 3.50, no money moved**. Stale copies fixed in
  REPORT.md (rank table + budget table), PRODUCT.md (cost table), REGISTRY example.
- **Test-first evidence**: `tests/test_budget_show.sh` written first → run against the
  unfixed tool → **13 passed / 33 failed** (every failure = a real defect: no hook,
  prose spent, zero validation, undocumented help). After fix → **49/49 green**.
  **2 mutations**, both precondition-asserted: M1 validation gate defeated (`if errors:`
  occurs once → mutant wrongly exits 0), M2 `_math_ok` forced true (fixed-string replace
  — line contains `[`/`]`, sed BRE mangled it; precondition asserted exactly once AND
  re-checked surgical: mutant still rejects the empty ledger, still accepts the valid one).
- **Hermetic**: fixtures in throwaway sandbox under `${TMPDIR:-/tmp}/opencode/`,
  trap-cleaned; only case L reads the real ledger (read-only) and it pins **no amounts**
  — content-agnostic so a future investor-approved purchase can't spuriously redden it,
  but it now *proves the real ledger parses, balances and is non-negative every run*.
- **Full regression, 22 suites green — 746 assertions, 0 failed**: shell: budget 49 +
  dns_verify 40 + identity 28 + promote_gate 67 + smtp_relay 38 + source_sync 30 +
  mx_soa 31 + unread 24 = **307**; PHP: 14 suites = **439**.
- **Live**: `budget-show` human+json → 0 (corrected note visible, `spent:"1.50"` numeric);
  via `/data/tools` link → 0; **dev :8000 + prod :8001 `/api/budget` both return the
  corrected ledger** (PHP parses independently: spent 1.5, remaining 3.5, 4 allocations);
  `/budget` + `/healthz` → 200 on both; `system-status` → ALL SYSTEMS HEALTHY rc 0;
  `source-sync-check` → rc 0.
- **Docs**: REGISTRY §budget-show rewritten (contract, hook, exit-code nuance, 49-assertion
  suite + mutations — its old "Tests" section was three manual live commands).
- **Money/scope**: model spend **0.00** (`*-free` only), no purchase, no DNS write, no
  secret read, no service restart, **prod untouched** (PHP sources unmodified this run —
  only the ledger data file they read changed, via the repo path they share).
- **Next-candidate queued, not actioned**: (1) NEEDS-INVESTOR **#57 A/B/C public-gating**
  + **SOA MNAME** — both still awaiting investor/operator, cannot proceed unilaterally;
  (2) LE certs on :465/:993/submission via certbot (operator's next-step list);
  (3) TEAM-page public verification is live (200 + names) — last run confirmed.
> build · mimo-v2.6-flash-free
$ cd /data/repo && grep -n '^## ' agent-logs/PROGRESS.md | tail -8 && grep -n '^### ' agent-logs/PROGRESS.md | tail -8
4014:## 2026-09-24T11:40Z run-34 — STEP 0 investor check + promote now ships (and staleness-checks) every tree the envs serve
4088:## Run 2026-09-24T12:00Z — promote: dev-sync gate (exit 6) closes the repo→prod gap
4146:## Run 2026-09-24T12:18Z — STEP 0 clear + queued stdout/stderr ordering fix (promote gate)
4223:## 2026-09-24T~13:00Z main-loop run — STEP 0 clear; hermetic case4 (identity suite) landed
4271:## 2026-09-24T13:00Z main-loop run — STEP 0 clear; symlink repo-root fix landed (candidate (1) DONE, both affected tools)
4331:## 2026-09-24T14:10Z main-loop run — STEP 0 clear; system-status exit-code collision fixed (queued candidate (2) DONE)
4393:## 2026-09-24T13:50Z main-loop run — STEP 0 clear; dns-verify's documented exit-code/`--timeout`/type contract was fiction — fixed + first test suite (40 assertions)
4481:## 2026-09-24T14:35Z main-loop run — STEP 0 clear; queued candidate (1) DONE: stale BUDGET.md narrative corrected + budget-show's first test suite (49 assertions, 2 mutations)
50:### Go CLI (`app/src/go/`)
71:### MCP Server Production Features
81:### Investor App (PHP+SQLite)
98:### VS Code Extension
1691:### Item 1 — TEAM route: **DONE** (commit: this one)
1707:### Item 2 — TLS / Let's Encrypt: **DONE** (commit: this one)
1738:### Item 3 — WEBMAIL mailbox tab: **DONE** (commit: this one) — with one escalated pre-existing caveat
1794:### Item 4 — POSTFIX :25 greeting bug: **DONE** (commit: this one)
> build · mimo-v2.6-flash-free
STEP 0 re-verify (both DBs + INBOX) before writing the entry:
$ for db in /opt/startup/dev/data/messages.db /opt/startup/prod/data/messages.db; do
  n=$(sqlite3 "$db" "SELECT count(*) FROM messages WHERE direction='investor_to_agent' AND read=0;" 2>&1)
  echo "$db unread_investor_to_agent=$n"
done
grep -c 'HANDLED' /data/repo/INBOX.md; grep -ciE '^\s*- \[ \]|UNHANDLED' /data/repo/INBOX.md || true
date -u +%Y-%m-%dT%H:%MZ
/opt/startup/dev/data/messages.db unread_investor_to_agent=0
/opt/startup/prod/data/messages.db unread_investor_to_agent=0
67
0
2026-09-24T16:09Z
> build · mimo-v2.6-flash-free
STEP 0 clear (0 unread both DBs, 0 unhandled inbox). Appending the run entry:
$ cd /data/repo && cat >> agent-logs/PROGRESS.md <<'EOF'

## 2026-09-24T16:09Z main-loop run — STEP 0 clear; tls-check's exit-code collapse + undescribed rejects + 5-of-7 defaults fixed — first test suite (99 assertions, 4 mutations)

- **STEP 0**: 0 unread `investor_to_agent` on dev AND prod; `INBOX.md` 0 unhandled
  (67 HANDLED marks). No investor replies owed this run.
- **Step taken** (queued-defect class, 4th of its kind: documented contract not honored,
  zero tests — after system-status, dns-verify, budget-show): hardened
  `tools/tls-check`, the Phase-0 core tool whose REGISTRY entry documented fiction.
- **Defects closed** (all proven by the pre-fix run: **15 passed / 84 failed**):
  (1) **exit-code collapse** — DNS failure, refused, handshake timeout, hostname
  mismatch, untrusted chain and an *expired cert* all exited 3 ("Cert not found"), so
  no caller could tell "never reached a server" from "reached it, cert is bad"; now
  **3 = unreachable / 4 = expiring / 5 = rejected** with `reject_reason` ∈
  expired|hostname_mismatch|untrusted. (2) **rejected certs were undescribed** —
  verification inside the handshake returned cn=null/sans=[]/dates=null for every
  reject (an expired cert was indistinguishable from no cert); now a `CERT_NONE`
  reconnect + offline DER parse fills cn/sans/dates/`days_remaining`/`covers_name`
  even on the reject path. (3) **defaults were 5 of the cert's 7 SANs** —
  `log.gladex.de` and `team.gladex.de` (the investor's team page!) were silently
  unchecked by a bare `tls-check`; defaults now = all 7 SANs (AST-pinned in the
  suite, cross-checked live). (4) dead `args.domains == ["--help"]` branch removed;
  no `--port` existed so the LE certs on :465/:993 were uncheckable — added
  `--port`/`--timeout`/`--ca-file` (timeout also now bounds DNS/refused paths;
  was a hardcoded 5s `settimeout`). Arg validation hardened: bad port/timeout/
  warn-days/ca-file(missing|dir|garbage)/domain syntax (empty labels, >63-char
  label, spaces) → exit 2; help epilog documents all 6 exit codes.
- **Test-first evidence**: `tests/test_tls_check.sh` written first → run against the
  unfixed tool → **15/84** (every failure maps 1:1 to the four defects — unknown
  flags fell into arg-validation exit 2, hardcoded 5 domains, rejects as 3,
  undescribed JSON, dead branch). After fix → **99/99 green**. **4 mutations**, each
  precondition-asserted (the target must occur exactly once) AND surgical-checked
  (mutant must still pass a valid cert): M1 folds rejects back into exit 3, M2 makes
  `--timeout` a no-op (mutant waited 3085ms ≥ 3000), M3 defeats expired
  classification (reason reclassifies to untrusted, exit stays 5), M4 drops
  team.gladex.de from defaults (AST flags BAD, other 6 kept).
- **Hermetic**: certs minted in-sandbox (cryptography lib), 6 local TLS servers on
  127.0.0.1 ephemeral ports (incl. a hang server that accepts and never handshakes);
  only the DNS case touches .invalid (fails locally, asserts status only, no error
  text — wildcard-resolver-proof) and case L reads live gladex.de (apex + :993 +
  :465) pinning **no expiring dates**.
- **Full regression, 23 suites green, 0 failed**: 9 shell suites (budget, dns 40,
  identity, promote, smtp 38, source-sync, mx_soa 31, unread 24, **tls 99**) + 14 PHP
  suites (539 assertions counted) — no other suite touched.
- **Live**: default human run → 7 domains OK rc 0 (all expiring 2026-12-22, 89d);
  `--warn-days 90` → rc 4 (documented example still true); `--port 993` → OK rc 0;
  `.invalid` → UNREACHABLE rc 3 (distinct from rejected ✓); `system-status` →
  ALL SYSTEMS HEALTHY rc 0; `source-sync-check` → in sync rc 0.
- **Docs**: REGISTRY §tls-check rewritten (old entry claimed "3 = Cert not found or
  expired" and "5 domains" — both were the fiction); PROGRESS entry (this one).
- **Safety**: model spend **0.00** (`*-free` only), no secrets in prompts/commits,
  no DNS write, no service restart, **prod untouched**. Exit-code change is safe for
  callers: repo-wide + system-cron/systemd/opt-startup grep found **no callers** of
  tls-check (system-status does its own inline openssl check).
- **Still blocked (investor-owned)**: NEEDS-INVESTOR **#57 public investor-route
  gating A/B/C** and **SOA MNAME** (`a.misconfigured.dns.server.invalid.`) — cannot
  proceed unilaterally. LE mail-port certs verified done this cycle (certbot
  dry-run renew succeeded, deploy hooks + timer active, MX propagated).
EOF
echo appended; tail -3 agent-logs/PROGRESS.md
appended
  gating A/B/C** and **SOA MNAME** (`a.misconfigured.dns.server.invalid.`) — cannot
  proceed unilaterally. LE mail-port certs verified done this cycle (certbot
  dry-run renew succeeded, deploy hooks + timer active, MX propagated).
> build · mimo-v2.6-flash-free
Final STEP-0 check at commit time, then commit and push:
$ for db in /opt/startup/dev/data/messages.db /opt/startup/prod/data/messages.db; do sqlite3 "$db" "SELECT count(*) FROM messages WHERE direction='investor_to_agent' AND read=0;"; done
cd /data/repo && git status --porcelain
0
0
 M agent-logs/PROGRESS.md
 M tools/REGISTRY.md
 M tools/tls-check
?? tests/test_tls_check.sh
> build · mimo-v2.6-flash-free
$ cd /data/repo && git log --oneline -6 && git add -A && git commit -m "tls-check: split exit 3/4/5 (unreachable vs expiring vs rejected), describe rejected certs, default to all 7 cert SANs; add first test suite (99 assertions, 4 mutations)

- exit-code collapse fixed: DNS/refused/timeout=3, expiring=4, rejected=5 with
  reject_reason (expired|hostname_mismatch|untrusted) — all six paths used to exit 3
- rejected certs now described (CERT_NONE reconnect + offline DER parse): cn, sans,
  dates, days_remaining, covers_name survive the reject path (was cn=null everywhere)
- defaults were 5 of 7 SANs: log.* and team.* were silently unchecked; now all 7,
  AST-pinned by the suite
- new --port/--timeout/--ca-file; timeout bounds connect+handshake (was hardcoded 5s);
  arg validation hardened (bad port/timeout/warn-days/ca-file/domain -> exit 2);
  dead args.domains==[\"--help\"] branch removed; help epilog documents all exit codes
- tests/test_tls_check.sh: pre-fix 15/84 -> post-fix 99/99; 4 precondition+ surgical
  mutations; hermetic local TLS fixtures + one live case (no expiring dates pinned)
- REGISTRY section rewritten (old entry documented the fiction); no callers found in
  repo/cron/systemd (system-status has its own inline check) — exit change is safe" && git log --oneline -1
c492278 fix: the spend ledger told a stale story and budget-show proved incapable of noticing — BUDGET.md still said 'purchase pending at registrar' + log line 'Awaiting registrar purchase + DNS creds' four days after RDAP showed gladex.de status active (changed 2026-09-20T11:10:57+02:00) and dig verified A/NS/MX live (evidence already in DOMAIN.md), with the same stale claim copied into REPORT.md x2, PRODUCT.md and the REGISTRY example output — the ONE file the investor reads for spend was wrong while its numbers were right (5.00/1.50/3.50); and budget-show, the tool that displays it, had ZERO tests and exited 0 with null JSON on EVERY form of broken ledger (empty file, missing remaining:, non-numeric amount, math mismatch allowance-spent!=remaining, overspend remaining<0, bad month, missing file) despite REGISTRY documenting '1 - General error (file not found, parse error)' — the same documented-contract-is-fiction class as the dns-verify/system-status fixes; fix: budget-show now validates the ledger BEFORE display (month YYYY-MM, bare numeric allowance/remaining, spent: numeric-prefix with prose split into new spent_note so JSON spent is NUMERIC — it was prose, so float(d[spent]) crashed any consumer, allowance-spent==remaining at 2dp, remaining>=0 per brief §4 overspend ban) with each violation naming its field on stderr, exit 1, stdout empty so machines key off the exit code, human format glues € to the amount (was '1.50 (...) €'), documented GLADEX_BUDGET_PATH test hook added (path was hardcoded to /data/repo/BUDGET.md = untestable without editing the live ledger), --help now documents hook+exit codes; ledger corrected: spent-line reads 'registered + DNS live since 2026-09-20', dated log history kept VERBATIM (dated entries are history) with two appended entries recording same-day completion evidence + this correction, numbers unchanged, no money moved; stale copies fixed in REPORT.md rank+budget tables, PRODUCT.md cost table, REGISTRY example; test-first: new tests/test_budget_show.sh run against the UNFIXED tool -> 13 passed/33 failed with every failure being exactly these defects (no hook honored = fixture assertions saw the live file, prose spent, all-invalid-ledgers exiting 0, help missing hook/exit codes), after fix 49/49 green; 2 mutations precondition-asserted: M1 'if errors:' gate defeated (occurs once -> mutant wrongly exits 0) and M2 _math_ok forced true via fixed-string python replace (the line contains [ ] which mangled a sed BRE attempt; precondition asserts exactly once, and post-checks prove it surgical: mutant still rejects the empty ledger AND still accepts the valid one); hermetic: fixtures in trap-cleaned sandbox under ${TMPDIR:-/tmp}/opencode, only case L reads the real ledger and it pins NO amounts (content-agnostic so a future approved purchase cannot spuriously fail it) while proving the real ledger parses/balances/is-negative-checked every run; full regression 22 suites green = 746 assertions 0 failed (shell 307: budget 49 + dns 40 + identity 28 + gate 67 + relay 38 + sync 30 + mx 31 + unread 24; PHP 439); live: budget-show human+json rc 0, /data/tools link rc 0, dev:8000 + prod:8001 /api/budget both serve the corrected ledger (PHP parses it independently: spent 1.5, remaining 3.5, 4 allocations), /budget + /healthz 200 on both, system-status ALL SYSTEMS HEALTHY, source-sync rc 0; REGISTRY §budget-show rewritten (contract/hook/exit nuance/49-assertion suite — old Tests section was three manual live commands); STEP 0 clear first (0 unread dev+prod, 65 HANDLED, 0 unhandled, re-verified at commit); spend 0.00, prod untouched (only the shared ledger data file changed, no PHP modified)
85fb604 fix: dns-verify honors its documented exit-code/--timeout/record-type contract — three defects in a Phase-0 core tool that had ZERO tests, all the same class as the system-status fix last run (documented contract not honored): (1) --timeout was a NO-OP — verify_dns() called run_cmd(cmd) and never passed args.timeout, so the documented 'Timeout per query in seconds' did nothing and dig never got a +time= deadline, letting a hung resolver hang the caller forever; (2) exit 4 'Network/timeout error' was UNREACHABLE — an unreachable server makes dig exit 9, the tool folded that into has_records=false and exited 3, i.e. it reported 'no records found' for a query that never completed (a missing dig did the same), conflating 'asked and got nothing' with 'never got an answer'; (3) an unknown record type was ACCEPTED and echoed back as a verified record — dig does not reject unknown types, it silently reinterprets the token as the QUERY NAME (proved live: 'dig BOGUSTYPE gladex.de' asks for name BOGUSTYPE type A, SERVFAIL/ANSWER 0) and the LAN wildcard answers any name with 10.2.3.8, so the tool emitted {"type":"BOGUSTYPE","value":"10.2.3.8"} and exited 0 — machine-readable output asserting a query never made; fix: +time=N +tries=1 to dig AND an N+2s subprocess backstop, transport failures now counted in new JSON fields network_errors/errors[] and exit 4 while a completed-but-empty answer stays 3, [record-type] validated against a fixed allowlist + RFC 3597 TYPE<n> before dig is invoked (else 2), --timeout<1 -> 2, human format says 'absence is unproven' instead of 'No DNS records found'; test-first: new tests/test_dns_verify.sh (40 assertions, scenario dig stub ok/empty/netfail/sleep + one PATH with only python3 for missing-dig, no live DNS) run against the UNFIXED tool -> 24 passed/16 failed with all 16 being exactly these defects (bogus type rc 0 + fabricated record, dead server rc 3, missing dig rc 3, no +time in dig argv, hung dig 5065ms then rc 0, --timeout 0 accepted, both mutation preconditions absent); 2 mutations both precondition-asserted to occur exactly once so 'caught' can never be a no-op edit — M1 folds the network exit back to 3 and M2 defeats the type guard, both caught; after fix 40/40 in 4.0s; full regression green: shell 258 (dns_verify 40 + identity 28 + promote_gate 67 + smtp_relay 38 + source_sync 30 + mx_soa 31 + unread 24) + PHP 439 = 697 assertions, 0 failed; live verified gladex.de->0 with network_errors:0, BOGUSTYPE->2 with an explanation, dead resolver->4, --help->0, system-status ALL SYSTEMS HEALTHY rc 0, source-sync 30/30; scope-checked no caller (repo-wide grep + /etc/cron* /etc/systemd/system /usr/local/sbin /opt/startup) consumed dns-verify's exit codes, so nothing depended on 'network failure == 3'; REGISTRY §dns-verify rewritten — its old entry documented the fiction (4 listed as reachable, --timeout as functional, 'Tests' as 4 manual live commands); STEP 0 clear first (0 unread dev+prod, 65 HANDLED, 0 unhandled); spend 0.00, prod untouched
0a021bb fix: system-status honors its own exit-code contract — the script ended with exit $ERRORS, i.e. it exited with its raw error COUNT, so exactly 2 failing checks exited 2, indistinguishable from the documented '2 = invalid arguments' (and 3+ exited undocumented 3/4 = not-found/unavailable codes); now clamps to 0 healthy / 1 unhealthy / 2 reserved for the arg parser, count still carried by the JSON errors field + human 'N CHECK(S) FAILED' line; test-first: new case 11 in mx_soa suite drives TWO failing checks (missing MX + non-numeric SOA) via the scenario dig stub — pre-fix FAIL 'got exit 2 — would collide with invalid arguments' (29/31 pre-fix, 31/31 after); mutation M3 restores exit $ERRORS on a copy -> exits 2 again -> caught; scope-checked no cron/systemd/identity-run caller depended on count-rc, sibling tools audited (healthcheck/budget-show/dns-verify/version-check/verify-landing/ip-drift-check/tls-check/source-sync-check all clamp already — system-status was the only offender, all --bogus=2); REGISTRY 'Exit-code nuance' limitation replaced by the fixed contract (27->31 assertions + M3), unread case-10 comment corrected; suites green mx 31/31 + unread 24/24 + gate 67/67 + sync 30/30 + identity 28/28 + relay 38/38 + PHP 14/14 (462); live rc=0 healthy / bogus=2 / help=0 / via /data/tools link rc=0; STEP 0 clear first (0 unread dev+prod, 66 HANDLED, 0 unhandled); spend 0.00, prod untouched
96b4b8d fix: tools derive their repo root with realpath, so the documented /data/tools symlink stops breaking them — source-sync-check reported repo=/data and failed closed (exit 3 MISSING-DIR) while system-status died under set -e at the git check (exit 128, ZERO output, silent failure of the STEP-0 guard); regression case N (4 assertions) + case 10 (4 assertions) written first and run against the unfixed code (4/3 FAIL), fix reverted as mutation (rc128, 0 bytes) -> caught; suites green 30/30 + 24/24 + 27/27 + gate 67/67; live via link: repo=/data/repo exit 0 / 30 lines rc0; REGISTRY counts+notes (incl. exit $ERRORS nuance), STRUCTURE repo-root derivation rule, PROGRESS STEP-0 clear
b4eee82 fix: identity-run.sh honors TMPDIR + case4 asserts the sandbox capture dir — suite hermetic against concurrent real persona shifts (28 assertions, decoy-/tmp verified); register test_identity_wrapper.sh in REGISTRY, STRUCTURE 26->28 + TMPDIR hook contract, PROGRESS STEP-0 clear
0253d54 fix: promote-dev-to-prod line-buffers stdout so gate refusals can never overprint the earlier-emitted 'Starting promotion' line in 2>&1 captures — suite X + mutation X, 67 assertions green (was 63)
[main 4b3b9f3] tls-check: split exit 3/4/5 (unreachable vs expiring vs rejected), describe rejected certs, default to all 7 cert SANs; add first test suite (99 assertions, 4 mutations)
 4 files changed, 814 insertions(+), 105 deletions(-)
 create mode 100644 tests/test_tls_check.sh
4b3b9f3 tls-check: split exit 3/4/5 (unreachable vs expiring vs rejected), describe rejected certs, default to all 7 cert SANs; add first test suite (99 assertions, 4 mutations)
> build · mimo-v2.6-flash-free
Committed `4b3b9f3`. Pushing to origin:
$ cd /data/repo && git remote -v && git push origin main 2>&1 | tail -3
origin	git://git.gladex.de/gladex.git (fetch)
origin	git://git.gladex.de/gladex.git (push)
To git://git.gladex.de/gladex.git
   c492278..4b3b9f3  main -> main
> build · mimo-v2.6-flash-free
Run complete. Summary:

## This cycle's step: `tools/tls-check` hardened (4 defects, same class as the system-status/dns-verify/budget-show fixes)

**Test-first evidence:**
- Pre-fix run of new suite: **15 passed / 84 failed** — every failure maps 1:1 to a defect
- Post-fix: **99 / 99 green**, with 4 precondition-asserted + surgical mutations all caught

**Defects closed:**
1. **Exit-code collapse** — DNS failure, refused, timeout, hostname mismatch, untrusted chain *and expired certs* all exited 3. Now: `3` unreachable / `4` expiring / `5` rejected with `reject_reason` ∈ expired | hostname_mismatch | untrusted
2. **Rejected certs undescribed** — rejects returned `cn=null, sans=[], dates=null` (expired indistinguishable from no cert). Now a `CERT_NONE` reconnect + offline DER parse fills cn/sans/dates/`days_remaining`/`covers_name` on the reject path
3. **Defaults were 5 of 7 SANs** — `log.` and `team.gladex.de` (investor's team page) silently unchecked; now all 7, AST-pinned
4. Dead `["--help"]` branch removed; new `--port`/`--timeout`/`--ca-file`; arg validation → exit 2; help epilog documents all 6 codes

**Verified:**
- Full regression: **23 suites, 0 failed** (9 shell + 14 PHP)
- Live: 7 default domains OK (expiring 2026-12-22, 89d), `--warn-days 90` → 4, `:993` OK, `.invalid` → 3, `system-status` healthy, `source-sync-check` in sync
- Callers grep (repo + cron + systemd + /opt/startup): **none** — exit change is safe
- REGISTRY §tls-check rewritten; PROGRESS.md entry appended
- STEP 0 clear at start **and** at commit (0 unread investor msgs on dev+prod, 0 unhandled inbox)

**Committed & pushed:** `4b3b9f3` → `origin/main` (4 files, +814/−105). Spend: **0.00** (`*-free` only), prod untouched.

**Still blocked (investor-owned):** NEEDS-INVESTOR #57 (public route gating A/B/C) and SOA MNAME — cannot proceed unilaterally.
exit=1

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)
[Fri Sep 25 02:51:53 2026] 127.0.0.1:56846 Accepted
[Fri Sep 25 02:51:53 2026] 127.0.0.1:56846 Closing
[Fri Sep 25 02:51:53 2026] 127.0.0.1:56860 Accepted
[Fri Sep 25 02:51:53 2026] 127.0.0.1:56860 Closing
[Fri Sep 25 02:51:59 2026] 127.0.0.1:40878 Accepted
[Fri Sep 25 02:51:59 2026] 127.0.0.1:40878 Closing
[Fri Sep 25 02:51:59 2026] 127.0.0.1:40882 Accepted
[Fri Sep 25 02:51:59 2026] 127.0.0.1:40882 Closing
[Fri Sep 25 02:52:01 2026] 127.0.0.1:40896 Accepted
[Fri Sep 25 02:52:01 2026] 127.0.0.1:40896 Closing
[Fri Sep 25 02:52:06 2026] 127.0.0.1:37672 Accepted
[Fri Sep 25 02:52:06 2026] 127.0.0.1:37672 Closing
[Fri Sep 25 02:52:08 2026] 127.0.0.1:37676 Accepted
[Fri Sep 25 02:52:08 2026] 127.0.0.1:37676 Closing
[Fri Sep 25 02:52:08 2026] 127.0.0.1:37682 Accepted
[Fri Sep 25 02:52:08 2026] 127.0.0.1:37682 Closing
[Fri Sep 25 02:52:14 2026] 127.0.0.1:37690 Accepted
[Fri Sep 25 02:52:14 2026] 127.0.0.1:37690 Closing
[Fri Sep 25 02:52:15 2026] 127.0.0.1:40204 Accepted
[Fri Sep 25 02:52:15 2026] 127.0.0.1:40204 Closing
[Fri Sep 25 02:52:15 2026] 127.0.0.1:40208 Accepted
[Fri Sep 25 02:52:15 2026] 127.0.0.1:40208 Closing
[Fri Sep 25 02:52:23 2026] 127.0.0.1:40212 Accepted
[Fri Sep 25 02:52:23 2026] 127.0.0.1:40212 Closing
[Fri Sep 25 02:52:24 2026] 127.0.0.1:40226 Accepted
[Fri Sep 25 02:52:24 2026] 127.0.0.1:40226 Closing
[Fri Sep 25 02:52:26 2026] 127.0.0.1:49438 Accepted
[Fri Sep 25 02:52:26 2026] 127.0.0.1:49438 Closing
[Fri Sep 25 02:52:38 2026] 127.0.0.1:58048 Accepted
[Fri Sep 25 02:52:38 2026] 127.0.0.1:58048 Closing
[Fri Sep 25 02:52:46 2026] 127.0.0.1:47932 Accepted
[Fri Sep 25 02:52:46 2026] 127.0.0.1:47932 Closing
[Fri Sep 25 02:52:46 2026] 127.0.0.1:47938 Accepted
[Fri Sep 25 02:52:46 2026] 127.0.0.1:47938 Closing
[Fri Sep 25 02:52:47 2026] 127.0.0.1:47946 Accepted
[Fri Sep 25 02:52:47 2026] 127.0.0.1:47946 Closing
[Fri Sep 25 02:53:04 2026] 127.0.0.1:41018 Accepted
[Fri Sep 25 02:53:04 2026] 127.0.0.1:41018 Closing
[Fri Sep 25 02:53:06 2026] 127.0.0.1:41026 Accepted
[Fri Sep 25 02:53:06 2026] 127.0.0.1:41026 Closing
[Fri Sep 25 02:53:06 2026] 127.0.0.1:41030 Accepted
[Fri Sep 25 02:53:06 2026] 127.0.0.1:41030 Closing
[Fri Sep 25 02:53:14 2026] 127.0.0.1:41042 Accepted
[Fri Sep 25 02:53:14 2026] 127.0.0.1:41042 Closing
[Fri Sep 25 02:53:14 2026] 127.0.0.1:48086 Accepted
[Fri Sep 25 02:53:14 2026] 127.0.0.1:48086 Closing
[Fri Sep 25 02:53:15 2026] 127.0.0.1:48100 Accepted
[Fri Sep 25 02:53:15 2026] 127.0.0.1:48100 Closing
[Fri Sep 25 02:53:29 2026] 127.0.0.1:42960 Accepted
[Fri Sep 25 02:53:29 2026] 127.0.0.1:42960 Closing
[Fri Sep 25 02:53:30 2026] 127.0.0.1:42970 Accepted
[Fri Sep 25 02:53:30 2026] 127.0.0.1:42970 Closing
[Fri Sep 25 02:53:31 2026] 127.0.0.1:42972 Accepted
[Fri Sep 25 02:53:31 2026] 127.0.0.1:42972 Closing
[Fri Sep 25 02:53:54 2026] 127.0.0.1:34548 Accepted
[Fri Sep 25 02:53:54 2026] 127.0.0.1:34548 Closing
[Fri Sep 25 02:53:54 2026] 127.0.0.1:36926 Accepted
[Fri Sep 25 02:53:54 2026] 127.0.0.1:36926 Closing
[Fri Sep 25 02:53:55 2026] 127.0.0.1:36942 Accepted

Generated 2026-09-25 00:53:55 UTC · Gladex.de