Skip to main content
← Back to list
01Issue
FeatureOpenSwamp CLI
AssigneesNone

Relationships

#854 Epic #847 · Unit 2: score_grants append-only ledger write-model in Mongo (shadow, no read flip)

Opened by keeb · 6/27/2026

Part of epic #847. Unit 2 of 6. Prerequisite: none — pure Mongo domain work; may proceed in parallel with #853 (ClickHouse foundation). Unit 3 (#TBD) builds on this.

Problem

UserScore.contribute(key, …, points) (lib/domain/scoring/user-score.ts:129-144) overwrites a single bucket per key and _recompute() sums buckets — scoring is recompute-from-counts, which is non-monotonic. The target (specced in scoring/scoring.md → "Scoring model — monotonic XP ledger") is an append-only score_grants ledger with a running balance that is never re-derived.

Scope

  1. New score_grants collection with a unique index on grantId (append-once / idempotent).
  2. UserScore.grant(grantId, key, category, source, amount, grantedAt) — additive, idempotent; increments running-balance fields (totalScore, categoryTotals, sourceTotals) on the doc rather than recomputing from counts.
  3. Route the two already-grant-shaped contributions (content:feed-post:<id>, award📛<id>) through grant() with no behavior change — they are the migration template.
  4. Shadow mode: keep contribute() and the existing recompute total intact; the running balance is computed in parallel. No user-facing read flips in this unit.

DDD

Extends the existing Scoring aggregate (lib/domain/scoring/). grant() is a command (writes only); the append-once invariant is enforced by the unique grantId index in infrastructure. No new aggregate. Repo registered as a traced repo in lib/app/repos.ts.

Acceptance

  • Grants persist; a duplicate grantId is a no-op (unique-index race → second insert rejected, handled gracefully).
  • Running balance == Σ grant amounts.
  • feed-post and badge contributions are unchanged in observable behavior.
  • Leaderboards still read the legacy recompute total (no flip).

Tests

  • tests/domain/ — grant idempotency, accumulation, category/source totals.
  • tests/infrastructure/ — unique-index enforcement on grantId (duplicate-key race).

Files

  • lib/domain/scoring/user-score.ts
  • lib/repositories/ + lib/infrastructure/mongo-* (score_grants repository)
  • lib/app/repos.ts (register traced repo)
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

6/27/2026, 3:33:37 AM

No activity in this phase yet.

03Sludge Pulse

Sign in to post a ripple.