Skip to content

feat(ai): offer new installs one AI title suggestion after their first analysis - #92

Merged
PAMulligan merged 1 commit into
mainfrom
feat/80-first-run-ai-try
Oct 24, 2026
Merged

PAMulligan merged 1 commit into
mainfrom
feat/80-first-run-ai-try

Conversation

@PAMulligan

Copy link
Copy Markdown
Contributor

Part B of #80: give new installs a reason to try the AI. Independent of #91 (the usage counts): the two share no files, and merged together locally they pass 595 unit tests and the 26 e2e tests.

What changed

  • What counts as a fresh install (app/src/lib/first-run-ai-try.ts): dismissing onboarding stores first_run_ai_try: "pending". The issue says "onboarding not yet dismissed", but the onboarding overlay covers the setup screen, so it is always dismissed before the first analysis can run. The flag carries "this install just went through onboarding" forward to that analysis. Installs that were past onboarding before this ships never get the flag, so existing users never see the prompt.
  • Once per install: the first completed analysis spends the flag ("done") whether or not the prompt shows. Pro, or a free allowance that is already used up, spends it without a prompt, and the prompt also hides if the install becomes Pro while it is showing. The decision waits for the entitlement to load, so a Pro install is never mistaken for a free one.
  • The prompt (app/src/components/FirstRunAiTry.tsx, on the score page between the score and the breakdown) reads "Try one AI suggestion for this page's title" with "Powered by Claude (Anthropic). Uses one of your 25 free AI recommendations this month.", a "Suggest a title" button and a dismiss X. The click calls generateRecommendation("title-keyword", keyword, pageData.title, advancedOptions), the call the Meta Tags breakdown makes. So it goes through /ai/generate, counts against the 25 free calls and shows the same error messages. The result appears inline in the usual EditableRecommendation box with its copy button, and stays while you move between the score and its sections.
  • The paywall and quota behavior are unchanged. docs/free-vs-pro-gating.md gains a row for the prompt.

Verification

pnpm -C app lint            clean
pnpm -C app tsc --noEmit    clean
pnpm -C app test            567 passed (35 files), 22 of them new
pnpm -C app build           ok
pnpm -C app test:e2e        26 passed (headed Chromium, local)
  • first-run-ai-try.test.ts: never for an install that was already past onboarding, once for a fresh one, no re-arming, overlapping calls share one claim (StrictMode runs effects twice), and Pro or a used-up allowance spends the flag without a prompt.
  • FirstRunAiTry.test.tsx: appears once and only on a fresh install, waits for the entitlement, sends the exact title request, shows the result inline with the copy button, keeps it after leaving the score page, shows the quota error inline and as a toast, and dismisses.
  • Onboarding.test.tsx (both ways of dismissing set the flag; an install that already dismissed onboarding gets nothing), two ScorePage cases, and an assertion in the freemium e2e test that Get Started stores the flag in the built extension.
  • Run in the dev harness against staging from a clean profile: onboarding, then an analysis of example.com, then the prompt, then one POST /ai/generate (200, free quota 25 → 24). A second analysis showed no prompt. Checked light and dark at side-panel width.

🤖 Generated with Claude Code

…t analysis

Dismissing onboarding marks an install as fresh (chrome.storage.local
"first_run_ai_try" = "pending"); installs that were past onboarding before
this shipped never get the prompt. The first completed analysis spends the
mark: on the free tier the score page shows one dismissible card, "Try one
AI suggestion for this page's title". One click sends the same hosted
/ai/generate title request as the Meta Tags breakdown, counted against the
25 free calls as today, and the result shows inline in the usual box with
its copy button. Pro, or an allowance already used up, spends the mark
without a prompt. The paywall and quota behavior are unchanged.

Part of #80.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PAMulligan
PAMulligan merged commit 251f33d into main Oct 24, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant