Skip to content

Commit 3a0b61f

Browse files
dmealingclaude
andcommitted
docs(python): retract an unsupported cause from the previous commit
9165280 stated as fact that the red `python` lane was an OOM kill — in its commit message, in the CHANGELOG entry, and in the error string it shipped ("the machine most likely ran out of memory"). I inferred that from an empty stderr plus four heavy lanes in flight. It is not supported: the kernel journal records ZERO OOM events between 17:00 and 19:00 local, and the failure was at 18:16 local. The one OOM that day was at 14:22, victim `python` at 28.6GB RSS, four hours earlier and unrelated. `main` is forward-only, so this retracts it here rather than by rewriting that commit. Two things led to the overclaim, both worth naming: - The runner logs are UTC and this box is EDT, so the `22:16Z` failure is 18:16 local. My first journal query used a local-time window that was therefore in the FUTURE and returned "No entries" — an empty result that reads exactly like "no OOM found". - A plausible mechanism is not evidence. The Stryker gate has OOM'd on this runner before, which made the story feel settled. What survives is stronger than what was retracted, and is measured: ruff 0.15.14 reports a source it cannot parse as exit 2 WITH stderr (`error: Failed to parse at 1:7: ...`). So an empty stderr is PROVABLY not a source rejection — the process never got far enough to report. That is precisely why the old message mattered: it discarded the exit status, the one piece of evidence that would identify the cause. The fix is unchanged in substance and now records what happened without guessing why: ruff check was killed by signal 9 before it could format the source — this is not a source error; the formatter never ran to completion ruff check failed (exit 2) and wrote nothing to stderr — ruff reports an unparseable source WITH stderr, so this is not a source error No behaviour change beyond the message text; the six tests in tests/codegen/test_format.py still pass, and the suite is green at 1866. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3v6M6uqECZ2Sb5eUv6YY
1 parent 9165280 commit 3a0b61f

2 files changed

Lines changed: 36 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,24 @@ that: it returns a NEGATIVE code and writes no stderr, so the branch raised
1717
RuntimeError: ruff format failed:
1818
```
1919

20-
naming neither the cause nor the fact that ruff never looked at the source. Observed on the
21-
self-hosted runner with five lanes in flight at once (`ts-fast`'s mutation gate, `ts-slow`,
22-
`csharp`, `java-slow`, `python`), where the formatter was OOM-killed: `1 failed, 1971 passed`,
23-
and the one failure sent a reader hunting a syntax error that did not exist. The same test
24-
passes in 3.4s on an idle box, and the full suite is green there.
20+
naming neither the exit status nor the fact that ruff never looked at the source. Observed on
21+
the self-hosted runner with four lanes in flight at once: `1 failed, 1971 passed`, and the one
22+
failure sent a reader hunting a syntax error that did not exist. The same test passes in 3.4s
23+
on an idle box, and the full suite is green there.
2524

26-
A signal death now says so and names the signal, and every other non-zero exit carries its
27-
code rather than trailing off after a colon:
25+
**An empty stderr is provably not a source rejection.** Measured against ruff 0.15.14: a source
26+
it cannot parse exits **2 with stderr** (`error: Failed to parse at 1:7: ...`). So the branch
27+
that fired had a process which never got far enough to report. What killed it is **not
28+
established** — the kernel log records no OOM event anywhere near the failure — and that is the
29+
point: the old message destroyed the one piece of evidence (the exit status) that would say.
30+
31+
The message now records what happened and does not guess at why:
2832

2933
```
3034
ruff check was killed by signal 9 before it could format the source — this is not a
31-
generator bug; the machine most likely ran out of memory
32-
ruff check failed (exit 2) and wrote nothing to stderr
35+
source error; the formatter never ran to completion
36+
ruff check failed (exit 2) and wrote nothing to stderr — ruff reports an unparseable
37+
source WITH stderr, so this is not a source error
3338
```
3439

3540
Behaviour on the success path is unchanged; a genuine ruff rejection still raises, still with

server/python/src/metaobjects/codegen/format.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,35 @@ def _run_ruff(args: list[str], source: str) -> str:
2525
)
2626
if proc.returncode != 0:
2727
# A non-zero exit is read by the caller as "ruff rejected the source" — a
28-
# generator bug. A NEGATIVE code is not that: it is death by signal, and a
29-
# killed process writes no stderr, so this branch used to raise
30-
# `ruff format failed:` naming neither the cause nor the fact that ruff never
31-
# looked at the source. Seen on a CI box running five lanes at once, where the
32-
# formatter was OOM-killed and the message sent the reader hunting a syntax
33-
# error that did not exist. Always carry the code; never claim a source error
34-
# for a process that did not finish.
28+
# generator bug. Two things make that reading unsafe, so record what actually
29+
# happened instead of asserting a cause.
30+
#
31+
# First, ruff reports a source it cannot parse as exit 2 WITH stderr (measured,
32+
# ruff 0.15.14: `error: Failed to parse at 1:7: ...`). So an EMPTY stderr is not
33+
# a source rejection — the process did not get far enough to say anything.
34+
# Second, a NEGATIVE returncode is death by signal, which is not a verdict on
35+
# the source at all.
36+
#
37+
# This branch used to raise `ruff format failed:` — trailing off after the colon
38+
# whenever stderr was empty, naming neither the exit status nor the fact that
39+
# ruff never looked at the source, and sending a reader hunting a syntax error
40+
# that did not exist. Always carry the status; never claim a source error for a
41+
# process that did not finish, and do not guess at WHY it did not finish.
3542
detail = proc.stderr.strip()
3643
if proc.returncode < 0:
3744
raise RuntimeError(
3845
f"ruff {args[0]} was killed by signal {-proc.returncode} before it could "
39-
f"format the source — this is not a generator bug; the machine most likely "
40-
f"ran out of memory" + (f": {detail}" if detail else "")
46+
f"format the source — this is not a source error; the formatter never ran "
47+
f"to completion" + (f": {detail}" if detail else "")
4148
)
4249
raise RuntimeError(
4350
f"ruff {args[0]} failed (exit {proc.returncode})"
44-
+ (f": {detail}" if detail else " and wrote nothing to stderr")
51+
+ (
52+
f": {detail}"
53+
if detail
54+
else " and wrote nothing to stderr — ruff reports an unparseable source"
55+
" WITH stderr, so this is not a source error"
56+
)
4557
)
4658
return proc.stdout
4759

0 commit comments

Comments
 (0)