Skip to content

feat(cli): generate subcommand — tab-seeded riff generation (S6) - #59

Merged
PhysShell merged 2 commits into
mainfrom
claude/gp-generate
Jun 15, 2026
Merged

feat(cli): generate subcommand — tab-seeded riff generation (S6)#59
PhysShell merged 2 commits into
mainfrom
claude/gp-generate

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 15, 2026

Copy link
Copy Markdown
Owner

What

griff generate <input> <out.mid> [--seed N] [--bars N] — the S6 rule generator surfaced on the .gpx/.mid front door. The engine writes a fresh riff, seeded from your tab's own material, and saves it as playable MIDI.

The request is built from the source so the riff sits in the tab's world:

  • scale = the tab's distinct pitch classes (its palette)
  • rhythm = the note durations of its first sounding bar (its groove)
  • transport = its meter, tempo, PPQN, and pitch range

Then RhythmCopyPitchSubstitute lays a new line over that rhythm. Deterministic under a fixed --seed.

How

Thin CLI wiring over the already-tested core generate::generate; builds a RuleGenerationRequest from the imported Score, exports the result via midi::export_score (same path as export). No core changes. Adds a CliError::Generate variant for generation failures.

Verified

On the real GP6 tab (Dance Gavin Dance):

$ griff generate dgd.gpx riff.mid
generated 8 bars (RhythmCopyPitchSubstitute, seed 0) from a 12-tone scale (1207 bytes) -> riff.mid

→ an 8-bar riff in the tab's 4/4 / 122 BPM, drawing on its full chromatic palette; it re-imports cleanly (8 bars, 128 notes).

