Skip to content

Add Fix/Explain buttons to Quarto inline output errors#14735

Merged
nstrayer merged 5 commits into
mainfrom
feature/13228-quarto-fix-explain
Jul 10, 2026
Merged

Add Fix/Explain buttons to Quarto inline output errors#14735
nstrayer merged 5 commits into
mainfrom
feature/13228-quarto-fix-explain

Conversation

@nstrayer

@nstrayer nstrayer commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Closes #13228

This PR adds the same Fix/Explain buttons that the Positron Notebook Editor gets for cell errors to Quarto inline outputs. The buttons were extracted from the notebook quick-fix into a shared AssistantErrorQuickFix component, so both surfaces can share one implementation. This also let us fix a latent problem (Explain was fixing errors too) in one place for both.

Agent generated notes 👇

Summary

The notebook's Fix/Explain implementation was tangled with notebook-only gating, so this PR splits it in two:

  • AssistantErrorQuickFix (shared, presentational): the split buttons, ANSI cleanup, error attachment, and posit-assistant.newChat wiring. No gating -- callers decide whether to render it.
  • Per-surface wrappers own their gating and prompt wording: NotebookCellQuickFix (unchanged gates) and the new QuartoOutputQuickFix.

Key decisions:

  • Quarto gating: gates on the global ai.enabled setting (read live; unset counts as enabled) plus the posit-assistant.hasChatModels context key. It deliberately drops the notebook-only gates (positron.notebook.enabled, notebook.ai.enabled) -- Quarto inline output works regardless of whether the Positron notebook editor is enabled.
  • Explain is explanation-only: while testing, Explain would often go ahead and fix the error too. The shared component now appends "Do not make changes or edit any files; just explain the error." to every Explain prompt -- the notebook inherits this for free via the shared component. (A true read-only "ask mode" in the assistant was investigated and tabled; posit-assistant.newChat has no mode argument today.)
  • Component home: AssistantErrorQuickFix stays in positronNotebook even though positronQuarto imports it. The tidy home would be the positronAssistant contrib, but that's on a deprecation path; a shared home can be created when the assistant migration needs one.
  • Renderer lifecycle: the quick-fix mounts via the view zone's existing per-output React renderer registry (keyed `${outputId}::quick-fix`), so the existing disposal sweep tears it down on clear/re-execute/dispose.

The console's error quick-fix still hand-rolls its own version (different UX: plain buttons, always continues the current chat); unifying it is deliberately out of scope.

In-Action

quarto-fix.mov

Release Notes

New Features

Bug Fixes

  • The Explain button on notebook and Quarto error outputs now asks the assistant to explain the error without also attempting to fix it

Validation Steps

@:quarto @:positron-notebooks @:posit-assistant

  1. Open a .qmd with an erroring chunk (e.g. test/e2e/test-files/workspaces/quarto_inline_output/r_errors.qmd) and run the chunk.
  2. Verify Fix/Explain buttons appear below the error output; Fix opens a new Posit Assistant chat with the error attached; Explain does the same but the assistant only explains -- it should not edit files.
  3. Each button's dropdown ("continue in current chat") should reuse the current conversation instead of starting a new one.
  4. Set ai.enabled: false (or quit the assistant so no chat models are available) and re-run: no buttons should render.
  5. Run an erroring cell in a Positron notebook and confirm the buttons still behave as before (only change: Explain no longer fixes).

Unit coverage: AssistantErrorQuickFix.vitest.tsx (chat payload contract) and QuartoOutputQuickFix.vitest.tsx (gate matrix).

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

E2E Tests 🚀
This PR will run tests tagged with: @:critical @:quarto @:positron-notebooks @:assistant

Why these tags?
Tag Source
@:critical Always runs (required)
@:quarto PR description
@:positron-notebooks PR description
@:assistant Changed files

More on automatic tags from changed files.

Warning

Unrecognized tag(s) in the PR description, ignored: @:posit-assistant. Check for a typo, or see the valid tags list.

readme  valid tags

@nstrayer nstrayer requested a review from jmcphers July 7, 2026 19:51
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

PETE's assessment 🧪

Verdict: 🟢 Adequate -- Both new components ship focused Vitest suites that cover the payload contract (including the Explain-only bug fix) and the full gate matrix at the cheapest viable level.

What changed

  • Extracted the notebook Fix/Explain buttons into a shared, presentational AssistantErrorQuickFix component (buttons, ANSI cleanup, error attachment, posit-assistant.newChat wiring) with no gating.
  • Added a new QuartoOutputQuickFix wrapper that self-gates on ai.enabled + posit-assistant.hasChatModels, and wired it into quartoOutputViewZone._renderError via the per-output React renderer registry.
  • NotebookCellQuickFix is reduced to a gating wrapper delegating to the shared component (behavior-preserving, gates unchanged); plus a CSS rename and one i18n resource entry.

Tests in this PR

  • Unit (Vitest/Mocha) ✅ (added AssistantErrorQuickFix.vitest.tsx, QuartoOutputQuickFix.vitest.tsx)
  • Extension host ✅ (not applicable -- no extension-host code)
  • E2E (Playwright) ✅ (not required -- gating and payload are unit-testable; PR body already lists @:quarto @:positron-notebooks @:posit-assistant for existing regression coverage)

