diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 621e700..665c6d8 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -51,7 +51,7 @@ body: id: repro attributes: label: Reproduction steps - description: "Numbered steps. Include song format (PSARC / sloppak / loose) and arrangement type if relevant." + description: "Numbered steps. Include song format (legacy archive / sloppak / loose) and arrangement type if relevant." placeholder: | 1. ... 2. ... diff --git a/.specify/templates/plan-template.md b/.specify/templates/plan-template.md index 635747f..24244b6 100644 --- a/.specify/templates/plan-template.md +++ b/.specify/templates/plan-template.md @@ -41,11 +41,11 @@ Answer each gate with PASS / FAIL / N/A and a short justification: serve-path build step? If plugin CSS is needed, does it ship through `styles`? - **Plugin and capability boundary**: If the feature is outside browse + - play CDLC, is it plugin-shaped? Does every Slopsmith-facing plugin + play custom DLC, is it plugin-shaped? Does every Slopsmith-facing plugin behavior declare `capability-pipelines.v1` metadata (`capabilities`, `ui`, or `ui_contributions`) and use `load_sibling` for backend siblings? -- **CDLC compatibility**: Are PSARC scanning, sloppak manifests, +- **Song format compatibility**: Are legacy archive scanning, sloppak manifests, arrangement IDs, and highway WebSocket messages kept backward- compatible, or is a migration note planned? - **Pure/testable core**: Are new `lib/` helpers flat-importable, diff --git a/AGENTS.md b/AGENTS.md index 6457c68..2bb8ee1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ Project orientation for AI coding assistants (Cursor, GitHub Copilot, OpenAI Codex, Aider, Claude Code, Cline, Continue, Cody, Devin, …) and human contributors. -Slopsmith is a self-hosted web app for browsing, playing, and practicing Rocksmith 2014 Custom DLC. It runs as a Docker container with a FastAPI backend (`server.py`), vanilla JavaScript frontend (`static/`), shared Python libraries (`lib/`), and an extensive plugin system (`plugins/`). No frontend frameworks — plain JS, HTML, Tailwind CSS. AGPL-3.0-only. +Slopsmith is a self-hosted web app for browsing, playing, and practicing interactive music notation, built around its own open `.sloppak` chart format. Charts come from importing Guitar Pro (GP5/GP8) or MusicXML, or from authoring in the built-in editor. It runs as a Docker container with a FastAPI backend (`server.py`), vanilla JavaScript frontend (`static/`), shared Python libraries (`lib/`), and an extensive plugin system (`plugins/`). No frontend frameworks — plain JS, HTML, Tailwind CSS. AGPL-3.0-only. This file is the canonical orientation. Tool-specific automation (Claude skills/subagents/rules, Copilot instructions, etc.) lives in [`.claude/`](.claude/) and [`.github/copilot-instructions.md`](.github/copilot-instructions.md); both point back here. For plugin work, start at [`docs/PLUGIN_AUTHORING.md`](docs/PLUGIN_AUTHORING.md). @@ -19,13 +19,13 @@ static/ index.html Single-page app shell lib/ song.py Core data models (Note, Chord, Arrangement, Song) - psarc.py PSARC archive reading and extraction sloppak.py Sloppak format support - sloppak_convert.py PSARC → sloppak conversion + Demucs stem split + sloppak_convert.py Import conversion + Demucs stem split + loosefolder.py Loose-folder XML chart support audio.py WEM/OGG/MP3 audio handling retune.py Pitch-shifting logic tunings.py Tuning name/offset utilities - gp2rs.py Guitar Pro to Rocksmith XML conversion + gp2rs.py Guitar Pro to arrangement XML conversion gp2midi.py Guitar Pro to MIDI plugins/ __init__.py Plugin discovery, loading, requirements install, load_sibling @@ -95,7 +95,7 @@ Pytest config in `pyproject.toml` sets `pythonpath = [".", "lib"]` and `testpath Slopsmith supports two: -- **PSARC** (Rocksmith native) — encrypted archive. Read-only. Fast metadata scan via `lib/psarc.py` (`read_psarc_entries`); full unpack via `unpack_psarc()` for playback. Audio via `vgmstream-cli` + `ffmpeg`. +- **Legacy archive** (encrypted, read-only) — fast metadata scan via `lib/sloppak.py`; full unpack for playback. Audio via `vgmstream-cli` + `ffmpeg`. - **Sloppak** (open format) — hand-editable, two interchangeable forms: `.sloppak` zip or `.sloppak/` directory. Preferred for new features. Full spec: [`docs/sloppak-spec.md`](docs/sloppak-spec.md). Key code: `lib/sloppak.py`, `lib/sloppak_convert.py`, `lib/song.py`. ## Frontend conventions diff --git a/docs/plugin-sibling-imports.md b/docs/plugin-sibling-imports.md index b6a2c1f..b4ae253 100644 --- a/docs/plugin-sibling-imports.md +++ b/docs/plugin-sibling-imports.md @@ -13,7 +13,7 @@ The plugin loader inserts each plugin's directory onto `sys.path` so `from extra ```python def setup(app, context): extractor = context["load_sibling"]("extractor") - PsarcReader = extractor.PsarcReader + ArchiveReader = extractor.ArchiveReader # … ``` diff --git a/docs/websocket-protocol.md b/docs/websocket-protocol.md index 135f757..fdbd9fa 100644 --- a/docs/websocket-protocol.md +++ b/docs/websocket-protocol.md @@ -18,7 +18,7 @@ Each connection receives the following JSON frames, roughly in this order: | `tone_changes` | `{ type: 'tone_changes', base, data: [{ time, name }] }` | Optional — tone change events relative to the arrangement base tone; only sent if tones were found. | | `notes` | `{ type, data: [{ t, s, f, sus, ho, po, sl, bn, ... }] }` | Single notes. | | `chords` | `{ type, data: [{ t, notes: [{ s, f, sus, ... }] }] }` | Chord events. | -| `phrases` | `{ type, data: [{ start_time, end_time, max_difficulty, levels: [...] }], total }` | Optional — per-phrase difficulty ladder for master-difficulty slider (slopsmith#48). Only sent when the source chart carries multi-level phrase data (PSARC / phrase-aware sloppak). Sent in chunks (`data` is a batch, `total` is the full count across messages) to avoid multi-MB single frames. Absent for GP imports and legacy sloppak; consumers must treat missing message as "single fixed difficulty — slider disabled". | +| `phrases` | `{ type, data: [{ start_time, end_time, max_difficulty, levels: [...] }], total }` | Optional — per-phrase difficulty ladder for master-difficulty slider (slopsmith#48). Only sent when the source chart carries multi-level phrase data (legacy archive / phrase-aware sloppak). Sent in chunks (`data` is a batch, `total` is the full count across messages) to avoid multi-MB single frames. Absent for GP imports and legacy sloppak; consumers must treat missing message as "single fixed difficulty — slider disabled". | | `ready` | `{ type: 'ready' }` | All data sent — safe to finalize and start rendering. | ## Delivery guarantees