Skip to content

fix(moves): organize moves into a subfolder of the source no longer fail as overlapping#761

Draft
kevinheneveld wants to merge 1 commit into
Listenarrs:canaryfrom
kevinheneveld:fix/organize-descend-moves
Draft

fix(moves): organize moves into a subfolder of the source no longer fail as overlapping#761
kevinheneveld wants to merge 1 commit into
Listenarrs:canaryfrom
kevinheneveld:fix/organize-descend-moves

Conversation

@kevinheneveld

Copy link
Copy Markdown
Contributor

The bug

Naming templates that append subfolders (narrator, edition, …) make organize request moves like:

/audiobooks/Stephen King/Elevation  →  /audiobooks/Stephen King/Elevation/Stephen King

— a target inside its own source. The copy-walk mover can't execute that shape (the recursive copy would descend into its own output, and the post-copy Directory.Delete(source, true) would delete the result), so the overlap guard failed the whole class with "Source and target paths overlap".

On my live library this hit 97 consecutive organize jobs — every book whose folder didn't yet contain the template's narrator subfolder.

The fix

Descend-shaped moves don't need to copy anything: two same-volume atomic renames do it safely —

  1. rename the source aside (same parent, atomic): ElevationElevation.moving-<jobId>
  2. recreate the vacated path: mkdir Elevation
  3. rename into place: Elevation.moving-<jobId>Elevation/Stephen King

with best-effort rollback if step 2/3 fails (the recreated skeleton is empty by construction, so it's dropped and the aside-rename is reversed). The inverse shape — flattening a folder up into its populated ancestor — stays blocked exactly as before.

Implementation notes:

  • The rename logic lives in a MoveJobProcessor.DescendMove partial; the processor itself gains only the branch points (it was 1 line under the 500-line architecture rule).
  • The existing completion path (history entry, notifications, scan enqueue, status) is shared unchanged.

Testing

  • New integration tests drive ProcessJobAsync directly: the descend shape completes with files physically renamed into the child folder and a Moved history entry; the upward-overlap shape still fails with the original error and leaves files untouched.
  • Full suite: 1191 passed.
  • Live-verified on my instance before porting here: all 97 previously-failed jobs were requeued after this fix — 90 completed (the other 7 were stale requests whose source folders no longer exist).

Heads-up: #717 also works in the moves area — happy to rebase this on top of it if that lands first; the footprint here is deliberately small.

🤖 Generated with Claude Code

…scend moves)

Naming templates that append subfolders (narrator, edition) produce
moves like '.../Elevation' -> '.../Elevation/Stephen King': a target
inside its own source. The copy-walk mover can't do that shape — it
would recurse into its own output, then the source cleanup would delete
the result — so the overlap guard rejected the whole class ('Source and
target paths overlap'). A live library hit this on 97 consecutive
organize jobs.

Such moves now run as two same-volume atomic renames: rename the source
aside (same parent), recreate the vacated path, rename into place —
with best-effort rollback on failure. The inverse shape (flattening a
folder up into its populated ancestor) stays blocked. Logic lives in a
MoveJobProcessor.DescendMove partial to respect the 500-line rule; the
processor gains only the branch points.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant