Skip to contentWolf-Rayet

Decision records

ADR-0087 Emphasis nests, and so does everything else the inline renderer was flattening

Accepted2026-09-06Phase 6

#Context

The handoff for this work carried a known defect in one line: *"Six ADR titles containing code spans render their backticks literally on the docs site — observed at 69f3b31, not re-checked since."*

ADR-0086 built the site and put its check in CI, which made the defect measurable for the first time. It is not six titles. Scanned across every built page, 67 of the ADR pages rendered at least one literal backtick in visible text, and the sites were paragraphs, headings, summaries, table cells and link labels — not titles.

The count in the handoff was made by eye, and by eye is how it stayed at six while the real number was an order of magnitude larger. This record is what the measurement found.

#One defect, five times

apps/docs/app/site/markdown.tsx tokenises inline markup with a single regex over three alternatives — emphasis, code span, link — and then renders each token. Every failure below is the same mistake: a branch that treats its own contents as a terminal string instead of as more inline markup.

Emphasis did not recurse. out.push(<strong>{token.slice(2, -2)}</strong>) inserted the inner text raw, so **popover is built** rendered its backticks as characters. This prose bolds a sentence that names a token constantly, and it accounted for most of the 67.

Link labels did not recurse. The same shape one function down: [docs/spec-ramp-generator.md](…) rendered the label's backticks literally inside the anchor.

Headings never reached the renderer at all. SectionBlock took section.title as a string and put it in the <h2>, and the page's <h1>, its summary paragraph, the on-this-page list and its nested list all did the same. Six render sites, one missing call.

The table splitter cut cells inside code spans. line.split('|') does not know what a backtick is, so the playbook's row 049 — which names a baseline line as a code span reading baseline.json darwin-arm64 | 40 | 101 — was split into three cells, and the fragments began with an unclosed delimiter. Row 084 has the same shape with a TypeScript error containing Record<"arbiter" | "emitter", true>.

And a paragraph ended at any line starting with a backtick. The continuation test admitted a single backtick as a block opener, alongside a pipe and a quote — so it treated one as a fence. A paragraph whose next line begins with an inline code span is ordinary in prose that wraps, and every one of them was cut in two with the span's opening delimiter stranded at the start of the second half.

#Decision

Emphasis and link labels recurse. Both terminate for the same reason and it is in the grammar rather than in a guard: the emphasis alternative is \*\*[^*]+\*\* and the link alternative is \[[^\]]+\]\([^)]+\), so neither one's contents can match its own branch again.

Headings, page titles, summaries and both levels of the on-this-page list render through the one inline function, exported as heading(). It is deliberately not a second parser: a heading and a paragraph that both say region have to render the same way, and one implementation is how that is guaranteed rather than remembered.

The table splitter tracks whether it is inside a code span. A code span is delimited by backticks and cannot contain one, so a single boolean across the line is enough — there is no nesting to count.

A paragraph ends at a fence, not at a backtick. The continuation test now tests for a three-backtick fence where it tested for a single backtick.

The register cells, the ADR Status row and the roadmap's criteria are rendered through the same function, because all three are markdown-sourced prose that was being inserted as raw strings.

#What is left, and why it stays

Two text nodes in the whole site still contain a backtick, and both are correct.

One is a JavaScript template literal inside a fenced code block in ADR-0083, the line declaring ATTR_SEL. A code fence renders its contents verbatim and this is its contents.

The other is a $note value shown in the table that lists a JSON artifact's own keys. That table is a data view rather than prose: it shows what is in the file, and running markdown over arbitrary data values would be this site deciding that an asterisk in a token name is emphasis. The same note is *also* the page's summary, where it now renders as markup — so the page shows one string two ways, which is the honest consequence of the two places meaning different things.

#Rejected options

Add a check that no visible text contains a backtick, and fix whatever it reports. The right end state and not this pass's, because it needs the two legitimate cases designed for rather than excluded by name — and putting a check into CI before thinking it through is the mistake ADR-0086 made one commit earlier, when docs:check-site went red on a runner that could not run it. Named here as the next unit on this row.

Fix the six titles the handoff named. What was asked for. The measurement found sixty-seven pages and five distinct causes, none of which was titles specifically, and stopping at the reported symptom would have left every one of them.

Render the JSON data table through the markdown renderer too, so the check could be absolute. It would close the last node and it is wrong: a data table shows values, and a value containing an asterisk is a value.

Use a markdown library. The reasonable engineering answer and out of scope for a pass whose subject is a defect. This renderer is deliberately small — §6 permits bold and forbids italic, and the file says so — and replacing it is a decision about the docs stack rather than a bug fix.

#Consequences

Sixty-seven ADR pages become two text nodes site-wide, both legitimate. The scan is in this record's Measured section and is reproducible against any build.

Five render sites now call one function, where six called none.

docs:check-site is unchanged at 15 checks, 0 failing, 334 routes — the site was passing its own checks throughout, because none of them looks for this. That is the argument for the check named above, and it is also why the defect survived from 69f3b31 to here.

No component, no token, no scene, no baseline. A renderer, six call sites, and a count that was wrong by an order of magnitude.

#Measured

  • Built site, before this record: 67 ADR pages with at least one literal backtick in visible text *(darwin-arm64, this pass)*.
  • After emphasis recursed: 20. After headings, titles and summaries: 10. After link labels: 7. After the table splitter: 4. After the paragraph terminator and the last three call sites: 2 *(darwin-arm64, this pass, each measured on a clean rebuild)*.
  • The two that remain: a template literal inside a fenced code block in ADR-0083, and a $note value in the JSON key table on /foundations/content *(darwin-arm64, this pass)*.
  • pnpm docs:check-site — 334 routes, 15 checks, 0 failing, unchanged before and after *(darwin-arm64, this pass)*.
  • pnpm battery — 24 of 24 *(darwin-arm64, this pass)*.