Skip to content

bug(ai): AI copy tells subscribers to add an API key that no longer exists (G30) #39

Closed

Description

@Adron

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. When subscriber == true and providers is 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." When subscriber == 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.remaining is 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

  1. added
    bugSomething isn't working
    parityWeb-parity gap with the InterlinedList web app
    on Sep 7, 2026
  2. Adron commented on Sep 15, 2026

    @Adron
    MemberAuthor

    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:

    GET /api/ai/status → 200 {"subscriber":true,"providers":["anthropic"],
                              "defaultModels":{"anthropic":"claude-sonnet-5",…},
                              "quota":{"usedToday":0,"dailyLimit":50,"remaining":50}}
    

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingparityWeb-parity gap with the InterlinedList web app

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions