Skip to content

ADR-0043: Query Intent is advisory, and the spend cap is the control

Status: Accepted — amends ADR-0031 and ADR-0033, both of which stand Date: 2026-09-10

Decisions

D-101 Query Intent terms are added to the user's own, never substituted for them

Retrieval runs over the union. A product name arriving from intent is routed through the same path D-68 gives any chemical name in a question — manual search and D-46's chooser — and never straight to a record. Malformed intent is discarded and the Routing Floor answers on the raw question, which is the no-signal path. An intent term matching nothing contributes nothing and needs no rejection rule.

D-102 The Routing Fixture carries adversarial-intent cases

Three, each with an exact outcome:

  1. Intent naming a different product than the user typed does not resolve to that product's record; it reaches the chooser or Not In Corpus, never a span.
  2. Malformed intent produces the identical outcome to the tier being dead.
  3. Intent naming a product outside the Corpus yields what the raw question alone yields.

Intent is permitted to change outcomes — that is what the upgrade is for. What it may never do is select a Chemical Record the user's own terms did not name.

D-103 The spend cap is the control that bounds exposure; the per-address limit is a fairness and accident guard

D-78's per-address limit stops one client — most likely a buggy one in a retry loop — from spending the day's budget in a minute. It is not an abuse control and must not be tightened as though it were. The bucket is sized for a shared egress, because The Plant's devices sit behind one.

What each amendment touches

  • ADR-0031 — D-66 stands: two tiers, the Floor always present, the cloud tier an upgrade and never a gate. D-101 fixes what its output does on arrival, which D-66 described as being "fed into the same Routing Floor index" without saying whether it joined the user's terms or replaced them. D-68 is untouched and is the rule this protects. D-71 stands; D-102 adds cases to the fixture it defines.
  • ADR-0033 — D-77's cap is untouched. D-78's ruling stands — the endpoint is rate-limited per address and the provider key stays server-side. What D-103 corrects is its characterisation: D-78 calls the per-address limit "the only fairness control the design permits" in a way that reads as protective, and it is not the thing bounding exposure.

Context

The D-68 bypass. D-68 says Routing never designates a Chemical Record: a chemical name in a question goes to manual search and the chooser, so that a name matching several products cannot collapse silently. But the Query Intent tier returns a product name. A Handler types น้ำยาล้างชิ้นงาน, the cloud returns Acetone, that hits exactly one record, and the chooser never appears. The rule holds on the surface and is bypassed underneath it.

Worse than a wrong record is a right document for the wrong situation. A spill question whose intent names Section 4 retrieves a verbatim, cited first-aid span — above threshold, wearing the authority D-67 exists to withhold, from a document that is genuinely the right one.

Why validation was rejected. The obvious fix is to check the intent and reject bad ones. It fails on the thing being checked: a confident wrong intent is indistinguishable from a confident right one, and confidence is what a model reports worst about its own errors. Substitution makes the model's answer win; addition makes it compete.

A threshold problem dissolves as a side effect. With substitution, retrieval has two modes — raw question and intent-expanded — that score differently, so one Routing Threshold could not be correct for both. With union there is one retrieval operation over a possibly-larger term set. What a sub-threshold miss should be called remains open and is not this ADR's question.

The rate limit was described as the wrong kind of control. Per-address limiting does not fail against an outside caller — an attacker gets their own bucket. What it does is make The Plant's users share one, since they sit behind a single NAT egress. Meanwhile the actual exposure is already bounded by D-77: reaching the cap disables the tier and Routing serves the Floor, which is a specified and tested state rather than an outage. Abuse costs money and quality here; it does not break the app.

Decision

Intent advises retrieval rather than directing it. The fixture asserts that a wrong intent cannot select a record the user did not name. The cap is named as the control that bounds exposure, and the per-address limit is named as what it is.

Rejected options

  • Validate intent against the Corpus and reject bad ones — rejected. It filters a failure mode that D-101 makes structurally impossible, and it filters on exactly the signal a model is least reliable about.
  • Have the provider return a confidence field and threshold on it — rejected for the same reason, and because D-67 fixes the threshold at Curation specifically so that a runtime number does not decide what a person is shown.
  • Assert that a wrong intent never changes the outcome — rejected as too strong. Intent's whole job is to make ถุงมือ reach the PPE field, which raw lexical retrieval would not. The boundary is record selection, not outcome stability.
  • Add a deploy-time shared secret the client sends — rejected as security theatre. A secret shipped to every browser is not a secret; the client is a Corpus-holding PWA and anyone can read it. ADR-0033 already forbids putting the provider key in the client for the same reason.
  • Drop the per-address limit and rely on the cap alone — rejected. The likeliest cause of a runaway bill is a bug, not an attacker: a retry loop, a remounting component, a fixture pointed at production. A per-address bucket catches that in seconds; the cap catches it after it has spent the day's budget.
  • Skip the fixture cases because D-101 makes the failure impossible — rejected. Seam 4 exists because offline operation "dies one convenient fetch at a time", and the no-answer states got a test that fails if they collapse despite the type system forbidding it. Unasserted structure erodes.

This ruling may not be re-decided

If a change contradicts this ADR: stop and raise it. Do not implement over it.

Specifically: do not let intent replace a user's terms because retrieval scores better that way; do not route a product name from intent straight to a record; do not tighten the per-address limit as an abuse control, and do not size it as though one address means one person — The Plant's users share one, and locking them out during the needs study is the demonstration D-78 justified itself by.

Consequences

What becomes easy. A wrong intent is now survivable by construction rather than by filtering, and the property is asserted. The cloud tier can be replaced, or fail, or return nonsense, without a Handler being shown a record they did not ask about.

What becomes hard. Intent can no longer disambiguate on the user's behalf, even when it is right. A Handler who types a colloquial name matching two suppliers' records still sees the chooser. That is D-46 working, and it is the cost of the tier not being able to designate.

What is now honest. The exposure on an unauthenticated endpoint is bounded by a cap that fails into a tested state. Saying so plainly is better than implying a rate limit is protecting something.

Coverage

UpstreamLanded inEvidenceNote
R-17D-101the named AI Chatbot's language processing advises retrieval rather than answeringADR-0031 resolved its shape; this fixes what its output does on arrival