Skip to content

fix(ai): route /fix-code to the fence fixer instead of the formatter - #314

Merged
petertzy merged 1 commit into
petertzy:mainfrom
harsh-thakkar7:fix/fix-code-slash-dispatch
Oct 2, 2026
Merged

petertzy merged 1 commit into
petertzy:mainfrom
harsh-thakkar7:fix/fix-code-slash-dispatch

Conversation

@harsh-thakkar7

Copy link
Copy Markdown
Contributor

Summary

/fix-code ran the wrong tool. Instead of fixing code fences it applied the
plain Markdown formatter, so an unbalanced fence stayed unbalanced.

Root cause

build_ai_automation_fallback() dispatches on keywords in the prompt. The
"Format and Fix Code Blocks" template ships this prompt:

Format Markdown code fences and fix common fence syntax issues.

and the code-block branch matched on:

("format code", "code block", "correct syntax", "fix code")

None of those substrings appear in the shipped prompt. "format code" is not
in "format markdown code", and the prompt says "code fences" and "fence
syntax", not "code block". So it fell through to the generic formatter branch
below.

This is invisible to the existing test because tests/test_ai_automation_logic.py
passes the literal string "/fix-code", which the slash-command table maps to
"format code blocks and correct syntax" — that does match. The real UI sends
something else.

The real UI sends the template prompt, not the slash name

useSlashCommands.ts looks each template up by id and submits template.prompt:

const TEMPLATE_ID_BY_COMMAND = { ..., "fix-code": "fix_code_blocks" };
const prompt = templates.find((t) => t.id === templateId)?.prompt;
// ...
const message = await executeAIPrompt(command.prompt, text);

So /fix-code always arrived at the backend as the template's wording — the one
string that matched no keyword. Confirmed by dispatching every shipped
template prompt:

template before after
format_selection format_rules format_rules
generate_toc generate_toc generate_toc
generate_summary generate_summary generate_summary
fix_code_blocks format_rules fix_code_blocks

That table is now asserted by a test, so a template prompt that stops matching
its own handler fails CI.

Effect

For an unbalanced fence:

selection: ```python\nprint(1)
before:    reason=format_rules   content=```python\nprint(1)      <- unchanged
after:     reason=fix_code_blocks content=```python\nprint(1)\n```  <- closed

The formatter cannot close a fence, so the command reported success and changed
nothing. It also skipped the branch's own guard, so /fix-code with no
selection silently reformatted the whole document instead of asking the user to
select the block.

Aggravates the fence-corruption problem in the sibling PR: with the wrong handler
selected, a code selection was dragged through the formatter that rewrites
shebangs.

Changes

backend/ai_logic.py — two keywords added to the code-block branch:

"code fence",
"fence syntax",

Nothing else changed. The branch order already puts the code fixer ahead of the
formatter, so this only claims prompts that the formatter was never going to
handle correctly anyway.

Testing

tests/test_ai_automation_logic.py::TestTemplatePromptsReachTheirOwnHandler, 5
cases driving the real shipped prompts through build_ai_automation_fallback():

  • fix_code_blocks reaches the fence fixer and closes an unbalanced fence
  • it leaves an already-balanced fence byte-identical
  • it hits the selection_required_for_code_fix guard with no selection
  • every shipped template dispatches to its own handler (parametrised, and it
    fails if the template set changes)
  • /format still uses the formatter and is not diverted into the fence fixer

Verified all 4 behavioural assertions fail against the unmodified
ai_logic.py.

Verification

  • backend pytest — 200 passed (was 195 on main; this PR adds 5)
  • ruff check / ruff format --check on changed files — clean
  • npm test — 23 passed, unchanged
  • npm run lint / npm run build — clean

build_ai_automation_fallback() dispatches on keywords, and the 'Format and Fix
Code Blocks' template ships the prompt 'Format Markdown code fences and fix
common fence syntax issues.' None of ('format code', 'code block', 'correct
syntax', 'fix code') appear in it -- 'format code' is not a substring of
'format markdown code', and the prompt says 'code fences' / 'fence syntax',
not 'code block'. It fell through to the generic formatter.

The existing test passed the literal '/fix-code', which the slash table maps
to 'format code blocks and correct syntax' and so does match. The real UI
never sends that: useSlashCommands.ts looks the template up by id and submits
template.prompt verbatim, so the shipped wording is exactly what arrives.

Result: an unbalanced fence stayed unbalanced, because the formatter cannot
close one, and the command still reported success. The branch's own
selection-required guard was skipped too.

Add 'code fence' and 'fence syntax'. Branch order already puts the code fixer
ahead of the formatter.

Adds 5 cases incl. a parametrised check that every shipped template reaches
its own handler; verified 4 fail against the unmodified ai_logic.py.
@harsh-thakkar7
harsh-thakkar7 force-pushed the fix/fix-code-slash-dispatch branch from 7e0ac0e to c54c90e Compare October 2, 2026 09:52
@petertzy

petertzy commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Reviewed and verified locally. The fix correctly routes the shipped fix_code_blocks template prompt to the fence fixer by recognizing the actual “code fences” and “fence syntax” wording. The added tests cover unbalanced fences, balanced fences, the selection guard, all shipped template dispatch paths, and ensure /format still uses the formatter.

All 270 backend tests pass, and Ruff checks and formatting checks are clean. This change is focused and safe to merge.
Approved.

@petertzy
petertzy merged commit ac70747 into petertzy:main Oct 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants