Score extension publish events in the telemetry pipeline
Opened by stack72 · 4/9/2026· GitHub #228
Summary
Extension publishes (push + confirm) should contribute to a user's score through the telemetry pipeline, rewarding authors for publishing and maintaining extensions.
Motivation
Currently, extension scoring (lib/app/refresh-extension-score.ts) is computed by querying pull stats at score-refresh time — it rewards popularity (downloads) but not the act of publishing. Authors who publish multiple extensions or maintain frequent version updates get no direct score credit for that effort.
Publishing an extension is a meaningful contribution that should be scored alongside CLI usage and pull-based popularity.
Design sketch
- Event source:
confirmPushinlib/app/confirm-push.ts(or the telemetry extension consumer) emits a scorable event when an extension version is successfully published - Event shape:
{ event: "extension_publish", distinct_id: "...", properties: { username, extensionName, version } } - Scoring: Could be handled as a new scoring group in the activity algorithm (with its own cap), or as a fixed-point award per publish
- Deduplication: Same extension + version should only score once (idempotent on reprocessing)
Open questions
- Fixed points per publish vs. scaled by some factor (e.g., first publish worth more)?
- Should this be a new group in the activity scoring algorithm or a separate contribution type?
- Should version updates score the same as initial publishes?
- Relationship to #227 (award events) — could extension publishes just be a specific kind of award?
Closed
No activity in this phase yet.
stack72 commented 4/11/2026, 9:13:12 PM
Closing — the functional ask here already ships end-to-end. Extension publishes contribute to the author's score via the same telemetry→queue→refresh pipeline pulls do; they just use a different data source.
Pipeline trace for a publish:
- routes/api/v1/extensions/confirm.ts:110 fires track("extension_published", distinctId, { username, extension_name, version, ... }) after confirmPush succeeds.
- services/telemetry/lib/server.ts:196 lifts properties.username into eventUsername and writes it onto TelemetryEventDoc.username.
- services/telemetry/lib/watcher.ts:130 copies username onto the TelemetryEvent passed to the post-delivery callback.
- services/telemetry/lib/queue-producer.ts:20 — if (event.username) usernames.add(event.username) — the author is enqueued to swamp_club_events. (The extension_pulled-specific fan-out added in #56 sits alongside this generic path, not instead of it.)
- lib/infrastructure/event-queue-watcher.ts polls every ~5s → processScoreBatch → refreshUserScoreFromMetrics/FromApi → refreshExtensionScore.
- lib/app/refresh-extension-score.ts:38-60 calls extensionRepo.countVersionsByUser(operative.id), runs computeExtensionScore, and contributes the result as a distinct content:extension-publishes entry — separate from content:extension-pulls, with its own 5000-point cap.
Scoring constants (lib/domain/scoring/extension-score.ts):
- POINTS_PER_PUBLISH = 500, MAX_PUBLISH_SCORE = 5000
- Pinned by tests/app/refresh_extension_score_test.ts:110 (2 versions → 1000 publish points).
The open questions in the issue map to design choices already made:
- Fixed vs. scaled points? → Fixed: 500 per non-yanked version.
- New scoring group or separate contribution? → Separate contribution id content:extension-publishes within the existing content.extensions group.
- Version updates score the same as initial publishes? → Yes — countVersionsByUser counts every non-yanked version regardless of whether it's the first.
- Deduplication on reprocessing? → Satisfied structurally: the telemetry event is only a refresh trigger. The score is recomputed from extensions.versions, which confirmPush guards against duplicates via the VersionConflictError check.
One thing the issue proposes that isn't implemented is the event-sourced award model (emit a scorable event, grant points per insert_id, persist as an award record). That's a different shape — it would decouple score from countVersionsByUser and give "yank doesn't claw back points" semantics. That's a legitimate design question tied to #227 and worth its own issue if we want it; it's not a gap in what this issue asked for.
Known adjacent gaps that are out of scope for this issue and should be filed separately if we want them:
- Collective-token publishes (pushedByUserId: null in lib/infrastructure/mongo-extension-repository.ts:685) don't score any member of the collective.
- No explicit queue_producer_test.ts case for extension_published — the path works by construction but isn't pinned against regression.
Closing as already implemented.
Sign in to post a ripple.