Skip to content

fix: preserve multi-machine submissions on the leaderboard#376

Open
haridigresses wants to merge 7 commits intojunhoyeo:mainfrom
haridigresses:codex/fix-source-scoped-submit-merge
Open

fix: preserve multi-machine submissions on the leaderboard#376
haridigresses wants to merge 7 commits intojunhoyeo:mainfrom
haridigresses:codex/fix-source-scoped-submit-merge

Conversation

@haridigresses
Copy link
Copy Markdown

@haridigresses haridigresses commented Mar 31, 2026

Summary

This fixes the leaderboard overwrite case for users who submit from more than one machine.

Instead of merging every submit into a single submissions row per user, submissions can now be optionally source-scoped:

  • submits with meta.sourceId write to a (user_id, source_id) row
  • submits without sourceId keep the existing unsourced behavior
  • leaderboard/profile/embed reads continue to aggregate across all of a user's submission rows

This keeps the fix modest and aligned with the existing schema and aggregation patterns, without introducing nested per-source instances into daily_breakdown.source_breakdown.

Repro

Before this patch:

  1. Submit usage for user X from machine A with claude activity.
  2. Submit usage for the same user X from machine B with claude activity.
  3. Observe that the later submission overwrites the earlier machine's contribution for overlapping days.

Expected behavior:

  • both machines contribute to the user total
  • re-submitting from one machine replaces only that source's prior contribution
  • unsourced/older submissions keep current behavior

What changed

  • added optional source_id and source_name to submissions
  • replaced single-row-per-user uniqueness with:
    • one unsourced row per user
    • many sourced rows per user
  • updated /api/submit to target the correct submission row by source when provided
  • updated profile, leaderboard, and embed queries to aggregate across a user's submission rows
  • added CLI source metadata for submit payloads only
  • documented TOKSCALE_SOURCE_ID and TOKSCALE_SOURCE_NAME
  • added focused regression tests for source-scoped submit behavior and embed/profile aggregation

Validation

Automated:

  • DATABASE_URL=postgres://localhost:5432/tokscale bun run db:generate
  • bunx vitest run __tests__/api/submitSourceScope.test.ts __tests__/lib/getUserEmbedStats.test.ts __tests__/api/usersProfile.test.ts __tests__/api/submitAuth.test.ts __tests__/lib/getLeaderboardAllTime.test.ts __tests__/lib/getLeaderboard.test.ts

Both passed.

Notes

  • TOKSCALE_SOURCE_NAME is stored per sourceId; a later submit with the same sourceId and a new non-empty name updates that source's display name.
  • I did not build the Rust CLI in this environment because cargo was unavailable here.
  • bunx tsc --noEmit still fails on an unrelated existing import in packages/frontend/src/components/BlackholeHero.tsx.

Summary by cubic

Preserves multi‑machine submissions by scoping per source and aggregating totals across a user’s sources. Adds robust CLI source ID persistence with a stale‑lock cleanup fallback and normalizes profile/leaderboard/embed aggregates.

  • Bug Fixes

    • submissions are source‑scoped: rows keyed by (user_id, source_id); unsourced rows remain; empty/whitespace sourceId/sourceName are ignored; optional source metadata failures are tolerated.
    • /api/submit targets the correct per‑source row, updates sourceName only when provided, handles missing/invalid source metadata, and computes response metrics after commit.
    • Profile, leaderboard, and embed aggregate by summing across all a user’s rows; submissionCount uses sums; updatedAt uses the latest submit; embed ranks are normalized from string results.
    • crates/tokscale-cli includes sourceId/sourceName in submit meta, persists a stable per‑machine source ID with a small file lock, and cleans up stale locks by age if needed; supports TOKSCALE_SOURCE_ID, TOKSCALE_SOURCE_NAME, and TOKSCALE_API_URL.
  • Migration

    • Run DB migration 0005 to add source_id/source_name and replace single‑row uniqueness with per‑source partial unique indexes.
    • No changes needed for existing unsourced submits; reads aggregate across rows.

Written for commit 9eda26d. Summary will update on new commits.

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 31, 2026

@haridigresses is attempting to deploy a commit to the Inevitable Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 issues found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/tokscale-cli/src/auth.rs">

