Skip to content

fix(valuation): deterministic oldest-first canonical pick (chat#1889 row 10) - #794

Open
sweetmantech wants to merge 1 commit into
mainfrom
fix/canonical-pick-oldest-first
Open

fix(valuation): deterministic oldest-first canonical pick (chat#1889 row 10)#794
sweetmantech wants to merge 1 commit into
mainfrom
fix/canonical-pick-oldest-first

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Row 10 of the canonical-artist trilogy on chat#1889.

findCanonicalArtistBySpotifyId iterated socials newest-first, and enrichSearchedArtistProfile bumps socials.updated_at mid-flow — so the pick could flip between two lookups inside one add. Oldest-first is stable, and the oldest social is the true canonical (prod anchor: Ana Bárbara's 2025-05-30 row, shared by OneRPM org accounts + sweetmantech@gmail.com). No account context — per the 2026-07-29 decision that closed api#792.

TDD red→green: the new test stages a newest social resolving to a fresh duplicate and an older one resolving to the true canonical, and asserts the old one wins. 145 valuation+artists tests pass; tsc at the 236 baseline; eslint clean.

Sequencing: correctness is complete once database#49 (row 9) has collapsed existing duplicates — until then oldest-first is still a better pick than newest-first (stable + favors the true canonical) with no downside. Code-independent of api#793; no merge conflict (different files).

Tracked in chat#1889 (matrix row 10).

🤖 Generated with Claude Code


Summary by cubic

Make canonical artist selection deterministic by iterating socials oldest-first in findCanonicalArtistBySpotifyId, preventing flips when enrichSearchedArtistProfile updates socials.updated_at. Supports chat#1889 (row 10) and works without account context.

  • Bug Fixes
    • Sort socials by updated_at ascending and traverse oldest-first to favor the true canonical.
    • Add a test where the newest social maps to a duplicate and the oldest maps to the canonical; asserts the oldest wins.

Written for commit 475ec6b. Summary will update on new commits.

Review in cubic

…row 10)

findCanonicalArtistBySpotifyId iterated socials newest-first, and
enrichSearchedArtistProfile bumps updated_at mid-flow -- so the pick
could flip between the creation call and the valuation fallback within
one add. Oldest-first is stable, and the oldest social is the true
canonical (verified on prod: Ana Barbara's is the 2025-05-30 row that
OneRPM org accounts + sweetmantech@gmail.com already share). No account
context, per the 2026-07-29 decision.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 29, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
api Ready Ready Preview Jul 29, 2026 1:19pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@sweetmantech, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0304f3ea-681a-46b5-b64b-5a13bf26b8df

📥 Commits

Reviewing files that changed from the base of the PR and between 0b222d1 and 475ec6b.

⛔ Files ignored due to path filters (1)
  • lib/valuation/__tests__/findCanonicalArtistBySpotifyId.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (1)
  • lib/valuation/findCanonicalArtistBySpotifyId.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 2 files

Confidence score: 4/5

  • In lib/valuation/findCanonicalArtistBySpotifyId.ts, ties on equal updated_at are left to Supabase’s unspecified row order, so canonical artist selection can vary across runs and cause inconsistent valuation results; add a deterministic secondary sort key (for example id) to make tie-breaking stable.
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="lib/valuation/findCanonicalArtistBySpotifyId.ts">

<violation number="1" location="lib/valuation/findCanonicalArtistBySpotifyId.ts:31">
P2: Equal `updated_at` values are not deterministically resolved: this comparator returns `0`, preserving the unspecified order returned by Supabase. Adding a stable secondary key (such as `id`) would prevent the canonical pick from flipping when socials share the same timestamp.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

// pick can flip between two lookups in the same add (chat#1889 row 10).
// The oldest social is the stable, true canonical.
const oldestFirst = [...socials].sort(
(a, b) => new Date(a.updated_at ?? 0).getTime() - new Date(b.updated_at ?? 0).getTime(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Equal updated_at values are not deterministically resolved: this comparator returns 0, preserving the unspecified order returned by Supabase. Adding a stable secondary key (such as id) would prevent the canonical pick from flipping when socials share the same timestamp.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/valuation/findCanonicalArtistBySpotifyId.ts, line 31:

<comment>Equal `updated_at` values are not deterministically resolved: this comparator returns `0`, preserving the unspecified order returned by Supabase. Adding a stable secondary key (such as `id`) would prevent the canonical pick from flipping when socials share the same timestamp.</comment>

<file context>
@@ -24,7 +24,14 @@ export async function findCanonicalArtistBySpotifyId(
+    // pick can flip between two lookups in the same add (chat#1889 row 10).
+    // The oldest social is the stable, true canonical.
+    const oldestFirst = [...socials].sort(
+      (a, b) => new Date(a.updated_at ?? 0).getTime() - new Date(b.updated_at ?? 0).getTime(),
+    );
+
</file context>
Suggested change
(a, b) => new Date(a.updated_at ?? 0).getTime() - new Date(b.updated_at ?? 0).getTime(),
(a, b) =>
new Date(a.updated_at ?? 0).getTime() - new Date(b.updated_at ?? 0).getTime() ||
a.id.localeCompare(b.id),

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