Genre Classification SOP: Agent SOP for genre classification # Genre Classification > Agent SOP for evidence-based genre tagging. Classify genres across a Rekordbox collection using cached enrichment and audio evidence. ### Agent prompt [Section titled “Agent prompt”](#agent-prompt) Paste into your agent to start: ```plaintext Classify genres for my ungenred tracks. ``` ## Constraints * **Taxonomy is compiled.** The alias map in `genre.rs` cannot be changed at runtime. If the user disagrees with a mapping, the agent works around it by using the user’s preferred genre directly. * **No auto-tagging.** Every genre change requires explicit human approval. * **Cache-first.** Classification runs from cached data. If cache is empty for a track, it is flagged as insufficient evidence — do not trigger enrichment mid-classification. * **XML export only.** No direct DB writes. All changes flow through `update_tracks` → `preview_changes` → `write_xml`. * **Per-track reasoning is mandatory for low/insufficient confidence.** Do not skip or batch-approve these tracks. Delegate to subagents if the volume is large. ## Prerequisites ```plaintext cache_coverage(has_genre=false) ``` All providers must be at 100% before proceeding. If not, hydrate first: ```plaintext analyze_audio_batch(has_genre=false, skip_cached=true, max_tracks=200) enrich_tracks(has_genre=false, skip_cached=true, providers=["discogs"], max_tracks=50) enrich_tracks(has_genre=false, skip_cached=true, providers=["beatport"], max_tracks=50) ``` Repeat until all providers reach 100%. Report progress between batches. All `classify_tracks` calls support `max_tracks` (default 50, max 200) and `offset` for pagination. Continue until all tracks are covered. ## Steps ### 1. Normalize aliases ```plaintext suggest_normalizations(stage_aliases=true) ``` This auto-stages all non-debatable alias mappings (e.g. `Hip-Hop` → `Hip Hop`, `DnB` → `Drum & Bass`) and returns grouped results showing each mapping and its track count. Review the staged aliases. If any are debatable, unstage them with `clear_changes(track_ids=[...])` and ask the user. Store overrides for Step 2. **Unknown genres** (non-canonical, no alias mapping) will be classified in Step 2 using the `has_unknown_genre` filter. ### 2. Classify and get approval policy Get the confidence distribution first using `format="summary"`: ```plaintext classify_tracks(max_tracks=200, format="summary", genre_overrides=[...]) classify_tracks(has_unknown_genre=true, max_tracks=200, format="summary", genre_overrides=[...]) ``` The summary format returns genre-grouped counts with top artists per genre — no per-track data. Report the aggregate distribution: ```plaintext "N tracks classified: X high, Y medium, Z low, W insufficient." ``` Present the confidence distribution and ask the user which tiers to auto-approve: **“Approve all high-confidence? Approve high and medium? Or review everything?”** Default recommendation: approve high, present medium as summary, full review for low/insufficient. ### 3. Stage approved tiers Re-run classification with `auto_stage` to stage the approved tiers in one shot: ```plaintext classify_tracks(max_tracks=200, format="summary", auto_stage=["high", "medium"], genre_overrides=[...]) classify_tracks(has_unknown_genre=true, max_tracks=200, format="summary", auto_stage=["high", "medium"], genre_overrides=[...]) ``` The response includes a `staging` field with the staged count. No separate `update_tracks` call is needed. For medium-confidence tracks that are NOT auto-approved, use `format="compact"` to get the per-track list for review: ```plaintext classify_tracks(max_tracks=200, format="compact", genre_overrides=[...]) classify_tracks(has_unknown_genre=true, max_tracks=200, format="compact", genre_overrides=[...]) ``` Present as a numbered list grouped by genre. Highlight which evidence sources agree vs disagree: ```plaintext → Techno (8): #1 Cristi Cons — Bird In Space (Discogs: Techno, label: Cocoon → Techno, 124bpm) #2 Polar Inertia — Black Sun remix (Discogs: Techno, 131bpm) ... ``` Ask: **“Approve all, or reject/change specific numbers. Say ‘investigate #N’ for any you want more context on.”** For investigated tracks, use `resolve_tracks_data` to get full evidence, then check artist context via `search_tracks`. Stage approved changes with `update_tracks`. ### 4. Review low and insufficient confidence These tracks need per-track reasoning — do not batch-approve them. Use subagents to parallelize the work. #### 4.1 Get dispatch roster Use `format="dispatch"` to get low/insufficient tracks grouped by artist: ```plaintext classify_tracks(max_tracks=200, format="dispatch", genre_overrides=[...]) classify_tracks(has_unknown_genre=true, max_tracks=200, format="dispatch", genre_overrides=[...]) ``` The dispatch format returns artists sorted by track count descending, with per-track evidence and flags included. Partition into subagent batches: * Artists with 10+ tracks → dedicated subagent * Remaining artists → batch into subagents of \~40–50 tracks #### 4.2 Dispatch review subagents Launch as many subagents in parallel as possible. Each subagent receives: * A list of tracks from the dispatch roster (including evidence and flags) * The review prompt below **Review subagent prompt:** > You are classifying genres for tracks in a Rekordbox library. For each track, produce a genre recommendation and stage it. > > **Evidence and flags are included with each track in the dispatch data. You do NOT need to call resolve\_tracks\_data.** > > **Tools available:** > > * `get_genre_taxonomy()` — canonical genre list and BPM ranges > * `search_tracks(artist="...", has_genre=true)` — artist’s other genred tracks in the library (use when you need more context beyond the dispatch data) > * `get_track(track_id="...")` — single track detail > * `update_tracks(changes=[...])` — stage genre changes > > **What the decision tree already considered:** BPM range plausibility, Beatport/Discogs genre tags, label-genre mapping, audio energy profile, same-family depth resolution. > > **What you add:** Artist reputation beyond this library, label/scene context, remix conventions (e.g. remixer known for a specific genre), title interpretation. > > **Workflow:** > > 1. Call `get_genre_taxonomy()` to load the canonical genre list — only recommend genres from this list > 2. Review the evidence and flags included with each track > 3. Check if the artist has other genred tracks in the library via `search_tracks` where useful > 4. Consider artist reputation, label identity, and track title > 5. Recommend a canonical genre with one-sentence reasoning > 6. After classifying all tracks, stage your recommendations via `update_tracks` > > **Output format:** > > ```plaintext > #N Artist — Title > Evidence: [key signals from the evidence array] > Library: [artist's other genres, or "no other tracks"] > Recommend: GENRE — [why] > ``` > > **Tracks to classify:** \[track list here] #### 4.3 Verify staged results Once all subagents have completed, verify the aggregate results: ```plaintext preview_changes(format="summary") ``` Compare the staged track count against `dispatch_stats.total_tracks` from Step 4.1 (plus any tracks staged in Step 3). If the staged count is lower than expected, identify which artist batches may have failed and report them to the user. Present the summary to the user. Report: `"Review complete: N tracks staged across M genres."` Ask: **“Any genres or artists you want to review before export?“** ### 5. Export ```plaintext preview_changes(format="summary") ``` Ask user: “Export these changes to XML?” ```plaintext write_xml() ``` Report output path, then walk the user through the Rekordbox import: 1. **Add XML to Rekordbox** — Open Preferences → Advanced → rekordbox xml → **Imported Library** → Browse → select the exported XML file. 2. **Open the XML view** — In the sidebar, click the **“Display rekordbox xml”** icon. The imported tracks appear under “All Tracks”. 3. **Import into collection** — Select all tracks (Cmd+A), right-click → **Import To Collection**. When prompted “Do you want to load information in the tag of the library being imported?”, click **Yes** (tick “Don’t ask me again” for bulk imports).