Shirley XueAll work
Solo BuildOpen sourceDeveloper toolingPython2026

usage-guard — pause before the wall

Problem
A long Claude Code run burns the account's shared 5-hour usage window while the session's own meter still looks calm — and a mid-run reminder to stop at 90% queues behind the subagents it was meant to stop.
Solution
A daemon outside the session polls account usage, writes one control file, and the session reads it at checkpoints it already passes through.
Result
Open source under MIT, at v0.2.2, with a pytest suite running on macOS in CI.

Long agent runs have a wall in them. The account's 5-hour window is shared, so batch work, subagents and overnight loops spend a budget the session cannot see, and crossing 100% starts spending the extra usage wallet.

usage-guard arms once at the start of a long sitting. A daemon polls account-level usage on its own schedule and writes a single control file; the skill teaches the session to read that file at the points where stopping is cheap, and to stop dispatching new work when it says so. macOS, Desktop Code tab or CLI, MIT-licensed and public.

What the daemon owns, and what the session is allowed to conclude:

  1. The guard lives outside the session. The daemon polls the usage API and spends zero session tokens doing it.
  2. The session obeys one field — state: RUN, PAUSE, COOLDOWN or UNKNOWN — read at checkpoints. It never decides from a percentage on its own.
  3. Blind means stop. When the guard cannot read usage, it says UNKNOWN rather than RUN.

The session's meter is not the account's meter

Each chat sees only its own usage. The 5-hour window belongs to the account, so a session reading 58% can be sitting on a window already at 94% — or at 60% while the weekly bar is at 97%. The number that decides whether the next batch should run is the one the session does not have.

Timing is the other half. On long runs, chat messages queue while subagents work, so a message telling the agent to stop at 90% lands after the work it was meant to stop. A stop button does not reliably halt subagents already dispatched, either. That is why the guard is armed up front and reads from outside: an external control file plus a session contract agreed before the work starts, instead of a reminder injected into the middle of it.

One dogfood run made the case for the weekly bar. The 5-hour window had just reset to 4% and looked entirely safe; weekly sat at 99%, so the guard went to COOLDOWN with the reason recorded as weekly and stopped new subagents from being dispatched into the cap.

One daemon, one file, one field

The whole contract is a JSON file at a fixed path. The daemon writes it; the session reads it; nothing else passes between them.

  1. 01

    Arm

    One slash command before the long run starts the daemon and blocks until the first usage poll lands, so work never begins against null telemetry.

  2. 02

    Poll

    The daemon reads the account's 5-hour window, the weekly bar and the extra wallet through the same OAuth usage endpoint the desktop settings screen uses. Zero session tokens.

  3. 03

    Decide

    PAUSE when either limit crosses its threshold — 90% on the 5-hour window, 98% weekly when enabled. RUN returns only when both are clear, and a pause_reason field names which one fired.

  4. 04

    Hold and resume

    A macOS notification carries the reason and the number. The session finishes its current unit, writes a checkpoint, and stops dispatching new work until reset — then picks the task back up from disk.

Polling that tightens as the window fills

A guard that polls on a fixed timer is either wasteful or late. The daemon's sleep is a function of the current percentage: ten minutes through the low and middle range, five minutes above 85%, and no sleep at all past 90%, where every poll can be the one that pauses the run.

The low end used to be half an hour, which was wrong in exactly the case the tool exists for. A dogfood sitting went from 9% to 71% in about 28 minutes while the control file still showed the old reading — accurate when written, useless by the time anyone read it.

When it cannot see, it stops saying RUN

A daemon can die while the last thing it wrote still reads RUN at 40%, and a session trusting that file walks into the wall with the guard's blessing.

v0.2.2 closed it. After three polls that return no usage, state becomes UNKNOWN instead of RUN. Every write stamps a valid_until, and consumers treat anything past it as UNKNOWN whatever state says — an effective_state field does that check for them so the reader cannot get the comparison wrong. A LaunchAgent with KeepAlive restarts a crashed daemon while a deliberate disarm stays down. Blind alerts re-notify hourly, then daily, so the warning does not vanish with a single banner.

What the guard does, and what it leaves alone

The guard is cooperative on purpose. It briefs the session up front and sets a flag; it does not reach into a running subagent and kill it. What it stops is new work after PAUSE, which is the part that actually spends the window, and progress safety rides on checkpoint writes — so the skill asks for a checkpoint at the same points where it reads state.

It is an independent community tool, not an official integration. It reads an undocumented OAuth usage endpoint that can change without notice, and the front page of the repo says so.

The repo carries the same discipline as the daemon. Four pytest modules run on macOS in CI on every push and pull request, and the job's first step is a clean check that blocks personal paths from ever shipping in a public repo — wired into a pre-push hook as well, so the check runs before the mistake leaves the laptop.