Move the base-url check out of the else-if chain instead of into a
helper, and test both cases with an invalid auth-default-access, which
fails right after the checks, instead of starting a server.
Liveness no longer comes from keepalives: a topic keeps its rate visitor
while it has an open stream, and for a day after the last one closed or
the last subscribe. This removes the keepalive matching, taking a stale
topic back and its write re-check.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
A stream's keepalives refreshed only its own visitor, so a topic that held
the visitor of a newer stream or a poll from the same key lost it after a
day although a stream was still open. The topic now keeps when a
subscriber with its rate visitor's key was last seen, and a stream takes a
stale topic back.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
Tier to tier changes keep spend (no tier to a tier never did), only a tier's daily email limit is a cap, only the read over the size limit is refunded, and the #2119 bullet describes the fix.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
Without a tier, the daily email limit is only derived from the refill, for
display. Enforcing it as a cap cut what the email bucket allowed: with a burst
of 5 and a refill every 288 minutes, 5 emails a day instead of about 10. Only
a tier's daily email limit is a cap now, as before.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
Since every request gets its own visitor, a poll from the subscriber of an
open stream replaced the stream's visitor as the topic's rate visitor. Only
the stream's keepalives keep a rate visitor alive, so a day later UnifiedPush
publishes failed with 50701 while the stream was still open. A poll from the
same key now keeps the live rate visitor (and keeps it alive) instead.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
emails_remaining was clamped to what the email bucket holds right now, so a fresh
Supporter saw 10 of 50. It is the daily limit minus today's emails again, as in
v2.34.0 and like messages_remaining; the bucket still refuses a burst past it.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
Stale read the limiter key's last use, which every charged publish refreshes, so a topic kept
a vanished subscriber's limits (and its UnifiedPush subscription) for as long as anyone
published to it. The visitor now carries its own keepalive, set when it is built and by the
subscribe handlers' keepalive loops, like the shared visitor's seen was.
Also from the review: Prune returns and the manager logs the number of forgotten keys, the
unused visitorID wrapper goes, a stale comment and test name are updated, and the release
notes get the #1051 entry.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
The three fixes apply as they are (the email guard without the visitor mutex, which this
branch no longer has). Of the new tests, the two that pin the shared visitor's SetUser flush
and the expunge of visitor objects have no counterpart here; the rest compare visitor keys
where they compared object identity.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
A tier with a daily email limit of zero sends no emails (the limit service reads a daily
limit of zero as "no cap"), a tier edited in place flushes the visitor's pending user stats
before resetting its local counts like a tier ID change does, and a bucket refill with a
timestamp older than its last one neither charges tokens nor moves the refill point back.
The tests pin what the rework changed and what it must keep: shared keys count once in the
user stats, an expunged visitor continues its counts, the daily reset keeps the buckets,
zero bursts admit nothing, seeds apply once, prefix keying, tier changes in all directions,
concurrency on a shared key, and the account page's remaining values matching admissions.
Claude-Session: https://claude.ai/code/session_01DeXuhSjq5Gta5qPJgSceFe
Visitors are built per request, so what they share (the keying and limit
config, the config-based limits of visitors without a tier, the limiter and
the subscription gauge) is built once in New. Keys are built with string
concatenation instead of fmt.Sprintf.
A visitor's key is touched when the visitor is built, so a subscriber whose
request skipped the limiter (an exempt prefix on an existing topic) is not
dropped as a stale rate visitor. The auth and account limits are per key and
ignore the user, as on main.