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.md
source 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

  1. Create Playlist Model (

    ApiService/Models/Playlist.cs
    )

    • PlaylistId
      (ULID)
    • UserId
      (owner)
    • Name
      ,
      Description
    • Tracks
      (embedded
      List<PlaylistTrackEntry>
      )
    • TrackCount
      ,
      TotalDuration
      (denormalized)
    • Visibility
      enum (Private, Unlisted, Public)
    • CreatedAt
      ,
      UpdatedAt
  2. Create PlaylistTrackEntry (

    ApiService/Models/PlaylistTrackEntry.cs
    )

    • Position
      (0-based index)
    • TrackId
      (ULID reference)
    • AddedAt
  3. Add Configuration (

    ApiService/Configuration/PlaylistOptions.cs
    )

    • MaxPlaylistsPerUser
      (default: 200)
    • MaxTracksPerPlaylist
      (default: 10,000)
    • MaxTracksPerAddRequest
      (default: 100)
    • MaxMovesPerReorderRequest
      (default: 50)
    • MaxNameLength
      (default: 100)
    • MaxDescriptionLength
      (default: 500)
    • DefaultPageSize
      (default: 20)
    • MaxPageSize
      (default: 50)
  4. Add DTOs (

    ApiService/Models/
    )

    • PlaylistListQuery
      ,
      PlaylistDetailQuery
    • PlaylistListItem
      ,
      PlaylistDetails
      ,
      PlaylistTrackItem
    • CreatePlaylistRequest
      ,
      UpdatePlaylistRequest
    • AddTracksRequest
      ,
      ReorderRequest
      ,
      MoveOperation

Phase 2: RavenDB Indexes

  1. 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);
    
  2. 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

  1. 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)
  2. Custom Exceptions (

    ApiService/Infrastructure/Exceptions/
    )

    • PlaylistNotFoundException
    • PlaylistAccessDeniedException
    • PlaylistQuotaExceededException
    • PlaylistTrackLimitExceededException
    • PlaylistTrackNotFoundException
    • InvalidPositionException

Phase 4: API Endpoints

  1. 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");
    
  2. Rate Limiting Policies

    • playlist-list
      : 60 req/min
    • playlist-create
      : 20 req/min
    • playlist-update
      : 30 req/min
    • playlist-delete
      : 20 req/min
    • playlist-tracks-add
      : 30 req/min
    • playlist-tracks-remove
      : 60 req/min
    • playlist-reorder
      : 30 req/min

Phase 5: Track Validation

When adding tracks to playlists:

  1. Verify track IDs are valid ULIDs
  2. Verify tracks exist in RavenDB
  3. Verify tracks are owned by the same user
  4. Verify tracks are not deleted (
    Status != Deleted
    )
  5. 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:

  1. Query
    Playlists_ByTrackReference
    index to find affected playlists
  2. Remove all entries for the deleted track
  3. Reindex positions
  4. Update denormalized
    TrackCount
    and
    TotalDuration

Phase 8: Observability

  1. Metrics (

    ApiService/Infrastructure/Observability/
    )

    • playlist_list_requests_total
    • playlist_create_requests_total
    • playlist_get_requests_total
    • playlist_update_requests_total
    • playlist_delete_requests_total
    • playlist_tracks_add_requests_total
    • playlist_tracks_remove_requests_total
    • playlist_reorder_requests_total
    • playlist_track_count
      (histogram)
  2. Logging

    • Playlist operations with
      PlaylistId
      ,
      UserId
      ,
      CorrelationId
    • Track additions/removals with count and position

Phase 9: Testing

  1. Unit Tests

    • PlaylistServiceTests
    • Position reindexing logic
    • Quota enforcement
    • Track validation
  2. 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

FilePurpose
ApiService/Models/Playlist.cs
Playlist document model
ApiService/Models/PlaylistTrackEntry.cs
Embedded track entry
ApiService/Models/PlaylistVisibility.cs
Visibility enum
ApiService/Configuration/PlaylistOptions.cs
Configuration
ApiService/Services/IPlaylistService.cs
Service interface
ApiService/Services/PlaylistService.cs
Service implementation
ApiService/Endpoints/PlaylistEndpoints.cs
API endpoints
ApiService/Models/PlaylistListQuery.cs
Query models
ApiService/Models/PlaylistDetails.cs
Response DTOs
ApiService/Infrastructure/Indexes/Playlists_ByUserForSearch.cs
Search index
ApiService/Infrastructure/Indexes/Playlists_ByTrackReference.cs
Track reference index
ApiService/Infrastructure/Exceptions/PlaylistExceptions.cs
Custom exceptions

Modified Files

FileChanges
ApiService/Program.cs
Register services, rate limiting
Workers.Lifecycle/PhysicalDeletionService.cs
Add playlist cleanup

Stage 6 Documentation

Detailed specifications are available in

doc/implementation/stage-6/
:

DocumentDescription
00-overview.md
Architecture diagram and index
01-data-model.md
Playlist and PlaylistTrackEntry models
02-api-list-playlists.md
GET /playlists endpoint
03-api-create-playlist.md
POST /playlists endpoint
04-api-get-playlist.md
GET /playlists/{id} endpoint
05-api-update-playlist.md
PATCH /playlists/{id} endpoint
06-api-delete-playlist.md
DELETE /playlists/{id} endpoint
07-api-add-tracks.md
POST /playlists/{id}/tracks endpoint
08-api-remove-track.md
DELETE /playlists/{id}/tracks/{pos} endpoint
09-api-reorder-tracks.md
POST /playlists/{id}/reorder endpoint
10-service-interface.md
IPlaylistService and DTOs
11-ravendb-indexes.md
Search and track reference indexes
12-track-deletion-integration.md
Lifecycle worker integration
13-configuration.md
PlaylistOptions configuration
14-endpoint-implementation.md
PlaylistEndpoints.cs structure
18-test-strategy.md
Unit and integration test plan
19-implementation-tasks.md
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