Skip to content

fix: resolve the findings from the release smoke run - #245

Merged
frankea merged 2 commits into
mainfrom
fix/release-smoke-findings
Aug 29, 2026
Merged

fix: resolve the findings from the release smoke run#245
frankea merged 2 commits into
mainfrom
fix/release-smoke-findings

Conversation

@frankea

@frankea frankea commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Findings from a local smoke run of main on a fresh bottle before cutting 3.7.0. All nine are pre-existing (they date back to the v1.0 tree), but they sit on paths the 3.7.0 changelog advertises, so they should not ship again.

What was broken

  1. Raw localization keys. Five labels interpolate a value and the catalog only carried the plain key, so SwiftUI rendered config.graphics.currentlyUsing, config.virtualDesktop.matchDisplay, console.exitCode, and friends verbatim. The %@ / %lld catalog forms are added at their sorted positions (en + en-GB, no re-serialization).
  2. Empty diagnostics sheet. Analyze last run and View Latest Diagnosis used .sheet(isPresented:) with the content closure reading a separate optional. SwiftUI can evaluate that closure before the value lands, which presents a small blank sheet. Both are .sheet(item:) now, so the sheet is built from the value that presents it.
  3. Export and view buttons never enabled. Both gate on lastDiagnosisDate, and nothing ever set it. triggerCrashClassification stamps it when it records a diagnosis, so the ZIP export is reachable.
  4. No way to close the diagnosis sheet. Every presentation of DiagnosticsView is a sheet and it had no control of its own; the only exit was cmd-period. It has a Done button on the cancel action, matching the other sheets.
  5. "Analyze last run" did nothing before the first run. The handler guards on lastLogFileURL and returned silently. The button is disabled until a log exists.
  6. A bottle created in-session lost its first pin from the library, Dock menu, and menu bar extra. createNewBottle cleared inFlight and then called loadBottles(), which only keeps in-flight instances, so the selected bottle page kept writing to an instance BottleVM no longer held. Reproduced twice (pin on disk, "No pinned programs" in the extra, no card on the page) and gone after the fix: the reload runs while the bottle is still in flight and the kept instance is marked available.
  7. Kill-on-quit never fired. Two causes, both pre-existing. The Settings toggle is @AppStorage("killOnTerminate") = true, which never writes its default, and the handler read it with bool(forKey:), which reports an absent key as off: for anyone who never touched the toggle the setting was silently off. Behind that, applicationWillTerminate queued wineserver -k in a Task and the process exited before it ran. Verified with a running wordpad surviving quit before the fix. The read mirrors the AppStorage default now and a synchronous killBottleAndWait runs from the handler.
  8. Audio Troubleshooting opened as an empty sheet. Same isPresented plus separate optional race as the diagnosis sheet; presented from the engine now.
  9. Crash diagnosis history did not refresh in place. The list loaded on appear only; it reloads on .crashDiagnosisAvailable.

Verification

Second round (same Release build recipe, throwaway bottles only): crash classification end to end via the late-crash path (banner, View Diagnosis, history, "Last analyzed", export enabled), redacted ZIP ("arguments": "-token <redacted>", /Users/<redacted>, no token anywhere) and the sensitive variant with the raw value, remediation card apply, menu bar extra launch of a pin, test tone (WhiskyAudioTest.exe --beep returned status: ok), and the in-session bottle pin before and after the fix.

  • Release build, fresh bottle: the five labels render, both diagnostics buttons open a sheet with content (or the "No diagnosis available" state), Done dismisses it, export/view enable after a diagnosis is recorded.
  • Existing bottles untouched (metadata and user.reg hashes identical before and after).
  • swift test --package-path WhiskyKit green; pinned SwiftFormat 0.58.7 lint and swiftlint --strict clean; british-english.py --check passes.

Five defects surfaced while smoke testing main on a fresh bottle ahead of
3.7.0, none of them new to this cycle:

- Five interpolated labels rendered their raw key (Recommended graphics
  card "currently using" line and helper, Virtual Desktop detected display
  size, console exit code badge and footer). The catalog only carried the
  plain key, so SwiftUI could not resolve the %@ / %lld form.
- "Analyze last run" and "View Latest Diagnosis" presented a small empty
  sheet. Both used .sheet(isPresented:) with the content reading a separate
  optional that SwiftUI can evaluate before the value lands. They present
  from the value now via .sheet(item:).
- The bottle's "Export Diagnostic Report" and "View Latest Diagnosis"
  buttons could never enable: nothing ever stamped lastDiagnosisDate.
  triggerCrashClassification sets it when it records a diagnosis.
- DiagnosticsView had no control of its own; every presentation is a
  sheet, so the only exit was cmd-period. It has a Done button bound to
  the cancel action.
- "Analyze last run" clicked through to nothing before the first run (the
  handler guards on the last log). It is disabled until a log exists.
Four more from the same local run, all pre-existing:

- A bottle created during the session kept writing to an instance the
  rest of the app no longer read: createNewBottle cleared inFlight and
  then reloaded the bottle list, and the reload only keeps in-flight
  instances. Its first pin never reached the library, the Dock menu, or
  the menu bar extra until the next reload. The reload now runs while the
  bottle is still in flight so the instance survives, and it is marked
  available afterwards.
- Kill-on-quit never fired. The Settings toggle is an AppStorage that
  defaults to on without writing the key, and the handler read it with
  bool(forKey:), which reports an absent key as off. Behind that,
  applicationWillTerminate queued the wineserver kill in a Task and the
  process exited before it ran. The read mirrors the AppStorage default
  and a synchronous variant runs from the termination handler.
- The Audio Troubleshooting wizard presented as an empty sheet, the same
  isPresented-plus-optional race as the diagnosis sheet. Item-based now.
- The crash diagnosis history on a program's page did not reload when a
  diagnosis was recorded while the page was open.
@frankea

frankea commented Aug 29, 2026

Copy link
Copy Markdown
Owner Author

Reviewed hands-on rather than from the diff: every item was reproduced on a Release build with throwaway bottles before the fix and re-checked after a rebuild.

  • Raw keys: the five labels render on the Recommended graphics card, Virtual Desktop, and the console.
  • Diagnostics sheets: "Analyze last run", "View Latest Diagnosis", the crash banner's View Diagnosis, and Audio Troubleshooting all present with content; Done dismisses.
  • Export: with a recorded diagnosis the bottle's export and view buttons enable; the redacted ZIP has "arguments": "-token <redacted>" and no home paths, the sensitive ZIP has the raw value.
  • In-session bottle pin: reproduced twice before (pin on disk, "No pinned programs" in the extra, no card), gone after.
  • Kill-on-quit: a running wordpad survived quit before; after the fix no wineserver or wine process survives. Audited the rest of the app for the same bool(forKey:)-of-an-AppStorage-default shape; killOnTerminate was the only one.
  • Kit suite 1301 XCTest + 267 Swift Testing green; SwiftFormat 0.58.7 and SwiftLint strict clean; en-GB check passes.

codecov/patch is red because these are view and app-delegate changes with no unit coverage; nothing in WhiskyKit's logic changed except the synchronous kill helper, which needs a live wineserver to exercise. Merging.

@frankea
frankea merged commit a9f6cda into main Aug 29, 2026
8 of 9 checks passed
@frankea
frankea deleted the fix/release-smoke-findings branch August 29, 2026 19:56
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