Files
reaper-control/.sdd/changes/archive/2026-05-03-generate-song/change/spec.md
renato97 48bc271afc feat: SDD workflow — test sync, song generation + validation, ReaScript hybrid pipeline
- compose-test-sync: fix 3 failing tests (NOTE_TO_MIDI, DrumLoopAnalyzer mock, section name)
- generate-song: CLI wrapper + RPP validator (6 structural checks) + 4 e2e tests
- reascript-hybrid: ReaScriptGenerator + command protocol + CLI + 16 unit tests
- 110/110 tests passing
- Full SDD cycle (propose→spec→design→tasks→apply→verify) for all 3 changes
2026-05-03 22:00:26 -03:00

4.8 KiB

Delta: generate-song

ADDED Requirements

Requirement: CLI generates REAPER .rpp from arguments

scripts/generate.py MUST accept --bpm, --key, --output, and --seed arguments. When invoked as python scripts/generate.py --bpm 95 --key Am --output output/song.rpp --seed 42, the script SHALL produce a valid REAPER .rpp file at the specified output path using seed 42 for all random choices.

Scenario: Happy path — produces 52-bar arrangement

  • GIVEN no arguments beyond required ones; seed=42 is the default
  • WHEN the CLI is invoked with --bpm 95 --key Am --output /tmp/song.rpp --seed 42
  • THEN a .rpp file SHALL be written containing 9 tracks (7 normal + 2 return)
  • AND the arrangement duration SHALL equal 52 bars at 95 BPM
  • AND all 19 plugins (across tracks, returns, master) SHALL be present in the FX chains

Scenario: Default seed produces reproducible output

  • GIVEN two invocations with identical arguments (including --seed 42)
  • WHEN both are run in the same environment
  • THEN the resulting .rpp files SHALL be byte-for-byte identical

Scenario: Invalid BPM raises ValueError

  • GIVEN --bpm 0 or --bpm -10
  • WHEN the CLI is invoked
  • THEN a ValueError SHALL be raised with message matching bpm must be > 0

Requirement: validate_rpp_output(rpp_path) -> list[str] checks all structural invariants

The validation function MUST return an empty list for a valid .rpp, and a list of error strings for any violation.

Scenario: Returns empty list for valid output

  • GIVEN a .rpp produced by the CLI with all required structure
  • WHEN validate_rpp_output(path) is called
  • THEN the result SHALL be []

Scenario: Detects wrong track count

  • GIVEN a .rpp with fewer than 9 tracks
  • WHEN validate_rpp_output(path) is called
  • THEN the returned list SHALL include "Expected 9 tracks, got N"

Scenario: Detects missing plugin chains

  • GIVEN a .rpp where a track is missing its FXCHAIN block
  • WHEN validate_rpp_output(path) is called
  • THEN the returned list SHALL include "Track 'X' missing FXCHAIN"

Scenario: Detects broken audio clip paths

  • GIVEN a .rpp containing an audio clip whose SOURCE WAVE path does not exist on disk
  • WHEN validate_rpp_output(path) is called
  • THEN the returned list SHALL include "Audio clip path does not exist: /path/to/file.wav"

Scenario: Detects MIDI clips without notes

  • GIVEN a .rpp containing a MIDI clip with zero notes
  • WHEN validate_rpp_output(path) is called
  • THEN the returned list SHALL include "MIDI clip has no notes"

Scenario: Detects incorrect arrangement duration

  • GIVEN a .rpp whose final item ends before the expected 52-bar duration at the given BPM
  • WHEN validate_rpp_output(path) is called
  • THEN the returned list SHALL include "Arrangement ends at beat X, expected at least Y"

Scenario: Detects missing send routing

  • GIVEN a .rpp where a non-return track has no AUXRECV send to a return track
  • WHEN validate_rpp_output(path) is called
  • THEN the returned list SHALL include "Track 'X' missing send to return track"

Requirement: scripts/generate.py uses REAPER drumloops with fallback

The script SHALL pick drumloop files from the Ableton drumloop directory, cycling through available files per variant, and SHALL fall back gracefully when a file is absent.

Scenario: Picks from seco and filtrado variants

  • GIVEN the CLI is run with --seed 42
  • WHEN the resulting .rpp is inspected
  • THEN audio clips SHALL reference files from the seco and filtrado pools as defined in DRUMLOOP_FILES
  • AND the variant per section SHALL follow DRUMLOOP_ASSIGNMENTS

Scenario: Falls back when perc loop file is missing

  • GIVEN the file 91bpm bellako percloop.wav does not exist
  • WHEN build_perc_track is called for a verse or chorus section
  • THEN no Perc clip SHALL be added for that section (skip silently rather than crash)

Requirement: Test suite for generate-song

A test file at tests/test_generate_song.py MUST cover the CLI and validation function.

Scenario: CLI end-to-end smoke test

  • GIVEN tmp_path fixture
  • WHEN python scripts/generate.py --bpm 95 --key Am --output {tmp_path}/song.rpp --seed 42 is executed as a subprocess
  • THEN the resulting file SHALL exist and be non-empty

Scenario: Validation passes for valid output

  • GIVEN a generated .rpp at a known path
  • WHEN validate_rpp_output(path) is called
  • THEN it SHALL return []

Scenario: Validation detects track count violation

  • GIVEN a .rpp with 5 tracks (not 9)
  • WHEN validate_rpp_output(path) is called
  • THEN the error list SHALL contain a track-count violation message

Scenario: Reproducibility — same seed gives same output

  • GIVEN two temp paths
  • WHEN the CLI is run with --seed 42 to both paths
  • THEN both output files SHALL have identical content