Finding
Renaming a library while supported library-scoped background work is active changes the natural name used to derive scan and tag-write task IDs without migrating or quiescing the already-running tasks. Subsequent cancellation/deletion/status operations derive IDs from the new name and can therefore lose control of work still registered under the old name. In the deletion journey this defeats the intended quiescence boundary: deleting the renamed library can miss the old-name task and remove persistence while that task remains active.
User/System Journey
- An operator starts a normal quick/full scan or background tag-write for library
Music. Managed task IDs are derived from the mutable natural name (library-scan-Music and write_tags:Music).
- While that task is active, the operator uses the supported
PATCH /library/Music endpoint with name="Archive". The update path persists the rename without rejecting active scan/tag-write work or migrating its task registration.
- The already-running task remains registered under the ID derived from
Music, while future API requests resolve the persisted library as Archive.
- A later cancellation/status/deletion path derives its task ID from
Archive, so it does not address the task registered under Music.
- Most critically,
DELETE /library/Archive calls _quiesce_library_tasks() with the renamed Library. cancel_and_join() is therefore invoked for library-scan-Archive and write_tags:Archive; an active old-name task is not joined. The service can then proceed to delete the library persistence even though the pre-rename task is still executing.
- This requires only two supported operator actions during normal background processing. No injected state or internal-only caller is involved.
Files in scope
nomarr/interfaces/api/web/library_if.py — update_library() permits name changes without coordinating active library-scoped tasks; delete_library() relies on service quiescence after resolving the new name.
nomarr/services/domain/library_svc/admin.py — update_library() persists the mutable name; _quiesce_library_tasks() derives cancellation targets from the current Library value.
nomarr/services/domain/library_svc/task_ids.py — library_task_id() and write_tags_task_id() explicitly derive managed task keys from Library.name.
- Scan/tag-write start, cancel, status, and background-task boundaries that use these deterministic task IDs.
Expected behavior
Changing display/configuration metadata must not sever lifecycle ownership of already-running work. A successful rename should either be excluded while name-keyed work is active, migrate/retain a stable task identity, or otherwise guarantee that cancellation, status, and destructive deletion still target and join the pre-rename tasks.
Actual behavior
The mutable library name is both editable configuration and the key material for managed background task IDs. Renaming leaves existing tasks under old-name IDs, while subsequent lifecycle operations derive new-name IDs. Deletion can therefore consider the renamed library quiescent and remove its persistence while old-name work remains alive.
Correction direction
Restore a stable lifecycle identity across library rename. Background-task ownership should not depend on mutable display metadata, or rename must be coordinated as a lifecycle transition that cannot leave old task registrations reachable only by the previous name. Preserve the deletion invariant that every library-scoped scan/tag-write task is cancelled and joined before persistence removal, and add journey coverage for rename during both active scan and active tag-write followed by cancel/status/delete.
Metadata
QA-Agent: ChatGPT
Reviewed-SHA: 65230637a6c36943fb5367dbc9f036894179fcb9
Branch: feat/develop-branch-migration
Severity: High
Category: correctness
Review-Mode: user-journey-head
QA-Fingerprint: xiaden/nomarr|correctness|library-background-task-identity|mutable-library-name-orphans-active-task-lifecycle
Finding
Renaming a library while supported library-scoped background work is active changes the natural name used to derive scan and tag-write task IDs without migrating or quiescing the already-running tasks. Subsequent cancellation/deletion/status operations derive IDs from the new name and can therefore lose control of work still registered under the old name. In the deletion journey this defeats the intended quiescence boundary: deleting the renamed library can miss the old-name task and remove persistence while that task remains active.
User/System Journey
Music. Managed task IDs are derived from the mutable natural name (library-scan-Musicandwrite_tags:Music).PATCH /library/Musicendpoint withname="Archive". The update path persists the rename without rejecting active scan/tag-write work or migrating its task registration.Music, while future API requests resolve the persisted library asArchive.Archive, so it does not address the task registered underMusic.DELETE /library/Archivecalls_quiesce_library_tasks()with the renamedLibrary.cancel_and_join()is therefore invoked forlibrary-scan-Archiveandwrite_tags:Archive; an active old-name task is not joined. The service can then proceed to delete the library persistence even though the pre-rename task is still executing.Files in scope
nomarr/interfaces/api/web/library_if.py—update_library()permitsnamechanges without coordinating active library-scoped tasks;delete_library()relies on service quiescence after resolving the new name.nomarr/services/domain/library_svc/admin.py—update_library()persists the mutable name;_quiesce_library_tasks()derives cancellation targets from the currentLibraryvalue.nomarr/services/domain/library_svc/task_ids.py—library_task_id()andwrite_tags_task_id()explicitly derive managed task keys fromLibrary.name.Expected behavior
Changing display/configuration metadata must not sever lifecycle ownership of already-running work. A successful rename should either be excluded while name-keyed work is active, migrate/retain a stable task identity, or otherwise guarantee that cancellation, status, and destructive deletion still target and join the pre-rename tasks.
Actual behavior
The mutable library name is both editable configuration and the key material for managed background task IDs. Renaming leaves existing tasks under old-name IDs, while subsequent lifecycle operations derive new-name IDs. Deletion can therefore consider the renamed library quiescent and remove its persistence while old-name work remains alive.
Correction direction
Restore a stable lifecycle identity across library rename. Background-task ownership should not depend on mutable display metadata, or rename must be coordinated as a lifecycle transition that cannot leave old task registrations reachable only by the previous name. Preserve the deletion invariant that every library-scoped scan/tag-write task is cancelled and joined before persistence removal, and add journey coverage for rename during both active scan and active tag-write followed by cancel/status/delete.
Metadata
QA-Agent: ChatGPT
Reviewed-SHA:
65230637a6c36943fb5367dbc9f036894179fcb9Branch:
feat/develop-branch-migrationSeverity: High
Category: correctness
Review-Mode: user-journey-head
QA-Fingerprint:
xiaden/nomarr|correctness|library-background-task-identity|mutable-library-name-orphans-active-task-lifecycle