Repository navigation
Ask once for a star on GitHub, and count which moment earns it - #1553
Open
jerelvelarde wants to merge 5 commits into
Open
jerelvelarde wants to merge 5 commits into
jerelvelarde wants to merge 5 commits into
Conversation
…versations, report a show once, and name its telemetry apart from runs
jerelvelarde
requested review from
MikeRyanDev,
davidmckayv,
dustywusty,
guidovizoso,
mxmzb,
samjulien and
tylerslaton
as code owners
November 13, 2026 08:40
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 waitswhile 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_TRIGGERinapp/src/lib/star-prompt.ts) but is not thedefault: 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(or0) removes the card and the Settingslink for everybody, by the same rule as
OPENBOT_SELF_HOST_BANNER. It reaches the browser asstarPrompton/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-eventsaccepts a second strict body,{kind, trigger, surface}, forstar_prompt_shown,star_prompt_clickedandstar_prompt_dismissed.The server forwards them as
prompt_triggerandprompt_surface, named apart from the run events' owntriggerandsurface, plus the person's existing pseudonym. The event ID is the person, kind andtrigger, 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.sqlgives 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
offer is never asked to star.
Where it runs
users.preferences(Postgres jsonb). Themessage count and the "show reported" flag are in the browser's storage. Nothing in memory.
UPDATE … preferences || patch, so concurrent saves ofother preferences are kept, and saving
truetwice is the same row.Boundary and audit
existing consumption events and preference patches are.
triggerandsurfacecome from the browserbecause only the browser knows which moment drew the card; both are strict enums.
Changelog
CHANGELOG.mdunderUnreleased, Conversations.README.md,docs/configuration.md,.env.exampleanddocs/TELEMETRY.mddescribe the switch and the events.Verification
bun run typecheck,bunx biome format .andbunx biome lint --error-on-warnings .are clean.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 whenthe 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 countstill 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 thenotifications 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 itcounts a
settingsclick and answers the card.starPrompton/api/capabilitieswith 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.integrationanddev-actor.integrationpass against a fresh database.starPromptAnswered: true, it stays goneafter a reload, and Settings shows the link.
Merge notes
/api/capabilitiesgainsstarPrompt, and the product-events route accepts a second body shape; bothare additive. A branch that edits the composer column in
conversation-view.tsxor the exact-bodyguards in
server/tests/health.test.tswill need a small manual merge.