Skip to content

feat(usage): anonymous daily count of pages analyzed, with an opt-out toggle in Options - #91

Merged
PAMulligan merged 2 commits into
mainfrom
feat/80-analysis-usage-counts
Oct 24, 2026
Merged

PAMulligan merged 2 commits into
mainfrom
feat/80-analysis-usage-counts

Conversation

@PAMulligan

Copy link
Copy Markdown
Contributor

Part A of #80: count page analyses so the 10/30 review can set the free ceiling from real numbers. The backend half (POST /usage/analysis and the metric column) is PMDevSolutions/optia-backend#30, which has no branch yet. Until it ships, every report gets a 404 and is tried again the next day; nothing user-visible breaks.

What changed

  • Counting (app/src/lib/usage.ts): each completed analysis adds one to this install's count for the month, in chrome.storage.local under usage_analyses (for example { "2026-10": 41, "2026-11": 12 }; only the current and previous month are kept). The call sits in handleAnalyze right after calculateAnalysis, which has no other caller, so scoring.ts stays a pure function. Failed analyses and restored tabs don't count.
  • Report: right after an analysis, if sharing is on and no report was attempted in the past 24 hours, the side panel POSTs { installId, period, count } to ${BACKEND_BASE_URL}/usage/analysis. That is the month's running total, plus last month's final total once when it grew after that month's last report. It uses the same getInstallId() as ai-proxy.ts and sends no other fields, headers or credentials. A failed attempt (offline, an error status, the endpoint missing) still uses up the day, so the total goes out the next day. Counts are capped at the backend's 10,000.
  • Options: a "Share anonymous usage counts" toggle (on by default; it saves immediately, like "Use my Anthropic key"; off keeps the local count and sends nothing) and "N pages analyzed this month" under the AI quota line, which updates while the side panel works.
  • No analysis limit is enforced, and app/manifest.json is untouched, so no new permissions. The report is the only new network call.

Why the report leaves from the side panel and not a background alarm. The live store listing and the landing page say requests are sent "never in the background". Sending right after an analysis keeps that true and needs no service-worker change. The cost: an install that stops using Optia never reports its last partial day, which an alarm would catch. Heavy users, the ones the ceiling is about, report every day.

Worth knowing for #30. Periods are UTC months (toISOString().slice(0, 7), the same as the AI quota), so the backend's current-or-previous-month check should be UTC as well. Turning sharing back on sends the month's running total, which includes pages analyzed while it was off.

Privacy and store copy

The data-flow rule puts these in the same PR:

  • site/privacy.html and docs/privacy-policy.md (kept identical): a new section 4 names the install ID, the month and the count as the only things the report sends, and says how to turn it off. The short version gains a bullet, and "no analytics" becomes "no analytics services". Section 3 is corrected: it said the install ID existed "solely" for AI metering, but checkout and license activation have always sent it too. Last updated 2026-10-24. The page deploys when this merges.
  • docs/chrome-web-store-data-disclosure.md: User activity changes from No to Yes, with the detail text.
  • The PRIVACY FIRST paragraph in docs/chrome-web-store-listing.md, the privacy blurb in site/index.html, and README.md no longer claim "no analytics" outright; they describe the count and the toggle.
  • docs/architecture.md: the new route in the repo table, the diagram and the notes.

Dashboard steps for Paul

  1. Privacy practices form (Developer Dashboard → Optia → Privacy): mark User activity as collected, with the detail from docs/chrome-web-store-data-disclosure.md. Do it when you upload the release that carries this change, so the disclosure matches the build under review.
  2. Detailed description: replace the PRIVACY FIRST paragraph with the one in docs/chrome-web-store-listing.md. The live one still says "no analytics".
  3. Left alone here: brand-guidelines.json still lists "No tracking, no ads, no analytics, and your data is never sold." as an example phrase (changing the lockfile is a brand-session job), and docs/product-hunt-launch.md and docs/launch-social-posts.md repeat "no analytics" for any post not yet published.

Verification

pnpm -C app lint            clean
pnpm -C app tsc --noEmit    clean
pnpm -C app test            573 passed (34 files), 28 of them new
pnpm -C app build           ok
pnpm -C app test:e2e        26 passed (headed Chromium, local)
  • usage.test.ts: one increment per analysis, none lost when two finish together, rollover by UTC month, at most one report a day, offline today sends the next day, an error status behaves the same, nothing sent with the toggle off, last month's tail, the 10,000 cap, overlapping calls share one report. Two mutations (removing the sharing check, removing the record-before-send write) each fail the tests meant to catch them.
  • App.test.tsx: a completed analysis counts once, a failed one doesn't count, and a storage failure never blocks the results.
  • Options.test.tsx: the count line on Free and Pro, the singular form, current month only, live update, the toggle on by default and saving immediately.
  • Looked at: the Options page in the built extension (light, dark, sharing off), and the privacy page and landing blurb at 390 and 1280 px. Neither site page has dark-mode CSS today, so both render light either way.

Release notes: the PR title names the daily count and the toggle, as #80 asks. #80's last acceptance item, the ops query showing a real install's count, needs #30 deployed and a release.

🤖 Generated with Claude Code

PAMulligan and others added 2 commits October 24, 2026 05:16
… toggle in Options

Each completed analysis adds one to this install's count for the UTC month
(chrome.storage.local "usage_analyses", current and previous month only).
At most once a day, right after an analysis, the side panel POSTs
{ installId, period, count } to /usage/analysis with the month's running
total, plus the previous month's final total when it grew after that
month's last report. A failed report (offline, an error status, the
endpoint not live yet) waits a day. Nothing else is sent: no URLs, page
content, keywords or scores. No analysis limit is enforced.

Options gets "Share anonymous usage counts" (default on, saves at once;
off keeps the local count and sends nothing) and "N pages analyzed this
month" under the AI quota line.

Part of #80 (backend half: PMDevSolutions/optia-backend#30).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e disclosure and listing

The published policy (site/privacy.html, mirrored in docs/privacy-policy.md)
names the install ID, the month and the count as the only data the report
sends, says how to turn it off, and corrects the install ID section, which
claimed the ID existed solely for AI metering although license activation
and checkout send it too. The store disclosure now answers User activity
"Yes"; the listing's PRIVACY FIRST paragraph, the landing page blurb and
the README drop the flat "no analytics" claim; architecture.md shows the
new call.

Part of #80.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PAMulligan
PAMulligan merged commit 25d55cf 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