Skip to content

v1.7.1 - Bugfix: MidiPilot Stability & v1.7.0 Polish

Choose a tag to compare

@github-actions github-actions released this 16 May 12:01
· 11 commits to main since this release

[1.7.1] - 2026-05-14 - Bugfix: MidiPilot Stability & v1.7.0 Polish

Summary

  • Fixed a MidiPilot crash when starting a new chat while a request was still running - clicking New Chat (or being forced to, because a stalled Simple-mode request left the input disabled with no Stop button) deleted the live chat widgets out from under the in-flight streaming/agent callbacks, causing a use-after-free hang-then-crash. New Chat now aborts the running request cleanly first and never deletes widgets mid-callback (BUG-MIDIPILOT-001).
  • Simple mode now has a Stop button - previously only Agent mode could abort an in-flight request; a stalled Simple-mode streaming response had no recovery path except New Chat (which then hit the crash above). Simple mode now mirrors the Agent send→stop button swap, and a stale internal "agent running" flag can no longer permanently swallow a Simple-mode response (BUG-MIDIPILOT-001).
  • MidiPilot empty/unconfigured state is properly centred - before any API key is set, the Welcome to MidiPilot panel used to stick to the top with the input field floating in the middle of a big empty gap. It now occupies the chat area and is vertically centred, matching the configured layout (UI-MIDIPILOT-EMPTY-001).
  • "Agent (PR)" mode is no longer shown by default - the experimental AI-as-PR-creator mode (apply-then-review) overlapped with Protocol undo (Ctrl+Z already reverts an entire agent run) and was visible even with collaboration disabled. It is now behind a compile-time flag (default off); the code path stays intact for future re-evaluation (BUG-COLLAB-041).
  • Corrected the WAN pairing-code keyspace math in the manual - collab-cloudflare.html claimed 28⁴ ≈ 614 000 codes; the actual alphabet is 31 characters (A-Z minus I/L/O, plus 2-9) → 31⁴ ≈ 923 000 (BUG-COLLAB-036).
  • Removed a non-existent "Trace" logging level from the docs - the v1.7.0 changelog / README / What's New described five levels ending in Trace; the real ladder is Off / Errors / Warnings / Info / Debug. Docs now match the code (BUG-COLLAB-040).
  • Hardened a latent Windows compile-break - <windows.h> was included inside an anonymous namespace in src/main.cpp, which would break the NO_CONSOLE_MODE build the moment anyone enabled it (Win32 typedefs landed in anon-namespace scope). Moved above the namespace (BUG-COLLAB-035).
  • Release packaging robustness - the CI workflow's LICENSE/README/CHANGELOG copy step now has Test-Path guards mirroring release.bat, so a missing top-level file degrades gracefully instead of hard-failing the packaging step under PowerShell's stop-on-error default (BUG-COLLAB-038).
  • Two new unit-test executables - test_ice_config (11 cases) and test_rtc_signaling_token (18 cases) close the highest-value remaining collab-module coverage gaps; full suite is 41/41 green (TEST-1.7.1-001).
Full Changelog - Bugfix: MidiPilot Stability & v1.7.0 Polish

