The circuit-breaker trip of 2026-09-20, 21:54:54
The live window tripped jev-http's 30-questions-a-minute breaker
(~/.local/state/jev/paused.json, since 1789959294.51 = 21:54:54 CDT) about
eighteen seconds after Link took the sword from uncle. This is what did it,
from evidence rather than from the story that fit best.
The ledger
~/.local/state/jev/spend.jsonl has one line per paid request and, until the
fix below, no reason on any of them. Times relative to the trip:
| Seconds before the trip | Requests | Input tokens | Output tokens |
|---|---|---|---|
| -152 to -25 | 23, at 0.3-20 s spacing | 882-2477 | 72-192 |
| -24.9 to -7.4 | none (the sword-get text and pose: no control) | ||
| -7.40 to -0.18 | 17, at 0.29-0.94 s spacing (median ~0.33 s) | 1110 → 1262, rising | 143-150 |
The 30 requests in the minute before the trip are 11 from before the sword and
the final 17 plus two. The spacing of the last 17 is one round trip plus the
ten frames (6 + RELEASE 4) of the empty press that follows a choice
(Brain::took) - so every decision in that stretch was a question. The input
tokens rise by 0-31 per request, which is already_tried_from_this_spot
growing (a new "tried, still going" entry, or a times count going up) and
already_visited ticking: the same scene, re-asked, with the brain's memory
of its own failed attempts getting longer.
The snapshots
The app keeps has-sword.state (written 21:54:36.7, the moment FindUncle
was done) and resume.state (21:55:20, after the pause). Their work RAM is a
plain 128 KiB run inside the bincode (offsets 8573 and 8575 respectively,
found by matching the inventory block):
has-sword | resume | |
|---|---|---|
| module | $07 dungeon | $12 game over |
| room | $55 | $55 |
| Link | (2806, 2680) | (2683, 2899) |
| health | 24 of 24 | 0 of 24 |
| sprite slot 1 | $4B green knife soldier, state 9, (2880, 2896) | same |
| sprite slot 2 | $4B green knife soldier, state 9, (2768, 2912) | (2683, 2893) - on top of Link |
So there were two live soldiers in the passage, one of them walked to Link, and Link died where he stood.
The code path
Brain::navigate, before the fix:
if alarming.is_some() {
self.doing = None;
}
alarming is the nearest live enemy within CLOSE (56 px). While one stayed
that close this ran on every decision, so:
- decision N: no plan →
where_to→ a question (with adangerNoul attached); - the answer arrives;
tooksets the plan and presses nothing for 10 frames; - decision N+1: the enemy is still close → the plan is cleared before
driveruns →drivereturnsNone→where_to→ a question.
Link never took a step. took also never read the danger answer, so every
one of those requests paid for a question nothing looked at. Nothing else in
the brain bounded the rate: the stall throttle keys on unchanged, and
set_out resets unchanged on every choice, so a loop of choices can never
look stalled.
The fix (jev 6aab4e52 and after)
packages/brain/src/pace.rs: no question leaves the brain unlessPaceallows it - at most 20 per game minute, 30 frames apart, and never the same choices in the same place again within 600 frames. A refused question is decided by code (Jev's last choice if still on offer, else the goal's route, else the nearest) and Link keeps moving. Every question carries aWhy, whichjev_http::Jev::asknow REQUIRES and writes to the ledger line'sreason.- A threat clears the plan once, when it arrives, and never a fight in progress.
- With a sword, the nearest enemy within 112 px is a "fight" candidate:
Then::Strike, re-aimed at the enemy every decision, B toward it on arrival, over when no enemy is near. - The unread
dangerNoul is gone; the state still says how close it is. - Tests:
packages/brain/src/tests.rs,mod the_breaker_trip, which replays this scene at the live cadence.