Skip to content

Latest commit

 

History

History
244 lines (173 loc) · 11.3 KB

File metadata and controls

244 lines (173 loc) · 11.3 KB

AGENTS.md

This file provides guidance to LLMs when working with code in this repository.

Project Overview

mt is a desktop music player designed for large music collections, built with Tauri (Rust backend), basecoat (with Tailwind CSS), and Alpine.js. The backend uses Rust for audio playback and system integration, while the frontend is a modern web-based UI with reactive components.

General Guidelines

  • ALWAYS use atomic commits — see Git Workflow for interactive staging, patch mode, and worktree management
  • NEVER create *.backup files. This is a version controlled repo

Context7

Always use Context7 MCP when I need library/API documentation, code generation, setup or configuration steps without me having to explicitly ask.

Libraries

  • alpinejs/alpine
  • basharovv/musicat
  • dubzzz/fast-check
  • hunvreus/basecoat
  • jdx/mise
  • microsoft/playwright
  • mrlesk/backlog.md
  • nextest-rs/nextest
  • proptest-rs/proptest
  • serial-ata/lofty-rs
  • sharkdp/hyperfine
  • taiko2k/tauon
  • tailwindlabs/tailwindcss
  • tranxuanthang/lrclib
  • useblacksmith/setup-docker-builder
  • websites/deno
  • websites/last_fm_api
  • websites/rs_rig-core
  • websites/rs_tauri_2_9_5
  • websites/taskfile_dev

Quick Reference

Topic Guide
Commands, tooling, dependencies Development Guide
Testing strategy, E2E, coverage Testing Guide
Local GitHub Actions testing act Guide
Atomic commits, worktrees Git Workflow
System architecture, components Tauri Architecture
MCP bridge tools MCP Tool Reference
Remote debugging, crash analysis Debugging Guide
Last.fm scrobbling Last.fm Integration
Themes, dark mode toggle fixes Theming Guide
Cross-platform builds, CI/CD Build Configuration
Shuffle, Play Next pinning Shuffle Implementation
Agent script, prompt tuning Agent Script

Architecture Overview

MT uses a pure Rust + Tauri architecture:

  • Frontend: Tauri WebView (Alpine.js + Basecoat/Tailwind CSS)
  • Backend: Native Rust (91 Tauri commands)
  • Audio: Rodio/Symphonia
  • Database: SQLite via rusqlite (mt.db in Tauri app data dir)
  • Build Cache: sccache (shared across worktrees/workspaces)

App Data Paths

Platform App Data Dir
macOS ~/Library/Application Support/com.mt.desktop/
Linux $XDG_DATA_HOME/com.mt.desktop/

Database: mt.db, Logs: see Observability

See Tauri Architecture for full details.

Rust Toolchain (mise + rustup)

Three directories are involved — do NOT cross-link them:

Directory Purpose Managed by
~/.cargo/bin/ Real rustup binary + multicall symlinks (cargo -> rustup) rustup
~/.local/share/mise/installs/rust/<version>/ Relative multicall symlinks (cargo -> rustup) + rustup copy mise
$CARGO_HOME/bin/ (project-local .cache/cargo/bin/) Symlinks to ~/.cargo/bin/<tool> cargo:_setup-cargo-home task

cargo.yml adds ~/.cargo/bin to PATH so rustup tools are always findable. The _setup-cargo-home task symlinks project-local CARGO_HOME/bin/ to ~/.cargo/bin/ — never to the mise install dir, which has its own internal symlink structure.

Windows CI Toolchain Constraint

On Windows CI runners, RUSTUP_TOOLCHAIN MUST use the fully qualified name with host triple suffix (e.g. nightly-2026-02-09-x86_64-pc-windows-msvc). Bare channel names like nightly-2026-02-09 resolve using the runner's default host triple, which can be stale GNU — causing dlltool.exe not found errors. See Build Configuration — Windows Toolchain Pinning for details.

When modifying taskfiles/ci.yml or .github/actions/setup-tauri-build/action.yml:

  • Never set RUSTUP_TOOLCHAIN to a bare toolchain name on Windows
  • Always run rustup set default-host x86_64-pc-windows-msvc before toolchain installation
  • Pass PINNED_RUST explicitly from the composite action rather than relying on shell extraction in Task

Queue and Shuffle Behavior

The queue store (app/frontend/js/stores/queue.js) maintains tracks in play order — the items array always reflects the order tracks will be played.

  • Without shuffle: Tracks play sequentially in the order they were added
  • With shuffle enabled: The items array is physically reordered using Fisher-Yates shuffle
    • Current track moves to index 0
    • Remaining tracks are randomly shuffled
  • When shuffle is disabled: Original order is restored from _originalOrder
  • Loop + Shuffle: When queue ends with loop=all, items are re-shuffled for a new random order

Now Playing view: Always displays tracks in the order they will play (current track first, then upcoming).

Linting, Formatting, and Testing

All commands use Taskfile. Run task --list for full list.

Task Runner (preferred)

