Skip to content

Cockpit band legibility, plus the CI gate that should have caught it - #110

Merged
PhysShell merged 8 commits into
mainfrom
claude/cockpit-band-legibility
Jul 14, 2026
Merged

Cockpit band legibility, plus the CI gate that should have caught it#110
PhysShell merged 8 commits into
mainfrom
claude/cockpit-band-legibility

Conversation

@PhysShell

@PhysShell PhysShell commented Jul 14, 2026

Copy link
Copy Markdown
Owner

The cockpit painted the section band as bare colour blocks. The band's cells carry the class name — scene::resolve_band centres "Riff" or "Breakdown" in each section's span, and the ratatui preview prints it — but the cockpit's glyph_color had no BandFill arm, so it dropped the glyph. Same Scene, two renderers, different information: the divergence ADR-0016 exists to prevent. It also left the classification encoded by colour alone (WCAG 1.4.1), and Breakdown-red against Clean-green is precisely the pair a deuteranope cannot separate.

Fixing it changed the design, not just the code. The class hues are dark on this surface — Breakdown clears the 3:1 floor for meaningful graphics by 0.09 — so dimming the unselected sections with gamma_multiply(0.55) drove them under it (Riff landed at 1.54:1) and left the selected section darker, and so quieter, than its neighbours. Selection now lifts its hue 35% toward white: every section keeps a legible colour and clears 3:1, and the active one is the brightest thing in the band. The class label is painted on top, in white on the deep hues and near-black ink on the bright ones — every pairing ≥4.5:1. LABEL_FAINT (3.06:1 on the panel, under the text floor) is gone with it.

Nothing caught this because nothing could: there was no lint or test gate in CI. AGENTS.md documents cargo clippy --all-targets -- -D warnings and cargo test --workspace, but only the two cockpit web jobs ever ran. So this PR adds the gate — and then clears the 19 clippy findings that had accumulated behind it, 14 of them in the cockpit alone (including a 134-line generate_window and inline flag parsing in main, both split; the rest mechanical and behaviour-preserving).

The MSRV was fiction too: rust-version = "1.74" in a workspace whose egui/eframe 0.34 demand 1.92, so no 1.74 user could ever have built the cockpit. Raised to 1.92 — the true floor, not current stable — verified with cargo +1.92 check --workspace --all-targets, with a CI job so it cannot rot again. The decisions log already recorded that 1.74 was the maintainer's own toolchain and that the reason had lapsed.

Commits

94afb61 ci: the format / lint / test gate
324fec7 refactor(cockpit): clear the 14 findings the missing gate let through
93ea109 fix(cockpit): paint the class label; lift the selection instead of dimming the rest
fc654f2 test(cockpit): assert the painter emits the label — falsified against the old mapping, the frame paints "SECF#5C5C4C3", not one class letter
984d9ed chore(msrv): 1.74 → 1.92

The four contrast/label tests were committed red before the fix (in 331dcc4, already on the branch) and are green now.

Verification

cargo fmt --check clean · workspace clippy 0 errors (was 19) · cockpit 34/34. The one workspace test that fails, missing_file_golden, snapshots an English OS error string and fails on any non-English machine — pre-existing, untouched, and worth its own fix.

Follow-ups deliberately left out: the five rustdoc warnings in griff-core (they're why the doc step doesn't -D warnings yet), that locale-dependent golden, and the shared theme tokens (ADR-0028, next branch).

🤖 Generated with Claude Code

https://claude.ai/code/session_014r7EtXCXS4eR3uS9Z5GXt5

Summary by CodeRabbit

  • New Features

    • Improved cockpit visual contrast and label readability, including clearer selected sections and pitch labels.
    • Added more reliable command-line argument handling and diagnostics.
  • Bug Fixes

    • Improved sorting of corpus tags by frequency.
    • Strengthened validation of generated scale windows.
  • Documentation

    • Updated Rust toolchain requirements to Rust 1.92 and documented compatibility expectations.
  • Tests

    • Added automated checks for formatting, linting, documentation, compatibility, and visual contrast.

