Skip to content

Treat every published page as untrusted - #215

Open
Deicyde wants to merge 5 commits into
mainfrom
fix/untrusted-site-pages
Open

Deicyde wants to merge 5 commits into
mainfrom
fix/untrusted-site-pages

Conversation

@Deicyde

@Deicyde Deicyde commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Part 4 of 8 in the split of #75. Draft. #213 and #214 have merged, so this PR targets main directly.

A published blueprint page is written by agents and authors, not by the owners of the site's theme. Today an article can put a <script>, a javascript: link or an imitation status mark on the project's Pages origin, and #217 and #219 will put statement review on that site. This PR treats every published page as untrusted.

It never breaks a repository. check warns and keeps its exit code, and render still publishes: a page with markup it cannot trust is shown as typed, and everything else is published as before.

Maintainer decisions (2026-10-17)

These are answers to the review at #215 (comment).

  • Scope: trim. Keep the page check, the static-file allowlist and the MathJax configuration that render writes. Drop FormulaExtension and its math fence (the Pages build no longer installs autoform) and the graph.snapshot threading. The capture half had already gone with Publish blueprint sites transactionally #165.
  • Unsafe content: warn and neutralize, never refuse. This follows Let work and search omit what they can't represent, and have check warn #325's rule that tools inform and must not break a codebase.
  • TeX macros: warn, and the site ignores them. A formula that defines notation shows an error in red. check suggests moving the notation to blueprint/tex-macros.json.
  • Authored Mermaid: keep it, drawn strictly. Scale roadmap inventories and the full DAG explorer #90 keeps render's generated maps, which need loose security for their links. So render keeps marking its own graphs (bp-graph), and every other diagram is drawn with securityLevel: "strict", which shows labels as text and runs no click.

