Repository navigation
feat(welcome): never block setup, recognise what already works - #109
Merged
Merged
Conversation
IsFirstRun now opens the wizard only when nothing is usable: no key for any provider, no signed-in CLI, no saved provider that can run. Otherwise setup is marked complete silently and the log says why. Save and Reset to Defaults no longer move completed_setup, and the settings load outcome is logged once the logger is up.
Two screens: what KeyLint does, then which AI. The CLI is a normal option with a loading state, what already works is preselected, a stored key is recognised, and Set up later is always available.
A missing settings file now always opens the (non-blocking) wizard, so env keys or a CLI are not adopted unasked. An unknown saved provider moves to what works. Reset to Defaults keeps the active provider so the surviving keys still match it.
setKey now rejects on a keyring error instead of swallowing it. The wizard keeps a selection once a key is being typed or replaced and can go back to the saved key. Shortcut formatting is shared.
Reset now asks inline, naming what goes back to defaults and what is kept (API keys, the provider in use, completed setup). Cancel and Escape leave everything untouched; focus starts on Cancel.
This was referenced Oct 10, 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.
Why
After going back from a dev-channel build to v4.5.0-beta, the setup wizard opened for the owner and would not let him past the API key field. At that point the Claude Code CLI was signed in and an Anthropic key was already in the keyring. His words: "das ist so ne art von bevormundung die jetzt nicht so sehr spaß macht".
The product rule now: no flow ever blocks the user, and configuration that already exists is recognised and never asked for again.
Root cause: what made
completed_setupfalseInvestigated for a bounded time. The file was read (its
log_level: debugtook effect) and the keys survived, yetcompleted_setupwasfalse.Ruled out:
NewServicereturns the error andmain.goexits withlog.Fatalf, so there is no window and no wizard.completed_setupalso precedeslog_levelin every struct version.internal/features/updateronly reads settings. The NSIS uninstaller removes%APPDATA%\KeyLint.exe(WebView2 data), not%APPDATA%\KeyLint.Settingsis the generated class and round-trips whole.Paths that do write
completed_setup=false:Save(Default()). That writescompleted_setup:false,active_provider:openaiandlog_level:off, and leaves the keyring alone, so the keys survive. If the log level was then set back to debug, you get exactly the observed file. The guard runs only on app start, so the wizard first appears at the next launch, which here was the downgrade. Most likely.Get.wails.service.tsloadSettingsfalls back toBROWSER_MODE_DEFAULTS(completed_setup:false), and the settings page saves the whole object. Possible, but I found no concrete reason forGetto fail in the real app.To tell them apart in the affected
settings.json:"active_provider":"openai"with"models":nulland"app_presets":nullmeans Reset."claude"with{}and[]means the fallback save.Fixed here:
Save(and therefore Reset to Defaults) no longer touchescompleted_setup. Only the welcome service moves it.loadSettingsfallback is unchanged. With this PR it can no longer send anyone back to the wizard, but it can still overwrite other settings with defaults. That is worth a follow-up issue.What changed
Backend:
welcome.Service.IsFirstRuncompleted_setup=false(the reported case): returns true only ifcompleted_setupis false and nothing is usable:AWS_SECRET_ACCESS_KEYdeliberately doesn't count);settings.Update. If the saved provider is a key provider with no key, empty, or unknown to this build, the active provider moves to what works, in the same order the wizard preselects: signed-in CLI, then a provider with a stored key. An explicit CLI, Ollama or Bedrock choice is never switched (the settings page explains Bedrock).welcome: showing the setup wizard, nothing usable is configuredwith the active provider, key sources and CLI state;welcome: setup already usable, skipping the wizardwith the reason, any switch, key sources and CLI state.Backend: start-up diagnostics
settings.LoadOutcomerecords whatNewServicefound (file found or not, path,completed_setupas loaded).main.gologs it right afterlogger.Init:settings: loaded at start-up …orsettings: no file at start-up, using defaults. Those lines used to be emitted before the logger existed and were lost.Behaviour matrix (
completed_setup=false)With
completed_setup=truethere is never a wizard and nothing is asked.Wizard (
welcome-wizard.component.ts)shortcut_fix, with one press animation that respects reduced-motion.core/providers.ts, which the settings page now uses too.WailsService.setKeyno longer swallows backend errors, so a keyring refusal shows up in the wizard. It also fixes the settings page's own "Failed to save key" path, which could never fire before.finish()usesSetActiveProvider(one field) instead of saving a wholeSettingsobject.Tests
internal/features/welcome: a 16-case decision matrix covering nothing usable, key only, CLI only, probe timeout with and without a key, an explicit CLI or Ollama choice, the completed flag, and Bedrock. Also: the bounded probe wait, silent completion sticking, andCompleteSetup.internal/features/settings:Saveand Reset leavecompleted_setupalone;LoadOutcome.welcome-wizard.component.spec.ts(22 tests, DOM-first). Covers: CLI preselected with no key field; the "checking" state; a late probe not moving the user's choice; nothing preselected when nothing works; a stored key lets you through; the env-key note; replacing a key; a typed key saved; a keyring refusal, including on replace; keep the saved key; the selection held while replacing; Ollama; Check again; Set up later; a save error; no whole-object save.e2e/setup-wizard.spec.ts, using the extendedfake-backend.ts, which now also answersIsFirstRun/CompleteSetupwhen a spec setsfirstRun. Covers: new user plus key, signed-in CLI, stored key, Set up later.npm test: 333 passed.go test -race ./internal/...: ok.go build: ok.silent-fix.spec.ts, which is skipped by design.Review (Opus,
review-pr), fixed in the last two commitsSavenow keepscompleted_setuplike the real one. A stale binding comment, gofmt, theKEY_PROVIDERSreuse, shared shortcut formatting, and "Hold Ctrl and press G twice" wording are also done.probeCLIis not unit-tested (it uses the existing cached settings probe).Manual test on Windows
%APPDATA%\KeyLint\settings.jsonset"completed_setup": falseand"log_level": "info", and keep an Anthropic key stored in the app. Start KeyLint.debug.loghassettings: loaded at start-up … completed_setup=false, thenwelcome: setup already usable, skipping the wizard reason="…"andkeys="openai=none claude=keyring".completed_setup: true.claude-code.%APPDATA%\KeyLint\settings.jsonand start. Expected: the wizard opens at once, without waiting on the CLI. If you have a key or a signed-in CLI, screen 2 has it preselected, so it is one click to finish. Put the file back afterwards.completed_setupto false and start.