Skip to content

Auto-fix Rules

Whittl ships with 75+ regex-based auto-fix rules that catch common mistakes AI models make when generating Python code. They run in two places:

  1. During generation — after the AI produces code but before it hits your editor, the fixers scan for known mistakes and correct them silently.
  2. 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.iconsft.Icons in Flet)
  • Qt enum typos (Qt.AlignCenterQt.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:

  1. Run the app.
  2. App crashes with a traceback.
  3. Whittl reads the traceback, identifies the failure line.
  4. AI tries a tool-based edit (edit_code) targeting just the broken line.
  5. Re-run. If it works, done. If not, try again.
  6. Up to 5 rounds total (hard cap, see below).

This path DOES cost tokens, but each round is much cheaper than a full regeneration because it's a surgical edit:

  • Round on Claude Haiku: ~$0.005
  • Round on Sonnet: ~$0.02–$0.05
  • Round on Qwen3-Coder free: $0

Safeguards

Auto-fix has four 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.

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)

  • pyqtSignalSignal (PySide6 convention)
  • pyqtSlotSlot
  • QAction moved from QtWidgets to QtGui (Qt6 change)
  • Qt enum upgrades (Qt.AlignCenterQt.AlignmentFlag.AlignCenter, Qt.HorizontalQt.Orientation.Horizontal, etc.)
  • QVariant conversion 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.iconsft.Icons (mobile API change)
  • ft.colorsft.Colors
  • page.dialog deprecation → 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" vs ft.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_heightpage.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 tail
  • tk.BooleanVarctk.BooleanVar (CTk wraps these)
  • Window appearance mode not set before geometry
  • Missing customtkinter import when ctk. is used
  • Icon assignment before deiconify()

Threading safety (6 rules)

  • Widget access from worker threads (flags for signal-based dispatch)
  • Callback without QTimer.singleShot dispatch from non-Qt thread
  • threading.Thread without daemon=True flag
  • Missing join() on critical threads before shutdown
  • Audio callback inside a QMutex (deadlock pattern)
  • QThread vs threading.Thread use-case picker

File path handling (8 rules)

  • os.path.join vs string concatenation
  • Path traversal protection on AI-generated code (refuses .. paths)
  • Case-sensitive filename handling for Linux compat
  • Temp file cleanup in finally block
  • File handle leak pattern (missing with statement)

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 (funcitonfunction, retrunreturn)
  • 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:

  • Open Whittl's terminal panel
  • Run python -c "from core.autofix_rules import _ALL_RULES; print('\n'.join(r['name'] for r in _ALL_RULES))" (future v2.4 feature)

Or simply watch the [AUTO-FIX] log lines as you generate code — they print which specific rules fired.

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+ in v2.3. Every release pulls new rules out of the auto-learned file and promotes them to hardcoded fast-path fixes.

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 → Enable AI Auto-Fix (uncheck)

This disables post-crash AI iteration. The post-generation static rules always run — they're non-destructive and too valuable to turn off.

Troubleshooting

Auto-fix is stuck on the same error

The oscillation guard should catch this within 3 tries. If it somehow doesn't:

  1. Click Stop in the chat panel.
  2. Read the error yourself and manually edit the code.
  3. 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