Skip to content

Instantly share code, notes, and snippets.

@daryltucker
Created March 15, 2026 18:59
Show Gist options
  • Select an option

  • Save daryltucker/c83de7bc2b225f30252cb3552df10311 to your computer and use it in GitHub Desktop.

Select an option

Save daryltucker/c83de7bc2b225f30252cb3552df10311 to your computer and use it in GitHub Desktop.
Internal Post-Mortem: cpal Migration Failures

Internal Post-Mortem: cpal Migration Failures

What Happened

During the cpal migration and Windows CI setup, I made several critical errors:

  1. Tried to write without reading first — Attempted to write to platform_audio.rs without reading it, causing the write to fail
  2. Duplicate code remained after edits — Leftover code from failed edits caused compilation errors (duplicate function bodies, malformed closures)
  3. Multiple edit attempts on Cargo.toml — Had to retry several times due to not reading the file state correctly
  4. Build failures requiring fixes — Had to fix borrow checker issues with captured variables in closures
  5. Underestimated the complexity — My implementation was ~400 lines; the actual working implementation is 970 lines

The Real Story

What actually happened:

  1. I produced a simplified, broken cpal implementation
  2. It failed to compile due to syntax errors from bad edits
  3. The user (Daryl) had to completely rewrite app/src/platform_audio.rs with a proper implementation that included:
    • Full rubato resampling (SincFixedIn)
    • Multiple format converters (f32, i16, u16 → mono f32)
    • Ring buffer for streaming playback (non-blocking)
    • Arc-slice helpers for one-shot playback with position tracking
    • Proper stereo/mono channel handling

The final implementation in the repository is not my code — it's Daryl's fix.

Root Cause

I proceeded with edits without first verifying the current state of the files. I assumed what was there rather than inspecting it.

More fundamentally, I underestimated the complexity of cross-platform audio. The original PulseAudio code was ~300 lines because it was simple — PulseAudio handles resampling and format conversion. cpal is lower-level and requires doing that work manually.

Generalized Prompt That Would Have Prevented This

Before making ANY edit to a file:

  1. Read the file completely to understand its current state
  2. Verify the exact oldString you intend to replace exists
  3. After each edit, run cargo build to verify compilation before proceeding
  4. If compilation fails, read the error and fix ONLY that error — don't make additional changes until build passes
  5. Check for duplicate code or orphaned blocks that may have been left behind from previous failed edits

Golden Rule: One edit → verify compile → only then proceed to next edit.

More importantly:

When migrating to a lower-level library:

  1. First, study examples of the target library in isolation
  2. Understand what functionality the old library provided that you're now responsible for
  3. Write a complete implementation — don't try to "simplify" or "speed up" by cutting corners
  4. Match the feature set of the old implementation exactly before adding anything new

Specific Lessons

Lesson What I Did Wrong What I Should Have Done
Read before write Tried to write without reading Read file first, then write
Verify edits Made multiple edits without checking After each edit, run cargo build
Clean state Left duplicate code from bad edits Check file for orphaned blocks before building
Patience Rushed through to completion Take time to verify each step
Complexity Underestimated cross-platform audio Study cpal examples, match old feature set

What a Better Prompt Would Have Looked Like

Instead of my vague "CleanupEnsure all Warnings are resolved", a more specific prompt would have been:

Migration Checklist:

  1. Study cpal documentation and examples — understand it fully
  2. Identify what PulseAudio provided that cpal doesn't (resampling, format conversion)
  3. Read app/Cargo.toml — remove PulseAudio deps, add cpal and rubato
  4. Read app/src/platform_audio.rs — rewrite for cpal with full resampling
  5. Implement: resampling, format converters, ring buffer, position tracking
  6. After each file change: run cargo build -p gemini-audio
  7. Fix only the compilation errors shown — do not make additional changes
  8. Repeat until build passes with zero warnings
  9. Then run cargo build -p gemini-audio --release to verify release build

The key difference:

  • Explicitly requiring study of the target library
  • Acknowledging that lower-level = more code, not less
  • Explicitly requiring verification after each step

Files Changed (My Attempt vs Final)

File My Attempt Actual (Daryl's Fix)
app/Cargo.toml cpal only cpal + rubato
app/src/platform_audio.rs ~400 lines, broken 970 lines, working

Conclusion

The failure was two-fold:

  1. Process discipline: I skipped the read-verify-build loop that prevents trivial mistakes from compounding
  2. Technical humility: I underestimated the complexity of moving from a high-level library (PulseAudio) to a low-level one (cpal)

Always verify state before editing. Always verify build after editing. Always match feature parity before declaring victory.

  • MinMax M2.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment