Repository navigation
feat: mark a weekly quota window spent faster than its own clock - #451
Merged
Merged
Conversation
Forty percent of a week gone on day two and forty percent gone on day six draw exactly the same bar, and only one of them runs out early. The gauge had no way to tell them apart. Window.Ahead is derived from the reading lich already takes — no extra request, and neither provider reports it. The window's start is its next reset rolled back whole windows until it lands at or before now, which is the only way to place a cycle from the one timestamp the APIs give, and stays right however many cycles that reset sits ahead. Only weekly windows are paced, the account-wide one and the model-scoped caps alike. The five-hour one is out on purpose: it turns over too fast for a rate to mean anything, and the package's five-minute cache is a large fraction of five hours while it is nothing against a week. Nothing is marked in the first day after a reset, where the elapsed share is near zero and almost any use at all would read as far ahead. The fifteen-point threshold and the twenty-four-hour grace are the claude-swap project's measurements, which is the one thing borrowed from there; docs/ceilings.md says so, and says what a marker that never appears most likely means. Claude-Session: https://claude.ai/code/session_01EtriuLefbWGY9wRt4HbkQc
…apart An answer that names no ticket closes the oldest message delivered to that session. "Oldest" was read off time.Time, and on Windows the clock only moves every ~15.6ms — two hand-offs inside one tick carry the same timestamp, Before() is false both ways, and the winner falls to Go's randomised map iteration. About one run in ten, the answer went home to the wrong sender and both senders read a confident wrong report. The bug is the product's, not the test's: the same tie decides which errand a finished turn belongs to (turnErrand), so a Windows user with two errands open could have either one closed. Hand-offs are now counted. deliverySeq is the order itself rather than a reading of it, so no clock's resolution can flatten it, and both places that mean "oldest delivered" compare it. Measured with the service's clock truncated to 16ms, which is what the Windows runner has: TestReplyWithoutATicketAnswersTheOldestErrandDelivered failed 22 times in 200 before, 0 in 200 after. The new test drops the clock question entirely — two tickets sharing a delivered timestamp exactly, resolved 50 times over, which is red on the timestamp comparison and green on the counter. Claude-Session: https://claude.ai/code/session_01EtriuLefbWGY9wRt4HbkQc
omartelo
force-pushed
the
feat/weekly-pace-marker
branch
from
September 4, 2026 22:35
8a9d5fc to
bc6b57e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Forty percent of a week gone on day two and forty percent gone on day six draw
exactly the same bar today, and only one of them runs out early. This adds the
one thing the gauge was missing to tell them apart.
What lands
internal/quota/pace.go—markAhead(Window, time.Time) Window, pure, no I/Oand no clock of its own. It places the current cycle by rolling
ResetsAt—the next reset, the only timestamp either API gives — back whole windows
until it lands at or before now, which stays right however many cycles that
reset sits ahead.
Aheadis true whenpercent - elapsed/period*100 >= 15.Window.Ahead bool \json:"ahead,omitempty"`on the struct, mirrored by hand infrontend/src/lib/api-types.ts(no codegen — rootCLAUDE.md`).QuotaGauge.tsxdraws it as a lucidetrending-upatsize-3after thewindow's name, with a tooltip. It sits in the label column so the two
fixed-width numeric columns stay untouched and three stacked gauges keep
ending in the same place, and it carries no colour of its own — the row
already wears
usageColor. The owner picked this over the two alternativesfrom a two-theme mockup.
docs/ceilings.mdand aCHANGELOG.md[Unreleased]entry.Only weekly, and silent for a day
The five-hour window is out on purpose: it turns over too fast for a rate to
mean anything — the first spend in a fresh window is "ahead" almost by
definition — and the package's five-minute cache is a large fraction of five
hours while it is nothing against a week. Model-scoped caps are weekly and are
paced like the account-wide one. Codex's monthly free-tier window is not weekly
and is never marked.
Nothing is marked in the first 24 hours after a reset: the elapsed share is near
zero there and almost any use at all reads as far ahead. An
elapsed == 0guarddoes not cover that — a reading taken an hour in has a small elapsed, not a zero
one.
The three constants are borrowed, and the diff says so
15 points, 24 hours, weekly-only: all three are calibration measured by the
claude-swap project (
src/claude_swap/pace.py, its issue #125). Thatcalibration is the only thing taken from there; the code is ours. Each constant
carries the why in its comment, and
docs/ceilings.mdrecords that nothinghere has been tuned against a lich user's own accounts — plus the trap it sets:
a window 90% spent on day one is deliberately unmarked.
Invariant 5 — every provider
This is pure derivation over a
Windowthat already exists, applied once inService.Plansrather than per provider, so it holds for anything reporting aweekly window without knowing this code exists. That is Claude Code (
weekly_alland
weekly_scoped, bothweeklyWindow) and Codex (secondary_window, whichreports
limit_window_seconds: 604800— confirmed against the fixture inquota_test.go, so no ceiling had to be written for a shape that didn't match).The other six providers have no plan gauge at all, for the reason already in
docs/ceilings.md: they run on the user's own API keys and there is no plan toreport. No gap to record.
Tests
internal/quota/pace_test.gopins each boundary as a literal and mutatesN±1 around it — a test that reads
aheadPointskeeps passing after someoneedits
aheadPoints, which is the exact change it exists to catch. Covered:weekly ahead (64/65/66 against a flat 50% expected), weekly on pace, one second
short of the grace vs. the grace exactly elapsed, the five-hour window, the
monthly window (the case that actually pins the weekly-only guard — the grace
alone already excludes the 5h one), a model-scoped cap, a reset three cycles
further out, a reset in the past, an absent reset, an unparseable one, and a
window with no reported length. Each case also asserts
markAheadchangednothing but the mark. All three constants were mutation-checked: each one moved
turns the suite red.
Gate
gofmt -l .clean ·go vet ./...clean ·go test ./...green (31 packages) ·biome check .clean (17 pre-existing a11y warnings, none new) ·vitest rungreen (114 files / 1368 tests, cache cleared first) ·tsc -bandvite buildclean ·GOOS=linux|darwin|windowsbuild + vet clean.The render path is not covered by the node-environment suite;
AheadMarkis thesame tooltip-inside-tooltip composition
LockedReadingalready ships in thisfile.
A second commit, and it is not this feature
os-tests (windows-latest)went red on this branch ininternal/relay, apackage this feature does not touch (
git diff main -- internal/relaywas emptyat that point). It is a real product bug, reproduced and fixed here rather than
re-run:
errandOfLockedpicks "the oldest message delivered to this session" bycomparing
time.Time. Windows' clock only moves every ~15.6ms, so two hand-offsinside one tick carry the same timestamp —
Beforeis false both ways and thewinner falls to Go's randomised map iteration.
turnErranddecides which erranda finished turn belongs to by the same comparison. So on Windows, with two
errands open,
lich reply "<answer>"could send the answer home to the wrongsender and both senders would read a confident wrong report.
Hand-offs are now counted;
deliverySeqis the order itself rather than areading of it. Reproduced on Linux by truncating the service's clock to 16ms:
TestReplyWithoutATicketAnswersTheOldestErrandDeliveredfailed 22/200before and 0/200 after. The permanent guard drops the clock question
entirely — two tickets sharing a
deliveredtimestamp exactly, resolved 50times over — which is red on the timestamp comparison and green on the counter.
Gate re-run whole after it, cross-compile loop included.
Happy to split this into its own PR off
mainif you would rather review itapart from the feature — say the word and I will move it.
https://claude.ai/code/session_01EtriuLefbWGY9wRt4HbkQc