Skip to content

Conversation

@marcodejongh
Copy link
Owner

Summary

  • Adds duplicate detection when saving climbs based on holds hash
  • A duplicate is defined as a climb with the same holds AND same hold states, scoped to the same board type and layout
  • Uses a holds_hash column with index for O(1) duplicate lookup instead of expensive JOINs

Changes

Database

  • Added holds_hash column to board_climbs table
  • Added composite index on (board_type, layout_id, holds_hash) for efficient lookups

Backend

  • Created generateHoldsHash() utility that produces deterministic hashes from frames
  • Updated saveClimb to check for duplicates before inserting
  • Returns isDuplicate, existingClimbUuid, and existingClimbName if duplicate found
  • Updated shared-sync to compute hash for new climbs and backfill existing ones

Frontend

  • Added duplicate warning alert in create climb form
  • Shows "View Existing Climb" button to navigate to the duplicate
  • Provides "Dismiss" option to close the warning

Scripts

  • Added backfill-holds-hash.ts script to populate hash for existing climbs

Test plan

  • Create a new climb with unique holds → should save successfully
  • Try to create a climb with identical holds → should show duplicate warning
  • Click "View Existing Climb" → should navigate to the existing climb
  • Create climb with same holds but different states (e.g., start vs hand) → should NOT be detected as duplicate
  • Run npm run build:web → passes
  • Apply migration with npm run db:migrate

🤖 Generated with Claude Code

marcodejongh and others added 2 commits January 24, 2026 10:02
- Add frames encoding utility (`encodeMoonBoardHoldsToFrames`) to convert
  holds to the `p{holdId}r{roleCode}` format used by the database
- Update saveClimb API to accept Moonboard requests with holds format
  and convert to frames before saving
- Update MoonBoardCreateClimbForm to require authentication and save
  climbs via the API instead of IndexedDB
- Update MoonBoardBulkImport similarly for batch saves
- Add direct database query for Moonboard climbs in list page since
  GraphQL backend doesn't support Moonboard yet
- Skip GraphQL search and playlist operations for Moonboard to prevent
  errors
- Add getMoonBoardDetails helper throughout codebase to handle Moonboard
  board details separately from Aurora boards
- Enable MOONBOARD_ENABLED feature flag by default
- Remove IndexedDB saving functions (no longer needed)
- Restructure create form UI with header containing save button

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a holdsHash column to board_climbs for O(1) duplicate detection.
When creating a climb, the system checks if an identical set of holds
already exists (same board type, layout, and holds/states).

Changes:
- Add holdsHash column and index to board_climbs schema
- Create holds-hash utility for deterministic hash generation
- Update saveClimb to check for duplicates before inserting
- Show duplicate warning in UI with link to existing climb
- Compute hash during Aurora sync for new and existing climbs
- Add backfill script for populating hash on existing climbs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Jan 23, 2026

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

Project Deployment Review Updated (UTC)
boardsesh Building Building Preview, Comment Jan 24, 2026 0:09am

Request Review

@claude
Copy link

claude bot commented Jan 23, 2026

Claude Review

Ready to merge - Minor issues noted below, but nothing blocking.

Issues

  1. Migration journal idx mismatch - packages/db/drizzle/meta/_journal.json:24: New migration uses idx: 37 but the previous entry is idx: 36. The journal already has non-sequential idx values (note idx 24-29 are out of order), so this is consistent with existing patterns, but worth awareness.

  2. Unused import - packages/web/app/lib/api-wrappers/aurora/saveClimb.ts:5: convertLitUpHoldsStringToMap is imported and used, but climbHolds insert (lines 134-145) duplicates hold parsing logic that could fail silently for certain edge cases the conversion utility handles.

  3. CSS class missing - packages/web/app/components/create-climb/moonboard-create-climb-form.tsx:304: References styles.holdCountsBar but the CSS module only defines styles.holdCounts - this will result in no styling being applied.

  4. Backfill script updates one-by-one - packages/db/scripts/backfill-holds-hash.ts:93-103: Updates each climb individually in a loop. For large datasets, consider batching updates for performance.

  5. Error message check fragile - packages/web/app/lib/api-wrappers/aurora/saveClimb.ts:102: Race condition handling checks for index name in error message string (board_climbs_holds_hash_unique_idx). This is fragile - different databases or pg versions may format errors differently. Consider catching specific PostgreSQL error code 23505 (unique_violation) instead.

- Add unit tests for generateHoldsHash utility (18 tests)
- Move hash utility to @boardsesh/db to eliminate code duplication
- Add unique partial index for atomic duplicate prevention
- Handle race condition by catching unique constraint violations
- Re-export hash functions from web package for backward compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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.

2 participants