Claude-skill-registry implement-playlists
Plan and implement Stage 6 Playlists with CRUD endpoints, track management, and reordering (plan)
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/implement-playlists" ~/.claude/skills/majiayu000-claude-skill-registry-implement-playlists && rm -rf "$T"
manifest:
skills/data/implement-playlists/SKILL.mdsource content
Implement Playlists Skill
Plan and implement Stage 6 Playlists for NovaTune: CRUD endpoints, track management, stable ordering, and lifecycle integration.
Overview
Stage 6 implements playlist management with:
- GET /playlists - List playlists with search and cursor-based pagination
- POST /playlists - Create playlist with quota enforcement
- GET /playlists/{playlistId} - Get playlist with paginated tracks
- PATCH /playlists/{playlistId} - Update playlist metadata
- DELETE /playlists/{playlistId} - Hard delete playlist
- POST /playlists/{playlistId}/tracks - Add tracks at position
- DELETE /playlists/{playlistId}/tracks/{position} - Remove track
- POST /playlists/{playlistId}/reorder - Reorder tracks
Implementation Plan
Phase 1: Models and Configuration
-
Create Playlist Model (
)ApiService/Models/Playlist.cs
(ULID)PlaylistId
(owner)UserId
,NameDescription
(embeddedTracks
)List<PlaylistTrackEntry>
,TrackCount
(denormalized)TotalDuration
enum (Private, Unlisted, Public)Visibility
,CreatedAtUpdatedAt
-
Create PlaylistTrackEntry (
)ApiService/Models/PlaylistTrackEntry.cs
(0-based index)Position
(ULID reference)TrackIdAddedAt
-
Add Configuration (
)ApiService/Configuration/PlaylistOptions.cs
(default: 200)MaxPlaylistsPerUser
(default: 10,000)MaxTracksPerPlaylist
(default: 100)MaxTracksPerAddRequest
(default: 50)MaxMovesPerReorderRequest
(default: 100)MaxNameLength
(default: 500)MaxDescriptionLength
(default: 20)DefaultPageSize
(default: 50)MaxPageSize
-
Add DTOs (
)ApiService/Models/
,PlaylistListQueryPlaylistDetailQuery
,PlaylistListItem
,PlaylistDetailsPlaylistTrackItem
,CreatePlaylistRequestUpdatePlaylistRequest
,AddTracksRequest
,ReorderRequestMoveOperation
Phase 2: RavenDB Indexes
-
Playlists_ByUserForSearch (
)ApiService/Infrastructure/Indexes/Map = playlists => from playlist in playlists select new { playlist.UserId, playlist.Name, playlist.TrackCount, playlist.CreatedAt, playlist.UpdatedAt, SearchText = playlist.Name }; Index("SearchText", FieldIndexing.Search); -
Playlists_ByTrackReference (
)ApiService/Infrastructure/Indexes/Map = playlists => from playlist in playlists from track in playlist.Tracks select new { UserId = playlist.UserId, PlaylistId = playlist.PlaylistId, TrackId = track.TrackId };
Phase 3: Service Layer
-
IPlaylistService (
)ApiService/Services/ListPlaylistsAsync(userId, query, ct)CreatePlaylistAsync(userId, request, ct)GetPlaylistAsync(playlistId, userId, query, ct)UpdatePlaylistAsync(playlistId, userId, request, ct)DeletePlaylistAsync(playlistId, userId, ct)AddTracksAsync(playlistId, userId, request, ct)RemoveTrackAsync(playlistId, userId, position, ct)ReorderTracksAsync(playlistId, userId, request, ct)RemoveDeletedTrackReferencesAsync(trackId, userId, ct)
-
Custom Exceptions (
)ApiService/Infrastructure/Exceptions/PlaylistNotFoundExceptionPlaylistAccessDeniedExceptionPlaylistQuotaExceededExceptionPlaylistTrackLimitExceededExceptionPlaylistTrackNotFoundExceptionInvalidPositionException
Phase 4: API Endpoints
-
PlaylistEndpoints.cs (
)ApiService/Endpoints/group.MapGet("/", HandleListPlaylists).RequireRateLimiting("playlist-list"); group.MapPost("/", HandleCreatePlaylist).RequireRateLimiting("playlist-create"); group.MapGet("/{playlistId}", HandleGetPlaylist); group.MapPatch("/{playlistId}", HandleUpdatePlaylist).RequireRateLimiting("playlist-update"); group.MapDelete("/{playlistId}", HandleDeletePlaylist).RequireRateLimiting("playlist-delete"); group.MapPost("/{playlistId}/tracks", HandleAddTracks).RequireRateLimiting("playlist-tracks-add"); group.MapDelete("/{playlistId}/tracks/{position:int}", HandleRemoveTrack).RequireRateLimiting("playlist-tracks-remove"); group.MapPost("/{playlistId}/reorder", HandleReorderTracks).RequireRateLimiting("playlist-reorder"); -
Rate Limiting Policies
: 60 req/minplaylist-list
: 20 req/minplaylist-create
: 30 req/minplaylist-update
: 20 req/minplaylist-delete
: 30 req/minplaylist-tracks-add
: 60 req/minplaylist-tracks-remove
: 30 req/minplaylist-reorder
Phase 5: Track Validation
When adding tracks to playlists:
- Verify track IDs are valid ULIDs
- Verify tracks exist in RavenDB
- Verify tracks are owned by the same user
- Verify tracks are not deleted (
)Status != Deleted - Verify playlist track limit not exceeded
var trackDocs = await _session.LoadAsync<Track>( request.TrackIds.Select(id => $"Tracks/{id}"), ct); foreach (var (trackId, track) in trackDocs) { if (track is null) throw new TrackNotFoundException(trackId); if (track.UserId != userId) throw new TrackAccessDeniedException(trackId); if (track.Status == TrackStatus.Deleted) throw new TrackDeletedException(trackId); }
Phase 6: Position Management
Adding tracks:
var insertPosition = request.Position ?? playlist.Tracks.Count; // Shift existing tracks foreach (var entry in playlist.Tracks.Where(t => t.Position >= insertPosition)) entry.Position += request.TrackIds.Count; // Add new tracks var newEntries = request.TrackIds.Select((id, i) => new PlaylistTrackEntry { Position = insertPosition + i, TrackId = id, AddedAt = now }); playlist.Tracks.AddRange(newEntries);
Removing tracks:
playlist.Tracks.Remove(trackToRemove); // Reindex positions foreach (var entry in playlist.Tracks.Where(t => t.Position > position)) entry.Position--;
Reordering tracks:
foreach (var move in request.Moves) { var track = tracks[move.From]; tracks.RemoveAt(move.From); tracks.Insert(move.To, track); } // Reassign positions for (var i = 0; i < tracks.Count; i++) tracks[i].Position = i;
Phase 7: Lifecycle Integration
Extend lifecycle worker to clean up playlist references when tracks are physically deleted:
- Query
index to find affected playlistsPlaylists_ByTrackReference - Remove all entries for the deleted track
- Reindex positions
- Update denormalized
andTrackCountTotalDuration
Phase 8: Observability
-
Metrics (
)ApiService/Infrastructure/Observability/playlist_list_requests_totalplaylist_create_requests_totalplaylist_get_requests_totalplaylist_update_requests_totalplaylist_delete_requests_totalplaylist_tracks_add_requests_totalplaylist_tracks_remove_requests_totalplaylist_reorder_requests_total
(histogram)playlist_track_count
-
Logging
- Playlist operations with
,PlaylistId
,UserIdCorrelationId - Track additions/removals with count and position
- Playlist operations with
Phase 9: Testing
-
Unit Tests
PlaylistServiceTests- Position reindexing logic
- Quota enforcement
- Track validation
-
Integration Tests
- End-to-end CRUD flow
- Add/remove/reorder tracks
- Track deletion cascade to playlists
- Concurrent modification handling
Files to Create/Modify
New Files
| File | Purpose |
|---|---|
| Playlist document model |
| Embedded track entry |
| Visibility enum |
| Configuration |
| Service interface |
| Service implementation |
| API endpoints |
| Query models |
| Response DTOs |
| Search index |
| Track reference index |
| Custom exceptions |
Modified Files
| File | Changes |
|---|---|
| Register services, rate limiting |
| Add playlist cleanup |
Stage 6 Documentation
Detailed specifications are available in
doc/implementation/stage-6/:
| Document | Description |
|---|---|
| Architecture diagram and index |
| Playlist and PlaylistTrackEntry models |
| GET /playlists endpoint |
| POST /playlists endpoint |
| GET /playlists/{id} endpoint |
| PATCH /playlists/{id} endpoint |
| DELETE /playlists/{id} endpoint |
| POST /playlists/{id}/tracks endpoint |
| DELETE /playlists/{id}/tracks/{pos} endpoint |
| POST /playlists/{id}/reorder endpoint |
| IPlaylistService and DTOs |
| Search and track reference indexes |
| Lifecycle worker integration |
| PlaylistOptions configuration |
| PlaylistEndpoints.cs structure |
| Unit and integration test plan |
| Implementation checklist |
Related Skills
- add-api-endpoint - For endpoint structure
- add-cursor-pagination - For playlist list pagination
- add-ravendb-index - For creating RavenDB indexes
- add-rate-limiting - For rate limiting policies
- add-observability - For metrics and tracing
- add-playlist-reordering - For reorder implementation
- add-playlist-tracks - For track add/remove
Claude Agents
- playlist-api-implementer - Implement playlist service, endpoints, and models
- playlist-tester - Write unit and integration tests for playlists
Validation Checklist
- All CRUD endpoints return RFC 7807 problem details on error
- Rate limiting enforced on all mutation endpoints
- Playlist quota enforced (200 per user)
- Track limit enforced (10,000 per playlist)
- Track ownership verified before adding to playlist
- Deleted tracks not allowed in playlists
- Position indices maintained correctly
- Denormalized fields updated atomically
- Optimistic concurrency on updates
- Lifecycle worker removes deleted track references
- All operations logged with correlation ID