task lint                     # Run all linters (Rust + JS)
task format                   # Run all formatters (Rust + JS)
task test                     # Run all tests (Rust + JS unit)
task test:e2e                 # Run Playwright E2E tests
task pre-commit               # Run pre-commit hooks

Direct Commands

Frontend (uses Deno toolchain, not ESLint/Prettier):

deno lint                                         # Lint JS/TS
deno fmt                                          # Format JS/TS
deno fmt --check                                  # Check formatting without changes
cd app/frontend && npx vitest run                 # Unit/property tests (Vitest via Node)
cd app/frontend && npx playwright test            # E2E tests

Backend (Rust):

cargo clippy --workspace                          # Lint
cargo fmt --all                                   # Format
cargo nextest run --workspace                     # Tests (falls back to cargo test)
cargo check --manifest-path src-tauri/Cargo.toml  # Fast type check (no binary)

Dockerfiles:

hadolint docker/linux/Dockerfile                         # Lint Linux Dockerfile

GitHub Actions workflows:

actionlint .github/workflows/test.yml                    # Lint single workflow
actionlint                                              # Lint all workflows

When to Run

  • Before committing: task lint && task format
  • After changing frontend code: cd app/frontend && npx vitest run
  • After changing Rust code: cargo nextest run --workspace

Test Boundaries

Each test layer has a clear responsibility. Do NOT write a Playwright E2E test for something that belongs in Vitest or Rust tests.

What to test Where Why
Store logic (queue, player, library mutations) Vitest __tests__/ Fast, isolated, supports property-based testing
Pure functions (sorting, filtering, formatting) Vitest __tests__/ No DOM needed
CSS details (padding, user-select, computed styles) Nowhere (design review) Brittle, low value
User interaction flows (click, drag, context menu) Playwright tests/ Needs real browser
Cross-component integration (play track -> queue updates -> Now Playing renders) Playwright tests/ Tests wiring between components
Backend logic (audio, DB, concurrency) Rust #[test] / proptest Must run in Rust

Rules:

  • Playwright tests MUST involve real user interactions (clicks, keyboard, drag). If a test only calls page.evaluate() to manipulate Alpine stores, it belongs in Vitest.
  • Never duplicate store logic tests between Playwright and Vitest. Vitest is authoritative for store behavior.
  • Tag Playwright tests that need the Tauri runtime with @tauri. Default CI mode (fast) skips these.

Implementation Notes

  1. Components: Modular, single-responsibility. Use Alpine.js for interactivity, basecoat/Tailwind for styling.
  2. IPC: All backend operations via Tauri commands. Use async/await. Emit events for real-time updates.
  3. File Organization: Frontend in app/frontend/, backend in src-tauri/src/. Keep files under 500 LOC.
  4. Testing: See Test Boundaries above and Testing Guide for the full decision framework.
  5. Code Style: deno lint + deno fmt (frontend), cargo fmt + cargo clippy (backend). Run formatters before committing.

<CRITICAL_INSTRUCTION>

BACKLOG WORKFLOW INSTRUCTIONS

This project uses Backlog.md MCP for all task and project management.

CRITICAL RESOURCE: Read backlog://workflow/overview to understand when and how to use Backlog for this project.

  • First time working here? Read the overview resource IMMEDIATELY to learn the workflow
  • Already familiar? You should have the overview cached ("## Backlog.md Overview (MCP)")
  • When to read it: BEFORE creating tasks, or when you're unsure whether to track work

Key MCP Commands

Command Purpose
task_create Create a new task (status defaults to "To Do")
task_edit Edit metadata, check ACs, update notes, change status
task_view View full task details
task_search Find tasks by keyword
task_list List tasks with optional filters
task_complete Moves task to backlog/completed/ — only use for cleanup, not for marking done

Task Lifecycle

  1. Create: task_create — new task in backlog/tasks/
  2. Start: task_edit(status: "In Progress") — mark as active
  3. Done: task_edit(status: "Done") — mark finished, stays in backlog/tasks/ (visible on kanban)
  4. Archive: task_complete — moves to backlog/completed/ (use only when explicitly cleaning up)

IMPORTANT: Use task_edit(status: "Done") to mark tasks as done. Do NOT use task_complete unless the user explicitly asks to archive/clean up — it removes the task from the kanban.

Cross-Branch Task Scanning (disabled)

check_active_branches and remote_operations are both disabled in backlog/config.yml. With worktrees, these features scan other branches and pull in tasks that were already completed/archived on main but still exist in backlog/tasks/ on older branches — bloating the kanban with ghost tasks. Do not re-enable without accounting for worktree branch divergence.

Multiline Field Gotcha

The finalSummary, description, implementationNotes, and planSet MCP parameters are single-line JSON strings. Literal \n sequences are NOT interpreted as newlines — they render as the two characters \ n in the markdown file. To write multiline content:

  • Use task_edit with the field for short single-paragraph content
  • For multiline content, edit the task markdown file directly with the file editing tool (the file path is shown in task_view output)

The overview resource contains additional detail on decision frameworks, search-first workflow, and guides for task creation, execution, and completion.

</CRITICAL_INSTRUCTION>