1# Chapter 26: watch, one line of status 2 3Jev costs money, and the window can run for days. Something should check, 4once a minute, that questions are still being asked, and shout if they have 5stopped for a reason that will not clear by itself. `budget.sh` is the thing 6that something runs. It asks the running window over MCP and prints one 7line: 8 9``` 102026-10-02T14:03:11Z task=… mode=overworld link=2096,2000 lifetime_remaining=unknown q/min=4 stopped=no 11``` 12 13time, the bot's current task, the game mode, Link's position, a lifetime 14figure, questions in the last minute, and what (if anything) is stopping 15questions. Then it exits with a code a watchdog can act on. 16 17```bash 18nix develop -c tools/watch/budget.sh 19``` 20 21It needs no API key: it only talks to the window's MCP endpoint 22(`http://127.0.0.1:7637/mcp`), and it never turns dev mode on, because every 23tool it calls (`state`, `budget`, `bot`) is always there. 24 25> **Aside: "I don't know" is not "fine".** The most important exit code is 26> 4. If the window answers but the `budget` tool is missing, or the field 27> that says whether asking has stopped cannot be read, the script exits 4, 28> never 0. Every field goes through one function, `get`, which returns the 29> literal word `unknown` for a missing or null field: never `0`, never 30> `false`, never `no`. A watchdog must not mistake an old build that cannot 31> answer for a healthy one. 32 33| Code | Meaning | 34| --- | --- | 35| 0 | Asking works, or is throttled (the question bucket or the hour breaker) and will clear by itself; the line says which. | 36| 2 | Stopped for good: the vendor said the account is out of credit. Waiting will not clear it. | 37| 3 | No window answered at all (no `Mcp-Session-Id` came back). | 38| 4 | A window answered, but the `budget` tool, or whether asking is stopped, could not be read. Treat as stopped. | 39 40Meant for a once-a-minute watchdog: 41 42```bash 43tools/watch/budget.sh 44case $? in 45 0) ;; # fine 46 2) page-someone "stopped for good" ;; 47 3) page-someone "window down" ;; 48 4) page-someone "can't tell - treat as stopped" ;; 49esac 50``` 51 52> **Try it.** No window needed: 53> 54> nix develop -c bash tools/watch/budget.sh --self-test 55> 56> runs the parsing and formatting against recorded JSON-RPC bodies: a tool 57> present with nothing stopping questions, one throttled (not a stop), one 58> stopped for good, a tool missing entirely (the error shape an old build 59> really returns), and a leading SSE keepalive ahead of the real payload. 60> Every case that should read `unknown` does. 61 62## For the people who maintain it 63 64`lifetime_remaining` reads `unknown` against today's window. The ledger 65stopped reporting a remaining figure when the self-imposed lifetime cap was 66removed (2026-09-22); the `budget` tool now carries `lifetime_usd`, spent so 67far, and the script still asks for `lifetime_remaining_usd`. The exit code 68does not depend on it, so a watchdog is unaffected. 69 70### In this folder 71 72| Path | What | 73| --- | --- | 74| [budget.sh](budget.sh) | The one-line status, its exit codes, and `--self-test`. Shares its MCP session dance with `../hotpatch/patch.sh`. | 75| [README.md](README.md) | This chapter. | 76| [CLAUDE.md](CLAUDE.md) | The subshell and keepalive traps, for agents. | 77 78← Previous: [Chapter 25, mcp/](../mcp/) · Up: [tools](../) · Next: [Chapter 27, hotpatch/](../hotpatch/) →