Per-org abuse ceiling with client-recoverable rejection
Add a per-org request rate ceiling to the zenorm API — a high backstop for runaway loops and single-actor abuse, not a pricing tier. The server rejects excess requests with a machine-readable retry signal. The CLI client honours that signal with backoff. Server and client ship together, and rejection is cheap (no DB queries in the deny path).
“Nothing stops one user from hammering our API and taking the service down for everyone.”
The grilling
Nothing stops one user from hammering our API and taking the service down for everyone. Go read the code before you ask me anything. I want to know whether any rate limiting exists today, what happens to a request budget-wise, what the API measures about traffic, and how the CLI would react if we started returning 429s. That last part matters — check what the CLI retries. Then report and grill me. Feature scale: protecting the service and telling clients what they're allowed, not one middleware.
Now let me check the infrastructure layer to see if there's any existing API Gateway throttling, and also check the CLI's error handling where commands actually use the client. Here's what I found by reading the code: **No rate limiting exists today.** Zero. There's no middleware, no per-user/IP/org counting, no sliding window, no token bucket. The API is Hono-on-Lambda with a single Prometheus counter (`httpRequestsTotal` by route + status class) as its only traffic measurement. That's it — a raw total count with no per-caller breakdown. **AWS infra-level protection** (API Gateway default ~10K req/s account-level throttle + Lambda concurrency) is the only shield. One determined caller can consume all of it and degrade everyone else. **CLI behavior on 429:** The `ZenormClient` in `public/cli/src/api/client.ts` has **zero retry logic** for 429. It handles 401 (token refresh + retry) and 426 (hard fail with upgrade command) explicitly — but every other non-ok status falls through to `throw new ApiError(..., status, body)`. So a 429 is an unhandled error that propagates as a generic failure. The work daemon (`work.ts`) wraps its claim call in a catch-and-retry loop that catches *any* error, which would incidentally also retry a 429 — but only for that one endpoint; `pull`, `task`, `login` etc. would crash out with the error message. **What's measured:** Only route-level aggregate counts. No per-user session, no per-org, no per-IP dimension. The auth middleware already resolves `orgId` per request — the identity is available but nobody uses it for enforcement. Given this clean-slate starting point, my first question: That's the picture. Every route is unprotected. The CLI won't retry a 429 (except incidentally in one daemon path). The only observation is `httpRequestsTotal` — a single aggregate counter with no per-caller breakdown. The question above is the scope-defining one: which abuse pattern are we stopping?
Files the agent read
- read_repo_structure .
- ls .
- ls apps
- ls packages
- ls apps/api
- grep apps/api
- grep packages
- ls apps/api/src
- find **/cli*
- grep packages/agent/src
- read public/cli/src/api/client.ts
- read apps/api/src/handler.ts
- ls apps/api/src/routes
- read apps/api/src/app.ts
- read apps/api/src/config.ts
- read apps/api/src/auth.ts
- read apps/api/src/metrics.ts
- read deploy
- grep deploy
- grep public/cli/src
- read public/cli/src/util/errors.ts
- read public/cli/src/index.ts
- ls public/cli/src
- ls public/cli/src/commands
- grep public/cli/src/commands/work.ts
- grep public/cli/src/commands/pull.ts
- grep public/cli/src/commands/task.ts
- read public/cli/src/commands/work.ts
- read public/cli/src/commands/pull.ts
So there's no rate limiting at all, metrics are a single request counter with no per-org labels, and the CLI only retries on 401 — meaning if we shipped 429s tomorrow, every one of them would surface to a user as a hard failure. That last fact reframes the whole thing, and it's why I'm overruling what you recommended. You proposed per-plan rate limit tiers tied to subscription status — free gets N, paid gets more — as the shape of the feature. No. Tiered limits are a pricing mechanism, and I don't have the evidence to price a limit. I have zero users hitting any ceiling, no per-org traffic data to set a number from, and if I invent tiers now I'll pick numbers out of the air and then have to defend them. Worse, a limit that's a product promise is one I can't change later without breaking someone's integration. What I actually need is an abuse ceiling: a limit so high that no legitimate use ever touches it, that exists to stop a runaway loop or a bad actor from taking the service down. Not a tier. A backstop. Grill me on that.
You're right to overrule that. Tiered limits with zero traffic data would be pure guesswork, and a product-promise limit you can't change is the wrong shape entirely. What you want is a **backstop**: one generous, non-configurable ceiling that exists only to catch pathological behavior — runaway loops, buggy clients, single-actor abuse — not to meter normal use. That changes the design constraints significantly. Let me grill on the two axes that matter. One question per turn. Key question is up: the identity dimension you backstop against. Auth already resolves `orgId` on every request — per-org is the path-of-least-resistance for authenticated traffic. But unauthenticated endpoints exist too (the share routes at `/v1/public/`), so the edge coverage matters. While you think about that — note that a backstop so high no legitimate traffic touches it means the CLI **doesn't need explicit 429 handling**. Any client hitting a backstop that generous is already in pathological territory (a bug or attack). The one exception is the work daemon's existing catch-and-retry loop — it'd absorb a 429 gracefully without code changes. Other commands would crash out, which is arguably correct behavior in the abuse case. Worth confirming once we settle the shape.
Right, and your instinct about the client is the crux, so here's the governing constraint. This feature is not done when the server returns 429. It's done when a client that receives one behaves correctly. Today that means the CLI treats it as a fatal error and a user loses their work — so shipping the server half alone would convert a service-protection feature into a user-facing outage. The refusal has to carry a machine-readable indication of when to try again, and the CLI has to honour it with backoff rather than dying. Write that as a constraint in strong terms: no rate limit may be enforced against a client path that cannot recover from it. Server and client land together or not at all. Second: the limit is per organization, not per IP. IP limits punish the wrong people — an office behind one address, or CI running in a shared cloud region — and they don't identify the runaway loop, which is what we're actually defending against. Every authenticated request already resolves to an org, so use that. Third, and I want this measurable: a request that is refused must be refused cheaply. If we do a full auth resolution and three database queries before deciding to reject, then the rejection path is itself the denial of service and we've built nothing.
Clear decisions. Let me lock those in. The first question (primary failure mode) is now answered implicitly — the answer is both authenticated abuse AND the client-recovery coupling — so I'll dismiss it and capture your three directives. Intent section now has clear goals and constraints. Let me score it. Intent is solid at 0.9. I'll move to the constraints section — but there's a tension in your "cheap rejection" constraint that needs a decision before I can lock the section. You said the limit keys on `orgId` (resolved by auth middleware), but you also said rejection must be cheap — no expensive work before deciding to deny. The auth middleware today does Clerk JWKS verification or an OAuth introspection call, which is not cheap. Those two directives conflict if the rate limiter runs after auth: auth cost is paid on every request, even ones that will be denied. So: before I lock the constraints, I need a resolution on where the rate limiter lives and how it gets its key cheaply. Your "cheap rejection" and "per-org key" pull in opposite directions with the current auth middleware cost. Option C resolves it — the key is that the *algorithm* (the counter read + compare) is in-memory and costs nothing, whether the request is accepted or denied. Auth still runs, but the rate limit check itself is a single atomic counter operation, not a DB round trip or a remote call.
update_spec_metadatasummary too longThis summary is 475 characters — past the 1-3 sentence, 400-char budget. A summary states the decided scope once, like an elevator pitch; it must not re-enumerate the goals, constraints, or acceptance criteria the blocks already carry. Rewrite it as 1-3 sentences of what ships and for whom.
Stop the current edit sequence. Treat this as a plan-shape failure, not a wording issue: re-plan from the acceptance criteria into the smallest set of coarse, independently shippable user slices before retrying — as few as the distinct capabilities require (commonly 1-3, hard cap 7, no minimum); one narrow regression fix is exactly 1 slice. If two slices are steps of the same capability, merge them into one. If bad tasks or blocks already exist, update/delete that bad shape before adding more. Metadata updates are atomic: nothing from this call was committed. Retry with every requested field, preserving valid fields unchanged and correcting only rejected fields.
update_spec_metadataunsupported authoritative mechanicRequirement invents surface/queue/identifier/protocol mechanic(s) absent from authoritative PM or original-request evidence: exact protocol status 429. Keep the confirmed generic outcome, or ask only if this exact mechanic is product-owned.
insert_blocksunsupported authoritative mechanicRequirement invents surface/queue/identifier/protocol mechanic(s) absent from authoritative PM or original-request evidence: exact protocol status 429. Keep the confirmed generic outcome, or ask only if this exact mechanic is product-owned.
Two things to close. The agent turn routes are the expensive ones and they need different treatment from the read routes — but not a different limit, a different mechanism. There's already a lease manager governing in-flight turns per org. Concurrency is the right control for something that runs for two minutes; a per-minute request rate is meaningless there. Don't build a second thing that fights with it, and say in the spec which control governs which class of route. Non-goal, explicitly: no per-plan tiers, no purchasable quota, no rate limit on the marketing pages or the public share links. And no per-endpoint tuning table — one ceiling for the authenticated API, one concurrency bound for turns. If we ever get evidence that a real user hit the ceiling, that evidence is what earns a tiering conversation. I also want it stated that crossing the ceiling has to be observable to us before a user tells us. Right now we have a request counter with no org label, so we'd have no idea. If the metric can't tell us who is being limited, we're flying blind. Score acceptance and generate the tasks.
Good — you've settled the architecture. Let me confirm the lease manager's existing concurrency control before I lock the constraint. Now I have the full picture. Let me lock the remaining constraints and acceptance criteria. The lease manager (`agent-turn-lease-manager.ts`) governs individual turn lifecycles with 15-min leases and heartbeats. The workflow scheduler's `claimDue()` prevents duplicate claims per workflow. This is concurrency control for agent turns — completely orthogonal to the request rate ceiling. Correct call to keep them separate. Let me dismiss the pending question, update blocks, and build acceptance. The whole batch was rejected because of the "or equivalent" phrasing in the observability AC. Let me fix and re-send in two batches — constraints first, then acceptance criteria. The quality gate blocks exact protocol status codes. Let me rewrite the ACs to describe the observable behavior generically, keeping the outcomes the PM confirmed. All blocks inserted. Let me score the sections. All three sections pass at 0.9. No trade-off question needed — the scope is locked, the two capabilities are clear. Let me create the task set. Two capabilities, each independently demoable: 1. The server rate limiter middleware — the API rejects excess requests with a retry signal, excludes agent/public routes, and emits org-labelled metrics 2. The CLI client — the `ZenormClient` detects the over-limit signal, waits, and retries transparently Let me also do a final metadata sweep — the title/summary still reference the right scope. The gate is right — "middleware" is a layer, not a capability. The PM's requirement is one capability: the API rejects excess requests with a retry signal AND the CLI recovers transparently. Shipping either half alone is explicitly a regression. One task. Spec is complete. Here's the final state: --- **Spec: Per-org abuse ceiling with client-recoverable rejection** **Summary:** A per-org request rate ceiling on the zenorm API — a high backstop for runaway loops and single-actor abuse, not a pricing tier. The server rejects excess requests with a machine-readable retry signal. The CLI client honours that signal with backoff. Server and client ship together, and rejection is cheap (no DB queries in the deny path). **Goals (2):** - **Per-org abuse ceiling** — Protect API availability with a generous per-org backstop - **Client-recoverable 429 rejection** — Rejected requests carry retry timing; CLI recovers transparently **Constraints (6):** - **Client recovery mandate** — Server+client ship together; no enforcement against a path that can't recover - **Cheap rejection path** — No DB queries or expensive work in the deny path - **Non-configurable single ceiling** — One ceiling, not tiered/purchasable/per-endpoint - **Agent turn routes excluded** — `/v1/agent/*` already concurrency-governed by lease manager - **Public and marketing routes excluded** — `/v1/public/*`, `/health`, `/metrics`, `/internal/*` - **Throttled-org observability** — Prometheus counter with `org_id` + `route` labels on each over-limit response **Acceptance criteria (7):** - Request rejected when ceiling crossed (other orgs unaffected) - Retry timing in rejection response (never absent) - CLI retries with backoff, no user-visible error - Cheap rejection below 1ms p99, zero DB queries - Agent/public routes never throttled at 10x ceiling - Org-labelled Prometheus metric for operational discovery - Single shared per-org counter across all rate-limited routes **Task (1):** *Add per-org rate ceiling with Retry-After and CLI backoff to the API and ZenormClient* — one end-to-end capability covering server middleware + Prometheus metric + CLI retry logic, owning all 7 ACs.
insert_blocksambiguous acceptance outcomeThis acceptance criterion permits multiple unspecified observable outcomes ('or equivalent/similar/either'). Choose one deterministic user-visible result, or ask the PM to select it before acceptance closes.
Replace the menu of possible outcomes with one deterministic observable result. Ask the PM only if the exact user-visible result is not already confirmed.
insert_blocksunsupported authoritative mechanicRequirement invents surface/queue/identifier/protocol mechanic(s) absent from authoritative PM or original-request evidence: exact protocol status 429. Keep the confirmed generic outcome, or ask only if this exact mechanic is product-owned.
insert_blocksunsupported protocol statusAcceptance criterion invents exact protocol status HTTP 429 absent from PM/repository evidence. Preserve the confirmed observable outcome (for example, authentication is rejected), or ask the PM only if the exact wire contract is product-owned.
Remove or reword the unsupported claim: keep only content the PM stated, selected, or the repository verified. If the claim is load-bearing, ask the PM instead of asserting it. This gate family will not block again this session for a rewrite without the claim — but resubmitting this same rejected content is rejected every time; retry once without the claim, then move on; do not loop on rewording. Block inserts are atomic: nothing from this call was committed. Retry the batch without the rejected claim.
insert_blocksunsupported failure notificationRequirement invents a failure/error notification outcome absent from authoritative PM or repository evidence. Completion/success confirmation does not authorize failure messaging; remove it or confirm the failure recipient and outcome explicitly.
create_tasklayered taskThis task is a single architectural layer, not an independently demoable user capability, and the spec does not explicitly require a backend/API/internal-only deliverable. Merge it into the user-capability slice it serves so one task owns the necessary storage/API/UI behavior, or record the explicit non-UI boundary in the spec if that internal surface is itself the requested outcome.
Stop the current edit sequence. Treat this as a plan-shape failure, not a wording issue: re-plan from the acceptance criteria into the smallest set of coarse, independently shippable user slices before retrying — as few as the distinct capabilities require (commonly 1-3, hard cap 7, no minimum); one narrow regression fix is exactly 1 slice. If two slices are steps of the same capability, merge them into one. If bad tasks or blocks already exist, update/delete that bad shape before adding more.
Files the agent read
- grep apps/api/src/spec-agent-workflow-scheduler.ts
- grep apps/api/src
- read apps/api/src/agent-turn-lease-manager.ts
What it asked
What is the primary failure mode you need to prevent?
- ARogue user hammering the APIAgent's pick
A single aggressive authenticated user issues many concurrent requests and degrades latency for everyone else
- BUnauthenticated abuse at the edge
Unidentified traffic hitting the API—no valid token, scraping or DDoS pattern
- CBoth equally important
Both authenticated and unauthenticated abuse need limits, possibly different tiers
So there's no rate limiting at all, metrics are a single request counter with no per-org labels, and the CLI only retries on 401 — meaning if we shipped 429s tomorrow, every one of them would surface to a user as a hard failure. That last fact reframes the whole thing, and it's why I'm overruling what you recommended. You proposed per-plan rate limit tiers tied to subscription status — free gets N, paid gets more — as the shape of the feature. No. Tiered limits are a pricing mechanism, and I don't have the evidence to price a limit. I have zero users hitting any ceiling, no per-org traffic data to set a number from, and if I invent tiers now I'll pick numbers out of the air and then have to defend them. Worse, a limit that's a product promise is one I can't change later without breaking someone's integration. What I actually need is an abuse ceiling: a limit so high that no legitimate use ever touches it, that exists to stop a runaway loop or a bad actor from taking the service down. Not a tier. A backstop. Grill me on that.
What dimension does the abuse ceiling key on?
- APer authenticated orgAgent's pick
Keyed to the Clerk orgId already resolved in auth middleware — natural fit, every request carries it
- BPer authenticated user
One ceiling per Clerk userId, separate from org — survives org switching
- CGlobal API-wide ceiling
A total requests/second cap at the API level, no per-caller identity dimension
- DPer IP for unauthenticated, per org for authenticated
Dual key: IP-based for anonymous traffic, org-based for authenticated callers
Right, and your instinct about the client is the crux, so here's the governing constraint. This feature is not done when the server returns 429. It's done when a client that receives one behaves correctly. Today that means the CLI treats it as a fatal error and a user loses their work — so shipping the server half alone would convert a service-protection feature into a user-facing outage. The refusal has to carry a machine-readable indication of when to try again, and the CLI has to honour it with backoff rather than dying. Write that as a constraint in strong terms: no rate limit may be enforced against a client path that cannot recover from it. Server and client land together or not at all. Second: the limit is per organization, not per IP. IP limits punish the wrong people — an office behind one address, or CI running in a shared cloud region — and they don't identify the runaway loop, which is what we're actually defending against. Every authenticated request already resolves to an org, so use that. Third, and I want this measurable: a request that is refused must be refused cheaply. If we do a full auth resolution and three database queries before deciding to reject, then the rejection path is itself the denial of service and we've built nothing.
Where does the rate limiter sit vs auth middleware?
- AAfter auth, per-org in-memory check
Rate-limit check runs after auth resolves orgId. Auth cost is paid per request, but the limit check itself is an in-memory counter — no DB
- BBefore auth, per-IP fallback ceiling
A cheap per-IP ceiling runs before auth. After auth resolves orgId, a second per-org check applies. No auth cost on IP-level rejects
- CAfter auth, but reject is a no-DB counterAgent's pick
Same as A, but explicitly no DB calls in either accept or reject path — purely in-memory atomic counter per org
Two things to close. The agent turn routes are the expensive ones and they need different treatment from the read routes — but not a different limit, a different mechanism. There's already a lease manager governing in-flight turns per org. Concurrency is the right control for something that runs for two minutes; a per-minute request rate is meaningless there. Don't build a second thing that fights with it, and say in the spec which control governs which class of route. Non-goal, explicitly: no per-plan tiers, no purchasable quota, no rate limit on the marketing pages or the public share links. And no per-endpoint tuning table — one ceiling for the authenticated API, one concurrency bound for turns. If we ever get evidence that a real user hit the ceiling, that evidence is what earns a tiering conversation. I also want it stated that crossing the ceiling has to be observable to us before a user tells us. Right now we have a request counter with no org label, so we'd have no idea. If the metric can't tell us who is being limited, we're flying blind. Score acceptance and generate the tasks.
Where the server refused
Each of these rejected the agent's own tool call. The agent had to fix its output and try again before the spec could advance — this is the gate, not a prompt the model can talk past.
update_spec_metadatasummary too longThis summary is 475 characters — past the 1-3 sentence, 400-char budget. A summary states the decided scope once, like an elevator pitch; it must not re-enumerate the goals, constraints, or acceptance criteria the blocks already carry. Rewrite it as 1-3 sentences of what ships and for whom.
Stop the current edit sequence. Treat this as a plan-shape failure, not a wording issue: re-plan from the acceptance criteria into the smallest set of coarse, independently shippable user slices before retrying — as few as the distinct capabilities require (commonly 1-3, hard cap 7, no minimum); one narrow regression fix is exactly 1 slice. If two slices are steps of the same capability, merge them into one. If bad tasks or blocks already exist, update/delete that bad shape before adding more. Metadata updates are atomic: nothing from this call was committed. Retry with every requested field, preserving valid fields unchanged and correcting only rejected fields.
insert_blocksambiguous acceptance outcomeThis acceptance criterion permits multiple unspecified observable outcomes ('or equivalent/similar/either'). Choose one deterministic user-visible result, or ask the PM to select it before acceptance closes.
Replace the menu of possible outcomes with one deterministic observable result. Ask the PM only if the exact user-visible result is not already confirmed.
insert_blocksunsupported protocol statusAcceptance criterion invents exact protocol status HTTP 429 absent from PM/repository evidence. Preserve the confirmed observable outcome (for example, authentication is rejected), or ask the PM only if the exact wire contract is product-owned.
Remove or reword the unsupported claim: keep only content the PM stated, selected, or the repository verified. If the claim is load-bearing, ask the PM instead of asserting it. This gate family will not block again this session for a rewrite without the claim — but resubmitting this same rejected content is rejected every time; retry once without the claim, then move on; do not loop on rewording. Block inserts are atomic: nothing from this call was committed. Retry the batch without the rejected claim.
create_tasklayered taskThis task is a single architectural layer, not an independently demoable user capability, and the spec does not explicitly require a backend/API/internal-only deliverable. Merge it into the user-capability slice it serves so one task owns the necessary storage/API/UI behavior, or record the explicit non-UI boundary in the spec if that internal surface is itself the requested outcome.
Stop the current edit sequence. Treat this as a plan-shape failure, not a wording issue: re-plan from the acceptance criteria into the smallest set of coarse, independently shippable user slices before retrying — as few as the distinct capabilities require (commonly 1-3, hard cap 7, no minimum); one narrow regression fix is exactly 1 slice. If two slices are steps of the same capability, merge them into one. If bad tasks or blocks already exist, update/delete that bad shape before adding more.
What it had to clear
Every section had to score at or above 0.90 before create_task would run at all. This is the gate.
Two goals clearly defined — per-org abuse ceiling and client-recoverable rejection. Three constraints lock in the key decisions: client recovery mandate (server+client ship together), cheap rejection path (no DB queries in deny path), and non-configurable single ceiling (not tiered/pricing). Audience is clear (API + CLI users). Non-goal was explicitly stated (no tiered limits). No remaining ambiguities in intent.
Six constraints cover all key boundaries: client recovery mandate (server+client together), cheap rejection path (no DB in deny path), non-configurable single ceiling (no tiers, no per-endpoint tuning), agent turn route exclusion (lease manager handles concurrency), public/marketing route exclusion, and throttled-org observability (Prometheus metric with org_id label). All architectural tradeoffs resolved.
Seven measurable acceptance criteria cover all required observable outcomes: ceiling enforcement (exceeded requests rejected, others unaffected), retry timing in rejection, CLI backoff with no user-visible failure, cheap rejection <1ms p99 with no DB queries, agent/public routes unaffected at 10x load, Prometheus counter with org_id label for operations, and single shared per-org counter across all API routes.
The spec it produced
How it hangs together
Per-org abuse ceiling
- constrainsClient recovery mandate
- constrainsCheap rejection path
- constrainsNon-configurable single ceiling
- constrainsAgent turn routes excluded from rate limit
- constrainsPublic and marketing routes excluded
- constrainsThrottled-org observability
- validatesRequest rejected when ceiling crossed
- validatesCheap rejection below 1ms p99
- validatesAgent and public routes never throttled
- validatesOrg-labelled throttled metric
- validatesSingle shared ceiling per org
Client-recoverable 429 rejection
- constrainsClient recovery mandate
- validatesRetry timing in rejected response
- validatesCLI retries with backoff
Request rejected when ceiling crossed
- implementsAdd per-org rate ceiling with Retry-After and CLI backoff to the API and ZenormClient
Retry timing in rejected response
- implementsAdd per-org rate ceiling with Retry-After and CLI backoff to the API and ZenormClient
CLI retries with backoff
- implementsAdd per-org rate ceiling with Retry-After and CLI backoff to the API and ZenormClient
Cheap rejection below 1ms p99
- implementsAdd per-org rate ceiling with Retry-After and CLI backoff to the API and ZenormClient
Agent and public routes never throttled
- implementsAdd per-org rate ceiling with Retry-After and CLI backoff to the API and ZenormClient
Org-labelled throttled metric
- implementsAdd per-org rate ceiling with Retry-After and CLI backoff to the API and ZenormClient
Single shared ceiling per org
- implementsAdd per-org rate ceiling with Retry-After and CLI backoff to the API and ZenormClient
What the gate released
Add per-org rate ceiling with Retry-After and CLI backoff to the API and ZenormClient
Build an in-memory sliding-window per-org rate limiter in the Hono API (after auth middleware, before route handlers) and the corresponding retry logic in the CLI ZenormClient. The limiter keys on the resolved orgId, uses an env-var ceiling, skips /v1/agent/*, /v1/public/*, /health, /metrics, and /internal/*, returns an over-limit status with Retry-After header, and increments a Prometheus counter labelled by org_id and route with zero DB queries (p99 < 1ms). The CLI client detects the over-limit status, reads Retry-After, waits, and retries transparently — no user-visible error on successful retry. Automated assertions: - Request rejected when ceiling crossed - Retry timing in rejected response — No rejection response is returned without a retry signal - CLI retries with backoff — no error — the request succeeds on retry - Cheap rejection below 1ms p99 - Agent and public routes never throttled - Org-labelled throttled metric - Single shared ceiling per org
zenorm workYour coding agent claims the task, implements it against the full spec, and gates itself on typecheck, lint, and tests.
A real request, the questions the agent asked, and every place the server refused its output. No product announcements.