PhysShell and others added 6 commits July 14, 2026 11:20
AGENTS.md documents `cargo fmt --check`, `cargo clippy --all-targets -D
warnings` and `cargo test --workspace` as this repo's gate, but no workflow ran
them — only the two cockpit web jobs. Nothing caught a lint or test regression
on `main`, which is how 14 clippy findings accumulated in the cockpit alone.

`cargo doc` builds but does not deny yet: rustdoc flags five pre-existing
findings in griff-core (see the step's comment).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014r7EtXCXS4eR3uS9Z5GXt5
…through

Behaviour-preserving throughout; the new CI gate now holds the line.

Mechanical: ten absolute paths hoisted into `use` declarations, a `const fn`,
an `Option::map_or_else`, and a doc first paragraph split in two.

Structural, where the lint had a point:
- `generate_window` ran to 134 lines against an 80-line limit. Its lower half —
  the set's provenance line, the ranked rows, the keep actions — is now
  `generate_candidates`, which only reads the panel and reports the user's
  choice back through `show` / `keep` / `open`, exactly as the inline code did.
- `main` parsed its three flags inline, with a `match` per flag whose `None` arm
  bailed. That is now `parse_args() -> Result<Args, ExitCode>`, so `main` reads
  as the sequence it is: parse, load, open, run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014r7EtXCXS4eR3uS9Z5GXt5
…ction instead of dimming the rest

Turns the four red tests green.

The band's cells carry the class name — `scene::resolve_band` centres it in each
section's span — but `glyph_color` had no `BandFill` arm, so the cockpit painted
a bare colour block where the `ratatui` preview prints "Riff" or "Breakdown".
Two frontends, one `Scene`, different information: the divergence ADR-0016
exists to prevent. It also left the classification encoded by colour alone,
which WCAG 1.4.1 forbids and which the Breakdown-red / Clean-green pair makes
unreadable to a deuteranope. The label is now drawn, in white on the deep hues
and near-black ink on the bright ones — every pairing clears 4.5:1.

The de-emphasis is inverted while we are here. The class hues are dark on this
surface (Breakdown clears the 3:1 floor for meaningful graphics by 0.09), so
dimming the *unselected* sections with `gamma_multiply(0.55)` drove them under
it — Riff landed at 1.54:1 — and left the selected section darker, quieter, than
its neighbours. Selection now lifts its hue 35% toward white: every section
keeps its colour and clears 3:1, and the active one is the brightest thing in
the band.

`LABEL_FAINT` goes with it: at 3.06:1 on the panel it was under the text floor,
and the `SEC` header it drew now shares the gutter's `LABEL_DIM` (5.6:1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014r7EtXCXS4eR3uS9Z5GXt5
The colour-mapping tests pin what `glyph_color` returns; none of them prove the
glyph reaches a shape. This runs one CPU frame over the demo score and reads the
painted text back out of `output.shapes`.

Falsified against the old mapping, the frame paints "SECF#5C5C4C3" — the header
and the pitch labels, and not one class letter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014r7EtXCXS4eR3uS9Z5GXt5
…oses

`rust-version = "1.74"` described nothing: `egui`/`eframe` 0.34 demand 1.92, so
no 1.74 user could ever have built the cockpit. The decisions log already
recorded why 1.74 was picked (it was the maintainer's own toolchain) and that
the reason had lapsed.

1.92 is the true floor, not current stable — an MSRV should be the lowest
version that works, not the newest that happens to be installed. Verified by
`cargo +1.92 check --workspace --all-targets`, and a CI job now builds on
whatever the manifest claims, so the number cannot rot again.

ADR-0010 still cites 1.74 in passing; ADRs are a historical record, so the
correction lives in the decisions log rather than in its text.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014r7EtXCXS4eR3uS9Z5GXt5
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@PhysShell, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c58efc65-82e6-4cec-afd7-6a8fc8d3c2d2

📥 Commits

Reviewing files that changed from the base of the PR and between 984d9ed and 8d3cac9.

📒 Files selected for processing (11)
  • cockpit/web-test/helpers.js
  • core/src/boundary.rs
  • core/src/generate.rs
  • core/src/generation_input.rs
  • core/src/gesture.rs
  • core/src/pitch.rs
  • core/src/score.rs
  • core/src/scoring.rs
  • core/src/structure.rs
  • core/tests/wrap_free_traversal.rs
  • ui-core/src/viewport.rs
📝 Walkthrough

Walkthrough

The pull request raises the MSRV to Rust 1.92, adds CI validation, refactors cockpit CLI and corpus-loading code, updates cockpit rendering and generation controls, and applies test and sorting cleanups.

Changes

Toolchain Policy and CI

Layer / File(s) Summary
MSRV policy and workflow validation
.github/workflows/ci.yml, Cargo.toml, fuzz/Cargo.toml, docs/...
Rust 1.92 is declared across workspace metadata and fuzzing, documented in project guidance, and validated through standard and MSRV-specific CI jobs.

Cockpit Refactor and Rendering

Layer / File(s) Summary
Platform-gated corpus and CLI flow
cockpit/src/generation.rs, cockpit/src/main.rs
Native path and generation types are imported behind platform gates, corpus-loading signatures are simplified, and CLI parsing is moved into Args and parse_args().
Band palette and contrast validation
cockpit/src/lib.rs
Selected-band colors use lifted palette values, glyph colors are adjusted, and rendering tests validate labels, paint output, and contrast ratios.
Generation controls and native paths
cockpit/src/lib.rs
Candidate-list controls are extracted into generate_candidates, while native path aliases are applied to output and file-opening flows.

Code Quality Maintenance

Layer / File(s) Summary
Lint, assertion, and sorting cleanup
core/src/pitch.rs, core/tests/shuffle_window.rs, ui-core/src/dock.rs, ui-core/src/generate.rs
Test lint allowances and non-empty assertions are updated, and tag counts retain descending ordering through reverse-key sorting.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: claude

🚥 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 is concise and accurately reflects the two main changes: cockpit band legibility improvements and added CI enforcement.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/cockpit-band-legibility

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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.

🧹 Nitpick comments (3)
.github/workflows/ci.yml (2)

35-42: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Prefer a specialized Rust caching action.

Manual caching of Cargo's target/ and ~/.cargo/ directories without an eviction strategy typically leads to unbounded cache growth, as old artifacts (from updated dependencies or stale incremental compilations) are never removed. Consider using a dedicated community action, like Swatinem/rust-cache, which automatically cleans up unused artifacts, ignores unneeded .crate files, and optimally hashes cache keys.

  • .github/workflows/ci.yml#L35-L42: Replace the manual actions/cache block in the check job with uses: Swatinem/rust-cache@v2.
  • .github/workflows/ci.yml#L82-L89: Replace the manual actions/cache block in the msrv job with uses: Swatinem/rust-cache@v2. The action automatically includes the job name in its internal cache key to prevent conflicts across different toolchain runs.
🤖 Prompt for 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.

In @.github/workflows/ci.yml around lines 35 - 42, Replace the manual
actions/cache blocks in .github/workflows/ci.yml lines 35-42 for the check job
and lines 82-89 for the msrv job with Swatinem/rust-cache@v2, preserving each
job’s existing workflow structure.

53-61: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Enforce locked dependencies in CI.

Running Cargo commands without the --locked flag allows Cargo to silently fetch newer versions of transitive dependencies if they are updated upstream. This can break CI reproducibility and introduce spurious failures. It is highly recommended to enforce --locked across all verifiable CI builds.

  • .github/workflows/ci.yml#L53-L61: Append the --locked flag to the cargo clippy, cargo test, and cargo doc commands.
  • .github/workflows/ci.yml#L97-L98: Append the --locked flag to the cargo check command in the msrv job.
🤖 Prompt for 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.

In @.github/workflows/ci.yml around lines 53 - 61, Enforce locked dependencies
for all specified CI builds by appending --locked to the cargo clippy, cargo
test, and cargo doc commands at .github/workflows/ci.yml:53-61, and to the cargo
check command in the msrv job at .github/workflows/ci.yml:97-98.
cockpit/src/main.rs (1)

24-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Good extraction — but add unit tests now that parsing is testable.

parse_args was previously untestable inline logic in main; it's now a standalone function that can be driven directly with a Vec<String>-style input (or by refactoring to accept an iterator instead of always reading env::args()), enabling coverage of the --corpus/--out missing-directory error paths, -h/--help, and positional-input fallback. As per coding guidelines, refactors should be backed by characterization tests to guard against silent behavior drift going forward.

🤖 Prompt for 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.

In `@cockpit/src/main.rs` around lines 24 - 80, Refactor parse_args to accept an
injectable iterator or Vec<String>-style argument source instead of always
reading env::args(), while preserving its current parsing behavior and ExitCode
results. Add unit tests covering missing directories for --corpus and --out,
both help flags, positional input fallback, and successful corpus/out
assignment.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 35-42: Replace the manual actions/cache blocks in
.github/workflows/ci.yml lines 35-42 for the check job and lines 82-89 for the
msrv job with Swatinem/rust-cache@v2, preserving each job’s existing workflow
structure.
- Around line 53-61: Enforce locked dependencies for all specified CI builds by
appending --locked to the cargo clippy, cargo test, and cargo doc commands at
.github/workflows/ci.yml:53-61, and to the cargo check command in the msrv job
at .github/workflows/ci.yml:97-98.

In `@cockpit/src/main.rs`:
- Around line 24-80: Refactor parse_args to accept an injectable iterator or
Vec<String>-style argument source instead of always reading env::args(), while
preserving its current parsing behavior and ExitCode results. Add unit tests
covering missing directories for --corpus and --out, both help flags, positional
input fallback, and successful corpus/out assignment.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: abc9e557-b6e4-41fd-b9c7-a84672fb6ce3

📥 Commits

Reviewing files that changed from the base of the PR and between 6fc9cbc and 984d9ed.

📒 Files selected for processing (12)
  • .github/workflows/ci.yml
  • Cargo.toml
  • cockpit/src/generation.rs
  • cockpit/src/lib.rs
  • cockpit/src/main.rs
  • core/src/pitch.rs
  • core/tests/shuffle_window.rs
  • docs/decisions.log.md
  • docs/glossary.md
  • fuzz/Cargo.toml
  • ui-core/src/dock.rs
  • ui-core/src/generate.rs

Clippy reads `rust-version` from the manifest and holds back any lint whose fix
needs a newer compiler. Raising the MSRV from 1.74 to 1.92 therefore unlocked
twelve findings that had been suppressed for years — the gate added in this
branch catches them, which is the gate working.

- `f64::midpoint` / `u16::midpoint` in three places clippy flagged as
  overflow-capable `(a + b) / 2`;
- `Option::is_none_or` for a `map_or(true, …)`;
- `const fn` on eight accessors and two reducer steps.

All behaviour-preserving. Verified on the MSRV itself:
`cargo +1.92 check --workspace --all-targets`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014r7EtXCXS4eR3uS9Z5GXt5
The demo's first section classifies as Breakdown, and the band boots
with section 0 selected. Since the band's de-emphasis flipped from
dimming-the-rest to lifting-the-selection, the boot frame paints the
selected Breakdown at lift(#cf1322, 0.35) = #df656f — the base red the
signature expected is no longer anywhere in the frame (0 px), which is
what turned the smoke gate red on this branch.

Point the signature at the lifted fill instead; its 12-step tolerance
also covers the theme token #e0666f that replaces the computed lift one
branch up (ADR-0028).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NkqJUU6d1sW1RAfvyHrqVM
@PhysShell
PhysShell merged commit b74c56e into main Jul 14, 2026
4 checks 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.

1 participant