Agent SOP: Collection Audit
Scan audio files for convention violations and fix them with user approval.
Agent prompt
Section titled “Agent prompt”Paste into your agent to start:
Audit my collection for naming and tagging issues.Constraints
- Never rename imported files. Before any rename, check
search_tracks(path="...")— if the file is in Rekordbox, defer the rename. Rekordbox cannot update paths via XML import (creates duplicates). - Never manually resolve with
resolution="fixed"— that value is reserved for the scan engine’s auto-resolution. - Resolution options:
accepted_as_is,deferred,wont_fix. - Read-only by default. No files modified until user approves a fix plan.
- Never delete audio files. Renaming and tag editing only.
- WAV dual-tag rule.
write_file_tagshandles this automatically.
Prerequisites
- reklawdbox MCP server connected
- No external tools required — all tag operations use
audit_stateandwrite_file_tags
Steps
0. Determine scope
Default: full collection. Use the content_roots from read_library as the scan scope.
If the user specified a narrower scope in their prompt (an artist, directory, or subset), use that instead.
Always skip GENRE_SET — genre issues are handled by the Genre Classification and Genre Audit workflows.
0b. (Optional) Structural health scans
Before the tag audit, consider running these scans for structural library issues:
scan_broken_links()— tracks in Rekordbox whose audio files are missing from diskscan_orphan_files()— audio files on disk not imported into Rekordboxscan_duplicates()— duplicate tracks by metadata or file hashscan_playlist_coverage()— tracks not assigned to any playlist
These are independent of the tag audit and can be run at any time. Address any findings before or after the tag audit.
1. Scan
audit_state(scan, scope="/path/", skip_issue_types=["GENRE_SET"])Review summary: files_in_scope, failed_reads, new_issues by type, total_open, and warnings.
If failed_reads > 0, surface the unreadable paths from warnings and retry the scan. If a path remains unreadable, report an incomplete audit and do not take the clean-result path.
Only when both total_open = 0 and failed_reads = 0 → skip to Step 6.
2. Review and triage
Query issues by type:
audit_state(query_issues, scope="/path/", status="open", issue_type="WAV_TAG3_MISSING")Categorize by safety tier:
| Category | Safety tier | Issue types | Action |
|---|---|---|---|
| Auto-fixable | Safe | WAV_TAG3_MISSING, WAV_TAG_DRIFT, ARTIST_IN_TITLE |
→ Step 3 |
| Rename-fixable | Rename-safe | ORIGINAL_MIX_SUFFIX, TECH_SPECS_IN_DIR |
→ Step 3 (if not imported) |
| Needs review | Review | All others | → Step 4 |
Present triage summary to user.
Ask user: “Start with safe auto-fixes?”
3. Fix safe issues
Present planned writes for approval, then apply:
write_file_tags(writes=[{path: "...", tags: {artist: "...", title: "..."}}])Track every non-dry-run write result for the Rekordbox handoff:
- Retain the returned path and successful field changes from
fields_writtenandfields_deletedonly whenstatus="ok"and at least one field changed. Failed writes and no-op results do not enter the handoff list. - For every successful changed path, call the read-only
search_tracks(path="...")and require an exactfile_pathmatch before treating the file as imported. Do not assume an audit issue has a track ID. - For imported matches, keep the Rekordbox track ID, artist, title, path, changed fields, and intended post-write artist/title/album values. Deduplicate by track ID and exact path. Unimported files need no Reload Tag handoff.
Re-scan to verify fixes (scan auto-marks resolved issues as fixed):
audit_state(scan, scope="/path/")Never manually resolve with resolution="fixed" — that’s reserved for the scan engine.
WAV_TAG_DRIFT and Original suffixes: When the drift is caused by one tag layer having an (Original), (Original Mix), or (Original Version) suffix that the other lacks, strip the suffix from both layers rather than syncing it. These are store-generated filler — the shorter title is correct. More specific Original variants like (Original Club Mix) or (Original Demo Version) should be kept as-is.
4. Review-tier issues
Present each issue type in batches.
Empty/missing tags (EMPTY_ARTIST, EMPTY_TITLE, MISSING_TRACK_NUM, MISSING_ALBUM, MISSING_YEAR):
- Check the
detailfield — it may benullfor empty/missing-tag issues. Infer fix from filename and parent directory structure. - Use
lookup_discogs()/lookup_beatport()/lookup_bandcamp()/lookup_musicbrainz()for missing album/year - Ask user to confirm each fix
Filename issues (BAD_FILENAME, MISSING_YEAR_IN_DIR, FILENAME_TAG_DRIFT):
- For
FILENAME_TAG_DRIFT: comparedetail.filenamevsdetail.tagvalues (see handling below) - For imported files needing rename: defer with note about manual Rekordbox relocate
FILENAME_TAG_DRIFT — handling:
On macOS, / is the only forbidden filename character. The scanner normalises / to - before comparison, so AC/DC in a tag won’t flag drift against AC-DC in a filename. All other characters (: ? " * | < >) are valid on macOS — if a download tool substituted them, the file should be renamed to match the tag.
Workflow:
- Read the
detailJSON — containsfilenameandtagvalues for each drifted field - If the file is not imported into Rekordbox, rename to match the tag
- If the file is imported, defer with a note about manual Rekordbox relocate, or accept as-is
- If the drift is a genuinely different name (not just character substitution), look up via
lookup_discogs/lookup_beatport/lookup_bandcamp/lookup_musicbrainz - If ambiguous after lookup, flag for user review — do not guess
Genre tags (GENRE_SET, if included):
- Per file: keep / clear / migrate to comments
- When migrating: read existing comment first via
read_file_tags, prepend genre, never overwrite write_file_tags(writes=[{path: "...", tags: {comment: "Genre: Deep House | <existing>", genre: null}}])
No-tag files (NO_TAGS):
- Infer metadata from parent directory name, companion files, filename
- Present inferred values to user for confirmation before writing
For items user wants to skip:
audit_state(resolve_issues, issue_ids=[...], resolution="accepted_as_is", note="...")audit_state(resolve_issues, issue_ids=[...], resolution="deferred", note="...")audit_state(resolve_issues, issue_ids=[...], resolution="wont_fix", note="...")5. Verification scan
audit_state(scan, scope="/path/")Check failed_reads before reviewing issue totals. If it is nonzero, surface the warning paths and retry the scan. Persistent failures mean the audit is incomplete, even when total_open = 0.
If total_open > 0, return to Step 2 or confirm remaining items are intentionally deferred.
6. Synchronize changed imported tags in Rekordbox (blocking)
If the deduplicated handoff list is empty, state that no successfully changed files were imported into Rekordbox and continue to Step 7.
Otherwise, show the deduplicated list with artist, title, path, and changed fields. Warn the user that Reload Tag replaces Rekordbox-side values with file tag values: intentional database-only edits in fields Rekordbox reloads can be overwritten.
Ask the user to select only the listed tracks in Rekordbox, right-click, and choose Reload Tag.
Pause here. Do not continue until the user confirms Reload Tag is complete.
After confirmation, verify a bounded sample of up to 5 changed imported tracks (all tracks when there are fewer than 5):
- Read each sample file’s artist, title, and album with
read_file_tags. - Re-read the same exact path with
search_tracks(path="...")and require an exactfile_pathmatch. - Compare the Rekordbox database artist/title/album with the file tag values.
Any mismatch means the Reload Tag checkpoint is incomplete. Report the mismatched paths and values, ask the user to retry the scoped Reload Tag, and do not proceed to the final report.
For WAV files, Reload Tag reads RIFF INFO, which has no label or track-number fields. Do not claim those fields were synchronized; set them later through XML import or manually in Rekordbox when needed.
7. Final report
audit_state(get_summary, scope="/path/")Carry failed_reads forward from the most recent scan response; get_summary reports persisted issue state, not read-attempt counters.
Present: scope, files scanned, failed_reads, pass rate, fixes by type, deferred
items, Reload Tag checkpoint outcome, and next steps (enrichment, then the genre
classification SOP). Declare the audit clean only when total_open = 0 and
failed_reads = 0; otherwise label it an incomplete audit and list the
unreadable paths.
Issue Type Reference
| Issue type | Safety tier | Detection | Fix method |
|---|---|---|---|
EMPTY_ARTIST |
Review | artist field empty/null |
Parse from filename |
EMPTY_TITLE |
Review | title field empty/null |
Parse from filename |
MISSING_TRACK_NUM |
Review | track field empty/null |
Parse from filename |
MISSING_ALBUM |
Review | album field empty/null |
Directory name or enrichment lookup |
MISSING_YEAR |
Review | year field empty/null |
Enrichment lookup |
ARTIST_IN_TITLE |
Safe | title starts with {artist} - |
Strip prefix |
WAV_TAG3_MISSING |
Safe | WAV file with tag3_missing non-empty |
Copy from tag 2 |
WAV_TAG_DRIFT |
Safe | WAV id3v2 and riff_info values differ |
Sync to tag 2 (but see Original-suffix note in Step 3) |
GENRE_SET |
Review | genre field non-empty |
User decides keep/clear/migrate |
NO_TAGS |
Review | All 14 tag fields empty/null | Infer from filename/dir |
BAD_FILENAME |
Review | Filename doesn’t match canonical | User review |
ORIGINAL_MIX_SUFFIX |
Rename-safe | Filename contains (Original), (Original Mix), or (Original Version) |
Strip suffix from filename and tags (defer rename if imported) |
TECH_SPECS_IN_DIR |
Rename-safe | Directory contains [FLAC], [WAV], etc. |
Strip from dir name |
MISSING_YEAR_IN_DIR |
Review | Album directory missing (YYYY) suffix |
Enrichment lookup |
FILENAME_TAG_DRIFT |
Review | Filename artist/title disagrees with tags | Rename to match tag (defer if imported) |
