Repository navigation
bug(ai): AI copy tells subscribers to add an API key that no longer exists (G30) #39
Description
Summary
AI (G15, PR #31) was built on the premise that the user brings their own OpenAI / Anthropic / Gemini key via the web Integrations page. That premise is void. AI is now powered by Claude and provided by InterlinedList as part of the subscription.
Consequence: when GET /api/ai/status returns an empty providers[], the shipped app tells a paying subscriber to go do something that is impossible.
AI.swift:143→"Add your own AI provider key in Settings on interlinedlist.com to use AI features."
There is no such setting on the live Integrations page.
Evidence (live, 2026-09-07)
GET /api/ai/status (Bearer, test account):
{"subscriber":true,"providers":["anthropic"],
"defaultModels":{"anthropic":"claude-sonnet-5","openai":"gpt-4.1-mini","gemini":"gemini-2.0-flash"},
"quota":{"usedToday":8,"dailyLimit":50,"remaining":42}}/help/ai: "AI writing assistance … is a Subscriber feature, powered by Claude and included with your subscription, so there is no API key to set up and no separate AI bill." … "If you are subscribed and the AI controls are missing, the site's AI provider is temporarily unconfigured. The features hide themselves in that case rather than failing partway through an action."
/help/settings: "There is nothing to set up. AI is powered by Claude and provided by InterlinedList. You do not supply an API key, and there is no separate AI bill." … "AI actions are capped at 50 per day per account. A preview and its confirmed action each count."
What is actually wrong
The gating logic is already correct — AIAvailability.isAvailable keys off subscriber + non-empty providers. This is copy, comments, and one behavioural rule (hide vs. explain).
Division of labor
Domain
-
AI.swift:143— replace the unavailable-reason copy. Whensubscriber == trueandprovidersis empty, the reason is the site's AI provider, not the user's setup. Suggested: "AI is temporarily unavailable. InterlinedList's AI provider is not responding right now — try again later." Whensubscriber == false, the reason stays a subscription prompt. - Split the two cases explicitly rather than returning one string for both — a Free user and a subscriber during an outage need different sentences.
-
AI.swift:112— the doc comment "Provider slugs the account has configured" is wrong; these are the platform's configured providers. -
AI.swift:167— "the cost of the user's own provider key" is wrong; the cost lands on InterlinedList's account and is bounded by the daily quota.
App
-
AIAssistantViewModel.swift:9— stale BYO-key comment. - Follow the documented behaviour: when AI is unavailable, hide the composer AI control and the Powered Template / Powered Document entry points rather than showing a disabled control with an explanation. Free accounts "do not see the AI controls at all".
- Surface the daily quota where it helps:
quota.remainingis already decoded. When remaining is 0, say so plainly ("You've used all 50 AI actions for today") instead of showing a generic failure — and note that a preview and its confirm each cost one.
Docs
-
docs/spikes/ai-materialize-live-shapes.md— strike the two bring-your-own-key paragraphs.
Tests
- happy — subscriber +
providers:["anthropic"]→ available, controls visible - invalid — non-subscriber → controls hidden, subscription copy
- upstream-failure — subscriber +
providers:[]→ controls hidden, provider-outage copy, and the string does not mention an API key - boundary —
quota.remaining == 0→ distinct quota-exhausted copy
Acceptance criteria
- No user-facing string anywhere in the app instructs anyone to add an AI provider key.
- A subscriber during a provider outage sees AI hidden, not a broken instruction.
- Full E2E gate green.
Notes
work-consolidation.md tracks this as G30. Size XS — copy plus one visibility rule.
Activity
Adron commentedon Sep 15, 2026
The user-facing copy was fixed by PR #83; the stale comments around it are corrected by PR #88, which closes this out.
Confirmed live 2026-09-15 on a subscriber account:
AI is powered by Claude and provided by InterlinedList as part of the subscription — there is no user-supplied key. The copy now says an empty
providers[]is a service-side state and asks the user for nothing they cannot do, and the surrounding comments no longer argue the opposite.