0.35.x¶
0.35.0¶
- Released:
11.09.2026
The story of this release, in five arcs:
a PDF is now one command and one binary —
ubc build pdf (alpha) typesets the documents the root toctree reaches into a single paginated file,
with no Sphinx and no LaTeX in the path: prose, tables that span pages, images, need cards,
needlist and needtable, and needpie / needbar as vector charts,
with anything not yet typeset drawn as a labelled placeholder box and counted;
MyST grew attributes, images, equations and heading links —
{#id .class key=value} groups give any element an id, CSS classes and per-element options,
raw <img> tags become real images that the build collects and checks,
bare \begin{align} environments typeset without a $$ fence,
and a #fragment link reaches a plain heading in either document, reStructuredText targets included;
the needs domain got its interactive table —
a needtable sorts, filters, pages, hides columns and exports to CSV in the browser,
in ubc build html (alpha) and in the editor’s rendered preview alike;
cross-references land where they should, and a finding names the file it is written in —
a directive :name: whose slug was already taken now anchors the element you named,
an .. envvar:: becomes a real object description, a target carrying a line wrap reaches its inventory,
content a needextract pulls in no longer shifts the host page’s anchors,
and a warning inside an included fragment, a prologue or a viewed need’s body
names the file that holds the text rather than a plausible line of the page;
and the tools tell you what they are doing —
a live progress bar while a project is indexed, while a site or PDF is built and while its links
are checked, Cypher path queries that no longer rebuild every element’s field map,
and HTML anchors allocated once instead of by three walks over the same page.
Ten new warning-level diagnostic codes reach ubc check, the editor and ubc build html,
all on by default, so an unchanged project can see new warnings (see Breaking Changes);
the PDF builder’s own twelve codes are reported only by ubc build pdf.
✨ PDF build (alpha)¶
ubc build pdf— the whole project as one PDF (alpha). The documents your root toctree reaches, concatenated into one paginated file with an outline, real internal links and page numbers, typeset from the same index the editor andubc checkuse. It draws prose, headings and lists, tables that span pages with their header row redrawn, images and SVG, externally rendered PlantUML diagrams, admonitions and version-change boxes — in the site’s own colours — and needs as cards, withneedlistandneedtable. See Building a PDF.--need-idand--need-filterbuild a selection, out of whole documents in toctree order.--need-filtertakes the same Python-like expression over need fields that ubc query filter takes;--need-idis repeatable, with the ids unioned. An id that names no need, or an expression that will not compile, exits2; a selection that matches nothing writes no file and exits1. See Building a subset of needs.needpieandneedbarare vector pictures, drawn straight into the page in the palette the site uses — paths and real text, not a picture of a chart. With:legend:the legend follows as a table. See Charts are vector pictures.Never quietly incomplete, and reproducible. A construct the builder cannot typeset yet — footnotes and citations, maths,
contents,raw,video,download,todolist,needflow,needsequence,needganttand Mermaid — is drawn as a labelled placeholder box and counted on abuild.pdf_*warning naming it. The default bar is--deny warning, as forubc build html, and two builds of unchanged sources are byte-identical. See What is not rendered yet and Warnings and the quality gate.Configuration under
[build.pdf]: the page size, the four margins, the base type size, the external PlantUML renderer and the quality bar. The file lands at_build/pdf/<root_doc>.pdf, opens with a title page and carries each document’s title in the top margin, both on by default. See PDF document. It is in alpha: the typeset output, the theming surface and the available options may still change between releases, so it is not yet recommended for production deliverables. There is no incremental PDF build yet — the index underneath it is incremental as ever.
✨ MyST¶
Attributes:
{#id .class key=value}. With theattrs_inlineandattrs_blockextensions enabled, an attribute group gives an element an id, CSS classes and per-element options, inline or on a line of its own. An#idbecomes a cross-reference target; on a directive fence the whole group becomes directive options. Five diagnostics report what MyST passes over in silence:attrs.invalid_value,attrs.invalid_choice,attrs.no_target,attrs.unsupported_targetand the informationalattrs.unsupported_key. See Attributes and Markdown attributes.html_image: a raw<img>tag becomes a real image. A standalone<img src="pics/logo.png" width="50%">behaves like an{image}directive: the file is collected into_images/, thesrcis rewritten, and a missing file is reported at build time. Six attributes beyondsrcare read as the matching{image}options and the rest are dropped; a tag with nosrcreportsmd.html_image_no_srcand leaves its block raw. See Parsing.amsmath: bare LaTeX environments.\begin{align}…\end{align}and the other twelveamsmathenvironments, each also in its starred form, typeset as display maths with no$$fence. Equation numbers are not emitted. See Parsing.A
#fragmentreaches a heading.[](#some-heading)and[](other.md#some-heading)both resolve to a plain heading now, with no explicit(target)=, and the target document may be reStructuredText, so[](guide.rst#installation)reaches that section. A heading is the last rung, so an explicit target, a label, a document of that name and an inventory match all still win. See Cross-referencing with links.
✨ Needs¶
Interactive
needtables. Aneedtableis now interactive by default: the reader sorts by column, filters the rows, pages through them, hides columns, and copies or downloads the table as CSV — inubc build htmland in the editor’s rendered preview alike;:style: tablekeeps the static table. It is the same asset Sphinx-Needs ships, against the same markup contract. New with it: the:page_size:option and the[needs]keystable_style,table_page_sizeandtable_page_sizes. See Needs.
✨ Cross-references¶
Environment variables:
.. envvar::and the:envvar:role. The directive drew a placeholder and the role drew a chip; both now work, on reStructuredText and Markdown pages alike. An environment variable reaches everything a cross-reference target reaches:objects.inv, the:any:ladder, the general index, site search, and the editor’s hover and go-to-definition. A name defined twice reportsstd.duplicate_envvar; a dangling:envvar:stays silent, as in Sphinx.
✨ The command line¶
Progress while a project is indexed. Every command that indexes a project for a person —
ubc check, everyubc build,ubc report,ubc schemaandubc diff— now shows a live progress bar on a terminal while the files are parsed:indexing 412/1001 [████████░░░░░░░░░░░░] 4s
It counts the files this run actually parses, so a warm run with one edit reads
1/1.--verbosereplaces it with oneparsed <path>line per file;--quiet, a pipe, a file, a CI job and aTERMthat isdumb, empty or unset each suppress it on their own.ubc queryand theubc agentverbs stay silent. See while it works.ubc build html(alpha) andubc build pdf(alpha) then show one bar per build phase after the indexing one —rendering,assemblingandfinishingfor the site,lowering,laying outandwritingfor the PDF — each row cleared before the next opens, and the last before the summary line. The same suppressor rules apply, and under--verbosethe existingTimings:line reports each phase’s duration instead.
👌 Improvements¶
One PlantUML executable for every builder.
[build.plantuml]names the renderer once for the project; a builder’s own table such as[build.html.plantuml]inherits every key it does not set and wins on every key it does. See the shared renderer table.A diagram both builders draw is rendered once.
ubc build htmlandubc build pdfshare one content-addressed store of externally rendered PlantUML diagrams, so running both costs one run per diagram.The language server reports indexing progress as a percentage. Its
ubcode indexing$/progresstoken now carries apercentageand anN/M filesmessage — one report per whole point, strictly increasing — so an editor shows how far a cold index has got.ubc build linkchecksays what it is doing while it does it (alpha). Before the first request it printsChecking 143 URLs on 37 hosts (98 from cache), and on a terminal it then draws the same progress bar the indexing phase draws:checked 37/45 [████████████████░░░░] 12s 9 hosts waiting
A failing
ubc build linkcheckprints only the findings that failed it, so one dead link is not buried under the redirects that did not fail anything. The bar is the deny threshold, orwarningwhen only--max-warningstripped, and the lower of the two when both did; a run that withholds anything says how many and names the bar.A cosmetic redirect is no longer reported by
ubc build linkcheck. A site root redirecting to that site’s own landing page —https://example.test/tohttps://example.test/en/stable/— and a barewww.label appearing or disappearing now read as working, joining the trailing slash, the default port and the dropped fragment. Stored results are re-read under the new rule, so no URL is re-requested. See Link checking.Much faster Cypher queries that navigate a path. A query that only navigates a path —
nodes(p)orrelationships(p)in a list construct, element equality,length(p),count(p)orp IS NULL— no longer rebuilds the full field map of every element on the path. Measured at 100k nodes in the change’s own benchmarks, such queries run 4 to 76 times faster, and the openCypher conformance suite is unchanged either side — the same scenarios pass, and every skip reason is identical.HTML anchors are allocated once, while a document is lowered, instead of by three walks over the same page. Indexing is faster on every pass, and building or previewing a page that uses footnotes or citations is faster end to end; a page with neither is slightly slower to build. Anchors are otherwise unchanged, except that a section heading carrying an
:index:or an:envvar:role now publishes one anchor rather than two.A warning names the file the construct is written in. A finding inside an
.. include::d fragment, or inrst_prolog/rst_epilog, is reported against the fragment at its own line; a finding inside a card shown by aneedextractnames the need’s own document.ubc check, both builders andubc build linkchecknow print the identical string for the identical fault, and theubc build htmltail(showing N locations)reads(N shown).The font licence notices travel with every copy of
ubc.THIRD-PARTY-NOTICES.mdandOFL-1.1.txtare staged beside the downloaded executable and at the root of the installed VS Code extension.
‼️ Breaking Changes¶
Unchanged pages render differently. Every
needtablethat sets no:style:of its own is now an interactive table rather than a static one; set[needs] table_style = "table", or:style: tableon the directive, to keep the old one. Source comments and the MyST block break+++stop reaching the built page. A toctree entry naming a titleless document disappears from the in-page list and the sidebar, and that document’s title reads<no title>... envvar::and:envvar:draw their real output, soobjects.invgains a row per definition. A view directive whose:filter:met a need holding no value for a field renders its real selection instead of failing. An inline image writes itsaltattribute before itssrc.Some anchors move. Content pulled in by a
needextract,needimport,src-traceorneedreportnow anchors under the card’s own namespace, and a host page’s ownid{N}andindex-{n}anchors shift back to the sequence it has with no view on it. A:name:whose slug was contested or comes out empty gets a different anchor. A:ref:, anobjects.invlink and a:need:stay correct; a deep link copied by hand off a built page may need updating.per-file-ignoresmeans one thing on every channel: a finding is suppressed when a rule matches any file that finding names. An included fragment’s finding names the fragment and the page it was spliced into, so a rule naming either still silences it. The one family that gained a file is a card shown by aneedextract: it names the need’s own document and not the page, so an existing page-scoped rule stops suppressing findings inside those cards — name the need’s home document instead. See Code matching and .* wildcards.Unknown MyST extension names are now reported.
[parse.parsers.<name>] extensionsused to pass an unrecognised name through. Only the names ubCode implements take effect now, and any other is reported:config.extension_unimplementedfor a MyST-Parser name ubCode has not implemented —substitution,linkify,html_admonition,smartquotes,replacements— andconfig.unknown_md_extensionfor a name MyST-Parser does not recognise either.attrs_imagereportsconfig.extension_deprecatedand keeps working. Remove the entry, or silence the code under Linting. See Parsers and file routing.New warnings may appear on an unchanged project. Ten new warning-level codes reach
ubc check, the editor andubc build html, and on the default gate (deny = "warning") a warning failsubc checkandubc build html. Two fire on any project:toctree.no_title, for a titleless toctree target, andstd.duplicate_envvar, for two.. envvar::definitions of one name. Three fire on an existing[parse.parsers.<name>].extensionslist:config.extension_unimplemented,config.unknown_md_extensionandconfig.extension_deprecated. Five arrive only once a new MyST extension is enabled —attrs.invalid_value,attrs.invalid_choice,attrs.no_target,attrs.unsupported_targetandmd.html_image_no_src; the informationalattrs.unsupported_keynever fails a default gate. Two existing codes also fire anew on Markdown pages: every well-formed[^label]is a footnote reference now, so prose that merely looks like one — a bare regex character class in running text — reportsref.unknown_footnoteandref.too_many_autonumbered, and a case-mismatched[^Foo]against a[^foo]:definition does the same, withstd.footnote_unreferencedfor the definition. Silence a code individually or by family under[lint]:ignore = ["attrs.*"]. The twelve newbuild.pdf_*codes are not in this group: they are reported only byubc build pdf.One-time full re-index and re-render on upgrade. The cache format moved (6.167 → 6.180) and the build manifest with it (99 → 112). The first run after upgrading rebuilds the index and re-renders every page once — about the cost of a cold build — then incremental behaviour resumes. Self-healing. The link-check result cache is not discarded: its own version is unchanged, so
ubc build linkcheckre-reads every stored verdict and requests nothing it did not have to.
🐛 Fixes¶
A
:name:on a directive is now reached by its own name. When that name’s slug was already taken, the directive was renumbered but every reference to it kept landing on whatever won the slug, silently.:ref:, MyST links andobjects.invnow all reach the element you named.A cross-reference to a target after a
needextract,needimport,src-traceorneedreportnow lands on it. Content one of these pulls in used to take its anchors from the host page, shifting everything below it, so such a reference landed on the wrong element in HTML, or on nothing at all in a PDF.A need holding no value for a field no longer aborts the whole query. A filter such as
'x' in statusused to fail for the entire project as soon as one need held no value forstatus— inubc query filter, the editor’s needs panel, the MCP query route and every view directive’s:filter:. Such needs are treated as not matching now, and the encounter is reported once per query.Built pages no longer publish your source comments. A comment written in the source reached the built page as an HTML comment, so authoring notes were readable in the page source. They are dropped now, and the MyST block break
+++renders nothing in a built page; the editor preview still draws its dashed rule for+++.A cross-reference whose target carries a line wrap reaches its inventory. ubCode collapsed whitespace in a cross-reference target for standard-domain object kinds only, so a
:ref:,:doc:,:term:,:any:or:external:reference written across two lines missed everyobjects.invit should have matched.A toctree entry naming a document with no title is reported, not rendered. Such an entry generates no link, exactly as in a Sphinx build, and now reports
toctree.no_title. The document still builds, and reads the placeholder Sphinx shows for it. A.. title::does not count as a heading; see A page can override its browser title.Every MyST
[^label]is a footnote reference, whether or not a definition was written nearby — and a reference resolves against every definition on the finished page, including across an{include}splice in either direction. A label is matched exactly, so[^Foo]does not answer a[^foo]:. See Footnotes.Footnote parity. A footnote finding written inside an included fragment names the fragment and its own line instead of the top of the host page, so
ubc checkon the fragment reports it. A footnote marker inside link text no longer nests one link inside another, and the.. contents::listing and the “On this page” panel keep and drop the markers a Sphinx build does. See Footnotes and citations.A glossary term’s index entry reports the term’s own line, not the first line of the file, so
ubc query searchand the editor’s reference search no longer send you to the top of the page.Incremental builds reconcile a page’s index entries. An edit made in a warm session kept the page’s pre-edit entries, so site search and
ubc query searchcould show entries at stale lines, miss a second definition of aconfvalorenvvarname, and keep serving a deleted.. index::.