Skip to content

fix: re-file the Quarto output cache when an untitled document is saved in web - #15331

Merged
midleman merged 3 commits into
mainfrom
mi/quarto-untitled-save-cache-rebind
Aug 5, 2026
Merged

fix: re-file the Quarto output cache when an untitled document is saved in web#15331
midleman merged 3 commits into
mainfrom
mi/quarto-untitled-save-cache-rebind

Conversation

@midleman

@midleman midleman commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

When you run a Quarto cell, Positron saves a copy of the output to a cache filed under the document's address. Saving an untitled document with Save As is supposed to re-file that cache under the new filename, but the code only did so when the new address looked like a plain local file. In Positron Web and remote sessions a saved file has a different kind of address, so the cache stayed filed under "Untitled-1" and a window reload found nothing to restore, so the output vanished.

  • Re-file the cache for any saved address, not just plain local files
  • The output looked fine right after the save because a copy in memory was matched by cell content; only a reload exposed the loss
  • Explains why this failed on web (71% on sles/chromium) but almost never on desktop, where a saved file does have a plain local address
  • One thing to flag for review: when a cached cell matches no cell in the saved document, the untitled cache is cleared anyway, so a cell edited during the save loses its output. The new test pins that behavior; making the clear conditional would be a separate change
  • Adds quartoOutputManagerSaveAs.vitest.ts covering the re-filing for a remote address, a local file, and untitled-to-untitled

Release Notes

New Features

  • N/A

Bug Fixes

  • Quarto inline output no longer disappears after reloading the window when an untitled document was saved with Save As

Validation Steps

@:quarto @:web @:win

  1. Open a new untitled Quarto document, add a Python cell, and run it
  2. Save As to a real filename, then reload the window
  3. The cell's output should still be there

E2E Triage Diagnosis

🟢 High confidence -- CORRECTED (supersedes the parse-race summary below): on Save As the Quarto output cache is never rebound from the untitled URI to the saved document, because _transferCacheFromUntitled is gated on this._documentUri.scheme === (file) (quartoOutputManager.ts:391) and in web a saved document URI is vscode-remote. The post-save assertion still passes via the in-memory findCacheByContentHash fallback to the live untitled cache; that fallback cannot survive a window reload, so post-reload restore finds no cache under the saved URI and mints zero view zones.
  • Test: Quarto - Inline Output: Persistence > Python - Verify inline output works in untitled Quarto document and persists through save
  • Targeted failure: Pattern A -- expect(locator).toBeVisible() on locator('.quarto-inline-output').first().locator('.quarto-output-content'), 5 occurrences / 71.4% of 7 runs on main, sles/chromium, rep sha dea62542943085c854378d6c4132f800c0c5a1ba (run 30889748402).
  • Signal: VERBOSE=1 e2e-chromium run (renderer trace + console forwarded to e2e-test-runner.log), repeat1 failing at spec line 92: 19:22:39.886 Saved output for cell 0-c2a393fb-unlabeled in untitled:Untitled-1; 19:22:40.719 Initializing for vscode-remote://.../test-...qmd (Save As -> vscode-remote scheme, so no Transferring cache from untitled to file line ever appears); 19:22:40.777 No cache file found for vscode-remote://...; 19:22:40.780 Found matching untitled cache: untitled:Untitled-1 with 1 matching cells -> Found cache by content hash match -> Restored cached outputs for 1 cells (this is what makes the post-save assert pass); 19:22:43.75 Flushing all caches / Flush complete; after reload 19:22:46.16 Initializing for the saved URI -> 19:22:46.399 No cache file found -> 19:22:46.541 No cached outputs to restore, with no untitled-cache match this time. Environment skew confirmed by mechanism: desktop saves to a file-scheme URI so the transfer runs (0.8% on win/electron) while web never does (71.4% on sles/chromium).
  • Frequency: 5/7 runs (71.4%) on main, sles/chromium
  • Hypothesis: Dropping the file-scheme requirement on the untitled->saved transition (accept any non-untitled scheme) makes the transfer write the cache under the real saved URI, so the post-reload restore finds it. The fix(quarto): restore inline output on close+reopen before parse #15091 deferral fix is a genuine but separate bug: its log lines (No cells parsed yet, waiting for parse to retry cache load -> Cells found after parse, retrying cache load, 19:22:37) show it running as designed on the untitled document without affecting this failure. Ruled out: lost debounced write (the cache service joins flushAll on onWillShutdown and Flush complete is logged before the reload); _transferCacheFromUntitled losing a parse race (it is never called at all -- no transfer log line); the deferral gate (fix present and exercised, e2e rate unchanged: pre-fix 1/3, post-fix 4/6, verbose 4/4).
  • Supersedes: PR fix(quarto): restore inline output on close+reopen before parse #15091 (closed unmerged 2026-07-23, same mechanism, first found via the dataframe close+reopen flake)

…ed in web

The untitled->saved transition transfers the Quarto output cache to the saved
document's URI, but required a `file` scheme. In a remote or web window a saved
document is `vscode-remote`, so the transfer never ran and the cache stayed
keyed to the untitled URI. Output still rendered right after the save because
the content-hash fallback matched the live untitled cache in memory, but a
window reload found nothing under the saved URI and restored no view zones.

Accept any non-untitled scheme, and cover the rebind with a vitest.
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

E2E Tests 🚀
This PR will run tests tagged with: @:critical @:quarto @:web @:win

Why these tags?
Tag Source
@:critical Always runs (required)
@:quarto PR description
@:web PR description
@:win PR description

More on automatic tags from changed files.

readme  valid tags

@midleman midleman changed the title Re-file the Quarto output cache when an untitled document is saved in web fix: re-file the Quarto output cache when an untitled document is saved in web Aug 4, 2026
@midleman
midleman force-pushed the mi/quarto-untitled-save-cache-rebind branch from 4046cbc to a29ba3d Compare August 4, 2026 20:10
…matching branches

The rebind tests only checked which URIs the cache service was called with, so
a change that wrote the cache but dropped the in-memory re-attach would have
passed. Assert the outputs land on the saved document too, and add the two
matching partitions that had no coverage: a cell whose index shifted (matched by
content-hash prefix) and a cached cell that matches nothing in the saved
document. Drop the redundant workbench preset, which the contribution preset
already implies.
@midleman
midleman force-pushed the mi/quarto-untitled-save-cache-rebind branch from a29ba3d to 3cc342a Compare August 4, 2026 20:29
@midleman
midleman marked this pull request as draft August 4, 2026 20:29
The vscode-remote and file cases were identical apart from the URI. Fold them
into one it.each so the scheme-independence is stated rather than implied, and
trim the file header and the rebind comment to the part the code cannot show.
@midleman
midleman marked this pull request as ready for review August 4, 2026 23:14
@midleman
midleman requested a review from jmcphers August 4, 2026 23:14

@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.

Looks good!

@midleman
midleman merged commit 0b378ad into main Aug 5, 2026
27 checks passed
@midleman
midleman deleted the mi/quarto-untitled-save-cache-rebind branch August 5, 2026 22:53
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 5, 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.

2 participants