Skip to content

Ask once for a star on GitHub, and count which moment earns it - #1553

Open
jerelvelarde wants to merge 5 commits into
CopilotKit:mainfrom
jerelvelarde:jerel/grow-647-star-the-repo-prompt
Open

jerelvelarde wants to merge 5 commits into
CopilotKit:mainfrom
jerelvelarde:jerel/grow-647-star-the-repo-prompt

Conversation

@jerelvelarde

Copy link
Copy Markdown
Contributor

Ask once for a star on GitHub, and count which moment earns it

What this changes

The problem

OpenBot is open source, and a star on GitHub is how other people find it: it decides whether the
project trends, and it is the first number a developer reads before trying it. The people best placed
to give one are the people already getting work done in OpenBot, and nothing in the app ever asks
them. Most run OpenBot Desktop or a self-hosted copy and never visit the repository again after
installing, so the people who use OpenBot most are the ones the repository page never reaches.

Asking badly costs more than it earns. An ask during onboarding comes before the person has seen
anything worth starring. An ask that repeats, or sits in a dialog in front of the work, makes the
product worse. A deployment running OpenBot for its own organization should not be asking its
employees for anything. And without knowing which moment works, the only signal is the repository's
daily star count, which moves with every launch and post and cannot say whether the ask did anything.

The approach

One card, once, after the person has had value. Once five of a person's messages from a browser
have been answered, a card above the message box says "Enjoying OpenBot? Star it on GitHub so more
people find it." with Not now beside a Star on GitHub link. It is the notifications offer's card, so it
reads as part of the composer and never covers the work. Either answer is saved to the person's
preferences (starPromptAnswered), so it is asked once on every device. One card at a time: it waits
while the notifications offer is open, and while OpenBot Desktop has not yet said whether
notifications are on, and never shows in a conversation that cannot take a message. The link is a
real link, opened in a new tab, or in the system browser from Desktop, where people are signed in to
GitHub.

Five messages, not a new Bot. Right after somebody creates a Bot is the other natural moment. It is
built behind one constant (STAR_PROMPT_TRIGGER in app/src/lib/star-prompt.ts) but is not the
default: a fresh install already has Bots, so many people never create one. Switching is one line.

What earned it lives in the browser; the answer lives on the account. Counting messages on the
server would be a write per message for a nudge. The answer has to follow the person, so it is what is
saved. The preference is optional and read as "not answered" when absent, so stored rows and fixtures
stay valid without a migration.

The operator decides first. OPENBOT_STAR_PROMPT=false (or 0) removes the card and the Settings
link for everybody, by the same rule as OPENBOT_SELF_HOST_BANNER. It reaches the browser as
starPrompt on /api/capabilities; a server too old to say reads as off.

A permanent, quiet way in. Settings → General gains an OpenBot section with "Star OpenBot on
GitHub". It stays after the card is answered, and pressing it also answers the card.

Counted per moment, as people. POST /api/telemetry/product-events accepts a second strict body,
{kind, trigger, surface}, for star_prompt_shown, star_prompt_clicked and star_prompt_dismissed.
The server forwards them as prompt_trigger and prompt_surface, named apart from the run events' own
trigger and surface, plus the person's existing pseudonym. The event ID is the person, kind and
trigger, so counts are people; the browser also reports a show once per trigger, so an ignored card is
not an event per conversation opened. Query F in docs/telemetry-queries.sql gives people shown,
clicking and dismissing per trigger. Clicked means the repository opened, not a star.

The cost is worth naming: one more card above the composer, and the moment that earns it is a guess
until the counts come in.

What is not covered

  • Messages are counted per browser: three on a laptop and three on a phone ask on neither.
  • A new conversation's first message counts when the conversation is made, even if its run then fails.
  • No split between moments in one release: compare by shipping one, reading query F, then switching.
  • The mobile app and external channels never ask. A browser that never answers the notifications
    offer is never asked to star.
  • The recording seeds the five messages: the local build it was made on has no AI backend.

Where it runs

  • New state that outlives a request? The answer, in users.preferences (Postgres jsonb). The
    message count and the "show reported" flag are in the browser's storage. Nothing in memory.
  • Second replica? Reads the same preferences row. The telemetry route keeps no state.
  • Anything serialised? The answer is one UPDATE … preferences || patch, so concurrent saves of
    other preferences are kept, and saving true twice is the same row.
  • Fanned out to a browser? No. Another device reads the answer on its next preferences load.
  • New listener, port, or schedule? None: a second body shape on an existing route.

Boundary and audit

  • No acting call is added; nothing here acts for a person or a Bot.
  • New refusals write no audit row: a malformed star event or preference patch is a 400, as the
    existing consumption events and preference patches are.
  • The actor and pseudonym are resolved on the server. trigger and surface come from the browser
    because only the browser knows which moment drew the card; both are strict enums.

Changelog

  • A line in CHANGELOG.md under Unreleased, Conversations. README.md, docs/configuration.md,
    .env.example and docs/TELEMETRY.md describe the switch and the events.

Verification

  • bun run typecheck, bunx biome format . and bunx biome lint --error-on-warnings . are clean.
  • Full app suite: 3,682 pass, 74 skip. The one error, in app-access-card-connect-before-tools.test.tsx,
    needs TEST_DATABASE_URL; given a test database, it passes.
  • app/tests/star-prompt.test.tsx (16): asked at the fifth answered message, not the fourth; never when
    the deployment turns it off, a server is too old to say, preferences are loading, failing or
    answered, the conversation cannot take a message, or Desktop is still checking notifications; the
    link's address, tab and rel; each button hides it, saves the answer and is counted; a refused count
    still hides it; a show is reported once per browser; storage that throws earns nothing; a refused
    send is not counted; creating a Bot earns only bot_created; the card waits behind the
    notifications offer. The counting tests fail with their code removed.
  • app/tests/star-on-github-setting.test.tsx (3): the link, its absence when off, and that pressing it
    counts a settings click and answers the card.
  • Server (239 across the five touched suites): the switch's values; starPrompt on
    /api/capabilities with both exact-body guards updated; saving the answer and refusing non-booleans;
    forwarding under prompt_* with only the pseudonym, one event ID per person, kind and trigger,
    refusing unknown values and extra fields, and sending nothing with telemetry off.
    user-preferences.integration and dev-actor.integration pass against a fresh database.
  • By hand on a local build: the card appears, Not now saves starPromptAnswered: true, it stays gone
    after a reload, and Settings shows the link.

Merge notes

/api/capabilities gains starPrompt, and the product-events route accepts a second body shape; both
are additive. A branch that edits the composer column in conversation-view.tsx or the exact-body
guards in server/tests/health.test.ts will need a small manual merge.

This branch has not been deployed

No deployments
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