Latest patch + the v2.4.0 major release
Jump to: v2.4.1 (May 2026) · v2.4.0 (April 2026)
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.
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.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.
ui/dialogs/update_dialog.py via QStackedWidget..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..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./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.os.replace + os.execv. Atomic on same filesystem, copy-fallback on cross-device. User CLI flags (--debug, --verbose) preserved through the re-exec.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.
~/.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:
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.
{question: str, options: [2-5 short strings]}. Empty question rejected, <2 options rejected, >5 options clipped to 5.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.
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.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)._auto_learned.md is always eager regardless.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 repoWhittl-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.
Field-driven fixes from real stress-test sessions:
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.core/python_imports.py) shared by runtime install, desktop builder, and APK builder. Added: paho-mqtt, python-Levenshtein, discord.py.sys._MEIPASS guards added at four asset-loading sites in ui/fonts.py, ui/icons.py, main.py, and ui/projects_panel_qt.py.python_standalone_linux/ alongside the Windows one. Linux users get a working bundled Python at install time instead of falling through to system python3.except TypeError fallback was dropping image_data on retry.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.! with multiple ? marks in the body no longer trigger unwanted code regen.~/.whittl/skills/ path takes precedence over both Claude-compatible paths. Your custom rules win.~/.claude/skills/, they'll auto-load alongside Whittl's. Toggle off in Preferences → AI Generation if you want token cost bounded.last_update_check key is no longer written (auto-update is user-initiated, not scheduled).