Bug Fixes

  • Fixed MidiPilot use-after-free crash on New-Chat-while-running (BUG-MIDIPILOT-001) - reported from daily field use: after start, MidiPilot looked busy in Simple mode but the play button never swapped to Stop, the input field was disabled, and there was no way to abort. Clicking New Chat to recover hung the app and crashed it. Root cause was three compounding defects in src/gui/MidiPilotWidget.cpp: (A) onNewChat() unconditionally cleared _conversationHistory/_entries and deleteLater()'d every chat-bubble widget with no running-state guard - while a request was live the streaming/step callbacks still held pointers to _thoughtLabel, the streaming bubble and _agentStepsWidget, so deleting them was a use-after-free; (B) Simple mode never showed a Stop button (Stop was Agent-mode-only), so a stalled streaming response had no recovery path; (C) onResponseReceived/onErrorOccurred early-returned on if (_isAgentRunning) - a stale-true flag (missed terminal agent signal) silently swallowed a genuine Simple-mode response and left the input disabled forever, and AiClient::cancelRequest() is intentionally silent (AgentRunner relies on that to avoid a double errorOccurred) so a Simple-mode cancel never self-healed the UI. Fix: a new MidiPilotWidget::abortActiveRequest() helper (agent → AgentRunner::cancel() which self-resets; simple → silent cancelRequest() + explicit UI reset); onNewChat() now bails early when busy - aborts the request, shows "Stopped the running request - click New Chat again to clear", and returns without touching any widget; Simple mode performs the same send→stop button swap as Agent and the _stopButton handler routes through abortActiveRequest() for both modes; the _isAgentRunning guards in onResponseReceived/onErrorOccurred now cross-check _agentRunner->isRunning() and recover from a stale flag instead of dropping the signal. The crash path (New Chat while running) is now structurally impossible. The original trigger (why the first Simple request stalled with no terminal signal - likely a provider-side cold-start stall) is not definitively root-caused, but the dead-end is now a recoverable state. Regression risk contained: agent flow self-reset behaviour is unchanged.
  • Fixed "Agent (PR)" mode being shown even with collaboration disabled (BUG-COLLAB-041) - the mode item in the MidiPilot dropdown was gated only by the compile-time MIDIEDITOR_COLLAB_ENABLED (default on), never by the runtime Settings → Collaboration → Enable collaboration features master toggle, so it shipped visible regardless. Combined with user feedback that the apply-then-review UX duplicates Protocol's existing Ctrl+Z undo, the mode is now behind a MIDIPILOT_EXPERIMENTAL_AGENT_PR compile-time flag (default 0). The downstream snapshot/diff/PrReviewDialog code path is left compiled in so a developer can flip the flag and rebuild to re-evaluate without re-implementing anything. No QSettings hook, no UI - genuinely hidden, not a discoverable runtime toggle.
  • Fixed <windows.h> included inside an anonymous namespace (BUG-COLLAB-035) - in src/main.cpp the Win32 include sat inside namespace { … }, putting HINSTANCE/LPSTR/WINAPI/EXCEPTION_POINTERS into anonymous-namespace scope. It compiled today only because NO_CONSOLE_MODE is off; enabling that documented build flag would have failed to find the typedefs for the global-scope WinMain. Moved the include above the namespace; the now-redundant second include inside the NO_CONSOLE_MODE block is a header-guarded no-op kept as a clarifying stub. Latent build-break, never fired in a shipped configuration.
  • Fixed wrong pairing-code keyspace math in the Cloudflare manual (BUG-COLLAB-036) - manual/collab-cloudflare.html and Planning/10_DOCUMENTATION.md claimed 28⁴ ≈ 614 000. The rendezvous.js alphabet excludes only 0/O/1/I/L, leaving 31 characters (A-Z minus I/L/O = 23, plus 2-9 = 8), so the real keyspace is 31⁴ ≈ 923 000. Documentation-only; the worker code was always correct.
  • Fixed unreleased-version caption in the logging manual (BUG-COLLAB-037) - manual/logging.html captioned the settings screenshot "Settings → Logging in v1.7.1" while the page shipped in v1.7.0. Corrected to v1.7.0 (the release the logging tab actually shipped in).
  • Removed the non-existent "Trace" logging level from the docs (BUG-COLLAB-040) - the v1.7.0 changelog summary + LOGGING-001 entry, README.md (Features table + Logging section) and manual/index.html (What's New) all described a five-level ladder ending in Trace. LoggingConfig::Level has no Trace - it is Off / Errors / Warnings / Info / Debug. All four surfaces now state the real ladder; the size-callout wording was softened from "yellow at Debug, red at Trace" to "escalates with the level" so it stays accurate if thresholds shift. manual/logging.html itself was already correct (its "four severities" + "five levels" split is intentional).
  • Hardened CI release packaging (BUG-COLLAB-038) - .github/workflows/ci.yml copied LICENSE/README.md/CHANGELOG.md into the artifact with bare Copy-Item calls. Under GitHub Actions' default $ErrorActionPreference='stop' for pwsh, a missing top-level file would hard-fail the packaging step - asymmetric with release.bat, which guards the same copies with if exist. Each Copy-Item is now wrapped in if (Test-Path …).
  • Clarified the CMAKE_POLICY_VERSION_MINIMUM scope comment (BUG-COLLAB-039) - the comment in CMakeLists.txt claimed the set(CMAKE_POLICY_VERSION_MINIMUM 3.5) (needed so CMake 4.x accepts libdatachannel's pre-3.5 cmake_minimum_required) was "scoped to this block". CMake has no block scope for if/endif; it is directory-scoped and would leak into the later add_subdirectory(tests). The comment now states this correctly and an explicit unset(CMAKE_POLICY_VERSION_MINIMUM) is issued right after FetchContent_MakeAvailable(libdatachannel) so the loosened policy floor does not bleed into the test tree. Behaviour was already correct in practice (the test CMakeLists has no cmake_minimum_required); this is hygiene + accuracy.

Improvements

  • UI-MIDIPILOT-EMPTY-001 - MidiPilot empty/unconfigured panel layout - when no API key is configured, the Welcome to MidiPilot + Open Settings panel previously sat at the top of the sidebar with the (disabled) input field floating in the middle of a large empty gap, because the hidden chat-scroll area's stretch was unclaimed and the input field's default Expanding size policy absorbed the slack. The setup panel now takes the chat area's place (same stretch=1) with inner stretches centring the title/description/button, so the empty state mirrors the configured layout - input/status pinned to the bottom, welcome content vertically centred.

Test Harness

  • TEST-1.7.1-001 - Two new collab-module test executables - tests/test_ice_config.cpp (11 Qt Test methods: Google STUN-defaults sanity + multi-port coverage, load() fallback for unset / empty / whitespace-only / all-comment values, multi-entry save→load round-trip, blank-line + #-comment stripping in the parser, empty-save reverts to defaults; sandboxed via QStandardPaths::setTestModeEnabled). tests/test_rtc_signaling_token.cpp (18 methods: looksLikeToken prefix-match incl. negative/empty cases, encode prefix shape, offer vs answer distinctness, round-trip preserving SDP + sessionId for both kinds incl. a realistic ICE-candidate SDP, compression shrinks repetitive payloads to <50 % of raw size, and the full decode error matrix: empty / missing-prefix / missing-sessionId / missing-kind / unknown-kind / empty-base64 / corrupted-base64 + errorOut population). Both build with -DMIDIEDITOR_WEBRTC_ENABLED and Qt6::Core only - no libdatachannel link surface. Full suite: 41/41 green.

Files Modified

  • CMakeLists.txt - version bumped to 1.7.1; unset(CMAKE_POLICY_VERSION_MINIMUM) after the libdatachannel FetchContent + corrected scope comment (BUG-COLLAB-039).
  • src/main.cpp - <windows.h> moved above the anonymous namespace; redundant in-namespace include replaced with a header-guard explanatory stub (BUG-COLLAB-035).
  • src/gui/MidiPilotWidget.h / .cpp - new abortActiveRequest() helper; onNewChat() running-guard; Simple-mode send→stop button swap; _stopButton handler routed through the helper; stale-_isAgentRunning cross-check in onResponseReceived/onErrorOccurred; empty-state setup-panel layout (stretch + inner centring); Agent (PR) item behind MIDIPILOT_EXPERIMENTAL_AGENT_PR compile flag (BUG-MIDIPILOT-001, BUG-COLLAB-041, UI-MIDIPILOT-EMPTY-001).
  • .github/workflows/ci.yml - Test-Path guards around the LICENSE/README/CHANGELOG copies (BUG-COLLAB-038).
  • manual/collab-cloudflare.html, manual/logging.html, manual/index.html, Planning/10_DOCUMENTATION.md - keyspace math, version caption, and "Trace"-level documentation corrections (BUG-COLLAB-036/037/040).
  • README.md - Logging Settings row + Logging section reworded to the real Off → Debug ladder (BUG-COLLAB-040).
  • tests/test_ice_config.cpp, tests/test_rtc_signaling_token.cpp, tests/CMakeLists.txt - two new test executables registered with the Windows PATH-fix block (TEST-1.7.1-001).
  • Planning/03_bugs.md - Round-4 verification fix-status + the BUG-MIDIPILOT-001 field report.