Imported from xiaotwu/Muses (
AGENTS.md). Install upstream withnpx skills add xiaotwu/Muses. Copyright stays with the author.
Muses Project Guidance
Purpose and Scope
This file defines durable product, engineering, UX, design, performance, and verification rules for all work in this repository. It is persistent agent context, not a roadmap or task list.
Muses is a native macOS YouTube-native music and podcast application. The library includes imported YouTube videos and playlists, stable YouTube-backed catalog identities, and followed podcast shows. Playback, queue, artwork-led browsing, a persistent PlayerBar, immersive Now Playing (cover + vinyl), on-demand video, and native macOS integration remain the product. It is not a local-file media player or a demo.
As of 2026-08-20 the local-file era is retired: no folder scanning, no file import, no M3U workflow, no local additions to imported YouTube playlists, and no LocalAudioEngine in the running app. The approved store cutover decision (D1) chose a one-time logical snapshot/import into a clean versioned store. Build and validate the replacement store at a distinct URL. Do not delete a source store as part of pointer activation; after manifest-complete validation and a successful cold restart without the legacy store, remove the legacy store family and its compatibility pipeline. The user explicitly chose no long-term migration capsule on 2026-08-24.
Project Layout
Standard SwiftPM application layout:
Package.swift— single package: executableMuses, executableMusesWebHomeHelper, targetsMusesWebHomeProtocol/MusesWebHomeCore, test targetMusesTests.Sources/Muses/— application sources (App/,Domain/,Features/,Infrastructure/,Persistence/,Services/,Resources/).Sources/MusesWebHomeHelper|Core|Protocol/— the isolated Web Session Home helper and its parser/protocol layers.Tests/MusesTests/— Swift Testing suites; fixtures underTests/MusesTests/Fixtures/.Scripts/— packaging, DMG, icon, yt-dlp bootstrap, and a local run helper.docs/— user-facing documentation that also powers GitHub Pages.
Build and verification entry points: make build, make test (swift test --no-parallel), make app, make dmg, ./Scripts/build-app.sh --identity <identity> (OAuth client injected via MUSES_GOOGLE_OAUTH_CLIENT_ID[/SECRET] build environment; never committed).
Isolated Web Session Home Boundary
Web Session Home ("个性化首页") is a durable product boundary, not a task artifact:
- Off by default; enabling requires an explicit dedicated consent with the detected Safari/Chrome/Firefox source pinned at confirmation time.
- All web fetching happens in a separate one-shot
MusesWebHomeHelperexecutable at a fixed bundle path, reached over versioned stdin/stdout IPC with signature verification, strict size limits, and timeouts/cancellation. - Browser cookies live only in a permission-restricted temporary jar (0700/0600) deleted when the helper exits. SAPISIDHASH is generated in memory per request. Continuation tokens live only in volatile memory.
- Cookie, auth-hash, raw payloads, and continuation tokens are never logged, persisted to SwiftData, or written to ordinary caches. Web snapshots live in the partitioned
~/.muses/cache/home-feed/<scope>/web-v1cache and are normalized, whitelisted-parsed values only. - The Web layer is read-only display data: it never participates in playback authority, Push, playlist writes, or user-data truth. Web channel identity must exactly match the connected OAuth channel (fail closed otherwise).
- Preferred failure states degrade to baseline/public Home with an explicit recovery banner; never silently broaden capability.
- Do not host playback in WKWebView and do not copy cookie/credential material into the app process beyond the in-memory jar handle lifecycle described above.
Source of Truth
Use this hierarchy when sources disagree:
- Explicit current user decisions and an approved task specification.
- Current executable source.
- Current behavior verified at runtime.
- Current tests.
- Historical documentation and plans.
The approved visual reconstruction contract is encoded in the source and its guardrail tests; tests that encode a superseded visual or product contract must be updated. Historical plans were working documents; they are not kept in the repository. Investigate uncertainty; do not guess. Distinguish confirmed facts, evidence-backed implications, and speculation.
Core Architecture
Composition
- Keep
MusesAppas the composition root. - Preserve stable app-lifetime identities for core services.
- Inject shared services through the existing environment boundaries; do not instantiate parallel library, playback, queue, search, import, or persistence services inside views.
- Keep view-local presentation state near its owning surface unless a demonstrated cross-feature need requires otherwise.
Playback and Queue
PlaybackServiceis the UI- and system-facing playback facade. Views, commands, and system integrations must not manipulate engines independently.- Primary engine:
YouTubeStreamEngine(yt-dlp → stream URL →AVPlayerimmediate start, then cached file /AVAudioEnginewhen the download finishes). - Persist downloaded media under
~/.muses/cache/streamskeyed byvideoId+ quality. Changing quality in Settings re-downloads the current track. - Official YouTube IFrame is not the playback engine. Do not host a WKWebView as the sound source.
- Now Playing cover mode shows artwork (square). Vinyl mode shows circular artwork. Do not leave an empty 16:9 video slot.
- Preserve collection-context playback: playing from a playlist, search result set, recent list, or YouTube import must retain meaningful previous/next context.
- Preserve the conceptual distinction between the current collection queue, explicit Up Next insertions, and history.
- Treat repeat, shuffle, previous/next, completion, queue markers, history recording, and queue persistence as one behavioral system.
track.youTubeId == nilis not a playable library item in production.
Data and Concurrency
-
App-managed library and cache files live under
~/.muses/dataand~/.muses/cache; isolated acceptance bundles use~/.muses/acceptance/<bundle-id>/data|cache. Keep OAuth tokens in Keychain and native preferences in UserDefaults. Preserve existing user truth while relocating legacy app-managed files. -
Keep SwiftData model objects on their intended actor/context boundaries. Do not pass them into detached tasks or other real-time work.
-
Preserve immutable
Sendablevalue boundaries such asTrackSnapshotfor playback, queueing, and detached computation. -
Continue the established fresh-context, UUID re-fetch pattern when mutating persisted objects unless an explicitly scoped architectural change replaces it.
-
Persisted user truth—stable IDs, likes, playlists and item order, YouTube imports, history, queue state, notes/bookmarks, pins, podcast follows and episode progress, and user metadata—must survive store upgrades.
-
Do not carry retired local rows,
ScanRoot, scanner/M3U state, or rebuildable catalog caches into the clean store. Preserve the source during cutover validation, then delete the legacy store family and old-schema compatibility code after the approved cleanup gate passes. -
Releases and artists require stable YouTube Music browse/playlist or YouTube channel IDs; never merge catalog objects by display name alone. Music Videos are a
Trackmedia kind, not a parallel playable entity. -
Keep expensive network, metadata, palette, and recommendation work out of SwiftUI
body. -
Large playlist screens must use lazy stacks and snapshot values. Do not eager-
VStackhundreds of SwiftData relationships.
Media Sources
- The unified
Trackrow remains and production rows are YouTube-backed (youTubeIdset). The clean active store and normal runtime contain no retained local rows or local-file read path. - YouTube playlist import creates
YouTubeImport+YouTubeImportItem+ lazy.youtubeTrack. Local additions to YouTube playlists are removed as a product feature. - Preserve explicit deletion semantics: deleting an import and deleting its associated tracks are different operations.
- yt-dlp remains the stream-resolution path for
YouTubeStreamEngineand the metadata source for playlist import and fallback search. Do not describe the official IFrame player as the production audio path. - Treat cookie settings, timeouts, signing, and personal-use distribution constraints as part of the product boundary.
Product Contracts
- Use the native unified toolbar for AppKit-owned traffic lights, sidebar toggle, and real window-local Back/Forward history. Search has its own native toolbar history. Cold-restored details seed their parent list; Settings seeds Home. Video close, sheet cancellation, and data rollback are separate actions.
- The liquid-glass music sidebar extends behind the toolbar; navigation content starts below it. Search / Home / New, library destinations, playlists, and a bottom Settings row remain available. The collapsed sidebar is an 88pt icon rail. No sidebar wordmark, top-bar tabs, Inbox chrome destination, or Radio.
- Entering Settings replaces the music sidebar with nine first-level categories: General, Playback, Appearance, Account, Lyrics, Diagnostics, Library Review, About, and Help & Privacy. Preferences and explanations are sections in the main pane, with no second category rail or nested settings pages. Preserve the same navigation history and PlayerBar.
- The PlayerBar is a floating glass capsule over browsing content. Its idle state retains the normal layout and a rounded lyre-mark tile; unavailable transport, lyrics, and video actions are disabled while queue and app volume remain usable. Hide it in Now Playing and the video overlay.
- Queue is an integrated full-height trailing pane. Keep current collection, Up Next, and history distinct, including repeat, shuffle, Smart Shuffle, reorder, and persistence behavior.
- Now Playing is a fullscreen overlay with square cover or circular vinyl on the left and optional lyrics on the right. The native toolbar Back action closes it; opening it closes Queue and hides the PlayerBar. Lyric display modes belong to the lyrics options menu. Current timed lyrics use artwork-derived multicolor gradients with a neutral high-contrast fallback; never invent timing or source.
- Selection and playback accents are adaptive black/white with restrained static halos on active controls. Keep YouTube red and destructive/error colors semantic. Shared action buttons and compact selection controls use native glass and capsule shapes where available, with supported-system and accessibility fallbacks. Group glass within bounded control clusters; artwork, video, and reading regions keep their aspect ratios.
- Volume and output controls share
PlaybackServicestate across surfaces. Output selection is trailing and visually distinct from volume. App volume never writes system volume. Volume scales retain keyboard and accessibility adjustment without an enclosing focus rectangle, and pointer input maps across the entire visible scale. - Use heavier monochrome SF Symbols for sidebar and player chrome, with at least 28pt hit targets. The macOS application menu stays text. The menu-bar status item uses a monochrome template lyre; its compact 332pt popover has one native surface without a second framed glass card.
Unless a task explicitly changes them, preserve:
- The persistent PlayerBar across browsing and detail navigation.
- Contextual previous/next behavior.
- Current queue, Up Next, history, repeat, shuffle, reorder, and persistence semantics.
- Artwork-led discovery, page-specific content patterns, and playlist detail hierarchy. Songs and playlist details use the approved D3 centered card-deck stage + expandable complete sortable table; playlists overview uses square cards; Home and New retain measured editorial hero regions.
- Songs, playlists, pins, recently played, search, YouTube imports, followed podcasts, and subscriptions. Inbox tables remain on disk but have no chrome entry.
- Likes, pins, metadata, and playback-history preservation.
- YouTube resynchronization of imported playlists.
- Podcast episode progress, speed, skip controls, followed-show paging, and stable channel identity. Video comments are read-only; preserve reply paging without comment write or permission expansion. Shorts from subscribed channels retain stable video identity and collection playback context.
- Keyboard shortcuts, application menus, context menus, sharing.
- System media controls, notifications policy, and window restoration.
- Immediate language switching via
tr(_ en: String, _ zhHans: String, ...).
Do not restore:
- Folder/file scanning,
ScanRootsettings, drag/drop of audio files, M3U file import, “Add Local”. - Radio.
- A bottom video well. The on-demand YouTube video overlay (pauses audio; optional resume) is a product feature. The video has no outer glass frame or title strip; keep a visible close glyph and Escape.
Albums, Artists, and Music Videos are approved under D2: Releases and Artists use stable YouTube-backed identities, Music Videos use the Track media-kind model, and Radio remains absent. Every restored destination must define refresh, stale-cache, loading, empty, unavailable, and collection-context playback behavior.
Do not simplify mature queue/history workflows merely to make implementation easier.
UI and Design Philosophy
Muses should remain distinctly macOS-native. Prioritize artwork (and Now Playing video), hierarchy, depth, clarity, responsiveness, desktop information density, and an expressive playback surface.
Chrome layout follows live Apple Music Web: left nav (Search / Home / New + Library), page-specific editorial and table patterns, square shelves, integrated Queue, and a floating capsule player. Visual skin uses SF Pro, neutral surfaces, adaptive monochrome selection/playback accents, and restrained semantic glass. Native SwiftUI interpretation, not a WebView wrap of music.apple.com or music.youtube.com.
Avoid:
- Generic decorative glassmorphism on browsing cards.
- An enlarged iOS layout.
- Glow on idle icons or rail cards; active halos are restrained and static.
- Pink as a general selection or playback accent.
Visual Hierarchy
- Browsing surfaces—Home, songs, playlists, search, and settings—should remain comparatively restrained and information-efficient.
- Playlist detail may use stronger artwork integration and purposeful motion.
- Now Playing is the primary expressive surface. Cover mode is a large square with title/artist beneath; vinyl (settings-only) is a circular spinning cover with no disc rim. Lyrics sit in the right column and can fill the slot. The dock keeps transport.
- Do not make every surface compete visually with Now Playing.
- Preserve legibility and functional control contrast over artwork-derived backgrounds in light, dark, and high-contrast appearances.
- Home includes a measured Apple Music Web editorial hero region, portrait Top Picks, and square shelves. New includes landscape editorial content, compact song matrices, and square shelves. Both pages stay calmer than Now Playing.
- Songs and playlist details center collection identity/actions above a virtualized, fan-shaped all-track hero deck. The deck has one canonical focus shared by drag, trackpad/wheel, chevrons, keyboard, and a first-to-last scrubber. Only hero-card activation performs the centered ember-burn playback ritual. A dedicated chevron handle or upward swipe replaces the stage with the complete sortable table inside the content pane; sidebar and PlayerBar remain. Songs defaults to title A–Z with no manual order; playlists default to their persisted Playlist Order. Table sorting never rewrites canonical order or playback context.
- Search opens one auxiliary window with a centered field, source scope, categories, grouped results, and its own toolbar history. Settings is an integrated main-window destination with a replacement category sidebar, semantic icons, accessible native forms, and flat content sections.
- Icon-first chrome: controls that can be an icon should be an icon, with
.helpand VoiceOver. Track titles, empty states, and settings explanations stay as text. - YouTube affordances use
YouTubeMark(red rounded play rectangle), not a generic SF Symbol stand-in.
Liquid Glass Direction
Muses is expected to evolve toward deep integration with Apple's modern macOS Liquid Glass design language.
When performing explicitly scoped Liquid Glass work:
- Verify current platform APIs and availability for the supported macOS deployment range.
- Prefer native macOS structure, controls, toolbar/sidebar behavior, sheets, popovers, and system-provided glass first.
- Do not recreate native behavior unnecessarily.
- Remove legacy backgrounds or material layers when they conflict with the intended system glass composition.
- Use custom glass only for meaningful application-specific surfaces.
- Establish reusable primitives and semantic surface roles instead of scattering blur or material modifiers through individual views.
- Group related custom glass elements coherently; avoid fragmented floating decoration.
- Use tint only when it communicates selection, status, playback, or another clear semantic meaning. Selection and playback use adaptive monochrome accents; semantic YouTube red and destructive/error colors remain distinct.
- Preserve legibility over artwork and support light, dark, and high-contrast, and Reduce Transparency modes.
- Maintain appropriate fallback behavior when a supported OS does not provide the desired native API.
Liquid Glass must express hierarchy and interaction, not merely add decoration.
Artwork
Artwork is a first-class part of the product identity.
- Preserve the artwork-led hierarchy and avoid obscuring covers behind excessive effects.
- Do not reduce image fidelity unnecessarily.
- Prefer a centralized resolution path for YouTube thumbnails, remote artwork, and placeholders.
- Guard asynchronous artwork, decoding, and palette results by current media identity so stale work cannot update a newer selection.
- Keep decoding, resizing, palette extraction, and blocking cache access out of SwiftUI
body. - Preserve graceful loading, placeholder, and failure states.
- Artwork-derived color may shape environment and depth, but functional UI must remain legible.
- Home rails crop YouTube thumbnails to square. Now Playing cover mode is a large square, not a 16:9 video slot.
Motion
Motion should communicate hierarchy, navigation, continuity, playback state, expansion/collapse, or spatial relationships.
A centralized motion/continuity system may coordinate:
- Artwork continuity (PlayerBar ↔ Now Playing matched geometry).
- Glass morphing on chrome only (PlayerBar, Queue, compact controls).
- Queue presentation.
- Contextual controls and hover Play.
- History recap glyphs (appear-only; Reduce Motion → static).
Rules:
- Prefer continuity between related surfaces over unrelated transitions.
- Hover is 120–180ms ease, a few points of lift, no bounce, no idle motion on browsing surfaces.
- Playback-position and vinyl may animate. List rows and chrome must not sample those clocks.
- Do not glass-morph browsing cards.
- Do not add animation to high-frequency state without evaluating frame pacing, CPU, energy, and accessibility impact.
- Respect Reduce Motion in every new animation path, including custom Metal/AppKit rendering. Reduce Motion fallback is instant swap or opacity.
Native macOS Interaction
Do not sacrifice desktop-native behavior for visual effects. Preserve and test:
- Pointer and hover behavior.
- Keyboard navigation, focus, focus rings, and existing shortcuts.
- Application menus and a complete track context menu on every track surface.
- Native sheets, popovers, Lists, Forms, toolbars, and window behavior.
- System media commands, notifications, and window restoration.
Prefer standard SwiftUI/macOS controls when they provide the required behavior. Custom controls must justify and replace any lost keyboard, focus, pointer, accessibility, and semantic behavior.
Accessibility
Accessibility is part of design and implementation, not a final cleanup phase.
- Provide meaningful VoiceOver labels and values, especially for image-only controls and
YouTubeMark. - Preserve keyboard access and visible focus indication.
- Support high-contrast appearances.
- Honor Reduce Motion and Reduce Transparency.
- Maintain legibility over artwork, video, and glass.
- Use sufficiently large and predictable interaction targets.
- Evaluate accessibility from the rendered application, not source inspection alone.
Performance and Real-Time Safety
Muses contains high-frequency media and visualization state. Treat performance as a design constraint, especially around:
- Playback-position and completion observation.
- Vinyl animation and lyrics timelines.
- Artwork loading, decoding, palette extraction, and large images.
- Large playlist lists and SwiftData refreshes.
- Queue persistence and rapid playback changes.
- Video WKWebView lifetime (one on-demand surface, never per-row).
Rules:
- Do not broaden high-frequency observable state or subscriptions without a concrete need.
- Do not perform expensive or blocking work in SwiftUI
body. - Keep async work cancellable where stale results can affect current playback or artwork.
- Do not claim an optimization without profiling or concrete evidence.
- Label performance findings as measured, strongly suspected, or speculative.
- Visual enhancements must not noticeably degrade playback reliability, frame pacing, launch time, scrolling, CPU, memory, or energy use.
High-Risk Areas
Changes touching these areas require narrow scope, explicit reasoning, and proportionate verification:
PlaybackServiceYouTubeVideoStageand its on-demand WKWebView hostYouTubeStreamEngine(primary production engine)QueueServiceand queue persistenceNowPlayingManagerPlayerBarNowPlayingView- SwiftData schema, relationships, contexts, and migrations
- Artwork and stream caches
- YouTube import ownership and deletion semantics
- Packaging, signing, entitlements, and bundled
yt-dlp
Do not opportunistically refactor these systems during unrelated visual tasks.
Change Discipline
- Prefer incremental, independently reviewable changes.
- Keep refactors targeted to the task and explain why an established subsystem must change before modifying it.
- Preserve user changes and unrelated work in a dirty worktree.
- Avoid broad rewrites, unrelated cleanup, speculative abstractions, architecture changes hidden inside UI work, and new dependencies without clear justification.
- Do not treat unfamiliar implementation choices as mistakes before tracing their purpose and tests.
- Do not silently convert a visual task into a behavior, persistence, playback, or release change.
- Source code, identifiers, and comments in new or edited files are English. User-visible strings go through
tr.
Verification
Match verification to the risk and phase using an appropriate combination of:
- Build checks.
- Focused unit tests with fake playback and HTTP clients; do not require a live WebView in CI.
- Cross-subsystem integration tests.
- Runtime playback and interaction inspection.
- Rendered screenshots at relevant window sizes and appearances.
- Keyboard, pointer, menu, and focus testing.
- VoiceOver, Reduce Motion, Reduce Transparency, and high-contrast checks.
Visual work must be judged in the rendered application, not from source alone. Performance claims should be measured whenever practical. If a phase is explicitly read-only, do not run commands that create build, test, cache, or packaging artifacts.
Known Investigation Areas
The following findings are candidates for separately scoped investigation. They are not automatically in scope and must not trigger unsolicited refactors:
- Possible overlapping observation loops in
NowPlayingManager. - Playback-load cancellation and rapid-selection races.
- Queue, current-index, history, and completion consistency.
- Videos that reject embedding even with cookies, and video-session handoff to system media controls.
- Artwork cache propagation, eviction, and asynchronous identity checks.
- Accessibility labeling and focus behavior.
- Reduce Transparency and complete Reduce Motion coverage.
Commit attribution
Never add automated-assistant attribution, co-author trailers, or generator branding to git commits, tags, or PR bodies.