<violation number="1" location="crates/tokscale-cli/src/auth.rs:153">
P1: Lock ownership is not tracked: stale-lock stealing plus unconditional lock-file deletion can break mutual exclusion and allow concurrent source-id writes.</violation>

<violation number="2" location="crates/tokscale-cli/src/auth.rs:171">
P2: Source-ID lock recovery window is inconsistent: acquisition times out (~2.5s) before stale-lock cleanup (10s), causing predictable transient failures after crashes.</violation>
</file>

<file name="packages/frontend/__tests__/api/submitSourceScope.test.ts">

<violation number="1" location="packages/frontend/__tests__/api/submitSourceScope.test.ts:385">
P2: Test coverage misses the existing `(userId, sourceId)` re-submit path, so overwrite/replace behavior can regress undetected.</violation>

<violation number="2" location="packages/frontend/__tests__/api/submitSourceScope.test.ts:410">
P2: Test is brittle because it depends on `whereCalls[0]` (exact call order) instead of asserting that the expected submissions lookup exists anywhere in captured calls.</violation>
</file>

<file name="packages/frontend/src/app/api/submit/route.ts">

<violation number="1" location="packages/frontend/src/app/api/submit/route.ts:88">
P1: `loadUserSubmitMetrics` reads Drizzle `select` results as objects instead of row arrays, causing response metrics to fall back to zero/null.</violation>

<violation number="2" location="packages/frontend/src/app/api/submit/route.ts:227">
P2: Concurrent first submissions can race between lookup and insert, causing unique-constraint errors to surface as 500 due to missing conflict handling/retry.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@haridigresses haridigresses marked this pull request as draft March 31, 2026 05:45
@haridigresses haridigresses marked this pull request as ready for review March 31, 2026 06:08
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/frontend/src/lib/validation/submission.ts">

<violation number="1" location="packages/frontend/src/lib/validation/submission.ts:87">
P2: Optional source metadata currently rejects blank/whitespace values, causing full payload validation failure instead of treating metadata as absent.</violation>
</file>

<file name="packages/frontend/src/app/api/submit/route.ts">

<violation number="1" location="packages/frontend/src/app/api/submit/route.ts:67">
P2: `loadUserSubmitMetrics` counts all distinct `dailyBreakdown.date` rows, but elsewhere active days are defined as days with `tokens > 0`. If any zero-token daily rows exist, this will over-report active days and make the response inconsistent with the rest of the metrics logic.</violation>
</file>

<file name="crates/tokscale-cli/src/auth.rs">

<violation number="1" location="crates/tokscale-cli/src/auth.rs:216">
P1: Lock liveness check treats command execution failures as "owner dead", enabling false stale-lock removal and possible concurrent entry.</violation>

<violation number="2" location="crates/tokscale-cli/src/auth.rs:370">
P1: Submit can fail entirely when optional source-id lock/file operations fail, because source metadata errors are propagated with `?` instead of degrading to no sourceId.</violation>
</file>

<file name="packages/frontend/__tests__/lib/getUserEmbedStats.test.ts">

<violation number="1" location="packages/frontend/__tests__/lib/getUserEmbedStats.test.ts:128">
P2: Tests only validate numeric rank mocks and miss string-return normalization, allowing runtime type regressions in `rank` to slip through.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 8 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/tokscale-cli/src/auth.rs">

<violation number="1" location="crates/tokscale-cli/src/auth.rs:309">
P2: Stale lock cleanup no longer runs when the liveness probe fails. `lock_owner_is_alive` returns `None` on command errors, but `owner_is_dead` only matches `Some(false)`, so a stale lock with a valid state will never be removed if the probe command is unavailable. That can cause lock acquisition to time out and block submissions in constrained environments.</violation>
</file>

<file name="packages/frontend/__tests__/lib/validateSubmission.test.ts">

<violation number="1" location="packages/frontend/__tests__/lib/validateSubmission.test.ts:83">
P3: Optional chaining in these assertions allows the test to pass even if `result.data` is missing. Add an explicit assertion that `result.data` exists (or use a non-null assertion) before checking nested fields so the test enforces the valid-submission contract.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

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