← The specification

Request approval & quotas

Rendered from spec at76d46f3 (2026-10-25) · its source · Edit this page

Status: Accepted · Audience: Both · Area: D — Content & household


Purpose

Decide what happens when a household member asks for something — without the operator becoming a bottleneck, and without the disk filling because someone requested an entire nineteen-season procedural.

Two failure modes sit at opposite ends. Approve everything and the operator’s disk and bandwidth are at the mercy of anyone in the house. Approve nothing automatically and the operator becomes a manual queue — every request waits on them, household members chase them, and the promise of self-service evaporates.

Behaviour

Policy is chosen once, in plain terms

Policy Behaviour
Trusted household (default) Everything auto-approved within quota
Approve large requests Auto-approve unless it exceeds a size or item threshold
Approve everything Every request waits on the operator
Per person Different policy per household member

“Approve large requests” is the default recommendation: it removes the operator from routine decisions while catching the case that actually matters — someone requesting something enormous without realising.

Quotas are per person and per period

Expressed in terms people understand — requests per week, or gigabytes per month — rather than internal counters. A quota’s purpose is to prevent accidental excess, not to ration.

Cost is visible before requesting

A household member sees the approximate size of what they’re asking for. Most excessive requests are innocent: nobody intends to ask for 800 GB, they just didn’t know a complete series in 4K is that large.

Showing the number prevents most of the problem without any policy at all.

Decisions are fast and reachable

Pending requests appear in lemonfiber’s dashboard and can be notified (B5). Approval must not require opening Seerr — friction there is what turns “approve everything” into the only workable setting.

Declining requires a reason

The reason reaches the requester (D4). A silent decline is indistinguishable from being ignored and produces exactly the in-person follow-up the system exists to prevent.

Quota exhaustion is explained, not just enforced

The member is told the limit, what they’ve used, and when it resets — at the point of requesting, not after submitting.

On the companion

Removing a person and the queue they leave behind are one decision seen from two ends, and N13-R3 requires the first to show the second: before a person is removed, what they have outstanding, and whether they ask through the request service at all (N13). An operator deciding from a phone is the one most likely to remove an account without ever having seen its queue.

Declining requires a reason and quota exhaustion is explained, not just enforced are the same principle the companion applies to refusals everywhere: N13-R9 requires a refusal carried with the stack’s own reason rather than rendered as an error to retry. A quota that answers no without saying it was a quota is indistinguishable, from the other end, from a request service that is broken — and the household member’s next move is to ask again.

Long-pending requests remind the operator and expire is two halves, and the companion owns only one of them. The reminder is the stack’s to deliver: N17-R1 forbids the app operating a notification service of its own, so an app that decided a request had waited long enough and told somebody would be the thing that rule exists to prevent. What the app owes is the figure the reminder would be about — how long each request has been waiting — because a request approaching expiry is a different decision from one made this morning, and N2-R11 surfaces a request with enough to decide on, which is this page.

That figure is carried, and it is absent for two different reasons: a request somebody has already ruled on was not waiting, and a request whose service date could not be read is waiting for nobody knows how long. An app rendering both as no age tells an operator the second is the first.

They are told apart by the state beside it, not by the figure. A request still waiting for approval says so, so an absent age there is an age that could not be read, and is shown as one. An age absent on a request already ruled on is the figure correctly declining to count.

The one case neither field settles is a request whose state is itself absent, which is what the contract says about a status this build does not recognise. An app has nothing to go on there and N2-R14 is what it does about it: state neither, rather than pick the reading that makes the screen tidier.

States

Per request: auto-approved, pending, approved, declined, quota-blocked, expired (undecided beyond a period).

Per member: within-quota, near-quota, quota-exhausted, unlimited.

Edge cases

Situation Behaviour
Request exceeds remaining quota Block at submission with the limit and reset time stated. Never accept then silently drop.
Size unknown before grabbing Estimate from typical size for the type and quality; label it an estimate.
Actual size far exceeds the estimate Complete it — retroactive rejection is worse — but count the real figure and note the discrepancy.
Request pending a long time Remind the operator; expire after a period with the requester informed.
Operator unavailable for a long period Consider raising the policy. Suggest it rather than silently changing behaviour.
Quota reset mid-request Apply the quota at submission time only; don’t re-evaluate in flight.
Season-by-season requests circumventing a per-request limit Quotas are per period, not per request, so accumulation is caught.
Member requests something already requested Deduplicate; don’t charge quota twice.
Disk critical Requests blocked regardless of quota, with the reason stated as disk, not quota.
Member removed with pending requests Cancel and inform the operator.
Operator requests something Not quota-limited.
Partially available series requested Charge quota only for the missing portion.

Acceptance criteria

ID Requirement
D7-R1 Approval policy MUST be selectable in plain language, with per-member override supported.
D7-R2 Quotas MUST be expressed in terms the household understands — requests per period or volume per period.
D7-R3 On an operator’s surface, estimated size MUST be shown before a request is submitted. A member is told what it leaves of their allowance (D7-R15).
D7-R4 Estimates MUST be labelled as estimates.
D7-R5 A request exceeding remaining quota MUST be blocked at submission, stating the limit and reset time.
D7-R6 Pending requests MUST be approvable from lemonfiber without opening Seerr.
D7-R7 Declining MUST require a reason, and that reason MUST reach the requester.
D7-R8 Long-pending requests MUST remind the operator and MUST expire with the requester informed.
D7-R9 Actual size exceeding the estimate MUST NOT retroactively cancel an approved request.
D7-R10 Quota MUST be evaluated at submission and MUST NOT be re-evaluated in flight.
D7-R11 Duplicate requests MUST NOT consume quota more than once.
D7-R12 Partially available content MUST charge quota only for the missing portion.
D7-R13 Critical disk state MUST block requests, with disk stated to the operator as the reason, distinct from quota. A member is told as D7-R15 says.
D7-R14 Operator requests MUST NOT be quota-limited.
D7-R15 Before a member asks, what a request would use of their allowance MUST be stated as what it leaves room for, in titles of each kind counted against the allowance, and MUST NOT be stated as a size. An estimate MUST still say it is one (D7-R4). A request blocked for want of room on the disk MUST be told to a member as the house having no room for new things right now, and MUST NOT be told as their own limit.