Skip to content

fix(settings): restore the default-calibration-profile backfill dropped in 3abfaf60#76

Open
FourthWiz wants to merge 1 commit into
NeuroSkill-com:mainfrom
FourthWiz:fix/calibration-default-profile-backfill
Open

fix(settings): restore the default-calibration-profile backfill dropped in 3abfaf60#76
FourthWiz wants to merge 1 commit into
NeuroSkill-com:mainfrom
FourthWiz:fix/calibration-default-profile-backfill

Conversation

@FourthWiz

Copy link
Copy Markdown

The argument, in one command:

git show 3abfaf60 -- src-tauri/src/setup.rs

This shows a 13-line deletion: an is_empty()from_legacy() backfill, plus an active_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. 3abfaf60 moved calibration ownership from the Tauri client to the daemon and deleted the backfill that used to run in setup.rs at startup. Nothing re-established it daemon-side. The regression is not Vec::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, because setup.rs:370-382 backfilled 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/profiles200 []; /v1/calibration/active-profile200 null; ~/.skill/settings.json has "calibration_profiles": [], key present (not missing — the field has no skip_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/status mock 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 || !isConnected guard at +page.svelte:753 returns 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:

if s.calibration_profiles.is_empty() {
    s.calibration_profiles = vec![CalibrationProfile::from_legacy(&s.calibration)];
}
if s.active_calibration_id.is_empty() {
    s.active_calibration_id = s.calibration_profiles.first().map(|p| p.id.clone()).unwrap_or_default();
}

Both halves matter: only get_active_profile has an .or_else(.first()) fallback; background.rs's auto-start check and auto_start_pending do not, and CALIBRATION_AUTO_START defaults to true — 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 the settings_calibration.rs test fixture (mk_state()) — it was the last production-shaped seed left after 3abfaf60, and it made the existing list_profiles_returns_defaults test 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-writes settings.json as raw JSON text with "calibration_profiles": [] explicitly present (not merely a missing file, which is the only case a Default-seed fix would ever see), and asserts the backfilled profile's id matches active_calibration_id.

Verification:

cargo test -p skill-daemon settings_calibration

→ 3 passed, 0 failed (note: no skill-daemon* crate is in any scripts/test-fast.sh tier, so this needs to be run explicitly — it isn't covered by npm run test:fast).

Acceptance:

  1. Fresh install (missing settings.json) yields exactly one profile.
  2. A settings.json with explicit "calibration_profiles": [] also yields exactly one profile.
  3. active_calibration_id equals that profile's id, and auto_start_pending returns it.
  4. Onboarding calibration starts.
  5. The new raw-JSON test fails if the backfill is removed (verified locally by reverting the fix and re-running).

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

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