View reports¶
The Reports workspace is a two-pane screen at /reports that gives you a bird's-eye view of your library's health, compliance, and metadata state. The left rail lists every available report; the right pane renders the one you select.
Open the Reports workspace¶
Click Reports in the sidebar. The workspace opens with the Compliance overview active by default. You can also navigate directly to /reports/{report-name} (for example, /reports/health).
The reports rail¶
The narrow rail on the left lists all built-in reports. A filter box at the top of the rail lets you type to narrow the list by name. Click any entry to load it in the right pane - the active entry is highlighted and the URL updates to match.
Live reports¶
Six reports have fully implemented right panes:
Compliance overview¶
Shows field and rule coverage across your library as a paginated artist table. You can search, filter by status, library, or health-score range, sort by any column, and export the current view to CSV.
The pane has two tabs:
- Results - the standard compliance list with per-artist health scores and violation counts.
- Matrix - a scrollable rule-by-artist compliance matrix for an at-a-glance view of which rules pass or fail across the catalog.
Use the Run button to refresh the pane from the latest data without navigating away.
Library health¶
Shows a compliance score summary, total and compliant artist counts, a breakdown of missing metadata types (NFO, thumb, fanart, MBID), and a ranked list of the top failing rules with per-rule pass rates.
Metadata completeness¶
Shows field-coverage percentages across your entire library and a table of the ten artists with the lowest completeness scores, broken down by library.
Rule pass rates¶
Lists every configured rule with its pass count, evaluation count, and pass percentage for the current library state. Pass rates are color-coded: green at 80% or above, amber between 50-79%, and red below 50%.
Blast radius¶
The blast-radius report answers "which values that I set did an automated change replace or blank". Click Blast radius in the reports rail to open it. The same rows are also available as a CSV spreadsheet, though only through the API for now - see Downloading the CSV below.
For each artist and field, the report shows the most recent change, and keeps it only when that change replaced or blanked a value that had been set. Two kinds of damage are reported, because both destroy what you set:
- Blanked - a value you had was emptied.
- Replaced - a value you had was overwritten with a different one.
A field whose value has since been put back drops out of the report automatically.
You can narrow the report by damage kind, by attribution, by field, or to a single artist. Both damage kinds are shown by default. The pane does not yet offer on-screen filter controls, so for now you set a filter by adding it to the page address; the pane reads the filters from the URL and keeps them as you page through results. The CSV download accepts the same filters. The API reference lists the exact filter names and values.
When a filter matches nothing, the pane says so and tells you how many changes the report still holds behind that filter. It does not report an all-clear, because a filter matching nothing says nothing about the rest of your library.
Putting a value back¶
Every row in the workspace pane has a Restore button that writes your old value back over what the automated change left. You can also tick the checkbox on several rows and use Restore Selected to put them all back in one go. The checkbox in the header ticks every row on the page you are looking at; because the report is paged, it cannot reach rows on other pages.
Restore is always two steps, and the first one never writes:
- Preview. Stillwater checks each selected row against the current state of your library and reports how many of them can be put back, and how many cannot. Nothing has been written at this point.
- Confirm. Only then does the write happen, and only for the rows the preview listed as restorable.
A preview is not a promise. Stillwater re-reads each value immediately before writing it, because a scan, a rule pass, or an edit you made in another tab can land in the gap between the preview and your confirmation. If the value changed in that gap, the restore is refused rather than overwriting the newer value with an older one. A row is also refused when the change record is gone, when the field keeps no change history, or when the row is itself an earlier restore, which would only chain undo onto undo.
Two refusals are specific to the value being put back. The first is a value the field will not accept, such as a name whose previous value was nothing but spaces, dashes, or underscores; restoring it would leave the artist with no usable name. The second is a name another artist now holds, since restoring it would recreate the duplicate that rename removed.
The name check runs while you are still deciding, so a row that cannot land is marked refused in the preview rather than at the moment you confirm. It is checked again as the value is written, because another artist can take that name in the gap, and a check that cannot complete refuses the row instead of letting it through.
That means a confirmed restore can come back reporting fewer values put back than the preview offered, including none at all. Stillwater tells you which happened:
- All rows restored - a success message with the count.
- Some restored, some refused - a warning naming both numbers. The restore is incomplete.
- Nothing restored - an error message with the refusal count.
Refused rows stay in the table so you can see they still need attention; only rows that were genuinely put back disappear from it. Each refused row carries its own reason underneath the Restore button, saying what to do about that particular row - the summary message gives you the counts, the row tells you why. The preview fills these in too, so you can read the reason before you confirm anything.
Whether retrying helps depends on the reason. Some refusals never clear, because they describe the value or the field itself: a field that keeps no change history, an old value the field will not accept, or a row that is itself an undo. Those call for a different fix rather than another attempt. Others describe the current state of something else, and clear once you have dealt with it. Reload the report so it reflects the current state, then try again. For a name another artist holds, rename or merge that artist first. A refusal that says the write was attempted and failed is the one where a plain retry is the right response.
Restoring a value does not republish it or re-run the writer that changed it. It does mark the artist for re-evaluation, so the next rule pass looks at it again. Only one restore can run at a time across the whole application; if another is already in progress, Stillwater says so instead of queuing yours.
What the report can prove about who made a change¶
Every row is labeled one of two ways:
- Automated - written by a scan, an import, a metadata provider, or a rule. The source was recorded, so this is certain.
- Unknown - anything Stillwater cannot positively identify as an automated writer, so nothing falls outside the two counts. Most of these are recorded as a manual change. Stillwater began recording scan-driven changes separately on 2026-07-19; before that, a scan that changed a value was recorded the same way your own edits are. These rows may be your edits or may be automated changes, and Stillwater cannot tell them apart.
Rows with unknown attribution are always listed and always counted separately. Narrowing the report to automated changes hides those rows from the list but does not remove them from the counts, so you can always see how many there are. A number that quietly excluded them would understate what was lost.
What the report covers¶
Only fields that keep a change history can be reported. The response lists both the covered and the uncovered fields.
Several editable fields keep no change history on the scan path, including disambiguation, name, and sort name. A field's absence from this report is not evidence it is undamaged if it is on the uncovered list - it means Stillwater has no record either way.
How far back it goes¶
Change history is not deleted on a schedule, so the report reaches back as far as your history goes.
It is not unlimited, though: change history is kept until an artist is deleted or merged into another. Either removes that artist's history along with it, including anything this report would have shown for it. If you are cleaning up duplicate artists, run this report first.
Downloading the CSV¶
The CSV carries the same rows plus the source label on each one. Note rows at the end restate the source limit, both field lists, and the retention boundary, so a spreadsheet you open weeks later still says what it does not cover. The export is capped at 10,000 rows and tells you when it was truncated.
There is no download button on the pane yet, so this export is reachable only through the API. The API reference has the endpoint and its parameters.
Rule-Written MusicBrainz IDs¶
This report answers "which artists did the automatic NFO rule fix pick a MusicBrainz ID for". It exists so you can find artists that fix may have misidentified. Click Rule-Written MusicBrainz IDs in the reports rail to open it, right next to Blast radius - both are automated-write reports the rail groups together. The same rows are also available as a CSV spreadsheet or as raw JSON through the API; see Downloading the CSV below.
Each row is one ID assignment: the artist, the current MusicBrainz ID it carries, the note the fix recorded at the time, and when it happened. If the fix wrote an ID for the same artist twice, you get two rows, because each one was a separate guess worth seeing. Every row has a Review link straight to that artist's page, since deciding whether a guessed ID is right means looking the artist up. You can narrow the report to a single artist by adding ?artist_id= to the page address, the same way the blast-radius pane's filters work; the API also lets you sort by date or artist name.
The pane's caveat band sits directly under the report title, always visible - never behind a toggle you have to open. Read it before trusting the count: this report is a floor, not a census, and the reasons why are below.
Read-only. This report does not contact MusicBrainz, does not check whether an ID is correct, and does not change or undo anything. Deciding what to do about a row is yours.
It filled blanks; it did not overwrite your IDs¶
This fix only ever ran on artists that had no MusicBrainz ID, so it never replaced an ID you had set. Every row is a blank field filled in with an ID that may be wrong. That is why there is no previous-value column: there was never a previous value.
What the report cannot see¶
This report covers the automatic NFO rule fix only. The automatic match steps of the Identify flow can also assign a MusicBrainz ID and record no change at all, so artists they affect can never appear here, however long the report runs. The bulk rule run now records its own assignments too, but under a different, more specific label, so this report still will not list them, even though a record of them does exist and can be found on the artist's own history. So for the Identify flow this is a permanent limit of what is recorded; for the bulk rule run it is a scoping choice this report makes, not a gap that cannot be closed.
Two consequences worth stating plainly:
- This list is not the complete set of machine-assigned MusicBrainz IDs.
- An artist's absence from this list is not evidence that a person chose its ID. Stillwater does not yet record operator confirmation of a MusicBrainz ID, so "not on the list" means only that this particular fix did not write it.
The counts are a minimum¶
Treat every count as a floor. Change history is kept until an artist is deleted or merged into another; either removes that artist's history along with it, including rows this report would have shown. A known side effect of these misidentifications was duplicate artists, so some affected artists have probably already been merged away and cannot be counted at all. The real number is at least as large as the number shown, and may be larger.
Reading the recorded note¶
The note is shown exactly as it was written at the time, and Stillwater never tries to interpret it. Two wordings exist:
- Older entries record only the ID and the artist name.
- Newer entries also record the matched name, where the match came from, the confidence score, and the runner-up.
When you act on a row, use the current MusicBrainz ID, not the note. The ID in the note is what the fix wrote then; the artist may carry a different one now, or none at all. An artist with no MusicBrainz ID recorded today is shown as "none recorded" rather than left blank, because a blank cell reads as nothing to see.
Downloading the CSV¶
Click Export CSV at the top of the pane, or reach the same file directly through the API. The CSV carries the same rows. Note rows at the end restate every limit above, so a spreadsheet you open weeks later still says what it does not cover. The export is capped at 10,000 rows and tells you when it was truncated.
Additional reports¶
Six further reports appear in the rail and are coming in a future release:
| Report | What it will cover |
|---|---|
| Underrated artists | Artists with strong library presence but a low health score |
| Image coverage | Thumb, fanart, logo, banner, and backdrop coverage |
| Connection sync | Where each artist is registered across connected platforms |
| ID/Metadata coverage | Per-provider linking and metadata status |
| State records | Artist lifecycle and refresh history |
| Weekly review queue | Artists scheduled for manual review |
Duplicate and foreign-file reports¶
The sidebar's Reports group also lists Duplicates, Backdrop Duplicates, and Unmatched Images. These are dedicated pages at their own URLs (/reports/duplicates, /reports/backdrop-duplicates, /reports/foreign-files), not entries in the two-pane workspace's rail. The Duplicates and Unmatched Images pills show a live count when there's something to review. See Merge duplicate artists for the duplicate-detection and merge workflow.
Backdrop duplicates¶
The Backdrop Duplicates report finds cases where the same backdrop picture has been written into several of one artist's backdrop (fanart) slots. This commonly happens when a media server's own image fetcher saves the same artwork under many tags, so one artist ends up with the same image repeated across fanart.jpg, fanart2.jpg, fanart3.jpg, and so on. It is an admin-only page; click Backdrop Duplicates in the sidebar's Reports group to open it.
The report scans every artist's backdrops on disk and finds exact duplicates: byte-for-byte identical files, matched by a content hash. Because a removed copy is identical to the one kept, collapsing them loses nothing.
That scan reads every backdrop file in the library, so it runs in the background rather than each time you open the page. The report shows the result of the most recent scan, with the time it was taken, so the numbers are a recent snapshot rather than a live count. The alternative would be re-reading the whole library on every page load, which on a large or network-stored library takes minutes.
Opening the page is what starts a scan: if none has ever completed, the page starts one in the background and shows you the current result immediately without waiting for it. If no scan has completed yet -- on a first run -- the page says the report is still being prepared. Reopen it once the scan finishes. The page deliberately does not show zeros in that state, because "nothing has been measured yet" and "your library is clean" are different answers.
The page summarizes how many artists are affected and how many exact redundant slots exist, with a per-artist breakdown. If some artists could not be scanned, a Partial Scan notice reports how many were skipped, so a partial result is never mistaken for a clean library.
Collapse exact duplicates¶
Click Remediate Exact Duplicates to collapse the exact (byte-identical) redundant slots across the whole library in one pass. For each affected artist, the lowest-numbered backdrop slot is kept and the identical copies are removed, with the remaining backdrops renumbered into a gap-free sequence.
Remediation is safe by design:
- A backdrop you set or locked yourself is never removed -- operator-curated artwork is protected from this tool.
- An artist always keeps one copy of each distinct backdrop; the tool only removes proven duplicates.
- Only one remediation runs at a time, and it will not run while a bulk image action is in progress, so the two can never touch the same files at once.
Only the copies stored locally are collapsed; backdrops already pushed to a connected platform are not removed by this action.
Perceptual duplicates¶
Some redundant backdrops are visually near-identical but not byte-for-byte the same file -- the same picture saved twice at different quality or resolution, for example. The exact-duplicate scan cannot see these, because it matches by content hash, and a re-encoded copy hashes differently even though it looks the same on screen.
Remediation reports these separately from the exact count it just collapsed, rather than folding them together. A run that removed only the byte-identical minority and left the look-alike majority in place should never read as a clean sweep -- "removed" and "declined to remove" are different outcomes, and the report keeps them distinguishable. This also means a re-run that appears to do nothing can be told apart from one that genuinely had nothing left to do: the first still shows a perceptual count, the second shows none.
If remediation has never run, or no report has ever measured perceptual redundancy, the count is shown as unknown rather than as zero -- an unmeasured library is not the same claim as a clean one.
Platform backdrop duplicates¶
Platform sync is additive: pushing fanart to a connected media server never deletes a surplus copy on that server, so a redundant slot that the local Backdrop Duplicates report already cleaned up on disk can still linger on Emby or Jellyfin. The Platform Backdrop Duplicates report finds those leftover copies directly on your connected platforms. It is an admin-only page at /reports/platform-backdrop-duplicates.
The report re-reads every artist's backdrops from each connected platform and finds exact duplicates: byte-for-byte identical files, matched by a content hash, the same standard the local report uses. It is a dry-run: nothing is deleted until you choose to prune.
That sweep asks every connected platform about every artist, so it runs in the background rather than each time you open the page. The report shows the result of the most recent sweep, with the time it was taken, so the numbers are a recent snapshot rather than a live count. The alternative would be querying every platform for every artist on every page load, which on a large library takes a minute or more before the page can draw anything at all.
Opening the page is what starts a sweep: if none has ever completed, or the last one is more than twelve hours old, the page starts one in the background and shows you the current result immediately without waiting for it. If no sweep has completed yet -- on a first run -- the page says the report is still being prepared. Reopen it once the sweep finishes. The page deliberately does not show zeros in that state, because "nothing has been measured yet" and "your platforms are clean" are different answers.
The page summarizes how many connections and artists are affected and how many redundant backdrops exist, with a per-artist, per-platform breakdown. If some artist/connection scans could not complete, a Partial Scan notice reports how many were skipped, so a partial result is never mistaken for a clean sweep.
Prune platform duplicates¶
Each row on the report carries a Prune This Artist button, and the page as a whole carries Prune All Artists. Both delete the exact (byte-identical) redundant backdrops, keeping one surviving copy per artist per platform. The prune re-scans each platform immediately before deleting, rather than trusting the report you're looking at, so it never acts on a stale count. Only one prune runs at a time, whichever button started it; starting a second while one is in progress is rejected rather than allowed to overlap.
The per-artist button exists so the first real use of an irreversible sweep does not have to be a library-wide one. Deleting a backdrop from a media server is not something Stillwater can undo. Being able to try it on one artist, look at the result in Emby or Jellyfin, and only then run it everywhere is the difference between trusting the feature and guessing about it.
There is deliberately no way to run the prune without saying which of the two you mean. A request that names neither one artist nor the whole library is refused rather than quietly treated as "everything".
You can also rehearse a run before committing to it. Through the API, a dry run that succeeds returns the complete plan -- for every copy it would delete, which copy would survive it -- and deletes nothing at all. A dry run that fails partway through your library returns the plan it had built up to that point rather than the whole one, so treat a failed rehearsal's plan as a partial picture. Every entry in that plan says what became of it: deleted, skipped, failed, or (in a dry run) merely planned. Read those rather than working it out from the total, because they are written as the work happens and cannot disagree with it.
Because the prune only ever removes copies that are byte-identical to a kept survivor, no distinct artwork is ever lost. If a platform's copy is later needed again, re-running fanart sync from the local library re-pushes it from the local survivor.
If a prune fails partway through -- a platform connection drops midway, for example -- you are told how much it actually removed before it stopped, not just that it failed. The prune works through your library page by page, deleting as it goes, so a failure on a later page does not undo the deletions a completed earlier page already made. Being told "failed after removing 40 backdrops" instead of a plain "failed" is what lets you tell a run that changed nothing from one that partly succeeded, and decide whether trying again is even necessary.
After a prune, the report goes back to saying it is still being prepared, and a fresh sweep starts in the background. It does not keep showing the rows it listed a moment ago. Every one of those rows is a claim about a copy the prune just deleted, so leaving them on screen would re-arm the prune button against images that are already gone. The per-platform duplicate counts under Images in the sidebar refresh for the same reason, so the two surfaces cannot end up disagreeing about artwork that is no longer there.
That fresh sweep follows the same background-sweep rules as any other. So if a sweep ran shortly before you pruned, the page can sit in the "still being prepared" state for up to fifteen minutes before the next one may start. This is expected rather than a fault: the report is telling you it has not re-measured your platforms yet, instead of showing a number it can no longer stand behind. Reopen the page once the sweep completes.
Cross-artist backdrop pollution¶
Where the two duplicate reports above find the same backdrop repeated within one artist, this report finds the harder case: a backdrop that belongs to one artist has bled into a different artist's backdrop slots. This happens when a media server's image fetcher saves an artist's promo shot under the wrong artist, so the picture ends up mirrored across two artists' fanart. The report matches backdrops by visual near-similarity (a perceptual hash) rather than byte-for-byte, because a re-encoded or resized copy is no longer byte-identical but is still the same picture in the wrong place.
Because the signal is a visual match, it is inherently ambiguous: two artists can legitimately share a picture (a duo, a collaboration, a festival photo), which looks identical to pollution. Every action below is built to absorb that ambiguity rather than assume it away, which is why the back-out is designed to be undone.
Back out polluted backdrops¶
Remediation quarantines each flagged backdrop, removes it from the artist locally, and deletes the mirrored copy from your connected platforms. It is admin-only.
This report has no screen of its own: unlike the two duplicate reports above, there is no page to open and no button to click. You run the back-out through the API, which is where the API reference documents the request in full. What it asks of you is worth knowing before you run it:
| Option | Meaning |
|---|---|
| Artist | Scope the back-out to a single artist. |
| All artists | Run library-wide. You must choose one scope or the other - a request with neither is rejected, so a forgotten scope can never become a library-wide delete. |
| Dry run | Preview the outcome without changing anything. |
| Tolerance | Optionally override the similarity cutoff, as a number above 0 and no greater than 1. Anything else is rejected rather than quietly falling back to the default. |
Remediation is designed to be safe on an ambiguous signal:
- It re-detects fresh from the current library at the moment it runs, never acting on a report that may have gone stale since you viewed it.
- Before removing any backdrop it re-checks the picture against the bytes on disk - both the suspect and the artist it collided with - and skips anything that no longer confirms the match.
- It copies the picture into a durable quarantine before removing the original, so a removed backdrop is always recoverable.
- The platform-side delete happens only after the local removal has committed; the fuzzy platform match never authorizes a deletion on its own.
The response summarizes how many artists were processed, how many slots were removed, quarantined, or skipped, and any failures, with a per-slot breakdown. On a dry run, slots are reported as "would-remove" and nothing is touched. Each run is tagged with an operation id - keep it if you may want to restore the run later.
Restore a back-out¶
If a back-out removed a backdrop that was not actually pollution, the restore puts that operation's quarantined backdrops back - both locally and on the platforms they were deleted from. It is admin-only.
The restore runs through the API too, and needs two things: the artist, and the operation id from the back-out run you want undone. A request missing either one is rejected, so a restore can never guess which run you meant. The API reference has the request itself.
Restore is content-addressed: it appends a recovered backdrop at the next free slot rather than trying to put it back at its old position, because removal renumbers the survivors and the old position no longer exists. Appending can never overwrite another backdrop. Each quarantined picture lands in one of three states, reported separately so a restore that needs your attention is never mistaken for a clean one:
- Restored - the backdrop was written back to disk.
- Already present - a byte-identical copy was already there, so nothing was needed.
- Needs review - a surviving backdrop only resembles the quarantined one but is not identical, so the restore was declined and the quarantined copy is kept for you to settle by hand. This is deliberately not counted as success.
One repair at a time¶
Both actions, the two duplicate remediations above, and any bulk image action all share a single lock: only one runs at a time, and starting one while another is in progress is rejected with a "backdrop repair or bulk action is already in progress" response rather than allowed to overlap. They all rewrite an artist's backdrop files on disk, so this keeps two runs from ever touching the same files at once.
Background appearance¶
The card surfaces in the Reports workspace follow the Background Opacity preference. See Customize preferences to adjust the frosted-glass opacity of cards and panels.