ADR-0006: Exclude content/docs MDX files from Prettier
Read the transcript
1. Six pages, silently corrupted
Host: So we’re digging into ADR-0006 today, and the setup is almost comic if it weren’t so nasty: six pages across your Learn module — Modules 1, 2, 4, 5, 6, and 7 — had Python code examples that just silently stopped being valid Python. Not a crash, not a warning, just quietly broken. Walk me through what actually happened.
Guest: Right, so these code examples weren’t sitting at the top level of an MDX file — they were nested inside a custom JSX/MDX component we use for walkthroughs. Someone ran pnpm format, which runs Prettier across the repo, and Prettier rewrote several of these nested fences: it stripped indentation off lines that came after a blank line inside the fence, merged comments onto the previous line of code, and it backslash-escaped underscores and asterisks in identifiers like __init__ and self._admit because it treated them as Markdown emphasis syntax instead of code.
Host: And none of that got caught anywhere in CI, which is the part that really stings — pnpm format and pnpm build ran fine, right through it.
Guest: Exactly, neither of those checks the actual contents of a fenced block against its declared language, so mangled Python sailed straight through. The only content-specific check we had was a lint script validating required headings and frontmatter — nothing was ever looking inside the fences, so there was no seatbelt at any layer for this.
2. Three ways out, and why two don’t hold
Host: So once you know nothing’s watching inside those fences, you’ve got three ways to respond. Walk me through them, starting with the one that feels cheapest.
Guest: Cheapest is just declaring a rule: never put a blank line inside a nested fenced code block. But that’s a landmine with no lint rule behind it — some future contributor who doesn’t know the rule hits the trigger, and gets silently corrupted code instead of a formatting diff. A failure mode that severe can’t rest on people remembering a rule nobody wrote down anywhere enforceable. The second option is ripping out CodeWalkthrough and similar components so every fence lives at the top level, which does dodge the bug, but it throws away the narrative framing those components give for every future example in the handbook, not just the six that got hit.
Host: So one option is unenforceable, and the other is a permanent tax on every doc author going forward. What made the third option — excluding content docs from Prettier entirely — the obvious landing spot?
Guest: Because it matches the actual severity: authors already hand-format code inside those fences since they’re excerpts from real source, not generated output, so Prettier touching them was never adding value, just risk. Excluding the docs MDX means we only lose automatic formatting of prose and JSX outside the fences — purely cosmetic — while removing the one thing that could silently mangle real code. Once nothing inside those fences could be trusted anyway, the trade was easy.
3. The decision, the repair, and the cost that’s left over
Host: So walk me through what actually landed in the repo. Not the philosophy anymore, the mechanics — what’s the diff look like?
Guest: One line in .prettierignore, apps/handbook/src/content/docs plus the glob for MDX, so the whole docs tree is hands-off for Prettier going forward. The six corrupted pages got hand-repaired, and then verified by literally parsing every fenced Python block with Python’s own ast.parse — not the linter, not Prettier, the actual language parser, which is strong enough to catch this exact failure mode, one that neither Prettier nor the existing content linter would. Module 5, Agent Engineering, and Module 7, LangGraph, are two of those six, and they’re the ones I’d point anyone to if they want to see real corrupted-then-restored examples rather than a synthetic case. The catch is what we lose: pnpm format:check no longer touches prose or JSX in those files, so sloppy spacing or wrapping in docs just won’t get flagged anymore, that’s a permanent, accepted cost. And it means every future code example dropped into a CodeWalkthrough is on the author to verify by hand, because a clean pnpm verify literally does not mean the code inside those fences is valid — nothing in CI parses Markdown fences today. Even this ADR says so itself: if Prettier ever ships a fix and someone’s tempted to re-enable it, the obligation is to re-run the same ast.parse check against real content first, not just trust a changelog entry.
Host: Which is really the whole shape of this decision in one sentence — trade a small, visible, cosmetic cost for removing a silent, invisible, correctness-breaking one. That’s ADR-0006. Thanks for walking through it.
Not covered
The planner wanted these and found nothing in the source to support them:
- What the corrupted Module 5 and Module 7 code examples actually taught about agent loops or LangGraph (the ADR only references these pages as evidence of the bug, not their content)
- Any detail of the agent loop, tool-calling, or LangGraph checkpointing material itself, since it’s unrelated to the Prettier decision beyond being the corrupted content
Generated from this page by Claude Sonnet 5 on , spoken by Kokoro-82M running locally. Two synthetic voices, not a recorded conversation. Every claim is drawn from this page — where it differs from the text above, the text is correct.
Status
Section titled “Status”Accepted. Implemented by adding apps/handbook/src/content/docs/**/*.mdx to .prettierignore,
and by repairing six already-corrupted module pages.
Context
Section titled “Context”Six Learn module pages (Modules 1, 2, 4, 5, 6, and 7) contain Python code examples inside
<CodeWalkthrough> components — a fenced code block as a child of a custom JSX/MDX component,
rather than at the top level of the document. Running pnpm format (Prettier, --write) across
the repository silently rewrote several of these blocks: indentation was stripped from statements
following a blank line inside the fence, comments got merged onto the preceding code line, and
underscores/asterisks in identifiers (__init__, self._admit) were backslash-escaped as if they
were Markdown emphasis syntax. The result was Python that no longer parsed, shipped past
pnpm format and pnpm build because neither checks that a fenced code block’s contents are
valid code in their declared language — only scripts/lint-content-structure.ts’s required-heading
check and Astro’s frontmatter schema ran against these pages, and neither would have caught this.
Problem
Section titled “Problem”Prettier’s MDX/remark-based formatter treats a fenced code block nested inside a custom JSX
component’s children differently from one at a document’s top level, and — specifically once a
blank line appears inside that nested fence — drifts from treating the block as an opaque code
fence into treating some of its lines as Markdown prose (applying Markdown escaping rules to
underscores and asterisks, and losing the leading whitespace a prose paragraph would ordinarily be
reflowed without). A minimal reproduction confirmed this: an identical code fence with a blank line
inside it was left untouched at the top level of an .mdx file, but corrupted when the same fence
was made a child of a JSX component. How do we keep the reliability Prettier gives the rest of the
codebase without exposing content authors to a formatter that can silently break their code
examples?
Options
Section titled “Options”- Never put a blank line inside a nested fenced code block. Technically avoids the trigger, but makes it a landmine for every future contributor who doesn’t know the rule, with no lint rule enforcing it and a failure mode (corrupted, unparseable code, not a formatting diff) severe enough that “just don’t do the thing” isn’t an adequate mitigation on its own.
- Stop wrapping code examples in
<CodeWalkthrough>and other components, keeping every fence at the document’s top level. This works around the bug but gives up the narrative framingCodeWalkthroughand similar components provide, for every future code example in the handbook, not just the ones already affected. - Exclude
apps/handbook/src/content/docs/**/*.mdxfrom Prettier entirely. Content pages lose automatic prose/JSX formatting, but authors already control formatting inside fenced code blocks by hand (they’re excerpts from real source or illustrative examples, not generated), so the practical loss is address only cosmetic JSX/Markdown formatting outside of code fences — nothing Prettier does inside a.mdxfile’s code fences today can be trusted anyway.
Decision
Section titled “Decision”Exclude every file under apps/handbook/src/content/docs/ from Prettier via .prettierignore.
Content authors are responsible for their own formatting in .mdx files; pnpm lint:content and
Astro’s build-time frontmatter schema remain the automated checks for these pages. The six affected
pages were repaired by hand and verified by parsing every fenced Python block with Python’s own
ast.parse() — a check strong enough to catch this exact failure mode, which neither Prettier nor
the existing content linter would.
Consequences
Section titled “Consequences”- Content
.mdxfiles no longer get automatic formatting — inconsistent spacing or line-wrapping in prose or JSX won’t be caught bypnpm format:check. This is a real, accepted cost, traded against a bug that silently breaks code correctness rather than just cosmetics. - Every future code example nested inside a doc component (
CodeWalkthroughor otherwise) needs to be verified by the author, not assumed safe becausepnpm formatran clean —pnpm verifypassing does not mean a fenced code block’s contents are syntactically valid, since nothing in this repository’s CI actually parses code inside Markdown/MDX fences today. - This ADR is itself evidence for the fix: if Prettier is ever re-enabled for content pages
(a newer version fixing the underlying bug, for instance), re-verify with the same
ast.parse()-per-block check before trusting it, rather than assuming a version bump alone fixed it.
References
Section titled “References”- Prettier’s Markdown/MDX printer — the component responsible for the behavior described here.
- Module 5: Agent Engineering, Module 7: LangGraph — two of the six pages whose code examples this bug corrupted and this ADR’s fix repaired.