Auto-fix Rules¶
Whittl ships with 75+ auto-fix rules that catch common mistakes AI models make when generating Python code. They run in two places:
- During generation — after the AI produces code but before it hits your editor, the fixers scan for known mistakes and correct them silently.
- After a crash — if the generated app fails to run, the AI iterates on fixes through a tool-use loop with hard safeguards.
Both paths compound. The static rules catch 80% of common mistakes instantly (cost: zero extra tokens). The iterating auto-fix handles the novel or complex failures.
When auto-fix runs¶
Post-generation (always on)¶
Every time the AI generates or modifies code, auto-fix runs as the final step before the code is written to disk. This catches:
- Trailing whitespace and missing end-of-file newlines
- Imports that hallucinated (QApplication from PySide6.QtWidgets vs QtCore)
- Deprecated APIs (e.g.,
ft.icons→ft.Iconsin Flet) - Qt enum typos (
Qt.AlignCenter→Qt.AlignmentFlag.AlignCenter) - SQLite parameter tuple mistakes (missing trailing comma)
- Duplicate
### FILE:markers in multi-file output
Zero token cost. Zero user intervention. Just silent correction.
Post-crash iteration (opt-in AI-powered)¶
If the generated app fails to run, Whittl offers to iterate via AI auto-fix:
- Run the app.
- App crashes with a traceback.
- Whittl reads the traceback, identifies the failure line, and, if the cause is just a missing pip package, installs it and re-runs without spending an AI round.
- Otherwise the AI makes a targeted edit (
edit_codeoredit_function) aimed at the failure. - Whittl re-runs the app itself (v2.5.0; earlier versions asked you to press Run). A clean run closes the cycle with "Error fixed!"; a new error starts the next round; the same error again stops the cycle with "Auto-fix stopped".
- Up to 5 rounds total (hard cap, see below).
Where it can, Whittl checks the fix before believing it. A fix for a missing-attribute error is verified against the real API: if the replacement attribute does not exist either, the AI is re-prompted with the actual list of attributes rather than allowed to declare success. When nothing can check a fix, the chat says so: "Auto-fix applied a change - re-running app", not "fixed".
This path DOES cost tokens, but each round is much cheaper than a full regeneration because it is a targeted edit:
- Round on Claude Haiku: ~$0.005
- Round on Sonnet: ~$0.02–$0.05
- Round on Qwen3-Coder free: $0
On a large project the auto-fix uses index-only context, reading only the code it needs. On one field project that took a round from 364,000 input tokens to about 15,000.
Safeguards¶
Auto-fix has five safeguards that protect you from cost-blowout scenarios:
1. Hard round cap (5)¶
No auto-fix cycle ever runs more than 5 rounds. Period. A competent fix lands in 1–3 rounds; more than 5 means the model is flailing. The cap fires a clear message:
Auto-fix stopped after 5 rounds without resolving the error. A competent fix usually lands in 1–3 rounds; more than this means the model is flailing. Try editing the code directly, rephrasing the request, or switching to a stronger model (Claude Opus/Sonnet, GPT-5, or Gemini 2.5 Pro).
2. Oscillation guard¶
If the same error fingerprint (error type + file + line) trips 3+ times across a sliding 6-entry window, the cycle aborts. This catches A→B→A→B→A loops where the AI alternates between two wrong fixes.
3. Read-only bailout¶
If the AI spends 3+ consecutive rounds only reading code without editing, the cycle aborts. Prevents infinite "thinking" loops where the model repeatedly examines code without committing to a fix.
4. Stop button¶
The Stop button in the chat panel persists across auto-fix rounds. Click once to cancel the queued round. Works even mid-API-call, and a response cut off by Stop is reported as stopped, never as an output-limit hit.
5. Same-error guard¶
If the re-run after a fix crashes with the identical error, the cycle ends rather than buying another round. The chat says which error came back so you can take over.
Rule categories¶
Auto-fix rules cluster into nine categories. Each category has a "what it catches" and "when it fires" shape:
Python syntax hygiene (13 rules)¶
- Trailing whitespace removal
- Missing end-of-file newline
- Trailing comma in import statements
- Tab-vs-space mixing
- Missing
# -*- coding: utf-8 -*-when non-ASCII chars present - Missing
__future__imports where needed for older Python - F-string f-prefix recovery (when AI writes
"Hello {name}"without f-prefix) - Duplicate import statement dedup
- Unreachable code elimination after return
PySide6 / PyQt patterns (12 rules)¶
pyqtSignal→Signal(PySide6 convention)pyqtSlot→SlotQActionmoved from QtWidgets to QtGui (Qt6 change)- Qt enum upgrades (
Qt.AlignCenter→Qt.AlignmentFlag.AlignCenter,Qt.Horizontal→Qt.Orientation.Horizontal, etc.) QVariantconversion methods stripped (not needed in Python)QApplication.quit()outside signal handler- Missing
app.exec()at module tail - Reserve missing imports (QLineEdit, QScrollArea, QScrollBar, and the ~30 most-commonly-missed Qt widgets)
Flet patterns (13 rules)¶
ft.icons→ft.Icons(mobile API change)ft.colors→ft.Colorspage.dialogdeprecation →page.open()/page.close()padding=removed from Column/Row (Flet requires Container for padding)- Tab content= parameter removal (Tabs are label-only in current Flet)
- String
"center"vsft.MainAxisAlignment.CENTER(string alignments auto-upgrade) - Hallucinated control names (ft.FloatingButton → ft.FloatingActionButton, etc.)
- Missing
ft.app(target=...)at module tail - Deprecated
ft.window_width/ft.window_height→page.window.width/page.window.height
SQLite patterns (4 rules)¶
- Single-parameter tuple missing trailing comma (
execute("SELECT ...", (id))→execute("SELECT ...", (id,))) - Cross-variation detection for multiline execute calls
- Triple-quoted SQL + separate params variable
- Named parameter dict style validation
Import fixing (8 rules)¶
- Missing imports detected via AST (vs. regex-based in previous versions; catches nested and lazy imports)
- Common Qt widget imports auto-added when used but not imported
- Local module detection (don't pip-install a folder that exists locally)
- Python stdlib vs pip package disambiguation
- Package name vs module name mismatch (PIL → pillow, cv2 → opencv-python, yaml → pyyaml)
CustomTkinter patterns (5 rules)¶
ctk.mainloop()missing at tailtk.BooleanVar→ctk.BooleanVar(CTk wraps these)- Window appearance mode not set before geometry
- Missing
customtkinterimport whenctk.is used - Icon assignment before
deiconify()
Threading safety (6 rules)¶
- Widget access from worker threads (flags for signal-based dispatch)
- Callback without
QTimer.singleShotdispatch from non-Qt thread threading.Threadwithoutdaemon=Trueflag- Missing
join()on critical threads before shutdown - Audio callback inside a
QMutex(deadlock pattern) QThreadvsthreading.Threaduse-case picker
File path handling (8 rules)¶
os.path.joinvs string concatenation- Path traversal protection on AI-generated code (refuses
..paths) - Case-sensitive filename handling for Linux compat
- Temp file cleanup in
finallyblock - File handle leak pattern (missing
withstatement)
Miscellaneous (6+ rules)¶
- JSON serialization of non-serializable objects
- Unclosed string literals at EOL
- Comment typo corrections ("pyqt5" → "PySide6", "Pthon" → "Python")
- Common typo fixes in keywords (
funciton→function,retrun→return) - Hallucinated API call normalizations
Viewing the full rule list¶
The canonical list lives in core/autofix_rules.py in the Whittl installation. For a human-readable view:
- Watch the
[AUTO-FIX]and[RUN AUTO-FIX]log lines as you generate and run code. They print which specific rule fired on which file.
Since v2.5.0 the rules that edit source work from Python's tokens and syntax tree rather than from regular expressions over raw text. That is what stopped a family of field bugs where a rule rewrote valid code: a format template turned into an f-string, a function-local import removed as a "duplicate", a word inside a comment "corrected". If you ever see a [RUN AUTO-FIX] line touch a file the AI just edited and the same error come back, that is the pattern to report.
How new rules get added¶
Whittl's _auto_learned.md file captures patterns from every successful auto-fix. When you or the dev (that's Lynden) notices a pattern that fires repeatedly, it graduates from "auto-learned via skill injection" to a proper regex rule in core/autofix_rules.py.
This is how the library grew from 30+ rules in v2.1 to 75+ today. Since v2.5.0 the first step of that pipeline is automatic: patterns that reach the promotion threshold are moved into the matching curated skill at startup (see Auto-learned skills). Graduating a pattern from a skill into a deterministic rule is still a release-time decision.
Disabling auto-fix¶
If you're debugging a generation issue and want to see the raw AI output without auto-fix intervention:
Edit → Preferences → AI Generation → AI Auto-Fix Errors (uncheck)
That switch covers both halves. The AI stops iterating after a crash, and the pattern rules stop being applied to your files when you press Test Run. What still happens is the pass over freshly generated code on its way into the editor. The distinction matters: with the toggle off, nothing rewrites a file you already have.
Troubleshooting¶
Auto-fix is stuck on the same error
The oscillation guard should catch this within 3 tries. If it somehow doesn't:
- Click Stop in the chat panel.
- Read the error yourself and manually edit the code.
- Report the specific error pattern so a rule can be added to prevent future cases.
Auto-fix made my code wrong
Rare but possible on edge cases. Use:
- Edit → Undo (Ctrl+Z) to revert the last auto-fix
- History panel to roll back to a previous generation
- Uncheck Enable AI Auto-Fix temporarily
Then re-generate or edit manually.
Auto-fix is making generation slow
Post-generation auto-fix adds 50–200ms per generation. Post-crash iteration is what adds real time (minutes, not seconds). If you're seeing long waits:
- Watch the
[AUTO-FIX]log lines to see if post-crash iteration is running - Check the round counter in the status bar
- Click Stop if the cycle is clearly not converging
What's next¶
- Skills System — the markdown-based complement to hardcoded rules
- Agent Mode — how auto-fix interacts with extended tool loops
- Debugging a Crash — when auto-fix doesn't solve it, what to do manually