← Back to Whittl
WHITTL RELEASE NOTES
v2.6.1, v2.6.0 and v2.5.0, plus the v2.4 series
Jump to:
v2.6.1 (October 2026) ·
v2.6.0 (October 2026) ·
v2.5.0 (September 2026) ·
v2.4.1 (May 2026) ·
v2.4.0 (April 2026)
v2.6.1: Two fixes
October 2026 · Patch release on top of v2.6.0
Two fixes, both found the day v2.6.0 shipped. If you have v2.6.0, Help > Check for Updates installs this.
In the installed version of Whittl, working on a project that has tests could open a second Whittl window, and the AI's progress in the first one would stop for up to a minute at a time. Whittl was starting a copy of itself where it meant to start Python to run your tests. It had done this since the test gate arrived in v2.5.0. Running Whittl from source was not affected.
- Your tests run with Whittl's own Python. The first time the AI works on a project with tests, Whittl installs pytest for them, and the status bar says so.
- A test run that never ran is not a pass. If the tests time out or cannot start, the status bar says the test gate did not run.
- The same mistake is fixed everywhere else Whittl starts Python: the package step of an Android build, and a few places Whittl fell back on when it could not find its own Python.
When the AI answered a question in the chat, for example by listing ideas for your app, and you then asked it to go ahead ("do all of them"), it could not see the answer it had just given and asked what you meant. It now can, on every backend.
v2.6.0: Plugins, and one agent for every model
October 2026 · Major release
Whittl 2.6.0 does two big things. You can now add to Whittl itself: a tool the AI can call, a tab, a readout, a menu item. And every model, on every backend, now works on your project the way the best ones do: reading, editing, running and checking its own work. Around those two, a month of using Whittl on real projects every day turned up a long list of smaller fixes, all listed below.
A plugin is a folder with a small manifest and a Python file. It can add a tool the AI calls while it works, a tab beside Code, a status bar readout, a dialog or a menu item under Edit > Plugins, a skill, or code that runs when a generation starts or ends, a project opens, or you run your app. Whittl's own Assets, Archives, Terminal and Help tabs are plugins built the same way, and so is the new context gauge in the status bar.
- Whittl writes them. File > New Project, pick Plugin, and describe what you want. Test Run checks it and installs it to your plugins folder, switched off.
- Checked before anything runs. A tool's examples are run, and a panel is built first in an invisible copy of Qt, so a broken plugin is held back instead of crashing the window. A hook that hangs is switched off after three seconds instead of freezing Whittl.
- Off until you switch them on, one by one, in Edit > Plugins, which also shows what each plugin adds and why one was switched off.
- Read before you enable. A plugin runs with the same access you have. Its manifest says what it uses and Whittl shows you that, but it is a description, not a sandbox.
- Yours to share. Plugins you write are yours to share or sell under any license. The templates and bundled plugins you start from are MIT No Attribution, so nothing from them needs crediting. Sharing a plugin is zipping its folder.
- Documented. The docs walk from a two-file hello world to your own tab, with a reference for the manifest and everything a panel can use.
Whittl used to decide how much a model could do by which backend it was on. Now every backend gets the same tools, and what a model is given (its tools, its round budget, whether it keeps a session between your messages) depends on what that model can do.
- Step by step. A model that calls tools reads what it needs, makes targeted edits, runs your app and your tests, and fixes what it broke. Files stream into the editor as they are written.
- Scripts for smaller models. A model that cannot call tools reliably writes short scripts that make the same edits through a small, safe API, so smaller and local models can work on a project too. A model that describes a change instead of making it is switched to scripts for that request.
- The AI can look at your app. It opens a desktop app it built invisibly, reads the window, clicks buttons, and can show you a picture of it in the chat.
- Reasoning carries across steps. A thinking model keeps its reasoning between tool calls on all five backends, and old tool results are summarised instead of cut off, so a long session keeps what earlier steps found.
- Stop stops. Every loop ends when you press Stop. A model stuck repeating itself is ended instead of watched, and a turn that keeps reading without learning anything new is stopped.
- Honest about failures. An overloaded or failing provider is retried and named as the cause, and an empty reply is reported as one instead of as a model that could not do the job.
- A file you change in another editor while Whittl is open is taken into the editor instead of being overwritten, and a new file you add joins the project instead of being deleted.
- A turn that fails partway keeps the edits it made, says so, and is never regenerated over them.
- A Stop or an error that cuts an edit short puts back the last version of the file that compiled.
- A message you send while Whittl is working is kept and delivered, along with its pictures, and stays where you said it in the chat.
- A fix that comes back as loose functions is put back by name, and a result that does not parse is never written.
Attach several images to one message (drop them, or paste), see each as a thumbnail before you send, and click any picture in the chat to see it full size. Pictures survive a restart, and they reach the model on every turn and every backend. Models that cannot read images get a description from one that can. The AI can hand you pictures too: a render of the app it built. You can also set your own name and picture for your messages in Preferences > Chat Appearance.
- A cost readout shows what the request in progress has cost so far, cached tokens included.
- A context gauge shows how much of the model's window the request is using.
- An optional Usage tab charts your spending by day and by model.
- On OpenRouter, Whittl chooses the provider for a new session: it refuses providers that admit running a model at reduced precision or cannot hold a full request, and keeps the session on one provider so its prompt cache stays warm.
- DeepSeek's peak and off-peak prices are applied by the clock, Claude's cached input and Gemini's rates are counted correctly, and the chat names the model that actually answered.
- Claude Opus 5.5, Sonnet 5.5 and Fable 5.1 on the Claude backend, and Gemini 3.1 Pro on Gemini.
- New built-in skills, every rule checked on the real libraries before it was written: QPainter drawing, named visual styles, games, tables, files and drag and drop, settings that survive a restart, serial ports, and 3D with Qt3D.
- When the AI fixes its own mistake before finishing, the lesson goes into the auto-learned library, as it already did for auto-fix.
- Claude Code skill folders are read the way Claude Code stores them.
The sections above are the headline. Below is the rest of what changed, grouped by area. Most of it was found by using Whittl on real projects every day and fixing what broke.
The agent loop
- Claude and Gemini now get the full tool set; the Claude backend had been sending 4 of its 20 tools.
- A model that has proven it can call tools is not demoted by one answer in prose, and an empty reply is not counted against the model.
- A tool call a model writes as text is run as the call it names, instead of being pasted into your code.
- A reasoning model that spends its whole output on thinking is retried with less reasoning, and the chat says so; a model that will not stop thinking gets its least reasoning instead of an error.
- A dropped connection, a server error, or a provider error returned inside a normal response is retried a few times; if it persists, the turn ends with your edits kept. A timeout ends the turn the same way on every backend.
- A command the AI runs can no longer outlive its timeout, and neither can anything it started.
- What a turn may do is checked when each tool runs, not only by which tools are shown: a review turn cannot edit.
- The AI can read files outside the project after you allow the folder once. It never writes there and never reads keys or credential files.
- Tests you add after a project's first turn are gated too.
Auto-fix
- One crash starts one auto-fix cycle, and a crash that was fixed no longer rides along with your later messages.
- A crash with no traceback goes to the AI with what to look for; the chat gets one line instead of a paragraph.
- Auto-fix looks at every file an error names, not only main.py.
- The rule pass on Play leaves alone a file that already ran without an error.
- An import inside a try that the app runs without, such as a helper another app provides, is not pip-installed, and a build no longer stops on one.
Chat and editor
- Test files no longer open as tabs, and the tabs you leave open come back when you reopen a project; undo, redo, archive restore and Refresh Files keep them too.
- A reply streams into its own place in the chat: a message you send mid-reply, or a question the AI asks, stays where it was said.
- A choice you answer turns into "You chose: ..." instead of staying clickable.
- The send button shows the Enter key and doubles as Stop.
- The Expand checkbox is gone: your message is always sent exactly as you wrote it.
- The code toolbar switches to icons when the editor is narrow.
- When the model you picked is unavailable, the chat names the one that took over.
Models and providers
- OpenRouter: a provider that cuts tool calls short is left out for the rest of the session, and the Models dialog lists the free models OpenRouter offers today, with a Free filter.
- Gemini reads screenshots with a model Google still serves, thinks at the depth you pick on tool rounds, and is priced at Google's rates.
- Each Claude model reports its real context window, and Opus 5.5's progress notes reach the chat.
- Ollama asks for a 16K window by default and says once when a request fills most of it; a local model that cannot read images is never sent one.
Web and files
- The AI can download files into the project (icons, fonts, data) from a source you name, without being given a URL, counted apart from its research budget.
- Fetching a file that is not a web page, such as an SVG or a JSON file, returns it as it is instead of stripping it to nothing.
- On Windows, grep, head, tail, cat and wc work in the AI's shell commands.
Projects and builds
- A Run can no longer empty a file Whittl only half tracks.
- A renamed project keeps its record of work done outside Whittl.
- Chat pictures are kept beside the project, not inside it, and are removed with it.
- The license agreement and a notices file listing every open-source component now ship with the app.
- The Linux AppImage is built from the same build spec as the Windows app, on Ubuntu 22.04, so it runs on Ubuntu 22.04, Linux Mint 21, Debian 12 and newer.
Interface and first run
- The New Project dialog is two screens: what you are building, then a name and a template.
- The welcome tour ends with your chat name and picture, then where the docs are.
- Preferences gained Chat Appearance and Web Research panels.
- Edit > Plugins is the one place for plugins, and the Help tab was rewritten from the code.
- Windows and Linux: Help → Check for Updates from inside v2.5.0. The in-app updater downloads, verifies the SHA-256, installs and relaunches Whittl.
- Windows on v2.4.1: if Check for Updates says it could not check, download the current installer from the link in your purchase email and run it over your existing install. The v2.4.1 updater cannot verify the download server's certificate chain.
- No data migration needed. Projects, settings, skills, OpenRouter favorites and API keys carry forward unchanged. Plugins you add stay off until you switch them on in Edit > Plugins.
v2.5.0 — Sharper with every cut
September 2026 · Major release
v2.5.0 is about one thing: the AI does not get stuck anymore. When it does not know, it can go and find out. When it makes a mistake, the mistake becomes a rule. And it does not get to say "done" until the tests agree. Three headline features carry that, and a long tail of reliability work found by using Whittl on real projects carries the rest.
The AI can now search the web and read pages while it works. When a model hits an API it does not know, a library quirk, or an error message it has never seen, it looks it up instead of guessing. The default backend is DuckDuckGo, so there is no API key to set up. Searches and page reads appear in the chat as they happen, and a per-session budget keeps a curious model from running away with it. Available in Agent Mode.
Every time an auto-fix resolves a real error, Whittl records what was wrong and what fixed it, with a short code example, and feeds that back to every model on every project. Duplicates are counted instead of repeated, entries that stop being useful are retired, and patterns that keep proving themselves are promoted into Whittl's permanent skill library. The longer you use it, the better every model gets at building Python desktop apps.
If your project has tests, Whittl runs them after the AI says it is finished and before you see the result, and reports anything the change broke in chat with a report the AI can act on. Tests that were already failing before the change do not count against it. You see the state on the status bar, and the AI can no longer skip the step under pressure. Automatic re-prompting inside the same generation is the planned v2.5.x follow-up.
- Web research. The AI can search the web and read pages while it works. No API key needed; searches show in chat as they happen, with a per-session budget. Agent Mode.
- Auto-learned skills. Every real auto-fix becomes a teachable pattern with a code example, fed back to every model. Duplicates are counted, stale entries retire, proven patterns get promoted into the permanent skill library.
- Test gate. If your project has tests, they run after the AI says it is done and before you see the result. Anything the change broke is reported in chat with a fix-ready report; pre-existing failures do not count.
- Auto-fix that finishes. A landed fix re-runs your app on its own and reports honestly: fixed, stopped, or unverified. No more "Testing fix..." forever, and Stop means stop.
- Auto-fix that verifies its claims. A fix for a missing-attribute error is checked against the real API before it is called fixed; if the replacement does not exist, the AI is re-prompted with the actual list.
- Auto-fix rules that understand Python. The pre-launch rule pass works from the language's structure instead of pattern-matching text, so it no longer rewrites valid code such as format templates, scoped imports or comments.
- Edits made outside Whittl are kept. Change a project file in another editor while Whittl is open and Play picks up your version instead of overwriting it.
- Big projects, fast Play. Play on a large project is near-instant after the first run, the first run is far faster, and project switching is quicker. The editor handles very large files without freezing.
- Cheaper auto-fix on huge projects. The AI reads only the code it needs instead of receiving the whole project every round: faster, and a fraction of the cost.
- Long-file surgical edits. Files past a few hundred lines are edited function by function instead of regenerated, at roughly a tenth of the cost; short files get one clean rewrite instead of a dozen partial edits.
- Every model, one engine. All five backends share one streaming, continuation and fallback engine. A long answer that hits a model's output limit is resumed instead of truncated, on every backend.
- Model-aware requests and the Claude 5 family. Whittl adapts each request to what the model supports, so the newest models are never silently downgraded, and a thinking-depth setting works across backends.
- Warm prompt caches on OpenRouter. Replies stay fast and cheap on big projects because Whittl keeps each session on the provider that already holds its cache.
- Review-only mode and deep review. Ask for a review and you get a review, not an unrequested rewrite, including on multi-file projects.
- Glob and multi-edit tools. Pattern-based file discovery and several edits in one call round out the agentic toolset.
- Claude Code skills, translated. Skills written for Claude Code are read from their usual folders and their tool names translated to Whittl's, so the guidance is right for the tool the AI actually has.
- New bundled skills. UI design quality, media playback, charting, plus file-format skills for images, Word, PDF and spreadsheets, and a hardened PySide6 WebEngine section.
- Linux, first-class. A tkinter-complete bundled Python on both platforms, a faster AppImage launch, and in-app builds that produce smaller executables that actually start.
- Project hygiene. Archives live on disk, saves reconcile the folder, build output is never treated as source, renames move the folder cleanly, and the install cache checks the runtime it claims to know.
- Updater hardening. Every connection Whittl makes itself now verifies with its own certificate bundle, which is what fixes the Windows update check going forward.
The twenty items above are the headline. Below is the rest of what changed, grouped by area. Most of it was found by using Whittl on real projects for four months and fixing what broke.
Auto-fix
- Fixes are verified against the real API for missing-attribute errors, and reported as "unverified" rather than "fixed" when nothing could check them.
- A crash-triggered fix re-runs the app automatically; a clean run, a clean exit or a repeated crash closes the cycle with a matching status, so the status bar never sticks.
- Pressing Stop mid-response is reported as stopped, not as an output-limit hit.
- A missing pip package goes to the auto-installer, not to a paid AI round.
- The pre-launch rule pass now works from Python's tokens and syntax tree: an escaped quote is not a string opener, a format template is not an f-string, a function-local import is not a duplicate, a comment is not a stylesheet, and an imported helper is not missing.
- Imports are placed with awareness of comments and strings; a rule that stripped a bare method call was narrowed so it stops reverting correct modern Qt code.
- Concurrent fix triggers are de-duplicated; a stalled modify reports honestly instead of falling through to a full regeneration; a no-progress loop counts successes, not attempts, so a stream of failing edits cannot run forever.
- A misclassified question can no longer force an implementation: the action is gated first, the classifier sharpened second.
- Error logging that had been silently dead since a bad guarded import is back.
The agentic tool loop
- Bounded auto-continue when a session hits the work cap, so a multi-step edit finishes instead of dying mid-file.
- Index-only context for large projects: the AI reads files on demand, source is never truncated, and an incremental-edit prompt keeps it working file by file.
- Truncation auto-continue on every backend, including Gemini and Ollama; the Ollama path is headroom-guarded so it cannot refill a full context window.
- Input budgets scale to the active model's context window; context scope is decided by budget, not file count, which ended a read-thrash on cached projects.
- read_code accepts offset and limit, matching how Claude Code reads.
- Screenshots survive multi-round tool-use generations; a wire-format leak between backends that broke later Claude rounds is fixed with copy-on-write and a sanitizer.
- Fresh generations honour a "make it modular" request; a structural refactor routes through the incremental path with a bundled playbook.
- Surgical edits on very large files are routed correctly; whole-file rewrites are preferred for short ones.
- The mid-generation question tool and the review-only intent are recognised from plain rephrasings, with a single bounded rule rather than a keyword pile.
Backends and models
- Claude 5 models and the Fable tier; sampling and thinking parameters are chosen per model, so a model that rejects an old parameter is no longer silently downgraded.
- A cross-backend thinking-depth dropdown, wired on every backend including OpenRouter.
- OpenRouter provider pinning keeps the prompt cache warm and re-routes fresh on a model switch.
- Reasoning is disabled on the tool and streaming paths where it only burned the output budget before a tool call could land.
- DeepSeek gains vision; every client tier is persistable and the connected chip names the model actually in use.
- One usage normaliser behind the cost meter, so cached tokens and cost are reported the same way from every backend.
- Every backend implements one typed request contract, and the probes that could silently drop a safety gate are gone.
Performance
- The Play-path rule pass and the requirements scan are memoised in memory and on disk, invalidated by any rule change or new release. A 67-file project went from a sixteen-second freeze per Play to well under a second after the first.
- Project switching loads the project once instead of four times; large files defer their lint and fold passes; the syntax highlighter uses one pattern per word class instead of a hundred.
- Windows stderr noise from a clean run is no longer attached to the next edit as a false crash.
Projects, files and builds
- Archives are stored on disk; saving reconciles the folder so removed files do not come back; the quick test runs against the tracked set, never stale leftovers.
- A dotted import of a new local package is not pip-installed; the auto-detector stops installing junk packages during refactors.
- PyInstaller output inside a project is never treated as source; a file named after a Windows reserved device no longer aborts the reconcile.
- Renaming a project moves the folder first and refuses cleanly if it is in use, instead of leaving an empty twin behind.
- The install cache checks that the runtime it remembers is really there, so a re-provisioned Python does not yield three crash-and-install cycles.
- Built executables: cffi-based audio libraries load in frozen apps, Linux builds bundle Tk correctly and start, and executables are stripped and smaller.
- Import guards for archives and data folders are scoped so a normal app's data folder survives import.
Platform and first run
- Both platforms provision the same tkinter-complete Python 3.12.13; a stale bundled runtime is migrated automatically.
- The first-run setup dialog shows real phase progress, in the app's own colours; the APK dialogs use the palette too.
- Provisioning downloads are bounded by timeouts, and every HTTPS connection Whittl opens verifies with its own certificate bundle.
Interface
- The favourites star fills when favourited; chat and prompt text follow the house style with no emoji or em-dashes; the status bar reports what the app is actually doing.
- The Help tab and the docs site carry the same guidance, including the upgrade page.
- Linux: Help → Check for Updates from inside v2.4.x. The in-app updater downloads, verifies the SHA-256, and replaces the AppImage in place.
- Windows, if Check for Updates works: same as above. The installer runs silently over the existing install and relaunches Whittl.
- Windows, if Check for Updates says it could not check: that is not your connection. The download server moved to a newer certificate chain that the v2.4.1 updater's built-in trust store cannot verify on some Windows machines, and nothing in v2.4.1 can be changed now. Download the v2.5.0 installer from the link in your purchase email and run it over your existing install. From v2.5.0 on, the updater carries its own certificate bundle, so in-app updates work again.
- No data migration needed. Projects, settings, skills, OpenRouter favorites and API keys all carry forward unchanged.
v2.4.1 — Auto-fix Stale Rule Patch
May 2026 · Patch release on top of v2.4.0
v2.4.1 is a fast follow-up patching a specific failure mode that surfaced in the first hours of post-release usage: an auto-fix rule based on outdated PySide6 5.x knowledge was reverting AI-generated imports on every Run, creating infinite fix-and-revert spirals on any project using PySide6.QtWebEngineCore classes. Plus a defensive improvement to the file-state merge logic that hardens against a class of similar bugs.
Field repro on a browser project running on PySide6 6.11.0: every press of the Run button produced this log signature, regardless of which model generated the project:
[AUTO-FIX] Moved QWebEnginePage from PySide6.QtWebEngineCore to PySide6.QtWebEngineWidgets
[RUN] Live error detected: cannot import name 'QWebEnginePage' from 'PySide6.QtWebEngineWidgets'
Did you mean: 'QtWebEngineCore'?
The AI correctly imported QWebEnginePage from PySide6.QtWebEngineCore — its real location in PySide6 6.0 and later. Whittl's add_missing_imports autofix looked up QWebEnginePage in its internal class-to-module mapping, found it pointing at QtWebEngineWidgets (the PySide6 5.x location), and "moved" the import — silently breaking it. The AI's next auto-fix round restored the correct import; Whittl's autofix reverted it again on the next Run. The user could not break out of the loop without manually editing the file outside of Whittl.
Root cause: core/autofix_rules.py had four WebEngine classes hardcoded to QtWebEngineWidgets. In PySide6 6.0+ only QWebEngineView stayed in QtWebEngineWidgets — everything else (Page, Profile, Settings, History, CookieStore, Script, DownloadRequest, plus 16 more) moved to QtWebEngineCore. The rule was written against PySide6 5.x and never updated when the library reshuffled its module layout. Fix: corrected the mapping for the four affected classes and added 19 more QWebEngine* classes to QtWebEngineCore. Pinned by the 321 existing autofix tests.
Broader pattern worth flagging: frontier models often know newer APIs than Whittl's static rules do. The usual assumption that "Whittl's deterministic rules know better than the probabilistic AI" inverts in cases like this. A v2.5 candidate is being tracked for stale-rule detection.
Discovered during investigation of the WebEngine spiral above. The merge-back logic in preview_panel.get_all_files() unconditionally trusted the editor's get_code() output over the in-memory file dictionary on every read. Under tight timing (AI tool edit → signal dispatch → set_code() → Qt deferred layout pending → user clicks Run), the editor's get_code() could return a previous-frame value that overwrote the AI's fresh content before Run wrote files to disk.
Fix: set_code() now calls document().setModified(False) after each programmatic write. get_all_files() only merges editor content back into the file dict when document().isModified() is true (the user typed since the last set_code()). Otherwise it trusts the file dict, which already holds the latest AI / autofix output. User typing still wins; programmatic writes no longer get clobbered by stale display state.
This wasn't the cause of the WebEngine spiral — that was the autofix rule above — but it's a real defensive improvement.
- Click Help → Check for Updates from inside v2.4.0. The in-app updater (new in v2.4.0) handles the rest: downloads, SHA-256 verifies, prompts to restart, installs silently, relaunches. No browser hop.
- If you generated PySide6 projects with WebEngine classes during v2.4.0 and your project got stuck in the autofix-revert loop, you can either restore from
archives/ in your project folder (Whittl auto-snapshotted before each generation) or ask the AI in chat to "fix the WebEngine imports for PySide6 6.x" — the new (correct) autofix rule no longer interferes.
- No data migration needed. Projects, settings, skills, OpenRouter favorites, and API keys all carry forward unchanged.
v2.4.0 — The Whittl Layer + In-App Updates
April 2026 · Major release
v2.3 and earlier dumped users at a browser download URL and expected them to find the installer, run it, click past SmartScreen, and restart manually. v2.4 replaces that with a proper in-app "Download → Restart to install" flow with SHA-256 verification and silent install handoff.
What changed
- Three-state dialog FSM — Available (Download button) → Downloading (progress bar + Cancel) → Ready (Restart to Install). All transitions handled in
ui/dialogs/update_dialog.py via QStackedWidget.
- Resumable downloads. Interrupted downloads leave a
.part file on disk. Next attempt sends an HTTP Range: bytes=N- header so only remaining bytes come over the wire. Most failures at 80% download finish in <2s on retry instead of restarting the whole thing.
- SHA-256 verification (fail-closed). Per-platform
.sha256 sidecars published alongside each installer. The downloader verifies before handing off to the silent installer. Missing sidecar now triggers an explicit "proceed without verification?" prompt with No as the default — no more silent skipping of integrity checks.
- Windows silent-install handoff via Inno Setup with
/SILENT /NOCANCEL /NOICONS /RELAUNCH. Combined with CloseApplications=yes and a runtime mutex (ShipIt-Whittl-Mutex), Whittl exits cleanly, the installer replaces files without sharing-violation races, and the new Whittl launches automatically.
- Linux AppImage in-place replace via
os.replace + os.execv. Atomic on same filesystem, copy-fallback on cross-device. User CLI flags (--debug, --verbose) preserved through the re-exec.
- Pending-update persistence. If you click "Install later" or close Whittl before applying, the verified installer stays on disk and re-opens the dialog directly in STATE_READY on next launch — no re-download.
This is the first Whittl release that genuinely auto-updates itself. Subsequent versions ship through the same flow.
Every other AI coding tool is a chat window over someone else's model. v2.4 formalizes what Whittl actually is: the knowledge layer that sits between you and the AI — 75+ auto-fix rules, a curated skills library, oscillation guards, hard round caps, tool executors, and validators. The model writes the code; Whittl makes the model better at building Python desktop apps.
This is positioning more than code — the components all existed in v2.3 — but v2.4 ships a versioned whittl_layer.json manifest pinning the layer's components and their versions. Foundation for v2.5's Whittl Commons: a download channel for community-sourced rule bundles that update independently of the binary.
- 75+ auto-fix rules covering Qt scoped enums, Flet hallucinations, PyInstaller resource paths, deprecated APIs, hallucinated class names, and dozens more
- Skill library auto-injected by framework — PySide6 patterns for desktop apps, Flet mobile rules for Android builds, threading rules for both
- Oscillation guard + 5-round cap prevent runaway autofix loops
- Validators catch bad code before it reaches your project — duplicate marker handling, file truncation detection, structural diff guards
- Bundled-vs-user skill split — vetted defaults ship with Whittl, user customizations layered on top at
~/.whittl/skills/
The v2.3 Help tab was a plain-text QTextEdit skeleton with 5 sparse sections nobody read. v2.4 rewrites it as a proper QTextBrowser with:
- Top bar with "Open Full Docs" button that launches lyndeneftoda.com/docs/ in your default browser
- Rich HTML content — 6-step quick start, "What Whittl can do" with inline doc cross-links, practical tips by scenario, ~20 keyboard shortcuts, troubleshooting ladder, "where to go next" pointers
- Theme-aware styling matching the dark and light brand palettes
Plus a brand-new full documentation site at lyndeneftoda.com/docs/ — from zero pages at v2.3 ship to 45 pages totalling ~62K words. Covers Getting Started (3 pages), Features (15), Backends (6), Workflows (9), Reference (8), and Troubleshooting (3). Built with MkDocs + Material, custom-styled to match the retro brand palette (VT323 + Press Start 2P, navy/cream/tan/copper).
The AI can now ask you structured design questions mid-generation when it has to guess at a decision — SQLite vs JSON, bottom tabs vs nav drawer, local vs cloud. Better than guessing wrong and regenerating.
- Tool definition:
{question: str, options: [2-5 short strings]}. Empty question rejected, <2 options rejected, >5 options clipped to 5.
- Per-session cap of 3 questions. Tool drops out of the schema once exhausted so the AI doesn't keep trying.
- Schema-gated. Disabled in Agent Mode (unbounded loops shouldn't block on user input) and during auto-fix rounds.
- Calibrated system prompt. Explicit "use only for design decisions with no safe default, max 3, not for stylistic preferences" guidance. Overuse kills the UX; calibration matters.
Skills can now declare YAML frontmatter that gates injection on the active project's actual imports and framework target. Skills without frontmatter keep today's always-inject behavior — no migration needed for existing libraries.
- Trigger-based injection. A skill declares
triggers: imports: [cv2, opencv-python] and only injects when those imports are present in the project files. The RedLight-tuned 8KB cv2-patterns skill no longer injects on every PySide6 generation.
- Lazy loading. Skills with frontmatter default to lazy: surface as a skill index in the system prompt (~125 tokens), AI calls
load_skill(name) tool when it needs the body. Token saving: a "fix this typo" request injects the index only (~125 tokens) instead of every always-on skill (~2000 tokens).
- Eager vs lazy split. Skills without frontmatter default to eager (legacy semantics).
_auto_learned.md is always eager regardless.
- load_skill tool added to the tool executor. Body strips frontmatter so the AI sees just the rules. Oversized skills capped at 32 KB.
SkillManager now discovers skills from three paths in priority order:
~/.whittl/skills/ — Whittl's own (wins on dedupe collisions)
~/.claude/skills/ — user-global, authored for Claude Code
./.claude/skills/ — project-local, checked into the repo
Whittl-wins dedupe protects vetted defaults — a random ~/.claude/skills/flet-mobile.md can't silently override Whittl's shipped guidance. Source-aware log line: [SKILLS] Injected 4200 chars (2800 whittl, 1400 claude-user) makes it visible when foreign skills are inflating the prompt. New Preferences toggle to disable Claude paths entirely if you want token cost bounded.
For projects with 500+ line files, full regen costs $0.05+ per turn and burns 30-60 seconds. v2.4 adds a third edit tier between surgical-diff and full-regen: per-function rewriter. The AI specifies {function_name, replacement_body}; Whittl uses AST to locate the function, replace the body, and preserve indentation across the file.
- ~10x cost savings on small fixes in big files. A typo fix in a 760-line file goes from $0.10 full regen to $0.01 surgical edit.
- AST-based location. The AI doesn't need to count lines or echo unchanged code — just name the function.
- Indent preservation. Pinned across class methods, nested defs, and module-level functions via 11 regression tests.
- Schema-gated to tier-S models on first turn. Tier-A and below get diff-only edits to avoid the failure modes weaker models hit when they try to surgically edit.
Field-driven fixes from real stress-test sessions:
- Auto-pip handles Qt Essentials/Addons split.
from PySide6.QtWebEngineWidgets import X now auto-installs PySide6-Addons on first run via a new SUBMODULE_TO_EXTRAS mapping covering 28 PySide6/PyQt6/PyQt5 addon submodules.
- IMPORT_TO_PACKAGE consolidation. Three drifted package-name dicts merged into one source of truth (
core/python_imports.py) shared by runtime install, desktop builder, and APK builder. Added: paho-mqtt, python-Levenshtein, discord.py.
- Linux AppImage assets bundle correctly. Fonts, icons, avatars, templates, bundled skills, and the runtime Python now all ship inside the AppImage.
sys._MEIPASS guards added at four asset-loading sites in ui/fonts.py, ui/icons.py, main.py, and ui/projects_panel_qt.py.
- Dedicated Linux Python bundle. Whittl's build pipeline now ships a Linux-native
python_standalone_linux/ alongside the Windows one. Linux users get a working bundled Python at install time instead of falling through to system python3.
- SHA-256 fail-closed UI. Earlier silent-skip-on-missing-sidecar was a security smell. Now requires explicit user opt-in to proceed without verification.
- Tool-use loop preserves screenshots across multi-round generations on OpenRouter. Earlier
except TypeError fallback was dropping image_data on retry.
- Stop button works during auto-fix loops. Now persists through queued rounds instead of flipping to Generate between them.
- APK builder stdlib detection.
posixpath and other stdlib modules no longer trigger spurious "pip install failed" warnings on APK builds. Uses sys.stdlib_module_names instead of a hardcoded list.
- Multi-question clarification heuristic. Responses ending in
! with multiple ? marks in the body no longer trigger unwanted code regen.
- edit_function indent preservation. Class methods no longer get dedented when the AI rewrites them.
- First time auto-update. Click Help → Check for Updates from v2.3. You'll see the new dialog. Click Download, watch the progress bar, click Restart to Install. No browser hop, no manual install. v2.4 will be the LAST manual upgrade for you — subsequent updates ship through this flow.
- No data migration needed. Projects, settings, skills, OpenRouter favorites, and Claude API keys all carry forward unchanged.
- Skills you've taught Whittl still work. The existing
~/.whittl/skills/ path takes precedence over both Claude-compatible paths. Your custom rules win.
- Claude-compatible paths default ON. If you have skills at
~/.claude/skills/, they'll auto-load alongside Whittl's. Toggle off in Preferences → AI Generation if you want token cost bounded.
- Settings file is unchanged. No migration steps required. The
last_update_check key is no longer written (auto-update is user-initiated, not scheduled).