The finding
examples/worker_seam.py shipped REQUEST_TIMEOUT = 300.0 in front of a
cortex that generates at ~23 tok/s and thinks for 4–11.5 k tokens per turn.
Every generation past ~6,975 tokens was cut mid-flight, re-run from the top,
and after four attempts discarded — while recording truncated: false. It cost
two scored answers and censored the completion-length distribution of every
measurement taken through that seam. Full account in
#41; fixed for that
harness under pre-registration amendment 1 and deviation d4.
This issue is about the general rule, because the specific fix does not
prevent the next instance.
Why "300 was too small, use 400" is the wrong lesson
The operator asked the right question: why 300 and not 330 or 360, in case we
miss? The answer is that no value chosen that way is safe, and the reason is
worth stating precisely.
At the measured ~23 tok/s:
| timeout |
tokens it buys |
| 300 s |
~6,975 |
| 330 s |
~7,670 |
| 360 s |
~8,370 |
| 600 s |
~13,900 |
The first call recorded after the fix was 11,458 completion tokens in
460 s — a perfectly ordinary successful turn. A 360 s timeout would have cut
it. The largest completion ever recorded through this seam before the fix was
6,372 tokens, and that number is not a fact about the model: it is a fact about
the timeout.
Which is the trap. Sizing a timeout as "observed tail × margin" requires
knowing the tail — but the timeout censors the tail. The distribution you would
size the margin from is the one the defect has already truncated, so the
estimate is biased low by exactly the mechanism you are trying to protect
against, and the bias is invisible: nothing in the record says "a longer turn
happened here and was deleted". A margin over a censored percentile is not a
safety factor, it is a restatement of the bug.
The same circularity bit our own pre-registration. §14 computed a 1.47×
margin over a 204.3 s measured tail and flagged it as thin — good instinct,
right axis, wrong quantity. That 204.3 s came from an easier task, so the
margin was over a percentile that did not describe the workload. It correctly
predicted the failure and still could not size the fix.
The rule proposed
A client timeout in front of a token-budgeted model is derived from the
budget, not from a latency percentile:
REQUEST_TIMEOUT >= max_tokens / slowest_measured_generation_rate
Two properties the percentile approach does not have:
- It is not circular.
max_tokens is a hard ceiling the model cannot
exceed, so time-to-exhaust-budget is a true worst case that can be computed
without observing a tail — including a tail the current timeout is hiding.
- It moves the binding constraint onto the one that reports itself. Above
the bound, a turn that runs long ends as finish_reason == "length":
visible, gradeable, and already handled as an instrument event. Below it,
the identical turn ends as a transport retry, and nothing downstream can
tell it happened. This is the same defect shape as
#37 (ModelResponse
carries no finish_reason) and the same reasoning that produced d16.
The rate belongs to the rig and should be cited, not guessed. Measured here on
2026-08-01, ten calls: 21.5 tok/s slowest, 23.2 mean
(docs/live-test-results/orchestrator-worker-preregistration.md §18).
Repo-wide audit — where every timeout sits today
Script: timeout-audit.py (to be committed with the fix, so it is re-runnable
rather than a number in an issue). Bound computed at 21.5 tok/s against the
largest token budget in each file.
| file |
constant |
secs |
budget |
need |
verdict |
examples/worker_seam.py |
REQUEST_TIMEOUT |
300 |
16000 |
744 |
BELOW BOUND (0.40×) — fixed to 1200 |
examples/league_commander.py |
REQUEST_TIMEOUT |
900 |
16000 |
744 |
OK, 1.21× — the narrowest passing |
examples/league_h2h.py |
REQUEST_TIMEOUT |
1800 |
16000 |
744 |
OK, 2.42× |
examples/devague_legs.py |
REQUEST_TIMEOUT_S |
600 |
4000 |
186 |
OK, 3.23× |
examples/muse_arms.py |
REQUEST_TIMEOUT_S |
600 |
3000 |
140 |
OK, 4.30× |
examples/worker_throughput.py |
BATCH_WAIT_TIMEOUT_SECONDS |
300 |
1200 |
56 |
OK, 5.38× |
Only one harness was below the bound, and it is the one that failed — which is
mild evidence the bound is measuring the right thing rather than flagging
everything.
league_commander.py at 1.21× deserves a look. Its margin over a full
budget is 156 s. That harness produced the 2.4–4.4× hierarchy cost figure this
repo cites regularly, and if any of its turns were cut the same way, its cost
numbers are inflated and its correctness numbers are understated. Whether that
happened is checkable: its committed records carry retries per call.
Proposed work
- Commit the audit as a test, not a script. A
test_timeout_bounds.py
that walks the harnesses, computes max_tokens / RATE per file, and fails
on any timeout below it. The value of the M3 discipline is that the number
is recomputed from a committed input — a timeout that drifts below its bound
should break CI, not wait for a series to notice.
- Re-examine
league_commander's committed records for cut turns —
retries > 0 on any call, and any gap between wall clock and
tokens / rate. If they are clean, say so and the 1.21× stands; if not, its
published figures need the same treatment C1-E got.
- Decide whether the bound belongs in
embodiment/ rather than per-harness.
Six harnesses have each invented a timeout; the rule is the same for all of
them and none of them is where it belongs. This needs a design decision
about whether the loop layer owns transport policy at all — it is a genuine
question and out of scope for a defect fix.
- Carry the rule to colleague. They dial the same cortex through their own
transport and would hit this identically. Filed separately as part of the
t19 findings post.
What this is not
Not a claim that 1200 s is right for every rig. The bound is
max_tokens / rate, and both terms are local: a faster card or a smaller
budget moves it. The transferable part is the derivation and the reason —
put the binding constraint on the quantity that reports itself.
The finding
examples/worker_seam.pyshippedREQUEST_TIMEOUT = 300.0in front of acortex that generates at ~23 tok/s and thinks for 4–11.5 k tokens per turn.
Every generation past ~6,975 tokens was cut mid-flight, re-run from the top,
and after four attempts discarded — while recording
truncated: false. It costtwo scored answers and censored the completion-length distribution of every
measurement taken through that seam. Full account in
#41; fixed for that
harness under pre-registration amendment 1 and deviation
d4.This issue is about the general rule, because the specific fix does not
prevent the next instance.
Why "300 was too small, use 400" is the wrong lesson
The operator asked the right question: why 300 and not 330 or 360, in case we
miss? The answer is that no value chosen that way is safe, and the reason is
worth stating precisely.
At the measured ~23 tok/s:
The first call recorded after the fix was 11,458 completion tokens in
460 s — a perfectly ordinary successful turn. A 360 s timeout would have cut
it. The largest completion ever recorded through this seam before the fix was
6,372 tokens, and that number is not a fact about the model: it is a fact about
the timeout.
Which is the trap. Sizing a timeout as "observed tail × margin" requires
knowing the tail — but the timeout censors the tail. The distribution you would
size the margin from is the one the defect has already truncated, so the
estimate is biased low by exactly the mechanism you are trying to protect
against, and the bias is invisible: nothing in the record says "a longer turn
happened here and was deleted". A margin over a censored percentile is not a
safety factor, it is a restatement of the bug.
The same circularity bit our own pre-registration. §14 computed a 1.47×
margin over a 204.3 s measured tail and flagged it as thin — good instinct,
right axis, wrong quantity. That 204.3 s came from an easier task, so the
margin was over a percentile that did not describe the workload. It correctly
predicted the failure and still could not size the fix.
The rule proposed
A client timeout in front of a token-budgeted model is derived from the
budget, not from a latency percentile:
Two properties the percentile approach does not have:
max_tokensis a hard ceiling the model cannotexceed, so time-to-exhaust-budget is a true worst case that can be computed
without observing a tail — including a tail the current timeout is hiding.
the bound, a turn that runs long ends as
finish_reason == "length":visible, gradeable, and already handled as an instrument event. Below it,
the identical turn ends as a transport retry, and nothing downstream can
tell it happened. This is the same defect shape as
#37 (
ModelResponsecarries no
finish_reason) and the same reasoning that producedd16.The rate belongs to the rig and should be cited, not guessed. Measured here on
2026-08-01, ten calls: 21.5 tok/s slowest, 23.2 mean
(
docs/live-test-results/orchestrator-worker-preregistration.md§18).Repo-wide audit — where every timeout sits today
Script:
timeout-audit.py(to be committed with the fix, so it is re-runnablerather than a number in an issue). Bound computed at 21.5 tok/s against the
largest token budget in each file.
examples/worker_seam.pyREQUEST_TIMEOUTexamples/league_commander.pyREQUEST_TIMEOUTexamples/league_h2h.pyREQUEST_TIMEOUTexamples/devague_legs.pyREQUEST_TIMEOUT_Sexamples/muse_arms.pyREQUEST_TIMEOUT_Sexamples/worker_throughput.pyBATCH_WAIT_TIMEOUT_SECONDSOnly one harness was below the bound, and it is the one that failed — which is
mild evidence the bound is measuring the right thing rather than flagging
everything.
league_commander.pyat 1.21× deserves a look. Its margin over a fullbudget is 156 s. That harness produced the 2.4–4.4× hierarchy cost figure this
repo cites regularly, and if any of its turns were cut the same way, its cost
numbers are inflated and its correctness numbers are understated. Whether that
happened is checkable: its committed records carry
retriesper call.Proposed work
test_timeout_bounds.pythat walks the harnesses, computes
max_tokens / RATEper file, and failson any timeout below it. The value of the M3 discipline is that the number
is recomputed from a committed input — a timeout that drifts below its bound
should break CI, not wait for a series to notice.
league_commander's committed records for cut turns —retries > 0on any call, and any gap between wall clock andtokens / rate. If they are clean, say so and the 1.21× stands; if not, itspublished figures need the same treatment
C1-Egot.embodiment/rather than per-harness.Six harnesses have each invented a timeout; the rule is the same for all of
them and none of them is where it belongs. This needs a design decision
about whether the loop layer owns transport policy at all — it is a genuine
question and out of scope for a defect fix.
transport and would hit this identically. Filed separately as part of the
t19 findings post.
What this is not
Not a claim that 1200 s is right for every rig. The bound is
max_tokens / rate, and both terms are local: a faster card or a smallerbudget moves it. The transferable part is the derivation and the reason —
put the binding constraint on the quantity that reports itself.