v1.3.0: launch hardening and Mac installer - #1
Merged
Merged
Conversation
… prior session) - Replace isolated deinit (crashes the macOS 15 test host via back-deployment) with main-thread teardown; move the playback timer into a MainActor clock. - Per-analyzer FFT setup ownership; stereo-aware normalization for opposite-phase sources; direct reads for interior separation windows. - Preserve renamed titles against late metadata and publish Now Playing. - Reserve the drop slot before async provider loading. - Macro/preset status, clip-LED hold, key transposition, timecodes. - VoiceOver labels for mixer, transport, and library controls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Readable text tokens now meet WCAG AA (>= 4.5:1) on every background and channel-strip fill in both modes; previous muted values move to textDisabled. - accentRed is mode-aware (#FF4245 dark / #C8141C light) with an onAccent foreground; add a warning token for comparison and loop markers. - Follow the system Increase Contrast setting for text and outlines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
safeFilename now trims leading dots, so titles such as "...Baby One More Time" no longer produce a hidden ZIP and hidden stems. It also replaces ? * " < > | alongside / : \ so shared ZIPs extract in Windows Explorer, and trims trailing dots and spaces, including after truncation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The peak limiter's look-ahead delayed every mix export by 88 frames and cut the same number of frames from the end. Render past it and discard the look-ahead, so a 1x mix stays sample-aligned with the source and the stems, at the same length. Time/pitch spreads the last frames past the stretched length and reports no latency, so at non-1x speed or shifted pitch up to about 80% of a final hit was lost. Render a fixed 4,096-frame tail whenever time/pitch is active. Such mixes are now 4,096 frames longer than the stretched length; the rate 0.5 length assertion is updated to match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cached Demucs stems are Float32 and often peak above 1.0, and baked stem EQ can push them further. Writing them straight to 24-bit WAV or FLAC pinned every such sample at full scale. archive() now renders each stem to a temporary Float32 file while measuring its post-EQ peak. It then encodes all four with one shared gain, so the loudest stem peaks at -0.1 dBFS and the relative balance and stem sum are preserved. Stems already below that level are written unchanged. archive() returns the applied gain (discardable) so the UI can mention it. Output formats stay 24-bit WAV and FLAC. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
render() takes a trailing, defaulted progress callback that receives the rendered fraction at most once per whole percent, ending at 1. Existing call sites compile unchanged; the mix export can now show real progress instead of "RENDERING 0%". archive() reports continuous progress: stem rendering fills 0-40% and encoding 40-80%, as before the archive step reports 80%. Stem entries are now stored rather than deflated, because deflating PCM or FLAC saves little space and was the slowest part of a stem export. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/usr/bin/zip on macOS stores UTF-8 entry names without general purpose bit 11, so Windows Explorer and other spec-following readers decode a title such as "Beyoncé" as CP437 mojibake. After zipping, set bit 11 in the central and local header of each entry whose name is non-ASCII, valid UTF-8. The stored bytes are already UTF-8 and CRCs cover only file data, so nothing else changes. Zip64 end records and extra fields are followed for archives over 4 GB. The step is best effort, so an unexpected archive layout never fails the export. ASCII-only names are left as they were. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n cancel publish() staged its copy as a hidden ".isolate-<UUID>" file beside the destination, and quitting during a long cross-volume copy left that file behind. It now stages in the system's item replacement directory on the destination volume and replaces or moves the file into place from there. Volumes that cannot provide that directory fall back to the previous sibling file. The zip step blocked in waitUntilExit, so it could not be cancelled. It now polls, terminates zip when the task is cancelled, and reports cancellation before the exit-status check, so a cancelled export surfaces as CancellationError and nothing is published. Render and encode loops already check for cancellation on every block. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The library used SwiftData's default configuration, which for this unsandboxed app is the shared ~/Library/Application Support/default.store. Other unsandboxed SwiftData apps open and migrate that same file, dropping each other's tables, so launching one could erase Isolate's library (and Isolate could erase theirs). The library now lives in Application Support/Isolate/Library.store. On first launch, rows from a pre-1.3 library are imported from a copy of default.store (plus -wal/-shm), and only after SQLite confirms the copy has Isolate's table; the shared file is never opened in place, modified or deleted. The container is created explicitly instead of through the scene modifier, whose failure path silently swapped in an in-memory store. An unreadable store is moved (never deleted) into a timestamped Library Backups folder and replaced, and the user is told where it went; only if that also fails does the session run in memory, with a visible notice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…enu bar - Quitting (Cmd-Q or the status menu) while a separation or export is running now asks first. Confirming a quit during separation cancels the import and waits (up to 15 s) for it to clean up before exiting. - The status menu's "Open Isolate" and track items targeted NSApp.windows.first, which is the status item's own window once the main window is closed, so nothing opened. They now use the scene's openWindow action, which reopens a closed window, fronts an open one and restores a minimized one. - Automatic window tabbing is off, so the tab bar's "+" can no longer create a second main window with duplicated modals and importers. - The image-only status item has a VoiceOver label and a Playing/Paused value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The track-number cleanup stripped leading digits from real titles
("7-Eleven" became "Eleven", "1-800-273-8255" lost its "1-"). Only an
unmistakable "01 - ", "01. " or "01_" prefix before a letter is removed
now.
- The artwork object is reused while the image is unchanged, and progress
is republished only when playback departs from what Control Center
extrapolates (seek, loop wrap, rate or duration change), so the
once-per-second timer call no longer pushes the full dictionary.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… sidebar - Search matched the shared stem file names and the full source path, so "bass", "drum", "the", "all" or the account name returned every track. It now matches the title, source file name and folder only, without the "stem"/"stems"/"all" show-everything special case. The placeholder reads SEARCH LIBRARY. - Media keys, Control Center and the status menu stepped through date-added order while the sidebar groups by folder; both now use one shared grouping helper, so Next goes to the row below. - Folders with the same name (two "Greatest Hits") get their parent folder in the header. - The filter runs once per update instead of once per visible row. - Typing a search closes an open inline track menu, whose invisible click catcher otherwise swallowed the next click on the player. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- CANCEL IMPORT owned Escape even while About or Settings was drawn over the progress screen, so pressing Esc to close About discarded minutes of separation. While another card covers it, the cancel button is disabled and has no shortcut; Escape closes that card instead. - About now closes with Escape like the other cards. - A separation started from a media key closes an open delete card, and deletion is refused while a separation runs, so the track being rebuilt cannot be removed underneath it. - Hiding the sidebar closes an open inline track menu, whose invisible click catcher otherwise swallowed the next click on the player. - Renamed titles are capped at 200 characters, and the delete card truncates the title so its buttons can't be pushed off screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rwrite silently - A quarantined app opened from a downloaded DMG runs from a randomized AppTranslocation path, so the /Volumes/ check never matched and the move prompt never appeared for the real distribution path. The original location is resolved first, and only read-only volumes (disk images) count, so apps kept on a writable external drive are left alone. - MOVE & RELAUNCH replaced any /Applications/Isolate.app without asking. An existing copy now needs a second, explicit REPLACE & RELAUNCH click and is moved to the Trash rather than deleted; an app with a different bundle identifier is never touched. The new copy is staged first, so a failed copy leaves the installed app alone. - The installed copy drops the download's quarantine flag, as a copy made by Finder would, so it is not translocated again on every launch. - The prompt no longer promises to eject the installer, and NOT NOW or a backdrop click only dismisses it for this launch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed cancel - Red is kept for interrupt and active states only: the loaded track's indicator bar, destructive delete, error toast, drop target and separation progress, all through theme.accentRed (mode-aware, AA as text). IMPORT TRACK, the search focus ring, active-row title, bullets, scroller, About badge and bullets, and the neutral primary buttons (CLOSE, SAVE, MOVE & RELAUNCH) use text/surface tokens; foregrounds on red fills use onAccent. - Library empty and no-match text used Color.gray literals (down to 1.7:1 in light mode); they use textSecondary/textMuted. - Error toasts are announced to VoiceOver and fade instead of sliding when Reduce Motion is on; the decorative red glow is gone. - CANCEL IMPORT was a stock Aqua push button that ignored its red tint. It is now a plain DotGothic16 button in the destructive style, and the separation screen leads with the percentage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Library store (legacy import from a copied default.store that stays untouched, another app's store left byte-for-byte, backup and recovery of an unreadable store, visible in-memory fallback), search and sidebar order, folder headers, rename bound, Now Playing title cleanup, artwork reuse and progress republishing, quit confirmation, Escape ownership, main-window targeting, window tabbing, status item VoiceOver name, and the move-to-Applications install and translocation checks. All tests use temporary directories and in-memory or temporary SwiftData stores. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DemucsEngine.accumulate evaluated MLMultiArray.dataType and dataPointer, both Objective-C getters, for every one of the 3.5 million samples in a chunk. Resolve the element type and storage once and walk raw buffers. The arithmetic and its order are unchanged, so output stays bit-identical; a test compares both loops on strided Float16 and Float32 tensors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
StreamingAudio asked ExtAudioFile for stereo, which keeps only channels 0 and 1 of wider files, so a 5.1 mix lost its centre vocal. Decode every channel with an explicit client layout (ALAC and AAC otherwise arrive in codec order) and downmix with AVAudioConverter; files without a declared layout use the WAV/FLAC default order. Some decoders end cleanly at a damaged region. For PCM, FLAC and ALAC, whose declared length is exact, fail with the decoded and expected durations when more than a second and 1% is missing, instead of caching part of the song under the file's hash. Core Audio failures now read as plain language with the four-character code kept for diagnosis. The cache pipeline version moves to v4 because multichannel output changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…idle model - A model that exists but fails to load or validate now reports its path and the Core ML reason instead of 'CoreML Model Not Found'. Duplicate bundle candidates are tried once. - The model loads in one shared detached task. Cancelling an import stops waiting at once; the load finishes in the background and the next import reuses it instead of compiling again. - The model is released after 60 s with no separation running and reloaded on demand. In a probe on the development Mac, reloading from the compiled cache took 3.2-3.7 s (about 17 s only the first time a new build loads it); after inference the process footprint was 1.6-2.7 GB and releasing the model brought it to between 40 MB and 1.2 GB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… iCloud files - Quitting or crashing mid-separation left .partial-* folders (and rarely .backup-*) in the stem cache forever. StemCache.removeAbandonedStaging() deletes them; each separation calls it once it holds the single separation slot, and DemucsEngine.removeAbandonedStaging() is safe to call at launch. - Before loading the model or decoding, compare the space the cache will need (five Float32 stereo files at the declared length, plus headroom) with the volume's available capacity and fail with a readable message. - An evicted iCloud Drive file is downloaded explicitly with a 'DOWNLOADING FROM ICLOUD...' status. The wait honours Cancel, surfaces iCloud download errors, and fails with a clear message after two seconds without a network path instead of blocking in a read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Failures in a batch are collected and shown once, naming each file with
its reason ('2 of 5 files could not be imported. ...'), instead of an
anonymous toast that the next failure overwrites. A cancelled batch
says how many remaining files were not imported.
- ImportCoordinator publishes currentFileName, batchIndex and batchCount
for the progress screen.
- Dropped or chosen folders expand recursively to supported audio files
in Finder name order, off the main actor; duplicates import once.
'aifc' replaces 'alac', which is not a file extension.
- New tracks take Finder's display name, so 'AC/DC' is no longer stored
as 'AC:DC', and Now Playing keeps that title after tag metadata loads.
- A reimport that moves a track to a new cache folder deletes the old one
when the cache owns it and no other track uses it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
play(at:) blocks for about one output IO cycle per player, so the fixed 30 ms lead let drums, bass, other and the original start 10-43 ms after vocals on every play, seek, loop wrap and autoplay. Size the lead from the device IO buffer, check the calls finished in time, and reschedule from the same frame with a longer lead if they did not. Pause now pauses the engine instead of each player, which freezes all five on one render cycle (no drift across pause/resume), resumes without play(at:) calls, and releases the output device so the Mac can idle-sleep. The engine also idles after the track ends and on unload. Imports pause playback directly because togglePlayback() ignores requests while splitting. Seeks and unloads flush the time/pitch tail from the old position; loop wraps keep it. Seeking to the end while looping wraps to the loop start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shortest A-B loop was 2% of the track (7.2 s on a six-minute song), so short phrases could not be looped. Use a half-second minimum instead. Pressing [ at or after B, or ] at or before A, now starts a new region rather than clamping back into the old one. Reselecting the loaded track (or Next/Previous in a one-track library) no longer reloads it and resets the mix, loop, speed and position. Selecting an entry whose stems and source are both missing shows the error without unloading a different track that is playing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Taps deliver about 100 ms per buffer regardless of the requested 1024 frames, and the spectrum only analysed the oldest 1024 frames. Analyse every FFT-sized hop, newest first, so recent audio and short transients register; 1024-frame buffers are processed exactly as before. Compare Original silences the stem sum after the per-stem taps, so the channel meters and spectra kept moving for stems nobody could hear. Clear them when bypass turns on and ignore stem readings while it is on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The metadata task inherits the main actor, so full-resolution artwork decoding and the synchronous AVAudioFile open of the source ran on the main thread. Decode embedded covers as bounded ImageIO thumbnails (at most 1024 px, oversized images rejected) and probe the source format on a worker; results still arrive through the request-ID guard. Fallback titles use the Finder display name, so "AC/DC" is no longer shown as "AC:DC" and a hidden extension is not stripped twice. New imports keep the library title in Now Playing instead of switching to the tag title until the track is selected again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Shortcuts tab scrolls inside its 345pt frame instead of clipping the first and last rows, and lists every real shortcut ([ ], ⌥L, ⌘1-5, ⌘E, ⌘, ⌘0, ?, Esc, arrow keys) (design-a11y-2). - Selected chips and tabs use an inverted neutral style and expose the isSelected trait; option groups are labelled (design-a11y-8). - Replace system red/white/black literals with theme tokens; red stays on the ON toggle only (design-a11y-3/-5/-6); reset button fill follows its rounded outline (design-a11y-20). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Hover was dead code under allowsHitTesting(false); track it on the track hit area within the cap's grab band and drop the unbalanced NSCursor push/pop (design-a11y-19). - Level fill, +6 tick and unlit clip LED use neutral tokens; red marks only the lit clip LED and a cap in hand (design-a11y-6). - Tick marks use theme tokens and follow Increase Contrast (design-a11y-13). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lity Layout (ui-1, ui-9) - Header reserves a measured share for the STUDIO HUD; the HUD drops track metadata, then shortens tab, EQ stem and macro labels, and telemetry switches to two-line cards, so nothing truncates from the 860pt threshold up (default 1280x800 window shows full tab names). - Transport picks regular/compact/tight control sizes from the space left for the seek bar, which keeps at least 100pt at the 960pt minimum window with the library open (was 50pt). - ⌘1-5 hide the library when the HUD cannot fit beside it, instead of switching an invisible mode. Controls - Double-click resets pan, EQ knobs and HUD EQ nodes; the tap runs alongside the zero-distance drag (ui-5). - ⌘E global EQ bypass shows "ALL EQ OFF" on every channel strip and "ALL BYP" in the HUD; clicking either restores EQ (ui-6). - Seek bar, LOOP and [ ] L ⌥L do nothing without a loaded track (ui-13). - Pan readout rounds, and keyboard steps snap to the 5% grid (ui-14). - HUD EQ node haptic plays once on entering 0 dB (ui-15). - Clear loop markers with ⌥L or the LOOP context menu (audio-engine-7). - Escape closes the shortcut cheat sheet; it lists ⌥L and ⌘⌥B and names ⌘E correctly (ui-10). Accessibility and theme - Labels, values and selected traits for balance M/S, HUD tabs, EQ stem pills, macro presets, BYPASS and album art; HUD EQ nodes are adjustable and keyboard-focusable; larger hit targets for steppers and chips (design-a11y-7/-8/-10/-11/-17/-23). - Marquee text ends in an ellipsis with a tooltip under Reduce Motion (design-a11y-18); rounded fills stay inside their outlines (-20). - Theme tokens replace system red/yellow/gray literals; red is kept for solo/mute, loop, clip, modified values, export in progress and the play button; comparison and loop markers use warning (design-a11y-3/-4/-5/-6). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Double-clicks and key presses are posted straight to an offscreen window, so the tests never move the pointer or use the keyboard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- README, INSTALL and TROUBLESHOOTING explain that Core ML in macOS 14 and 15 computes the model incorrectly on some paths, that Isolate checks it first, and what to do if it refuses (update to macOS 26). - AUDIO_ENGINE and MODEL describe the sample-time start and self-test. - Add the opt-in real-music smoke test as a documented release gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Starting at the last render time plus a lead could name a frame that had already passed when the timestamp was stale or rendering ran ahead, and on hosted macOS 15 some starts never played. Map the host-time start onto the players' timeline through the last render's sample and host times, so every player still gets one frame that is a real future moment. A watchdog confirms the players moved once the start has passed and otherwise restarts from the same frame on the host-time path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… doc accuracy - package_release.sh captured codesign output through grep -q under pipefail, so SIGPIPE could fail the check at random. - Run the release workflow on macos-26, where inference is verified; hosted macOS 14/15 refuse to separate, which would skip the gate. - RELEASE.md: merge to main before announcing and bump the cask there. - Correct cache, batch-summary, equalizer, shortcut-card and staging descriptions against the code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… started while listing A batch no longer asks iCloud Drive for every evicted file up front. The next file is requested from inside the loop while the current one separates, so Cancel leaves at most one extra download running instead of the rest of the folder, and prefetched sources no longer compete with the per-file disk-space preflight. (separation-1) A library track re-separated while a dropped folder was being listed held the engine, and every file in the batch then failed at once with "The import did not finish." The batch now waits for that separation before it starts. (separation-3) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two copies of Isolate at different paths (the DMG copy beside /Applications, or the move-to-Applications relaunch) share the Stems cache, and each one's abandoned-staging sweep deleted the other's live .partial folder, so that separation failed at publish after minutes of inference. A separation now holds a non-blocking flock on a .lock file inside its staging folder until it finishes. The sweep skips a .partial folder whose lock is held and still removes folders with no lock file or a released one, since the kernel drops the lock when the owner exits or crashes. The lock file is removed from the published cache after the move. If the lock cannot be taken the separation still runs, as before. (separation-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
folderLabels compared every folder against every other one and ran on each LibraryView body update, so search keystrokes and SETTINGS hover stalled the main thread in libraries with hundreds of album folders. Count each depth's suffixes in a dictionary instead (same labels), and keep the result in a non-observable cache that rebuilds only when the set of folders changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- EXPORT: "Cancel export" carries "<n> percent" as its value while rendering, and COMPLETED reads "Export complete" with no Cancel Export menu item or cancel tooltip. The UI test accepts either busy label, since a short render may already show COMPLETED. - MarqueeText draws with a fixed-size font so the width it measures is the width it draws; the artist line uses 11/10 pt, the sizes macOS 27 already drew for 10.5/9.5. Long album names no longer truncate with no tooltip, and the scroll reaches the end of the line. - The stem waveform's idle center row uses border under Increase Contrast so it stays brighter than the unlit dots. - The compact STUDIO HUD hides the EQ curve's Hz captions, which the band nodes and their names covered. - The shortcut cheat sheet cross-fades under Reduce Motion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… folders - Only a store SQLite itself reports as damaged (NOTADB/CORRUPT or a failed quick_check) is moved to Library Backups. Any other open failure (full disk, permissions, a lock, a newer schema) leaves the file in place and runs the session in memory with a notice that it was left unchanged. - If a new store cannot be started after the move, the original files are put back so the next launch retries them; if that fails too, the notice names the backup folder. - The backup location is saved and shown at every launch until the user closes the notice or the folder is gone (not while still in memory). - Moving the store aside is all-or-nothing, so a failed move never leaves Library.store without its -wal. - A failed legacy import is reported and retried at up to three launches instead of being given up on at once; a failed copy now shows a notice. - In an in-memory session the "will not be saved" warning is repeated when an import batch ends, since each file clears the startup toast. - Search also matches the folders below the path all tracks share, so artist folders shown in group headers match while /Users/<name>/Music does not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rden the move prompt - The quit confirmation now answers .terminateLater from a run-loop timer in common modes instead of a Task, so the reply arrives even when terminate runs inside a main-queue job (it no longer hangs forever). - Move & Relaunch quits from a run-loop callout instead of the install Task; if the user keeps a separation or export running, the card resets. - Quitting during an export cancels it and waits up to 5 s for its temporary renders and zip process to be cleaned up, as separation already did. - NOT NOW, Escape and the backdrop cannot hide the card while installing, so an install failure is always seen; declining lasts for the whole launch even when the window is reopened. - The replace prompt says when the installed copy is newer, and the second click of a double-click can no longer confirm the replacement. - The completion notification reads "Stems Ready" for a single track. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…move prompt FinalLibraryTests adds regression tests for library-1/2/4/5, shell-1/3/4/6 and export-2: a readable store that fails to open stays in place, a damaged one is put back when no new store can start (or its backup is named when it cannot), the recovery notice repeats until closed, a partial move aside is rolled back, artist folders are searchable but the shared path is not, a failed legacy copy is retried and then capped, the quit reply arrives from inside a main-queue job and at its deadline, and the move card cannot be dismissed while installing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st render Loop wraps and seeks still waited out the host-time lead (per-player term, presentation latency and a 5 ms pad), about 0.1 s of silence at a 512-frame buffer. The render-timeline start now uses its own lead: three output cycles past the newest render on the players' timeline, scaled by the time/pitch rate, and it retries unless every play(at:) landed with at least two cycles to spare. The long host-time lead and retry remain only for the fallback, and the watchdog is unchanged. The frame is derived from sample time alone. While the engine is paused the players' sample time stands still but the host time reported with it keeps the pause as an offset, so mapping a host time through it started every seek or wrap after a pause late by the pause (0.5 s measured) until the watchdog switched the session to host-time starts. After the engine starts, the anchor waits for its first render so it is never the one from before the stop. The host-time retry cap also leaves room past the margin on outputs whose latency alone nears 2 s, so later attempts can succeed. Measured on this Mac (512 frames at 48 kHz): loop-wrap gaps 32-43 ms, down from 99-104 ms; a wrap after a 0.8 s pause 32 ms, down from 493 ms with a watchdog restart. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reselecting the loaded track returns early to keep its mix, loop, speed and position, which also swallowed the click once the track had played to its end. With autoplay on, a stopped track at its end now plays again from the start, or from the loop start when looping, like the menu and media-key selection already did. Playing or paused mid-track, reselecting still changes nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…endering Rendering checks for cancellation only at the top of each block, and publishing did not check at all, so a Cancel clicked at RENDERING 99% or ARCHIVING 80% (while a cross-volume copy runs) still replaced the destination and revealed it in Finder. The mix export now checks before publishing, and publish checks again after its copy to the destination volume and before the swap; the existing defer removes the staged copy. Only a cancel during the swap itself can still complete, and then the file really was replaced, so beginExport keeps reporting it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Separation and export hold no power assertion, and pausing through the engine now releases the output device, so nothing kept an unattended folder import or long export from idle sleep or App Nap. Each separation and each export now holds a user-initiated activity that disables idle system sleep until it finishes, fails or is cancelled. The display may still sleep. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The end of playback arrives after the output device's latency, which is seconds on AirPlay, and the output idles 0.5 s after that, so a fixed sleep failed on high-latency outputs. Poll for both, with an 8 s bound, before the existing assertions. Also state the measured pause regression precisely in the new loop-wrap test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Cancel Export in the File menu stays reachable while an overlay covers the EXPORT control. - Document the render-timeline start and loop-wrap gap, damage-only library recovery with in-memory fallback, legacy import retries, next-file iCloud prefetch, quit-during-export cancellation, keep-awake activity, folder search and replaying a finished song. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
neokumar1
marked this pull request as ready for review
September 27, 2026 17:36
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft — launch-readiness audit in progress. First commit carries the prior session's uncommitted hardening, pushed early to verify the macOS 15 test-host crash fix on CI.
🤖 Generated with Claude Code