fix(settings): restore the default-calibration-profile backfill dropped in 3abfaf60#76
Open
FourthWiz wants to merge 1 commit into
Open
Conversation
…ed in 3abfaf6 commit 3abfaf6 moved calibration ownership from the Tauri client to the daemon and deleted a 13-line backfill that used to run in src-tauri/src/setup.rs: an is_empty() check that seeded a default CalibrationProfile via CalibrationProfile::from_legacy(), plus a fallback that set active_calibration_id to that profile's id. Nothing re-established either half daemon-side, so every fresh install (and any install with an explicit `"calibration_profiles": []` on disk, which is the actual on-disk shape today since the field has no skip_serializing_if) gets an empty profile list. The onboarding calibration button silently no-ops: the !calProfile guard returns before any daemon call is made, so there's no error, no log line, and no HTTP request — just a dead button. Restore both halves of the deleted block in load_settings(), the universal chokepoint every calibration consumer reads through. The migration is in-memory only, matching the shortcut/secret migrations directly above it: it re-applies on every load and lands on disk incidentally, the first time any route calls modify_settings_blocking. active_calibration_id must be backfilled too (not just calibration_profiles) — background.rs and auto_start_pending have no .first() fallback the way get_active_profile does, and CALIBRATION_AUTO_START defaults to true, so a half-restore would leave auto-start permanently dead for every backfilled user. Also drop the hardcoded CalibrationProfile::default() seed from the settings_calibration.rs test fixture (mk_state()) — it was the only production seed of its kind left after 3abfaf6, and it made list_profiles_returns_defaults assert the fixture rather than the actual backfill. Dropping it turns both existing tests into real regression tests for free, plus one new test that hand-writes settings.json as raw JSON text with an explicit `"calibration_profiles": []` to pin the case that a missing-file Default seed could never cover. Fixes the calibration button being permanently dead on any install running v0.0.122 or later (the release that shipped 3abfaf6), including the current stable v0.0.129.
This was referenced Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The argument, in one command:
This shows a 13-line deletion: an
is_empty()→from_legacy()backfill, plus anactive_calibration_id.first()fallback. This PR is not proposing a new mechanism — it's restoring your own deleted code, in the crate that now owns settings.What broke.
3abfaf60moved calibration ownership from the Tauri client to the daemon and deleted the backfill that used to run insetup.rsat startup. Nothing re-established it daemon-side. The regression is notVec::new()as the default — that was already the default at v0.0.121 (git show v0.0.121:crates/skill-settings/src/lib.rs→:1009) and calibration worked fine, becausesetup.rs:370-382backfilled it at every startup. The regression is deleting that backfill while moving the read path daemon-side, and never re-adding the equivalent there.Blast radius: every fresh install on v0.0.122+ (the release that shipped
3abfaf60), including the current shipped stable v0.0.129. Confirmed live on this machine:/v1/calibration/profiles→200 [];/v1/calibration/active-profile→200 null;~/.skill/settings.jsonhas"calibration_profiles": [], key present (not missing — the field has noskip_serializing_if, so every save writes it explicitly).User-visible symptom: the "Start Calibration" button in onboarding renders enabled (the daemon reports connected), but clicking it does nothing — no error, no HTTP request, no log line. I verified this directly in a browser harness (real dev-built daemon + real on-disk settings; only the Tauri bootstrap shim and one
GET /v1/statusmock differ from production, and neither affects the guard that's actually firing): a 4-second window after the click shows the same 6 background poll requests, all pre-existing, all GET — zero/v1/calibration/*requests, zero non-GET requests, screenshot pixel-identical before and after. The!calProfile || !isConnectedguard at+page.svelte:753returns before any daemon call fires; that's the entire "nothing happens."The fix. Restore both halves of the deleted block in
load_settings()(crates/skill-settings/src/lib.rs), the chokepoint every calibration consumer reads through (routes,background.rs,calibration_runner.rs) — no reader bypasses it:Both halves matter: only
get_active_profilehas an.or_else(.first())fallback;background.rs's auto-start check andauto_start_pendingdo not, andCALIBRATION_AUTO_STARTdefaults totrue— a half-restore (profiles only, no active id) would leave auto-start permanently dead for every backfilled user.This is deliberately in-memory only, matching the shortcut/secret migrations directly above it in the same function: it re-applies on every load and lands on disk incidentally, the first time any route calls
modify_settings_blocking. Not a bug — please don't "fix" it into a forced write if you're reviewing this.Test changes. Dropped a hardcoded
CalibrationProfile::default()seed from thesettings_calibration.rstest fixture (mk_state()) — it was the last production-shaped seed left after3abfaf60, and it made the existinglist_profiles_returns_defaultstest assert the fixture, not the backfill (false positive). Removing it turns both existing tests into real regression tests for free, and I added one more: hand-writessettings.jsonas raw JSON text with"calibration_profiles": []explicitly present (not merely a missing file, which is the only case aDefault-seed fix would ever see), and asserts the backfilled profile's id matchesactive_calibration_id.Verification:
→ 3 passed, 0 failed (note: no
skill-daemon*crate is in anyscripts/test-fast.shtier, so this needs to be run explicitly — it isn't covered bynpm run test:fast).Acceptance:
"calibration_profiles": []also yields exactly one profile.active_calibration_idequals that profile's id, andauto_start_pendingreturns it.Related: issue #49 documented an earlier, different calibration break (fixed in v0.0.124) — this is a later regression from
3abfaf60, which shipped in v0.0.122. A companion PR (fix(onboarding): guard get_status so a daemon hiccup cannot freeze the wizard) addresses a frozen-wizard bug that can mask this one: a frozen wizard never reaches the calibration step.Changelog fragment included:
changes/unreleased/fix-calibration-default-profile.md(Bugfixes).