Golden-snapshot tested (generate_golden, mirroring export's path-scrubbing — the summary line is pinned, the MIDI bytes are deterministic via the byte count). TDD red → green (golden fails first on the missing subcommand, then the command greens it). 20 cli tests green; clippy --all-targets -D warnings clean; fmt clean.

Scope

CLI surface only — the generation logic and its tests already live in core::generate. The tab→request adaptation (scale/rhythm/range extraction) lives in the CLI as request-construction glue, covered by the golden snapshots. Third of three (S4 phrases ✓, S14 structure ✓); S13 (complement arranger) is the remaining one.

https://claude.ai/code/session_01TTUbGjzD8ysnVnCJnZJE95


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added a griff generate CLI subcommand that converts MIDI/Guitar-Pro input into generated riff MIDI output, with --seed and --bars options and an on-screen generation summary.
  • Tests
    • Added golden snapshot coverage for the new generate command across multiple fixtures, asserting successful execution, expected stdout, and creation of the generated MIDI output file.

A golden test that runs `griff generate <fixture> <out.mid>` and snapshots its
summary line. Fails until the subcommand exists: clap rejects the unknown
subcommand and no snapshots are blessed yet.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2671f04c-5f93-40a9-8405-c785db2c8f3f

📥 Commits

Reviewing files that changed from the base of the PR and between dbb5575 and 06bc4d0.

📒 Files selected for processing (6)
  • cli/src/main.rs
  • cli/tests/snapshots/generate__multi_track.txt
  • cli/tests/snapshots/generate__seven_eight.txt
  • cli/tests/snapshots/generate__simple_4_4.txt
  • cli/tests/snapshots/generate__tempo_change.txt
  • cli/tests/snapshots/generate__two_phrases.txt
✅ Files skipped from review due to trivial changes (4)
  • cli/tests/snapshots/generate__seven_eight.txt
  • cli/tests/snapshots/generate__simple_4_4.txt
  • cli/tests/snapshots/generate__multi_track.txt
  • cli/tests/snapshots/generate__tempo_change.txt
🚧 Files skipped from review as they are similar to previous changes (2)
  • cli/tests/snapshots/generate__two_phrases.txt
  • cli/src/main.rs

📝 Walkthrough

Walkthrough

Adds a griff generate CLI subcommand that reads a MIDI/Guitar-Pro input file, extracts pitch palette and first-bar rhythm, constructs a RuleGenerationRequest, runs griff_core::generate::generate, and writes the result as a MIDI file. Error handling is extended with a CliError::Generate variant, and golden snapshot tests cover five fixtures.

Changes

griff generate subcommand

Layer / File(s) Summary
Command definition, dispatch, and error handling
cli/src/main.rs
Adds Command::Generate { input, output, seed, bars } variant, routes it in the main dispatch match, imports the generate module, extends CliError with a Generate(generate::GenerationError) variant plus its Display branch and From conversion.
cmd_generate and request-building helpers
cli/src/main.rs
Implements cmd_generate (reads score, builds RuleGenerationRequest, calls generate, exports MIDI, prints summary line) and helper functions that collect pitches, compute pitch range, derive interval-based pitch_material, and extract the first-bar rhythm template with a quarter-note fallback.
Golden test and snapshots
cli/tests/cli.rs, cli/tests/snapshots/generate__*.txt
Adds generate_golden test running griff generate against each fixture, asserting scrubbed golden output and output file existence; adds five snapshot files recording expected exit code and stdout for multi-track, 7/8, simple 4/4, tempo-change, and two-phrases fixtures.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit hops in with a tune to compose,
--seed and --bars and away the riff goes!
Pitches collected, the rhythm extracted,
Eight bars of music, perfectly enacted.
🎸🐇 The MIDI is written, the snapshot is set!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(cli): generate subcommand — tab-seeded riff generation (S6)' clearly and concisely summarizes the main change: introducing a new CLI generate subcommand for riff generation seeded from tab input.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/gp-generate

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cli/src/main.rs`:
- Around line 504-505: The success message is printing the raw CLI argument
value instead of the clamped value. The bar_count is set using bars.max(1) to
ensure a minimum of 1, but the format string at lines 504-505 prints the
unclamped bars variable, causing output to show 0 bars generated when 1 was
actually generated. Replace the bars variable reference in the format string
with the clamped bar_count variable. Apply the same fix to the second affected
location at lines 528-529 where the same issue occurs.
- Around line 587-612: The first_bar_rhythm function currently only examines the
first master bar in the score, but should instead find the first bar that
actually contains notes (the "first sounding bar"). Modify the function to
iterate through all master_bars sequentially until finding one that contains
notes in any track/voice combination, then extract the rhythm from that first
bar with content. If no bars contain any notes at all, fall back to the four
quarter notes default. This ensures the rhythm template matches the actual first
sounding bar semantics rather than potentially using silence from an empty first
bar.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5bc8d4e9-3d13-4966-a506-e4a98c9e1caf

📥 Commits

Reviewing files that changed from the base of the PR and between 371be19 and dbb5575.

📒 Files selected for processing (7)
  • cli/src/main.rs
  • cli/tests/cli.rs
  • cli/tests/snapshots/generate__multi_track.txt
  • cli/tests/snapshots/generate__seven_eight.txt
  • cli/tests/snapshots/generate__simple_4_4.txt
  • cli/tests/snapshots/generate__tempo_change.txt
  • cli/tests/snapshots/generate__two_phrases.txt

Comment thread cli/src/main.rs
Comment thread cli/src/main.rs Outdated
Surface the S6 rule generator (generate::generate, ADR-0011) on the .gpx/.mid
front door. `griff generate <input> <out.mid> [--seed N] [--bars N]` seeds the
generator from the source's own material — its distinct pitch classes as the
scale, the rhythm of its first sounding bar as the template, and its meter,
tempo, and pitch range — then writes a fresh riff (RhythmCopyPitchSubstitute)
to MIDI. Deterministic under a fixed seed.

Greens the generate_golden test by blessing its summary snapshots. On a real
GP6 tab: an 8-bar riff in the tab's key and groove, playable MIDI.
@PhysShell
PhysShell force-pushed the claude/gp-generate branch from dbb5575 to 06bc4d0 Compare June 15, 2026 23:04
@PhysShell
PhysShell merged commit bad7269 into main Jun 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants