fix(submit): adopt legacy rows for first device submit
Summary
- Fixes #248 by completing the remaining legacy-to-device cutover for multi-machine submissions.
- Keeps the merged device-aware submit model from #517: modern devices still get separate daily buckets, and totals are still recalculated from all stored daily rows.
- Adds regression coverage for the legacy cutover write path and for profile aggregation when multiple submitted devices have same-date rows.
Why
#517 fixed the normal modern case where multiple computers submit with stable device IDs, but migrated historical rows still live in the legacy-default device bucket. On current main, the first modern device-aware submit only looks for rows attached to the new device ID, so it can insert a new same-date row while the legacy row remains counted. That can double-count the same historical usage during the upgrade path and leave #248 reproducible for users with pre-device data.
Diff scope
packages/frontend/src/app/api/submit/route.ts: before writing incoming daily rows, the submit transaction now adopts a user's legacy daily rows into the first real device bucket when no modern device rows exist yet for that submission.packages/frontend/__tests__/api/submitAuth.test.ts: adds a regression test that fails on currentmainbecause the route inserts a duplicate daily row instead of adopting the legacy row.packages/frontend/__tests__/api/usersProfile.test.ts: adds read-path coverage proving same-date rows from multiple submitted devices render as one profile contribution with summed totals.
Branch integrity
- Base branch:
main - Validated base SHA:
64e278cdc4f55544576f3b42010644cd8ba6aa9d - Head SHA:
49a01dd44d89dc88ce55cd209f33569e53b1f10a - Ahead/behind:
0 behind / 1 ahead - Merge base:
64e278cdc4f55544576f3b42010644cd8ba6aa9d
Commit integrity
- Introduced commit:
49a01dd44d89dc88ce55cd209f33569e53b1f10a fix(submit): adopt legacy rows for first device submit - The PR contains one logical data-integrity fix plus targeted regression tests.
Diff hygiene
git diff --name-status origin/main...HEAD: PASS, only the submit route and two targeted frontend test files changed.git diff --check origin/main...HEAD: PASS, no output.git diff --cached --check: PASS, no output before commit.
Validation mode and proof
- Validation mode: Mode 3 - submit data-integrity change. The diff changes how legacy
daily_breakdownrows are attributed during the device-aware submit transition, so targeted submit/profile/leaderboard coverage is required. bun x vitest run __tests__/api/submitAuth.test.ts: PASS, 7 tests.bun x vitest run __tests__/api/submitAuth.test.ts __tests__/api/usersProfile.test.ts: PASS, 13 tests.bun x vitest run __tests__/api/submitAuth.test.ts __tests__/api/usersProfile.test.ts __tests__/lib/getLeaderboard.test.ts __tests__/lib/getGroupLeaderboard.test.ts: PASS, 22 tests.bun x vitest run __tests__/lib/getLeaderboardAllTime.test.ts: PASS, 4 tests.bun run lint -- src/app/api/submit/route.ts __tests__/api/submitAuth.test.ts __tests__/api/usersProfile.test.ts: PASS.bash scripts/check-version-coherence.sh: PASS,Version coherence OK: 2.1.3.- Full frontend suite and Rust suite: not run locally because they are not required for this selected validation mode; the local proof targets the changed submit write path, profile read path, and public leaderboard aggregation.
Ledger and version proof
- Ledger: not applicable - not required for selected validation mode/change family.
- Version: PASS via
bash scripts/check-version-coherence.sh.
Migration notes
- Not applicable - no DB migration changed.
- The fix uses the existing
submitted_devicestable and existingdaily_breakdown.submitted_device_iduniqueness model.
Runtime safety
- The adoption runs inside the existing
/api/submittransaction after the user submission row is selected withFOR UPDATE. - Legacy rows are adopted only when the target modern device has no rows yet and the submission has no existing modern device rows, avoiding arbitrary reassignment for mixed/ambiguous accounts.
- No new background jobs, blocking locks outside the existing transaction, unbounded queues, or schema invariants are introduced.
- No invariant regression introduced.
CI context confirmation
- Pending - remote CI has not run because this PR has not been opened yet.
- CI context names unchanged.
Rollback plan
- Rollback: revert this PR.
- DB downgrade: not applicable.
- Data repair: not applicable.
- Operational caveats: none known.
Known residual risks
- Existing accounts that already have both legacy rows and modern device rows are intentionally not auto-reassigned because the original device attribution is ambiguous.
- Remote CI remains pending until the PR is opened.
Summary by cubic
Fixes #248 by adopting legacy daily_breakdown rows into the first modern submitted device on the first device-aware submit, preventing duplicate same-date rows and double-counted totals. Keeps #517 behavior: modern devices still have separate daily buckets and totals are recalculated from all rows.
- Bug Fixes
- In
packages/frontend/src/app/api/submit/route.ts, inside the/api/submittransaction, when no modern-device rows exist for the submission and the incoming device is modern, reassign that submission’s legacy rows to the device; otherwise keep legacy rows separate. - Adds regression tests in
packages/frontend/__tests__/api/submitAuth.test.tsandpackages/frontend/__tests__/api/usersProfile.test.tscovering adoption (no duplicate insert), preserving legacy rows when another modern device already submitted, and profile aggregation into a single per-date contribution with summed totals.
- In