Repository navigation
Description
Problem
AbuseEvaluator::check_bytes (pkgs/id/src/abuse.rs:505-528) refuses a byte request larger than the bucket's burst capacity as a temporary BudgetExhausted refusal with a bounded retry_after_ms — but such a request can never be admitted. TokenBucket caps tokens_milli at capacity_milli on refill (abuse.rs:145-155), so a cost above capacity is unreachable at any future time. The refusal contract promises "wait this long and retry" for a wait that cannot help, forever.
The same trap exists on the frame path whenever a policy's frame_burst is 0 (cost 1 > capacity 0, abuse.rs:474-492).
Evidence (verified this run)
Laptop probe crate ~/git/fleet/probe-2026-11-01-laneB-abuseoversize/ — verbatim abuse.rs from main @3dbc7006 (sha256 625bcd348fe4dbe528dd67f813ce2b290d35ff8d1365774194d0b8441ed1358e, identical on sandbox and laptop clones), default AbusePolicy (byte_burst 4 MiB, bytes_per_sec 512 KiB):
- Control:
check_bytes(ep, true, 4*1024*1024, 0)→ Ok (exact burst admitted). check_bytes(ep, true, 4*1024*1024 + 1, 0)→Err(Refusal { reason: BudgetExhausted, retry_after_ms: Some(1000) }).- Retried at
now = 1000(the promised wait) → stillErr,retry_after_ms: Some(1000)again. - Retried at
now = 1_000_000_000ms (~11.6 days) → stillErr,retry_after_ms: Some(1000)again. - At that same instant the exact-burst request succeeds, proving the bucket is full — the oversize cost is simply above capacity, not below a refill horizon.
- Zero-burst frame policy (
frame_burst = 0):check_framerefuses withretry_after_ms: Some(1000)and still refuses atnow = 1_000_000_000.
Why the retry number is also wrong on its own terms: TokenBucket::retry_after_ms (abuse.rs:176-186) computes deficit.div_ceil(rate_per_sec) against current tokens without checking cost against capacity — for the oversize case it yields 1 ms, clamped to the 1 s floor by Refusal::new (abuse.rs:103-113). Every retry is told "1 s" indefinitely.
Why it matters
The structured Refusal is the one contract every comms surface will consume (#2244 / #2033 design; the core landed in #3881, wiring is #3882). #3882 maps these refusals to HTTP 429 + Retry-After and frame-path refusals: an oversize request (e.g. a byte charge above 4 MiB) will be answered "retry in 1 s" forever, inviting well-behaved peers into an infinite retry loop and giving operators no signal that the request is permanently inadmissible rather than momentarily over budget. Fixing the contract is cheapest now, before Packet B (#3882) builds the mapping on top of it.
Acceptance criteria
- A request whose cost exceeds the bucket's capacity is refused permanently:
retry_after_msisNone(a dedicated reason such asRequestTooLarge, or documented reuse of the permanent-refusal shape) — on bothcheck_bytesandcheck_frame. -
retry_after_msis only everSome(..)for requests that can actually be admitted after the stated wait. - Regression tests: oversize byte request refused with no retry at t=0 and at a far-future time; zero-burst frame policy likewise; a within-capacity request over a drained bucket still gets a bounded temporary refusal; the exact-burst control is still admitted.
- The module doc /
Refusaldoc states the permanent-oversize semantics so the id(comms): P0-A integration — frame-seam charging, HTTP 429+Retry-After, chat limiter consumer, counter visibility (phase of #2244) #3882 wiring maps it distinctly from429(e.g. a non-retryable refusal), or explicitly records why it still maps to 429.
Related: #3881 (core, closed — introduced the contract), #3882 (integration that will consume it), #2244 (parent), #2033 (design).
Activity
dezren39 commentedon Nov 1, 2026
🤖 Agent note · state: filed
56 remaining items
dezren39 commentedon Nov 3, 2026
🤖 Agent note · handed off
dezren39 commentedon Nov 3, 2026
Launched queue task 8301 for execution in the bound OpenCode session
ses_e5e5cb0aff8ffekXzrHOJiR5oR.dezren39 commentedon Nov 3, 2026
Coverage triage — 2026-11-03
mainis5b0aeb593c0c9bfcf14c2c9980fee82e484ac7e6. In this revision,TokenBucket::refillcaps accumulated tokens at capacity (pkgs/id/src/abuse.rs:173–183), butretry_after_mscalculates a delay without checking whether cost exceeds that capacity (212–220).check_frameandcheck_bytesthen return bounded retryable refusals for those costs (1073–1092,1104–1124). The existing 5-byte-against-4-byte regression inpkgs/id/src/frames.rs:388–418checks refusal/counters but not the impossible retry promise. Thus the reported defect is not fixed on main.mainand changes onlypkgs/id/src/abuse.rsandpkgs/id/src/frames.rs. It is the active implementation route for this issue; no duplicate fix task is being created. The intended scope remains a distinct permanent over-capacity refusal for frame/byte (including combined) checks, regression coverage for permanent versus temporary exhaustion, and the refusal/mapping documentation described above.dezren39 commentedon Nov 4, 2026
agent-note:v1
event: picked up
task: 8899 (triage; queue repo-health-code-pilot; worker repo-worker-code; lane 9aca43b4-5439-4939-9f71-323f5d32521b — OpenChamber scheduled lane)
by: repo-worker-code
Claimed from the queue safety-net task. The packet reports an oversize abuse-budget request can receive a temporary retry interval even though it exceeds bucket capacity, with a laptop probe showing the condition persists after the advertised wait and after a long refill interval. I am revalidating issue/PR state and checking for an existing fix owner before deciding whether a follow-up task is still needed.
dezren39 commentedon Nov 4, 2026
agent-note:v1
event: triaged — confirmed, existing fix route
task: 8899 (triage; repo-health-code-pilot; worker repo-worker-code; lane 9aca43b4-5439-4939-9f71-323f5d32521b — OpenChamber scheduled lane)
by: repo-worker-code
Verdict: confirmed. The recorded laptop probe shows byte cost
byte_burst + 1and zero-burst frame cost continue returningretry_after_ms: Some(1000)at the promised wait and at 1e9 ms, while exact-capacity byte cost succeeds. The root cause is the retry calculation not rejecting costs above bucket capacity before producing a temporary retry promise.Route: do not enqueue another implementation task. Existing PR #4555 (
fix(id): permanently refuse over-capacity bucket costs, branchqueue/6265-3927-permanent-oversize) is the active fix route, open against main with two files changed and checks SUCCESS; it implementsRequestTooLargeand regression/docs coverage per the prior triage. Its status remains open with Code Owners listed as pending reviewer. Keep #3927 open until that PR merges and the behavior is verified on main. Removed stalequeue/queuedandstatus/needs-triage; task claim labels are removed on completion.