suno-playlist-sequencer · diff
git:20260914.737a5e7 to git:20261003.240b880
31 added, 62 removed. Audit A to A.
---
name: suno-playlist-sequencer
description: Sequences tracks into album-craft playlists. Use when user says 'sequence my playlist', 'order my album', or 'plan my tracklist'.
---
# Playlist Sequencer
## Overview
This skill orders a body of tracks into a coherent album-craft listening experience, balancing sonic flow (Camelot key transitions, felt-BPM continuity, energy arcs) against narrative flow (thematic arcs, locked sequences, encore design). Act as an album producer who sequences for the listener's journey, not just for pairwise key compatibility. It runs deterministic audio analysis over a per-band playlist YAML, applies the album-craft methodology, and presents a recommended sequence with named, per-variable rationale.
- **Domain context:** Sequencing has two layers. The *transition-evaluation* layer (Camelot wheel, BPM tolerances, felt-vs-librosa-BPM correction) is mechanical and partly scriptable. The *album-craft* layer above it — energy arcs (inverted-U, W-shape, concert peak-end), load-bearing key positions (1/4/7/10), locked arcs, similar-songs-need-distance, encore structure — is judgment. The data layer is the *input* to sequencing decisions; it never makes them on its own.
+ **Domain context:** Sequencing has two layers. The *transition-evaluation* layer (Camelot wheel, BPM tolerances, felt-BPM correction, loudness steps) is mechanical and mostly scripted. The *album-craft* layer above it (energy arcs, load-bearing key positions, locked arcs, similar-songs-need-distance, encore structure) is judgment. The data is the *input* to sequencing decisions; it never makes them on its own.
**Design rationale (load-bearing):**
- - **Listening experience is the arbiter, not the Camelot score.** A Camelot-perfect transition with a 70+ felt-BPM gap is "tempo-jarring," not "the strongest option." Parallel-key transitions score JARRING on the wheel but are a deliberate emotional pivot the ear hears as continuity. Name what the ear hears; don't let the wheel override it.
- - **Felt BPM governs, not librosa raw.** librosa misreads halftime/double-time routinely (speed metal reads half, doom reads double). Verify felt BPM by ear before claiming tempo continuity across tracks. When the PyTorch audio tools are turned on (`/suno-setup`), the scripts read tempo with Beat This! (the Feedback Elicitor's `beat-grid.py`), which catches most slow-song double-reads, and `batch-full-analysis.py` lists each track's half-time and double-time **feel sections**; otherwise they use librosa. The report says which (`tempo_source`), and the ear still decides.
- - **Thematic verification is mandatory before any placement claim.** Read the full songbook entry — never infer a song's theme from its title or a line fragment. Poets don't telegraph; surface inference inverts reads reliably. The methodology's "Thematic Verification" section is non-negotiable.
- - **Never break a documented locked arc on the agent's own authority.** Surface locked arcs first; if a reorder would break one, stop and ask the user.
+ - **Listening experience is the arbiter, not the Camelot score.** Camelot measures the key relationship only. A key-compatible seam with a 70+ felt-BPM gap is "tempo-jarring," not "the strongest option." A parallel-key seam reads as distant on the wheel, yet the ear hears one harmonic center. Name what the ear hears.
+ - **Felt BPM governs.** Measured tempo misreads half- and double-time, so the ear decides. Once the user confirms a felt BPM, it lives in the playlist YAML (`felt_bpm:`) and the script applies it.
+ - **Placement weighs the thematic read as well as the math.** Read the song's songbook entry (or its `docs/song-thematic-dossier.md` entry) before any placement claim. Never infer a theme from a title or a line fragment: poets don't telegraph, and surface reads invert reliably. When two songs are cause and effect, never fix a key or tempo seam by handing the effect straight into its cause. That constraint binds the seam only: apart, the two can sit in either order.
+ - **Never break a documented locked arc on your own authority.** Surface locked arcs first. If a reorder would break one, stop and ask.
## Conventions
- Bare paths (e.g. `references/playlist-sequencing-methodology.md`) resolve from the skill root.
- `{skill-root}` resolves to this skill's installed directory (where `customize.toml` lives).
- - `{project-root}`-prefixed paths resolve from the project working directory.
+ - `{project-root}`-prefixed paths resolve from the project working directory, and so do `docs/...` paths (the project's data). Run scripts from `{project-root}` so their `docs/` defaults land there.
- `{skill-name}` resolves to the skill directory's basename.
+ - A sibling skill's file is named by skill ("the suno-band-profile-manager skill's `scripts/scaffold-playlist.py`") and resolves from that skill's installed directory.
## Activation Mode Detection
- **Check activation context immediately:**
-
- 1. **Headless mode**: If `--headless` or `-H` is present, or intent clearly indicates non-interactive execution:
- - Require a per-band playlist YAML path (`--playlist docs/{band-slug}-playlist.yaml`); without one, return the blocked contract below.
- - Run `scripts/playlist-sequencing-data.py` (and `scripts/batch-full-analysis.py` if a catalog-wide pass is requested), apply the methodology, and emit the success JSON. Headless **skips interactive felt-BPM confirmation and songbook-read pauses** — it must instead read the songbook entries itself for any thematic claim, or omit thematic claims and record that omission in `decision_log[]`.
- - **Success contract:**
- ```json
- {
- "status": "complete",
- "album": "string",
- "recommended_sequence": [{"position": 1, "name": "string", "rationale": "string"}],
- "locked_arcs_respected": ["string"],
- "dropped_tracks": [{"name": "string", "reason": "missing audio file | analysis error"}],
- "flagged_transitions": [{"from": "string", "to": "string", "issue": "string"}],
- "trade_offs": ["string"],
- "artifacts": {"sequencing_json": "docs/audio-analysis/playlists/{band-slug}.json", "companion": "docs/{band-slug}-playlist-sequencing.md", "decision_log": "docs/{band-slug}-playlist-sequencing/.decision-log.md"},
- "decision_log": [{"call": "string", "reason": "string"}]
- }
- ```
- `dropped_tracks[]` lists any playlist entry the script could not analyze (audio file missing/mistyped, or analysis error) — populate it from the script's per-track status, never silently sequence a short list. `flagged_transitions[]` carries the transitions the methodology judged jarring/degraded (worst first). `decision_log[]` must include an entry for every assumption made without the user — including any declined locked-arc break (recorded, not acted on) and any transition sequenced on felt-BPM-taken-on-faith (no interactive ear-confirmation available).
- - **Blocked contract:** `{"status": "blocked", "missing": ["playlist_yaml"], "reason": "one-line cause", "decision_log": []}`.
-
- 2. **Interactive mode** (default): Proceed to On Activation.
+ - **Headless** (`--headless` / `-H`, or the intent is plainly non-interactive): run On Activation steps 1-2 with no greeting, then follow `references/headless-contract.md` (inputs including `--locked` arcs, run order, success and blocked JSON).
+ - **Interactive** (default): proceed to On Activation.
## On Activation
- 1. **Resolve customization** — run `python3 {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --key workflow`. This reads the merged `[workflow]` block (base `customize.toml` → team `{project-root}/_bmad/custom/{skill-name}.toml` → user `{project-root}/_bmad/custom/{skill-name}.user.toml`) and supplies `activation_steps_prepend`, `activation_steps_append`, and `persistent_facts`. If the script is unavailable, read those three files directly in that order and merge by hand; if none exist, proceed with defaults. Run any `activation_steps_prepend` before the next step and load `persistent_facts` as foundational context.
- 2. **Load config via bmad-init skill** — use `{user_name}` for greeting, `{communication_language}` for communications, `{document_output_language}` for output artifacts. Fallback: greet generically, default to English. Do not block on missing config.
- 3. **Greet user**, run any `activation_steps_append`, and proceed to the workflow.
-
- ## Compaction Survival (HARD RULE)
-
- The load-bearing sequencing knowledge — the per-track variable stack, energy-arc models, key positions, locked-arc discipline, the felt-vs-librosa-BPM caveat, and the mandatory Thematic Verification rule — lives in `references/playlist-sequencing-methodology.md`. A long interactive review session can compact it out of context, and a sequence proposed without it silently drops felt-BPM correction, breaks a locked arc, or makes a thematic claim from a song title. **Before forming any recommendation, (re)load the methodology reference and treat its rules as non-negotiable inputs** — do not sequence from memory of these tables.
+ 1. **Resolve customization.** Run `uv run {project-root}/_bmad/scripts/resolve_customization.py --skill {skill-root} --project-root {project-root} --key workflow` for `activation_steps_prepend`, `activation_steps_append`, `persistent_facts` and `on_complete` (merge order: base `customize.toml`, then team `{project-root}/_bmad/custom/{skill-name}.toml`, then user `{skill-name}.user.toml`). If the script is unavailable, read those files in that order and merge by hand. Run any `activation_steps_prepend` and load `persistent_facts`, in headless runs too.
+ 2. **Load config.** Run `uv run {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root} --key core` for `{user_name}`, `{communication_language}` and `{document_output_language}`. Module settings (`songbook_folder`, `pytorch_audio_tools`) are in the `suno:` section of `{project-root}/_bmad/config.yaml`. If either is missing, greet generically, default to English and `docs/songbook`, and carry on.
+ 3. **Greet** `{user_name}` in `{communication_language}`, run any `activation_steps_append`, and start the workflow.
## Workflow
### Step 1: Intake
- **Open the floor.** Invite the user to share everything: which band/album, the per-band playlist YAML (or whether one needs scaffolding), any fixed/locked sequences, what they're trying to fix ("doesn't flow"), and whether this is a first ordering, a re-evaluation after a regen wave, or slotting one new track. Adapt the ask to what they hand you.
+ **Open the floor.** Invite everything at once: the band or album, the playlist YAML (or whether one needs scaffolding), what they want fixed ("doesn't flow"), and whether this is a first ordering, a re-eval after a regen wave, or one new track to slot. Ask here, once, about anything fixed: locked arcs, a deliberate opener or closer, a track they refuse to move.
- **Frame the scope before working.** Once you know the job, say back what you're about to do and how deep it goes — a full re-sequence with songbook-verified thematic claims is a different commitment from a one-track slot or a sonic-only transition pass. Let the user redirect the depth before you spend the effort.
+ **Frame the scope.** Say back what you're about to do and how deep it goes. A full re-sequence with a thematic read of every song is a bigger commitment than a one-track slot or a sonic-only pass. A 1-2-track adjacent swap, a sonic-only pass on a fixed order, or a playlist of four or fewer tracks skips the arc models (methodology, "When to Use").
- **Canonical input:** the per-band playlist YAML at `docs/{band-slug}-playlist.yaml` — the single source of truth for the band's track sequence. If a band has songbook entries but no playlist YAML, scaffold one via `uv run src/skills/suno-band-profile-manager/scripts/scaffold-playlist.py {band-slug} --from-songbook`, then have the user fill in audio filenames. Schema and lifecycle rules: `suno-band-profile-manager/references/profile-schema.md` "Per-Band Playlist YAML".
+ **Canonical input** is `docs/{band-slug}-playlist.yaml`, the band's single source of truth for track order. If a band has songbook entries but no YAML, run the suno-band-profile-manager skill's `scripts/scaffold-playlist.py {band-slug} --from-songbook` and have the user fill in the audio filenames.
- **Scope check (skip the heavy methodology):** reordering 1-2 adjacent tracks with no upstream/downstream impact, or a fixed-sequence user who wants only sonic-transition feedback, doesn't need the full review — handle it directly. A very short playlist (≤4 tracks) likewise has no real album-arc to shape — there's no front/peak/close geography to balance, so skip the arc models, surface any locked pair, and reason about the handful of transitions directly rather than narrating a methodology that doesn't have room to apply.
+ **Resume.** The workspace is `docs/{band-slug}-playlist-sequencing/`. If it holds a `.memlog.md`, read it once (and a legacy `.decision-log.md` once, if present) and treat settled placements as the standing record: raise any conflict with a prior call before changing it. Otherwise create one with `uv run {project-root}/_bmad/scripts/memlog.py init --workspace docs/{band-slug}-playlist-sequencing --field topic="{album} sequencing"`. If memlog.py is absent, append the same one-line `- (type) text` entries by hand.
### Step 2: Generate Sequencing Data
- Run the deterministic analysis over the playlist YAML:
-
- ```
- uv run scripts/playlist-sequencing-data.py --playlist docs/{band-slug}-playlist.yaml
- ```
-
- This produces per-track BPM, overall/entry/exit keys + Camelot codes, energy level, intro/outro energy %, entry/exit tempo (first and last 30 s; from Beat This! when the PyTorch audio tools are on, else librosa), loudness (integrated LUFS, LRA, by thirds, and entry/exit over the first and last 15 s), and per-transition quality (exit-Camelot of N → entry-Camelot of N+1, exit→entry BPM change, and the exit→entry loudness step). Output auto-archives to `docs/audio-analysis/playlists/{band-slug}.json` and refreshes the companion `docs/{band-slug}-playlist-sequencing.md` (AUTOGEN markers preserve hand-curated content). If librosa deps are missing the script returns JSON with install instructions (exit code 2) — surface that and continue with any data already on disk.
-
- **Reconcile the track count before sequencing.** Compare the number of tracks the script actually analyzed against the playlist YAML entry count. A missing or mistyped audio filename makes the script skip that track silently, and an unnoticed drop produces a confident sequence over the wrong list. If any track is MISSING or errored, name those tracks explicitly and confirm with the user (fix the filename, or proceed knowingly without them) before sequencing. In headless, never silently drop — record each dropped track in `dropped_tracks[]`.
+ Run `uv run scripts/playlist-sequencing-data.py --playlist docs/{band-slug}-playlist.yaml`. The JSON holds per-track tempo, keys, energy, and loudness, plus every seam's key relation, tempo change, and loudness step. It also archives itself and refreshes the companion `docs/{band-slug}-playlist-sequencing.md`. If the audio dependencies are missing, it exits 2 with install instructions: say so, and carry on with any archive already on disk.
- **Re-eval after a regen wave:** when the intent is re-evaluation rather than a first ordering, diff the fresh script output against the prior archive at `docs/audio-analysis/playlists/{band-slug}.json` and lead the proposal with what actually moved — which tracks' BPM/key/energy shifted and which transitions degraded as a result — instead of re-deriving the whole sequence from scratch.
+ **Drops.** Any entry in the top-level `dropped[]` was not analyzed (missing audio file or analysis error). Name each one and confirm with the user before sequencing: fix the filename, or go on knowingly without it. A confident sequence over the wrong list is the failure this prevents.
- **Fully degraded (deps gone AND no prior archive):** if librosa is unavailable and there's no archived JSON to fall back on, there is no audio data at all. Don't fabricate Camelot/BPM/energy numbers. Either help the user install the deps, or proceed on narrative/thematic grounds only (locked arcs, theme spacing, act logic) and state plainly that the sonic-transition layer is unverified.
+ **Re-eval.** The script reads the prior archive before overwriting it and adds a `compare` block: tracks whose tempo, key, energy or loudness moved, and seams that got worse or better. Lead the proposal with that, not a fresh derivation. A second run in the same session compares against the first, so keep the first run's `compare` block.
- **Optional catalog-wide deeper pass:** for energy shifts, section boundaries, spectral balance, and dynamic character across the whole catalog, run `uv run scripts/batch-full-analysis.py --audio-dir docs/audio` (writes `docs/catalog-analysis-report.md`; the scan is recursive, so it covers every band folder — pass `docs/audio/{band-slug}` for one band). Use it when dynamic-character or section-shape data informs the arc; skip it for a quick reorder.
+ **Fully degraded** (no deps and no archive): don't invent numbers. Help install the deps, or work on narrative grounds only (locked arcs, theme spacing, act logic) and say plainly that the sonic layer is unverified.
- The data layer is the *input* to the methodology — it does not decide the sequence.
+ **Optional catalog pass:** `uv run scripts/batch-full-analysis.py --audio-dir docs/audio/{band-slug}` adds energy shifts, section boundaries, and dynamic character. Use it when that shape informs the arc; skip it for a quick reorder.
### Step 3: Apply the Methodology
- (Re)load `references/playlist-sequencing-methodology.md` per Compaction Survival, then apply it: surface locked arcs first, verify felt BPM for tracks in the halftime/double-time danger ranges, **read the full songbook entry for every song in any thematic claim** (Thematic Verification — mandatory), identify the act/energy-arc shape, check load-bearing key positions, walk transitions on the full variable stack, and spot cluster opportunities (scattered felt-tempo cousins; adjacent thematic cousins that should be spaced). The reference carries the energy-arc models, key positions, same-key/sonic-palette/tempo-variety rules, similar-songs-distance, encore anatomy, and the parallel-key and genre-outlier caveats.
+ Reload `references/playlist-sequencing-methodology.md` before every recommendation. Long sessions compact it out, and a sequence formed from memory drops felt-BPM correction, locked arcs, and the thematic read. Work its three ordered gates: surface locked arcs (`uv run scripts/validate-sequence.py docs/{band-slug}-playlist.yaml` reports each one), settle felt BPM and do the thematic read for every song involved before drafting, then recommend with trade-offs.
- For a full re-sequence (not a quick reorder), the two flows are independent and easy to conflate, so review them as parallel lenses: a **sonic-flow** pass (Camelot/felt-BPM/intro-outro/sonic-palette transitions and the energy arc) and a **narrative-flow** pass (theme spacing, act logic, locked arcs, the closer-as-resolution). Reconcile where they disagree — a transition that's sonically smooth but thematically blurs two cousins, or a thematically perfect adjacency that's tempo-jarring — rather than letting one lens silently win.
+ For a full re-sequence, run a **sonic-flow** lens and a **narrative-flow** lens side by side, and reconcile where they disagree rather than letting one silently win.
### Step 4: Present the Sequence
- Before committing to a proposal, **soft-gate**: ask whether there's anything fixed you should respect — a locked pair, a deliberate opener/closer, a track the user refuses to move — before you build the order around assumptions. Cheaper to ask than to re-sequence.
+ Before building the order, ask "anything else before I build it?" Skip it if intake already covered the fixed points.
- Present an *opinionated proposal*, not a metrics dump: the recommended order with per-move rationale that names what each variable says, the energy-arc shape it produces, which arcs were held locked, and honest trade-offs (every move trades something — name it; don't claim "cleaner" when it's "trades A-jarring for B-jarring"). Where the user's ear should be the tiebreaker, say so. Invite refinement and iterate.
+ Present an *opinionated proposal*, not a metrics dump: the order, per-move rationale naming what each variable says, the energy-arc shape, which arcs held locked, and honest trade-offs ("trades A-jarring for B-jarring", not "cleaner"). Say where the user's ear should break the tie. Iterate.
- When the user volunteers a narrative aside mid-flow ("oh, that one's really about my dad," "these two were written as a pair") — capture it into the decision log rather than interrupting the sequencing to chase it. Those unprompted asides are often the load-bearing thematic context the songbook read would otherwise have to surface.
+ **Log as you go** with `uv run {project-root}/_bmad/scripts/memlog.py append --workspace docs/{band-slug}-playlist-sequencing`: `--type decision` for each placement, the alternatives rejected, and any locked-arc break the user authorizes; `--type direction` for narrative asides ("that one's really about my dad"). Capture asides rather than chasing them mid-flow. They are often the thematic context the songbook read would otherwise have to find. When the user confirms a felt BPM by ear, offer to record it as `felt_bpm:` on that track.
- The recommended sequence is a revisable artifact: write the proposal and a `.decision-log.md` as peers in `docs/{band-slug}-playlist-sequencing/` (the workspace); the decision log is canonical memory — it records each placement call, the rejected alternatives, and any locked-arc override the user authorizes, so a later re-sequence resumes cleanly instead of relitigating settled placements. On re-sequence, read the decision log first and enter the change as a signal against the standing record, surfacing any conflict with a prior call before applying it. At handoff, audit the log so the user signs off on how their sequencing decisions were handled.
+ **At sign-off,** write the accepted order to `docs/{band-slug}-playlist-sequencing/accepted-order.txt` (one name per line) and run `uv run scripts/validate-sequence.py docs/{band-slug}-playlist.yaml --order <that file>`. Show the user the moves it previews. Fix any error it names, and after the user confirms, run it again with `--write`. Then re-run the sequencing script with `--no-compare` so the archive and companion match the new order. Write the proposal narrative in `{communication_language}` and saved documents in `{document_output_language}`. Then run `{workflow.on_complete}` if set.
## Scripts
- **Invoke every script via `uv run scripts/<name>.py`** — uv reads each script's PEP 723 inline metadata and auto-provisions its dependencies (`librosa`, `numpy`, `pyloudnorm`, `pyyaml`), so no manual `pip install` is needed. If `uv` is unavailable, install it (`pip install uv`); these scripts require third-party deps, so `python3` alone won't run them without a manual install.
+ Each script documents itself with `--help`. All run with `uv run`, which provisions the dependencies.
- - `playlist-sequencing-data.py` — Generates per-track sequencing data (BPM, overall/entry/exit keys, Camelot codes, energy level, intro/outro energy %, loudness, per-transition key/BPM/loudness-step quality) for a per-band playlist YAML. Auto-archives JSON to `docs/audio-analysis/playlists/{band-slug}.json` and refreshes the per-band companion `docs/{band-slug}-playlist-sequencing.md` by default; `--no-archive` / `--no-companion` to skip. Run `uv run scripts/playlist-sequencing-data.py --help`.
- - `batch-full-analysis.py` — Catalog-wide deeper analysis (tempo stability, energy arc/shifts, section boundaries, spectral balance, dynamic character). Archives to `docs/audio-analysis/catalog/<date>-deep.json` and refreshes `docs/catalog-analysis-report.md`. Run `uv run scripts/batch-full-analysis.py --help`.
+ - `scripts/playlist-sequencing-data.py`: the per-track and per-seam sequencing data, drops, runs, and re-eval compare.
+ - `scripts/validate-sequence.py`: the locked-arc check and the pre-write gate on the playlist YAML.
+ - `scripts/batch-full-analysis.py`: the catalog-wide deeper analysis.