Skip to main content
← Back to list
01Issue
BugTriagedSwamp Club
Assigneeskeeb

Relationships

#850 Extension publish score is non-monotonic: yanking versions lowers a user's score

Opened by keeb · 6/27/2026

Summary

computeExtensionScore's publish term is non-monotonic: yanking extension versions lowers a user's score. This conflicts with the monotonic-ledger goal (scores should only ever accumulate).

Root cause

The publish input, countVersionsByUser (lib/infrastructure/mongo-extension-repository.ts), excludes yanked versions:

async countVersionsByUser(userId: string): Promise<number> {
  const notYanked = { yankedAt: { $in: [null, undefined] } };
  return await this.versions.countDocuments({ pushedByUserId: userId, ...notYanked }, AGG_OPTS);
}

Publish points = versionCount * POINTS_PER_PUBLISH (500/version). Yank a version and the count — and the score — drops. Example: 20 versions = 10,000 pts -> yank 5 -> 15 versions = 7,500 pts (loses 2,500).

Why it surfaced now

The pull/publish caps in lib/domain/scoring/extension-score.ts were removed (MAX_PUBLISH_SCORE = 5000). The cap was incidentally masking this: any author with >10 versions sat at the 5,000 ceiling, so yanking from 20->15 versions (10,000->7,500 raw) was invisible — both clamped to 5,000.

The cap removal did NOT create this bug; it unmasked a latent non-monotonicity that already existed in the formula.

Scope

  • Pulls are unaffected — countAuthorPulls reads append-only pull events; monotonic up.
  • Only the publish term regresses, only for authors who both had >10 versions AND yank. Bounded at 500/version.

Suggested fix

Make the publish input monotonic for scoring, independent of yank state. Options:

  1. Count versions ever-published (ignore yankedAt) for the scoring query specifically, or
  2. Track a per-user high-water mark of version count.

Note yank still needs to hide versions everywhere else (listings, registry) — only the scoring count should be yank-agnostic.

Verification

scripts/verify-uncapped-extension-score.ts (deno task verify:ext-score) proves pulls/publishes are uncapped, but does not yet assert publish-term monotonicity under yank. A regression check belongs there once the input is fixed.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW

Triaged

6/27/2026, 3:00:34 AM

Click a lifecycle step above to view its details.

03Sludge Pulse
keeb assigned keeb6/27/2026, 2:57:56 AM
Editable. Press Enter to edit.

keeb commented 6/27/2026, 3:11:43 AM

Deferred to #847 (monotonic-ledger + ClickHouse epic). This non-monotonic publish-score bug is a concrete symptom of the same recompute-from-counts model #847 replaces with an append-only score_grants ledger — fixing #847 makes this impossible by construction, so #850 is subsumed rather than shipped standalone.

Root cause (for #847 to absorb): countVersionsByUser (lib/infrastructure/mongo-extension-repository.ts) excludes yanked versions; that count feeds computeExtensionScore → publishScore, and UserScore.contribute() overwrites the bucket, so a recompute after a yank strictly lowers the stored total. Currently masked on main by MAX_PUBLISH_SCORE=5000 (cap removal #763 was reverted) — it surfaces the moment caps are removed again.

Agreed remediation (forward-compatible with the ledger): rename countVersionsByUser → countPublishedVersionsByUser, drop the yank filter (count versions ever published; the count is scoring-only and versions are append-only), and guard it with a real-Mongo scripts/ harness. De-defer trigger: any scheduled publish-cap removal before #847 lands.

Sign in to post a ripple.