Existing coverage

The new tests are the primary coverage. AssistantErrorQuickFix.vitest.tsx asserts the actual newChat payload (prompt, target, base64 attachment), ANSI stripping, blank-error omission, the appended explain-only constraint, and the "continue in current chat" dropdown path -- this directly pins the latent bug (Explain fixing errors) the PR claims to fix. QuartoOutputQuickFix.vitest.tsx covers all four gate combinations, matching the source condition aiEnabled !== false && hasChatModels. Since NotebookCellQuickFix now delegates its button behavior to the shared component, that path is covered transitively.

Suggested additions

None. The only untested surface is the thin view-zone glue in _renderError (renderer registration/keying), which is mechanical DOM mounting not readily unit-testable and low regression risk.

Deployment note (optional)

The Quarto quick-fix renders through browser-only view-zone glue and no surface-split (browser/ vs electron-sandbox/) code is touched, so there is no direct Windows/web gap. If the team wants PR-time confidence that the buttons render in the web build (where the assistant flow and rendering can differ), consider tagging an existing quick-fix e2e test @:web -- optional, not blocking.


PETE (Positron Extreme Test Experiment) - LLM-based test-coverage advisor, in pilot. Triggers on PR open and on /recheck-tests comments. Wrong verdict? Comment /recheck-tests (or /rePETE) on this PR to re-run. Please share feedback on how PETE performed here.

@jmcphers jmcphers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few problems I had when testing this:

The height is computed incorrectly when Fix/Explain are present and the error is restored from the cache (vs from a just-executed cell). Maybe we shouldn't show these affordances on errors loaded from the cache?

I also found that these buttons sometimes disregarded the error they were given and instead found problems elsewhere in the document. We might need some additional context naming the code that produced the error, and its location in the document, to help focus the LLM.

Image

@nstrayer nstrayer force-pushed the feature/13228-quarto-fix-explain branch 2 times, most recently from 2f05fc3 to 62f7098 Compare July 9, 2026 10:43
@nstrayer

nstrayer commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author
hide-on-reload.mov

Updated so that the buttons only run when the cell has been created in that session/window open.

Also updated the prompts to be more direct with what should be done in the fix situation.

@nstrayer nstrayer requested a review from jmcphers July 9, 2026 11:44

@jmcphers jmcphers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. A few nits, might be worth investigating before merge

After a fresh start, the first fix/explain button I press fails. It opens the Posit Assistant sidebar but this popup shows up and the error never goes to Assistant.

Image

Second, Assistant is kind of bad at fixing Quarto problems. Try creating these two cells:

```{r}
library(ggplot2)
```

```{r}
ggplot(faithful, aes(x = eruptions, y = waiting)) +
  geom_point()
```

Explain the error:

Image

Assistant says:

In Quarto, each code chunk is typically executed in isolation unless they're part of the same rendering session. The issue is that the library(ggplot2) call in the preceding chunk loads the package into that chunk's execution context, but the functions from ggplot2 may not be available in the next chunk

This is wrong and I would have expected Assistant to tell me that the problem is that I've run the chunks out of order. Assistant also thinks this is true of R Markdown files ("each cell is run in isolation") and encourages me to "fix" the problem by combining cells.

Maybe it needs to learn more about interactive execution, but probably that's outside the scope of this PR!

@nstrayer

Copy link
Copy Markdown
Contributor Author

I agree those are out of scope for this pr but I made followup issues in the assistant repo for them!

nstrayer added 5 commits July 10, 2026 14:57
Extract a shared AssistantErrorQuickFix component from the notebook
quick-fix, then wrap it in a thin QuartoOutputQuickFix that gates on
ai.enabled + posit-assistant.hasChatModels and attaches the failing
chunk's code and location to the assistant payload.

- Resolve cell context at render time (code, language, path, line range)
- Suppress buttons on cache-restored errors (structural: the cache-
  restore path sets fromCurrentSession=false on the view zone)
- aria-live='off' on the quick-fix container so async button mounts
  inside the output's role='log' region are not announced
- Add findCellByPreviousId to the document model for cell-remap support
- Explain prompt appends a constraint preventing agentic fixes
The stash-restore path (tab close + reopen in the same session) was
keeping fromCurrentSession=true and resolving cell context, which caused
buttons to reappear on errors that were no longer freshly executed.
Treat stash restores the same as cache restores: suppress the buttons.
The view zone defaulted to showing buttons and required restore paths to
explicitly suppress them. Flip the default: buttons are suppressed unless
the live execution path calls enableQuickFix(context?). This removes one
field, one setter, and the need for restore paths to opt out.
@nstrayer nstrayer force-pushed the feature/13228-quarto-fix-explain branch from 08149a6 to 68960b8 Compare July 10, 2026 18:58
@nstrayer nstrayer enabled auto-merge (squash) July 10, 2026 18:58
@nstrayer nstrayer merged commit 2eb8eb9 into main Jul 10, 2026
24 checks passed
@nstrayer nstrayer deleted the feature/13228-quarto-fix-explain branch July 10, 2026 20:19
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Quarto inline output: Add Fix/Explain buttons to errors

2 participants