Skip to content

Fix keep-md cleanup deleting the markdown output when output-file has an .html extension#14671

Open
cscheid wants to merge 2 commits into
mainfrom
fix/14669-output-file-html-ext-multiformat
Open

Fix keep-md cleanup deleting the markdown output when output-file has an .html extension#14671
cscheid wants to merge 2 commits into
mainfrom
fix/14669-output-file-html-ext-multiformat

Conversation

@cscheid

@cscheid cscheid commented Jul 11, 2026

Copy link
Copy Markdown
Member

Closes #14669.

Problem

Pairing an html format with a markdown format while output-file has an .html extension (e.g. output-file: index.html with format: html + commonmark, as nbdev sets up for llms.txt workflows) names the markdown output index.html.md — exactly the path the keep-md intermediate convention (<input-stem>.<base-format>.md, executionEngineKeepMd) assigns to the html format. The html render's cleanup removed what it took for a stale intermediate, deleting the freshly written markdown output:

  • website projects: the render then failed at the output-move step (NotFound … rename '<proj>/index.html.md' -> '<proj>/_site/index.html.md'), leaving index.html and site_libs/ stranded in the project root
  • default-type projects: exit 0, markdown output silently missing
  • keep-md: true: no crash, but the html render's intermediate silently fought with the markdown output for the same file

Diagnosis was confirmed by instrumenting renderCleanup and watching the filesystem at 2ms resolution: the commonmark output is written and deleted 29ms later by the html format's cleanup.

Fix

Principle: keep-md intermediate handling must never write to or delete a path that a declared format owns.

Commit 1 (core fix): compute the projected output paths of the formats being rendered (projectedOutputFile, next to formatOutputFile in core/render.ts), attach them to each RenderContext, and

  • skip the keep-md deletion in renderCleanup when the conventional keep-md path is another format's output
  • skip the keep-md intermediate write on the same collision, with a warning (the real output wins; warn rather than error for now, may revisit)

Commit 2 (partial-render protection, split out for review): a partial render (quarto render --to html) must not delete the markdown twin a previous full render produced (outputs live in place in default-type projects). Since renderContexts already resolves every declared format (inactive ones are marked active: false), the implementation is just dropping the active-only filter from commit 1's computation — one line plus a test.

Tests

New smoke tests in tests/smoke/render/render-output-file-collision.test.ts (fixtures under tests/docs/output-file-collision/):

  1. website project renders both outputs into _site, no root debris
  2. default-type project keeps the markdown output
  3. --to html partial render preserves an existing twin (commit 2)
  4. keep-md: true warns, and the twin holds the commonmark output rather than the intermediate

Each commit's tests verified to fail without that commit's change. Genuine stale-intermediate cleanup still works (keep-md: true render followed by keep-md: false removes the leftover). Together with #14670, the full nbdev/llms.txt scenario from #14667 now works end-to-end (verified on a local merge of both branches: render succeeds and the served page's index.html.md link resolves with HTTP 200).

🤖 Generated with Claude Code

cscheid and others added 2 commits July 10, 2026 19:55
When output-file has an .html extension and an html format is paired
with a markdown format (e.g. output-file: index.html with html +
commonmark, as nbdev sets up for llms.txt workflows), the markdown
output is named index.html.md -- the same path the keep-md intermediate
convention (<input-stem>.<base-format>.md) assigns to the html format.
The html render's cleanup removed the "stale intermediate", deleting
the freshly written markdown output: website renders then failed at
the output-move step, and other project types silently lost the file.

Compute the projected output paths of the formats being rendered and
attach them to each RenderContext; render cleanup now never deletes a
path that a format owns, and a keep-md: true render whose intermediate
location collides warns and skips saving the intermediate instead of
overwriting the output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…14669)

A partial render (quarto render --to html) of a document whose other
declared format owns the keep-md-conventional path (output-file:
index.html + a markdown format -> index.html.md) was still deleting
the markdown output a previous full render produced, since outputs
live next to sources in default-type projects.

renderContexts already resolves every declared format (inactive ones
are simply marked active: false), so extending the protection is just
including inactive contexts when collecting the owned output paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@posit-snyk-bot

posit-snyk-bot commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@cscheid

cscheid commented Jul 11, 2026

Copy link
Copy Markdown
Member Author

(Claude says:)

Note on the 8 failing windows checks (analysis after re-running them — they fail identically across 3 attempts):

  • The failing tests are unrelated to this PR: babel/TinyTeX package-auto-install docs (13633.qmd, latex-hyphen-lang-{zh,es}.qmd), caption-footnotes, the feature-format matrix, and one search-index test. The new render-output-file-collision tests pass on both platforms, and every ubuntu job is green.
  • Root symptom: TeX Live's fmtutil.pl/tlmgr aborts with kpsewhich -var-value=TEXMFROOT failed / 'kpsewhich' is not recognized. The PATH it reports has its first two entries joined by : (the unix separator) instead of ;, merging both TinyTeX bin dirs into one invalid entry — this happens inside TeX Live's perl, far below anything this PR touches (keep-md path bookkeeping).
  • Why this PR and not Fix index.html clean-URL rewrite mangling alternate-format links on index pages #14670 (same runner image windows-2025-vs2026 20260628.158.1, same fresh TinyTeX v2026.07 download, green 36 minutes earlier): this PR adds a smoke test file, which reshuffles the deterministic test-bucket sharding; Fix index.html clean-URL rewrite mangling alternate-format links on index pages #14670 added only playwright files, which don't. A latent test-ordering-dependent TinyTeX corruption (e.g. bucket 10 runs render-pdf-svg-conversion / render-verapdf before the babel doc) would fire deterministically on exactly this PR's bucket layout — matching the 3-for-3 rerun failures while main and Fix index.html clean-URL rewrite mangling alternate-format links on index pages #14670 stay green.

I couldn't bisect the specific polluting test without a windows box; happy to file the CI investigation as a separate issue if this diagnosis looks right to maintainers.

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.

output-file with .html extension loses the markdown-format output in multi-format project renders (website render fails at move step)

2 participants