Skip to content

Agent SOP: Collection Audit

Scan audio files for convention violations and fix them with user approval.

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_tags handles this automatically.

Prerequisites

  • reklawdbox MCP server connected
  • No external tools required — all tag operations use audit_state and write_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 disk
  • scan_orphan_files() — audio files on disk not imported into Rekordbox
  • scan_duplicates() — duplicate tracks by metadata or file hash
  • scan_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_written and fields_deleted only when status="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 exact file_path match 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 detail field — it may be null for 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: compare detail.filename vs detail.tag values (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:

  1. Read the detail JSON — contains filename and tag values for each drifted field
  2. If the file is not imported into Rekordbox, rename to match the tag
  3. If the file is imported, defer with a note about manual Rekordbox relocate, or accept as-is
  4. If the drift is a genuinely different name (not just character substitution), look up via lookup_discogs / lookup_beatport / lookup_bandcamp / lookup_musicbrainz
  5. 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):

  1. Read each sample file’s artist, title, and album with read_file_tags.
  2. Re-read the same exact path with search_tracks(path="...") and require an exact file_path match.
  3. 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)