What it adds

  • Page check (autoform_cli/publishable.py): publishable_article reads each piece the site publishes (the page, and a leaf's statement and notes) with the site's own Markdown converter. It reports:

    • raw HTML and live character references outside code and formulas;
    • unclosed comments;
    • links, images and link definitions to anything but the site's own pages and http, https and mailto addresses;
    • attribute lists, and fence braces that set more than a language;
    • fences left open at the margin.

    Each finding names its line.

  • As-typed publishing: shown_as_typed escapes a flagged page, or a flagged statement's box, into one preformatted block under a note. render publishes that instead, and check adds "the site shows it as typed until these are fixed".

  • TeX notation: formulas that define a command (\newcommand, \def, \DeclareMathOperator and so on) or use one the site turns off (\label, the mathtools tag forms) get one warning per article. The page is still published as written.

  • Published text (autoform_cli/markdown.py): render replaces a page's frontmatter with a header of its own, since the theme prints metadata unescaped. It drops complete HTML comments outside code, keeping those in code spans and code blocks. published_markdown and statement_and_notes are what check reads and render publishes. When a statement holds a <, main's body protection converts it on its own before boxing it, and check reads it that way.

  • Render (autoform_cli/render.py):

    • _publication computes each page once, for both check and render.
    • It returns the pages, the files and the warnings; only files in the way of render's own output stop render, where main fails half way.
    • Files outside the static allowlist (images, PDF, .txt, .csv, .json, .bib, .tex, .lean) and non-UTF-8 pages are left out with a warning.
    • publication_issues(graph, blueprint) gives check one list of warnings and reads source notes as pages without asking git. Files in the way of render's own output come from _in_the_way, which render checks before building anything.
    • A link, image or link definition naming a file the site leaves out keeps only its text, so mkdocs build --strict finds nothing missing.
    • Titles render writes itself (graph headings, the navigation, and the landing and generated-chapter metadata) go through markdown.title_text: the plain text a reader sees, with markup characters as numeric references, since the theme prints navigation and metadata titles unescaped.
  • MathJax (autoform_cli/mathjax.py): render writes javascripts/mathjax.js on every build. It pins MathJax 3.2.2, the ui/safe filter, and one TeX input with base, ams, noundefined, boldsymbol, mathtools and the project's macros. Commands that would let one formula change another are mapped to \notallowed.

    • An edited vault copy is replaced, with a warning.
    • A bad tex-macros.json entry is left out on its own, with a warning; valid entries are kept.
    • mkdocs.yml still lists the bundle after the script, as autoform init writes it.
  • Mermaid (autoform_cli/mermaid.py, render.py):

    • Graph labels are escaped and click links percent-encoded.
    • Diagrams are drawn one at a time at their own security level, and one that fails leaves the others drawn.
    • The flowchart palette goes only on render's own graphs.
  • CSS: a formula paints only inside its own block, and an as-typed block wraps.

  • README Validation and site sections; the setup skill notes tex-macros.json.

What changed in this revision

Size: +3,020/-294 in 28 files at 473a605; now +3,091/-278 in 18 files against main.

  • Rebuilt on main: 35a81f0 is the layer on main ec84968, and the merge commit 3e5f03e takes its tree over the old head, so the push is a fast-forward. Conflicts were resolved toward main's newer code:

    • main's link rewriting (bracketed destinations, kept queries, hidden source notes, the NUL and IPv6 guards) replaces this PR's _destination percent-encoding, which would also have double-encoded against Scale roadmap inventories and the full DAG explorer #90;
    • _sources_base keeps main's folder-prefix spellings;
    • _render_environment keeps main's _protect_markdown_in_html.
  • Merged main 400eb64 (Load a blueprint reached through a link that cannot be read #371) as 5e75131. render.py's markdown imports gain blueprint_root and directory_path, and publication_issues takes blueprint_root(blueprint), the root load_graph and render_site now use, so a blueprint behind an unreadable link checks cleanly.

  • Refusals became warnings throughout, per the decisions above.

  • Review fixes:

    • fix 2: comments in code spans are now kept too;
    • fix 3: main already guards the NUL path;
    • fix 4: a node --check test covers the generated script, and the README no longer cites a typesetting test;
    • fix 5: check uses the blueprint capture, which works on Windows, and never fails on its findings;
    • optional: check no longer spawns git for source notes, and .bp-readback names are trimmed.
  • Found in the rebuild: main now supports r<n and $a<b>c$ in statements. The check reads protected statements as render publishes them, and a formula's text excuses the markup it spells, so neither is flagged.

  • Main tests adjusted:

    • test_checking_the_output_site_hashes_its_files_without_holding_them uses a .png, since .bin is left out now;
    • test_a_capture_during_a_render_skips_the_stage_being_written hooks Path.write_bytes, which render now writes the site with;
    • the example's graph-page assertion expects the bp-graph element.
  • Review of the rebuild (eebe543):

    • A security pass confirmed five routes by running the code, all now fixed:
      • an H1 reached graph headings, the navigation and metadata as live HTML;
      • check raised a KeyError on a hidden roadmap article;
      • a left-out .svg left dangling links that fail the strict build;
      • a typed chapter got statements inside its block;
      • comment stripping misread multi-line and escaped code spans.
    • A style pass caught a fake header in a flagged statement box, and leftovers from the earlier designs.
    • Kept on purpose:

Until the next layers land

Validation

Stack

# PR Layer
1 #212 Fix roadmap loading, Lean index edge cases and undecodable JSON (merged)
2 #213 Extract Lean skeletons one probe per root module (merged)
3 #214 Run skeleton probes in parallel and isolate probe failures (merged)
4 #215 Treat every published page as untrusted
5 #216 Add read-back cards and the testimony validator
6 #284 Add statement-review commands
7 #217 Show statement review on the site
8 #219 Label statement approvals by the repository's code-owner review policy

@Deicyde

Deicyde commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

Review at f02614b

Verdict: Needs a maintainer decision on size, then fixes and a restack. This is layer 4 of the #75 split (+3,751/-311 in 27 files, on #214). check and render now treat every published page as untrusted: they refuse raw HTML, live character references, unsafe link schemes, extra attribute lists and fence braces, unclosed fences and TeX that redefines notation; render writes its own locked-down MathJax configuration and copies only images, PDFs and plain text besides Markdown. The outcome is that an agent-written or hostile article cannot run script on the project's Pages origin or fake the site's status marks. Nothing on main depends on that today, but it becomes a review-trust requirement once #217 and #219 put statement review on the site. About half the PR is already overtaken: the capture half landed via #165, and #90 removes the generated Mermaid maps this PR marks. So the size question comes before the rework.

Fixes before merge

  1. Trim to the part main still lacks. Keep the page check, the static-file allowlist and the render-written MathJax configuration. Drop the capture half (_capture_publication, the snapshot threading, the late-page refusal, the 64 MiB cap; Publish blueprint sites transactionally #165 supersedes all of it), the Mermaid marking (published_graph, GRAPH_CLASS, the div.bp-graph selector; Scale roadmap inventories and the full DAG explorer #90 removes the maps it guards) and FormulaExtension, which mainly serves Add read-back cards and the testimony validator #216's GitHub parity and is the only reason the Pages build must install autoform.
  2. HTML comments are deleted inside code too, silently (autoform_cli/publishable.py:179). Every <!--.*?--> is removed before Markdown sees the text, and render publishes that text. A fenced html block loses its comment line; a landing page's "Mark open work with `<!-- ... -->` in an article" is published with an empty code span; "opens with `<!--` and closes with `-->`" loses everything between them. Nothing errors, so check passes and the site shows different code than the vault and GitHub. Confirmed by running the pure functions with the pinned markdown 3.10.3 and pymdown-extensions 11.0.1. Fix: strip comments only outside code (outside_fences plus the inline-code mask), or refuse a comment that overlaps code.
  3. check crashes with a traceback on a link whose path decodes to NUL (autoform_cli/render.py:1733). [x](a%00.md) reaches moved_target through publication_issues (autoform_cli/render.py:267), where (source_dir / unquote(path)).resolve() raises ValueError ("embedded null character"). _check catches only GraphValidationError (autoform_cli/__main__.py:418-420). On main only render reaches this code; this PR puts it on the check path, whose input is now untrusted pages. Confirmed by trace. It fails closed, but as a traceback instead of an error line. Fix: treat an unresolvable path as not moved, or report it as a link error.
  4. The generated mathjax.js is never parsed or run (tests/test_site_math.py). The test only decodes the script's settings JSON; nothing runs node --check on it, as tests/test_render.py:697 does for blueprint-mermaid.js. The script drives MathJax internals (MathJax._.input.tex_ts.TeX, menu.constructor.loadingPromises, startup.output.reset()), so a slip leaves every formula on every site untypeset with only a console error. autoform_cli/README.md:1001-1002 and autoform_cli/mathjax.py:61-62 also cite a typesetting test that arrives only in Show statement review on the site #217 and skips in CI because no workflow sets AUTOFORM_MATHJAX_DIR. Confirmed by grep. Fix: add a node --check test here; in the layer with the harness, have CI fetch MathJax 3.2.2 or drop the README claim.
  5. Keep plain check off descriptor-only capture in the restack. Routing check through Publish blueprint sites transactionally #165's _capture_blueprint, which needs descriptor traversal, would make check fail closed on Windows, where it works today. From reading the two capture paths.

Optional

  • check reads every image and PDF in full twice (autoform_cli/render.py:797) and, without --lean-root, spawns git config and git rev-parse (autoform_cli/render.py:286) just to decide whether sources/ pages count, so its verdict depends on whether the clone has an origin. Check sources/*.md unconditionally and skip binary reads. Confirmed by trace.
  • Formula-bearing paragraphs, headings, lists and cells get overflow-x: clip (autoform_cli/render.py:2793-2794), so on a narrow screen the tail of a wide inline formula disappears. Plausible impact; consider overflow-x: auto inside statement boxes.
  • README, the .bp-readback selector and publishable_article name things that arrive in Add read-back cards and the testimony validator #216-Label statement approvals by the repository's code-owner review policy #219; trim them if those layers are cut.

Landing

Decision for the maintainer

This was a static review: no tests were run locally. CI at this head: all 9 checks pass, but on the stack's old base 8575a15.

Posted by PR swarm: PR Swarm Lead

Articles and the other vault pages are written by agents and published on
the project's site. check and render now read every published page as the
site renders it and refuse raw HTML, links to anything but the site's pages
and http, https or mailto addresses, attribute lists, fence braces that do
more than name a language, and code fences left open where render puts its
own markup. render replaces each page's frontmatter with a header of its own,
since the theme prints metadata unescaped, keeps HTML comments inside code
(as the statement an approval binds keeps them), writes the site's MathJax
configuration with the commands that reach past a formula turned off,
draws only the graphs it marks, copies only images, PDFs and plain text, and
publishes from one captured snapshot.

Part 4 of the split of #75.
Rebuilt from 473a605 onto main after #213 and #214 merged, with the
maintainer's decisions on the review at f02614b applied:

- Trimmed: FormulaExtension and its math fence, and graph.snapshot are
  gone; main's link rewriting is kept, so moved links are not
  percent-encoded.
- Nothing refuses: check warns about raw HTML, live character
  references, unsafe link schemes, attribute lists, fence braces and
  unclosed fences, and render publishes such a page, or such a
  statement in its box, as typed. Files outside the allowlist and
  non-UTF-8 pages are left out with a warning; an edited vault
  mathjax.js is replaced with one; a bad tex-macros.json entry is
  left out alone. Only files in the way of render's own still stop
  render, where main would fail half way.
- check warns about a formula that defines notation or uses a command
  the site turns off, which the site's MathJax ignores.
- Authored Mermaid keeps rendering, drawn strictly; render's own graphs
  keep their bp-graph marking and loose security for their links.
- A statement main converts on its own before boxing it, because it
  holds a "<", is read that way, and a formula's text excuses the
  markup it spells, so r<n and $a<b>c$ are published as written.
- Review fixes: HTML comments stay in code spans as well as code
  blocks; check reads source notes without asking git; a node --check
  test covers the generated mathjax.js.
473a605 sat on pre-review squashes of #213 and #214. Their merged
versions are on main, so this keeps that history reachable and takes the
rebuilt layer's tree, which is main plus this PR's own changes.
@Deicyde
Deicyde marked this pull request as ready for review October 17, 2026 23:17
Security review of 3e5f03e (all confirmed by running the code):
- A title render writes itself (graph headings, the navigation, the
  landing page's and a generated chapter's metadata) is the plain text
  a reader sees with markup characters as numeric references, so a tag,
  code span or formula in an H1 reaches the theme as text.
- check no longer fails with a KeyError on a hidden roadmap article,
  which capture skips and render never publishes.
- An image, link or link definition naming a file the site leaves out
  keeps only its text, so mkdocs build --strict finds nothing missing.
- A chapter shown as typed gets its summary and statements after the
  block, not inside it.
- Comment stripping reads code spans across lines and skips a backtick
  a backslash escapes.

Style review:
- A flagged leaf's box shows its statement and notes as typed, without
  the page header.
- The captured files are a plain dict; _sources_base keeps main's
  signature; the files in the way of render's own leave _Publication,
  and publication_issues returns one list; render and check share the
  rule for converting a statement alone; MATHJAX_SCRIPT joins the
  case-collision paths.
- Wording, a duplicate test, blank lines, and the README fixed; the
  unrelated test_markdown assertion dropped.
Brings in #371 (blueprints reached through an unreadable link). Two
resolutions:
- render.py's markdown imports keep this layer's block and add main's
  blueprint_root and directory_path.
- publication_issues takes the blueprint root from blueprint_root, as
  render_site and load_graph now do, so every node's path lies below
  it when the blueprint sits behind a link that cannot be read.
@Deicyde

Deicyde commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

Review of #215 at 5e75131

Verdict: fix before merge. The rebuild follows the 2026-10-17 decisions on scope, TeX notation and Mermaid, and the page check held up against a broad attack battery. But file and folder names still put script on the Pages origin with check silent, which is the hole this PR exists to close. There is also a bug that moves a chapter's statements, a crash on page content in check, and render refusals the description says it no longer has. Each fix is small. CI at 5e75131 passes every job, and the PR merges cleanly with main.

How it was checked:

  • Five reviewers (security, correctness, decisions and design, tests, claims). An independent verifier checked every finding, running it wherever it could be run (the Mermaid race was only read), a completeness critic looked for gaps, and a second set of verifiers rechecked the critic's two findings and SEC-1.
  • check, render and mkdocs build --strict on main and on this head for the bundled example: in the built HTML, the visible text, hrefs and ids are identical on every page. Five consumer blueprints plus Hartshorne (1,806 articles, after removing frontmatter errors that main rejects too): no page is shown as typed, and the only new warning is an Obsidian .canvas file.
  • The page check fuzzed with 3,300 random adversarial inputs, plus 100 end-to-end check and render runs, with no crash. A separate nesting-depth probe found the one crash (item 3 below).
  • 62 mutants of the security code: 49 killed. Of the 13 survivors, 5 change nothing observable. The rest are listed under "Should fix", except the open-fence mutant (behind the first test in the Nits) and one at render.py:2518 that no ordinary input reaches.
  • The generated javascripts/mathjax.js run against MathJax 3.2.2 in Node. Formulas typeset and project macros expand. \newcommand, \def, \let, \require, \href, \class, \style, \color and \unicode draw in red, and the turned-off commands map to \notallowed. Text outside the formula class is not typeset.
  • test_site_math (151) and test_render (218) pass on 3.10 and 3.13, and so do the unreadable-link tests from Load a blueprint reached through a link that cannot be read #371.

What held: no bypass of the link-scheme check in authored links (upper case, entities, control characters, autolinks, reference definitions and image sources are all flagged); item 1's folder name reaches a javascript: href through render's own links instead. Raw HTML is caught twice, by the tag pattern and by counting what the converter passes through. Generated graph labels are escaped and click links percent-encoded, an author cannot get render's bp-graph class, and the static-file allowlist ignores case. check exits 0 on every finding type.

Fix before merge

  1. File and folder names still reach script on the site. Main has the same holes, but they are the class of route the PR's opening says it closes, and mermaid._href already treats names as attacker input. If you would rather land this first, the description has to stop claiming every published page is covered, and a follow-up should close both routes before Show statement review on the site #217 and Label statement approvals by the repository's code-owner review policy #219 put approvals on the site.
    • A page with no H1, named <svg onload=X>.md: MkDocs takes the title from the file name: the theme prints it unescaped in the header and in a generated <h1>, and search/search_index.json stores it raw. PAGE_HEADER sets no title, and render writes titles only for pages in its navigation.
    • A chapter folder named javascript:alert(1), reached by book order from a link that has no scheme and passes check: render writes href="javascript:alert(1)/index.html" into the previous page's Next link with check silent, and into dependency links (bp-ref) from a page in the folder's parent (there check warns about the authored Depends-on line, but the written href stays live). A browser reads that as a javascript: URL and runs alert(1) on the Pages origin. relative_link output reaches these raw-HTML hrefs with only html.escape, and mkdocs does not rewrite raw HTML.
    • Fix for the links, checked on a copy of this head: in mermaid.relative_link, prefix ./ when the first segment holds a colon, and have render._as_published split the href as a string (href.rpartition("/")) instead of going through Path, which drops a leading ./ from the index.html links that the Next link and bp-ref use. Together these cover every caller, and ordinary links stay byte-identical (the render tests pass).
    • Fix for the title: write render's own escaped title into each page's header, the first H1 through title_text or else the stem escaped directly (title_text drops tags and returns nothing). A prototype of this closed the title route; the readers that strip the fixed PAGE_HEADER (render.py:500, publishable.py:241, markdown.py:770) must then strip by frontmatter end. Leaving such pages out in _publication with a warning was not prototyped, and a node article with such a name would then fail the graph on a missing dependency. Refusing paths that hold <, >, " or : in _capture_blueprint closes both routes too (Obsidian refuses those characters in note names, and Windows cannot check such names out), but it stops render, against the never-refuse decision. Do not drop names with & or ' ("Groups & Rings", "Zorn's lemma"); escaping already handles them. Add one test per route that scans the built site.
  2. One flagged statement moves every statement in its chapter to the end. published() adds the page to the typed set whenever it finds anything (render.py:515-517), and for a leaf that page is its chapter. _render_chapter then drops every slot (render.py:2521-2527), so all of the chapter's statements go under "Additional formalization targets", though the narrative is clean and renders normally. Reproduced with a two-leaf chapter where only one statement holds <kbd>. Fix: if whole: typed.add(page). With that change both statements return to their slots, the flagged box is still shown as typed, and test_site_math passes. Add that two-leaf case as a test.
  3. check and render crash on a deeply nested page. A list nested about 250 levels deep raises RecursionError from the site's converter through publication_issues, so check exits 1 with a traceback where main's check exits 0. (Main's mkdocs build fails on that page too, so this is about check's promise, not lost output.) Fix: catch it per page and warn, showing the page as typed.
  4. Render stops on a directory named dependencies.html, graph.html, progress.md or tex-macros.json. _in_the_way checks every name in _site_paths, which includes stale names render never writes, and tex-macros.json, which is an input. For the three stale names the error says "where autoform render writes a file", which is false. Main renders these vaults. Fix: check only the paths render writes, and treat a tex-macros.json that is not a file as no macros, with a warning.
  5. Description corrections.

Questions for the maintainer

  1. Is whole-page "as typed" what "warn and neutralize" meant? The chosen option said render publishes the page "with raw HTML escaped to text and unsafe links neutralized". The head escapes the whole page, or a leaf's whole statement-and-notes box, into one <pre> on any finding in it, and ordinary Markdown triggers it: &nbsp;, &amp;, &ge;, <br>, <sub>, <details>, and attribute lists such as {#id} or {width=50%} (attr_list is on in the scaffolded mkdocs.yml). Headings, links and formulas on that page stop rendering, and a chapter page also loses its navigation title and heading anchors. None of the consumer blueprints trips it today. If per-span escaping was meant, this becomes a blocker.
  2. Is losing MathJax's autoloaded packages acceptable? Main's page config kept the bundle's defaults, autoload included, so \cancel, \bra/\ket, \ce, \bbox, \unicode and CD worked. They now draw in red, and check does not warn. Dropping autoload as a whole is defensible, because it would bring back color, html and newcommand, but the harmless packages could be loaded explicitly. No consumer blueprint uses them today.

Should fix (fine as follow-ups)

  • BOM: a page that starts with a UTF-8 BOM and frontmatter now shows the frontmatter as visible text, because PAGE_HEADER goes in front of the BOM and MkDocs strips a BOM only at byte 0. Fix: strip a leading U+FEFF in published_page.
  • Landing page: a flagged blueprint/README.md gives a hero titled "Blueprint" with a lead escaped twice, sometimes ending in </pre>, and the as-typed note never appears there. The landing page is built from the published copy, not the author's text. The README case of test_a_page_that_is_not_an_article_is_held_to_the_same_rules skips the content check, so nothing catches it.
  • Comments in indented code: a comment that fills the first or last line of an indented code block (or the whole block) is still stripped, contrary to the description, README and published_page docstring. Comment masking blanks the line before _mask_indented_code sees it, and a blank line at either end of a block is not marked as code.
  • Untested guards: nothing runs the generated mathjax.js in CI, so its formula-class restriction, version check and early-load guard can each be removed with every test passing. The title test never reaches the navigation, so dropping title_text from the nav title survives. Also uncaught when removed: the rule that keeps only the text of a link to a non-UTF-8 page the site leaves out, and the \def notation warning.

Nits

  • On a site with a color-scheme toggle, a scheme change while diagrams draw starts a second draw() pass, which can call the global mermaid.initialize with securityLevel: "loose" while an authored diagram of a type not loaded yet (a class diagram or gantt, say) is still being parsed. Traced from the script and Mermaid's source, not run. Chain every pass on one module-level promise.
  • check uses about 6.4x the CPU on Hartshorne (1.56 s to 9.94 s user) and 7x to 32x on synthetic vaults; checking a chapter's stretches grows with slots times lines.
  • An authored frontmatter title: is dropped with the frontmatter, so the page's <title> changes; it could be kept through title_text.
  • Inline formulas wider than their paragraph are now clipped on every site (overflow-x: clip). The README says so, but the description does not list it as a visible change.
  • Obsidian .canvas files get the "a browser could run one" warning on every run; skip them like .obsidian.
  • Stale or false comments: "A card scrolls instead" (render.py:3601) and its test comment (test_site_math.py:1194), since no card at this layer scrolls (the scroll rules arrive with Add read-back cards and the testimony validator #216); moved_target's docstring says decoded paths are encoded again (they are not; check reads the rewritten text, so safety holds); publishable.py says a URL with no scheme stays on the site (//host/path does not).
  • Six exported names have no user outside their module anywhere in the stack, though the description keeps exports "which Add read-back cards and the testimony validator #216 and Show statement review on the site #217 build on".
  • The description says main "fails half way" and _in_the_way's docstring says the build would "stop half written". Main builds in a stage and fails without changing the output, so the gain is the clear message and the check warning.
  • Two tests pass on main without this PR's code (test_a_fence_the_next_margin_line_ends_is_published, test_the_landing_page_is_checked_without_the_dashboard_render_adds); assert on check's output too.

Landing

Disclosure: I wrote #371 and the four layers above this PR (#216, #284, #217, #219); this head's merge of main used the conflict resolution I sent for #371. The maintainer asked me for this verdict.

Reviewed by Claude Code (#75 stack session).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant