0.37.x¶
0.37.0¶
- Released:
14.10.2026
The story of this release, in five arcs:
derived fields, in lockstep with Sphinx-Needs 9.0.0 —
a derive rule in ubproject.toml computes a field or a link type for every need — a copy, a roll-up, a
transitive maximum, a hash or a fingerprint of the whole need — with the same values in both tools;
dynamic functions you can rely on —
[[…]] and <<…>> resolve in dependency order, filter= is evaluated at last, backlinks and dead-link flags
are readable, results are type-checked as Sphinx-Needs checks them, and an edit re-resolves only what it moved;
sphinx-test-reports in ubCode —
[test_reports] and all six directives, from one need per JUnit report to result tables and the report template,
in the editor, the preview, the site and the PDF;
needs written in code —
multi-line needs in source comments with a body in reStructuredText or Markdown, TypeScript and JavaScript sources,
and Python docstrings;
and a PDF that draws Mermaid and takes your classes —
ubc build pdf (alpha) draws Mermaid in a browser you already have, sets type and colour by class, prints
appendices and draws video posters.
Also: choose / when / otherwise, Pharaoh’s Intent from Code (alpha), variant data in the shared
[variants] table, one hue per token role in highlighted code, allowmixing PlantUML diagrams that keep their
content, and -c overrides that compose. New warnings can fire on an unchanged project (see Breaking Changes).
This release is the ubCode half of a lockstep with Sphinx-Needs 9.0.0: declared derived fields (derive),
including the fingerprint kind and rules per need type (derive_by_type), are read by both tools from this
release and 9.0.0 onward, with the same values and findings on a shared conformance corpus. A Sphinx build with an
older Sphinx-Needs ignores these keys without a word, and ubCode says so once per project
(config.derive_sphinx_minimum, which names 9.0.0).
✨ Computed values¶
A field a rule computes, the same in ubCode and Sphinx-Needs 9.0.0. A
derivetable on a[needs.fields.<name>]entry computes the field for every need:copya value from the need, another need or its link targets;sum,count,minormax(alsotransitive) over the needs it links or that link to it;any/all,collectandhash, narrowed withwhere. No author can set the field (needs.derive_authored),needs.jsonmarks itreadOnly, and a rule ubCode cannot read isconfig.derive_invalidwhile the configuration still loads. See Derived fields.Derived link types. On a
[needs.links.<name>]or[[needs.extra_links]]entry,derive = { kind = "links", where = "…" }links every need passing the filter andkind = "content_links"the needs the content references with:need:;include_parts = trueoffers need parts too, linked as<need>.<part>.A fingerprint of the whole need, to notice an approved need changing.
derive = { kind = "fingerprint" }holds a SHA-256 of the need’s id, type, title, status, tags, content, declared fields and links, so a link condition such as:links: REQ_1[fp == "…"]reportsneeds.link_condition_failedonceREQ_1changes;excludeleaves names out. See The fingerprint.Rules per need type,
derive_by_type— a table of need type to rule besidederive, overriding it on the types it names; a need of a type without a rule holds the field’s default (else null, or its empty value), and the field is closed on every type.typesis no longer a reserved role.Derived fields need Sphinx-Needs 9.0.0 on the Sphinx side. An older Sphinx-Needs leaves the field authorable and unset; the informational
config.derive_sphinx_minimumsays so once per project, and[lint] ignoresilences it once the Sphinx build uses 9.0.0. See Derived fields and sphinx-needs and Derived fields need sphinx-needs 9.0.0.Dynamic functions and variants resolve in dependency order. A
[[…]]or<<…>>that reads a value another one computes — another need’s field, acalc_sumover such values, a link list a call computes, a variant condition naming one — now reads the computed value, whatever order the needs and documents are written in; until now it read the value stored before resolution. See When a call reads a value another call computes.filter=is evaluated, andlinks_from_filterlinks what a filter passes. Thefilterargument ofcopy,calc_sumandlinks_from_content, andfilter_stringofcheck_linked_values, reported until now as not supported, run as Sphinx-Needs runs them, with[needs] filter_data.links_from_filter(filter, include_self=False, include_parts=False)links every need passing it, andid_parent/id_completeare readable by every filter.Backlinks and dead-link flags are readable. A call or a variant condition reads a need’s backlinks as
<link type>_back—copy('links_back'),<<['REQ_1' in links_back]: linked, unlinked>>— andhas_dead_links/has_forbidden_dead_linksare readable by calls, variant conditions, the:filter:of the view directives and needtable columns, as in Sphinx-Needs.needextendapplies by:extend_priority:. Extends apply lower priority first, default 500, as in Sphinx-Needs 9.0.0; an invalid value is reported and that extend skipped. Without the option the order is unchanged.Variant data in the shared
[variants]table. ubCode reads[variants] dataanddata_file, where Sphinx-Needs 9.0.0 and sphinx-mounts 0.3.0 read them;[needs] variant_data/variant_data_fileare still read when[variants]sets neither. Earlier Sphinx-Needs releases ignore the table, so keep the[needs]keys until the Sphinx build uses one that reads it. See The [variants] table.
✨ Test reports¶
sphinx-test-reports’ directives, in ubCode. With a
[test_reports]table inubproject.toml(an empty one will do) orsphinx_test_reportsin[parse] extensions,test-file,test-suiteandtest-caseread a JUnit report and create a need each, in reStructuredText and MyST, and:auto_suites:/:auto_cases:a need per suite and case. The section declares the three need types and their fields, so a converter’sneeds.jsonimports bare, and each mistake is a positionedneeds.test_report_*warning. See Test reports and Test reports: the directives.Report tables and the report template.
test-resultsandtest-envdraw a section and table per suite or environment, each row coloured by its result, andtest-report(reStructuredText) fills sphinx-test-reports’ template, or[test_reports] report_template, with the report’s counts, tables and needs. All six render inubc build html(alpha),ubc build pdf(alpha) and the preview, the need cards tinted by their result on the site and in the preview. See Results tables and A whole report: test-report.
✨ Needs written in code¶
Multi-line needs in source comments. With
[codelinks.projects.<name>.analyse] get_multiline_needs = true(off by default), a comment block from@needto@endneedis a need whose options read like a need directive’s; asrc-tracedraws its body, parsed in the block’s declared markup, on its card inubc build html,ubc build pdfand the preview, and the body’s labels, terms and citations register with that page, a name the page already holds being reported. Findings name the source line. See analyse.multiline_needs and Multi-line needs.TypeScript and JavaScript.
comment_type = "typescript"(or"ts") reads.ts,.tsx,.mts,.cts,.js,.jsx,.mjsand.cjsfiles,node_modules,dist,build,outandcoverageexcluded by default, and a JSDoc tag line is thejsdoc_tagwarning rather than a need. The VS Code extension attaches to the four TypeScript and JavaScript language ids; if you overrideubcode.server.documentSelector, add them. Python docstrings are now read too (see Breaking Changes). See analyse.
✨ PDF build (alpha)¶
Mermaid diagrams are drawn, in a browser you already have.
ubc build pdfdraws every authoredmermaiddiagram, directive or MyST fence, with the Mermaid the site uses, inside a Chrome, Edge, Chromium, Brave orchrome-headless-shellit finds or that[build.mermaid] browsernames: headless, on a throwaway profile, every request refused, the pictures cached, and nonodeneeded. It reports how many it drew and how long that took. Without a browser, or with[build.pdf.mermaid] enabled = false, a diagram stays a placeholder box. See Mermaid diagrams.Type and colour by class.
[build.pdf.theme.classes.<name>]sets thesize,weightanditalicof every element carrying that class, and itscolorstable repaints it in the roles[build.pdf.theme.colors]names — the paper twin of styling a class in the site’sextra_css. A table’s padding and row height scale with its type. sphinx-simplepdf’sssp-tinierandssp-tinyare built in at that extension’s values. See Type by class.[build.pdf] appendicesprints chosen pages after the body. Pages the root toctree does not reach, listed by name, follow the body in that order under one Appendices heading in the contents and the bookmarks; an entry that cannot be honoured isbuild.pdf_appendix_refused. See Appendices.A video’s poster is its picture. A
.. video::whose:poster:image can be read is drawn as that picture, sized, aligned and captioned; one without a usable poster stays a box that says why.
✨ Conditional content¶
The
choose,whenandotherwisedirectives. A.. choose::(MyST{choose}) includes its firstwhenwhose variant-data condition holds, else its finalotherwise, as Sphinx-Needs does. A mistake — an unevaluable condition, awhenwithout one, anotherwisewith one or not last — is reported once underchoose.*and skips the wholechoose, so a misspelt key never shows the default in its place. The editor fades the branches not taken and the builders leave them out. See Choosing one of several branches with choose, when and otherwise.
✨ Pharaoh (alpha)¶
Intent from Code: recover requirements, architecture and user stories from existing code. The new Pharaoh Intent from Code view in VS Code marks each source file not recovered, recovered or changed since; pick files, a stage and the evidence, and Reverse sends the work to your agent session.
ubc agent reversedoes the same from the command line, keeping a ledger under.pharaoh/rev-eng/to commit with your documents; [agent.reverse] names the stages andwrite_code_markers = falsestops the codelinks marker it writes into recovered files.ubc agent install --profile vmodeladds six recovery skills. See Recovering intent from code and the verbs.
👌 Improvements¶
Edits re-resolve far less, and schema validation is faster. An edit re-resolves only the calls and variants that read what moved, and a whole-project
calc_sumcolumn is summed once per pass: on a 20 000-need project, resolution after a one-need edit fell from about 5.5 s to about a tenth of a second. Schema validation is a quarter to a third faster on large projects, and up to half again where aselectreads only the type. Results are unchanged.An imported or external need whose
"id"is not its key is reported. It is still read under its key, now with oneneeds.import/needs.externalfinding, as Sphinx-Needs 9.0.0 reports it.Configuration errors name what a key accepts. A key that takes two shapes says which at the value itself, instead of “data did not match any variant of untagged enum”, and a key containing a dot is shown quoted (
var."a.b").Codelinks markers are read as Sphinx-Codelinks reads them. Ids after
@need-ids:may be separated by spaces, every marker on a line yields its references, and three warnings use Sphinx-Codelinks’ wording.One hue per token role in highlighted code, at AA contrast. The palette
ubc build html,ubc build pdfand the preview share gives keywords, types, keys, names, numbers, strings, built-ins and comments a hue each, every ink at least 4.5:1 on the code background; keys and values in JSON, YAML, INI and TOML now differ, and an INI section, a diff hunk header, a format placeholder and an HTML entity are coloured.The
ubc build htmlsidebar reads like furo’s (alpha). The whole row is the click target and takes a hover tint, nested entries are muted, the current page’s trail is bold and scrolled into view, links show a focus ring, and three new tokens,--ubc-nav-fg-top,--ubc-nav-fgand--ubc-nav-hover-bg, theme it.allowmixingand bold-link PlantUML diagrams keep their content in the preview andubc build html. A mixed diagram keeps its classes, enums, objects, actors, use cases and labels, a container its members,o--and*--their glyph and direction, andA => Band-[bold]->draw thick. Aneedumlorneedarchthat still draws with lines missing is reported asneeds.uml_partially_converted, naming them.Dependencies with security advisories are updated: DOMPurify 3.4.16 (GHSA-55q2-fjhq-7xh7) in the Mermaid the builders and the preview draw with, and
brace-expansion5.0.12 (GHSA-q2hr-2g5m-vwhr, GHSA-qhr7-859c-m2p7, GHSA-6j4f-fj2g-mc7p) in the VS Code extension.
♻️ Changes¶
Highlighted code changes colour on upgrade. Every default
--ubc-hl-*colour of the site, everyhl-*role ofubc build pdfand the preview’s palette take the new values; no class or token name changes. To keep an old colour, redeclare the--ubc-hl-*property in yourextra_cssor set thehl-*role under[build.pdf.theme.colors]. See Custom properties.A directive name has one owner.
[parse] extensionsswitches sphinx-test-reports on by name, or off with"sphinx_test_reports" = falsewhile keeping a[test_reports]section; a need type wins over a[parse.extend_directives]entry of the same name; a[needs.fields.<name>]entry named after a link type is reported and ignored, keeping the link type (a field namedlinksused to shadow it), whileparent_needsstill replaces the built-in. Built-in directive names: see Breaking Changes. See Extensions that carry configuration.ubc agent(alpha) refuses an unknown[agent]key in every file of theextendchain, and the language server runs the same check on each configuration load and shows its configuration-error notification, while the editor keeps using the configuration.ubc agent config-validatenames the key’s file and line.
‼️ Breaking Changes¶
One-time full re-index and re-render on upgrade. The cache format moved (6.199 → 6.240) and the build manifest with it (131 → 145). 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. Diagrams drawn by a configured PlantUML executable are rendered once more too, as their store lives in the same versioned cache. The link-check result cache and fetched intersphinx inventories are not discarded.
An external source’s
id_prefixis added as written.id_prefix = "p_"now names a needp_X; it was upper-cased toP_X. Sphinx-Needs does the same from 9.0.0 (8.x upper-cases it). If your pages reference the upper-cased ids, write the prefix upper-case in[[needs.external_needs]]— which gives the same ids in both tools and in every Sphinx-Needs release — or update the references.Codelinks’ removed
get_rstkey stops its project. The never-implemented marked-RST surface is gone:get_rst = trueor an[analyse.marked_rst]table is reported and that codelinks project is skipped, itssrc-tracedirectives creating nothing, and evenget_rst = falseis the warningconfig.codelinks_removed_key. Projects scaffolded byubc agentcarry the line twice, andubc agent updatedoes not rewriteubproject.toml: delete the lines.Codelinks reads Python docstrings, and source positions move. Unchanged Python sources can gain needs and references from their docstrings, and a docstring line whose start sequence is followed by an Epydoc field (
@param a: text) is thedocstring_tagwarning; choose a start sequence your docstrings do not use, such as[[. Of two one-line needs with one id on one row the leftmost is created, a need referenced from several places gets the URL of its first reference by path, row and column, and columns count characters, not bytes.Codelinks warnings in the build have their own codes. A malformed marker in a file a
src-tracereads was reported asneeds.src_traceat the directive; it is now reported at the marker’s source line undercodelinks.<code>(codelinks.too_many_fields, …).ignore = ["needs.src_trace"]no longer silences them;ignore = ["codelinks.*"]does. See Real-time diagnostics.A dynamic function’s result must fit its field. A number, boolean or list returned into a
stringfield, a list into aninteger,numberorbooleanfield, an item of another type into an array field and nothing into a field that may not be empty areneeds.dynamic_functionwarnings that leave the field empty, as in Sphinx-Needs; ubCode used to store"7.0"or"True". Declare the field with the result’s type (schema = { type = "number" }) to keep the value.Values on a cycle of reads, or read out of order, are warnings.
needs.derive_cyclereports a value that reads itself through what it reads — among them a variant reading the field it fills, such as:status: <<is_open: active, inactive>>withis_open = "status == 'open'", which earlier documentation showed: put the variant on another field.needs.derive_scopereports a read the resolution cannot order, such as a link field reading a computed value or a backlink, or aneedextendfilter naming a computed field. Both leave the value empty.An
ifwith no variant data configured excludes its body. Every.. if::,.. if:: Trueincluded, now reportsif.invalid_expressionand leaves its body out when the project configures no variant data, as Sphinx-Needs does: configure[variants] data(ordata_file), or remove theif. Errors inside a falsyifbody are no longer reported, and adefault-role,role,highlight,default-domain, content-lessrst-classor substitution definition inside one no longer affects the rest of the document.A need type named like a built-in directive takes the name. A
[[needs.types]]entry whosedirectiveisnote,todo,needtable,src-traceor another built-in name is now that need on every layer — parsed, indexed and drawn, its title kept — and reported asconfig.conflicting_directive_registration; with a[test_reports]section,test-file,test-suite,test-case,test-resultsandtest-envbelong to sphinx-test-reports instead. Give the type anotherdirective.Repeated
-coverrides no longer join into one document. Each-cis its own TOML document:-c "[needs]" -c "variant_data_file = 'v.json'"no longer opens the table for the second, whose key is then top-level and ignored — write it in the same-cor dotted (-c "needs.variant_data_file = 'v.json'"). A key repeated across-ctakes the last value instead of failing, and a failure names its snippet.ubc serve lspandubc serve mcpreadUBCODE_CONFIG_OVERRIDEbefore the-cvalues, so a-cwins.An unchanged project’s PDF comes out differently (
ubc build pdf, alpha). Mermaid diagrams are drawn wherever a browser is found ([build.pdf.mermaid] enabled = falsekeeps the boxes); highlighted code takes the new palette; an element classedssp-tinyorssp-tinieris set at half or three quarters of its size (size = 1.0under its[build.pdf.theme.classes.<name>]turns that off); a genericadmonitionhas a title band, and one classed with a kind’s name is drawn as that kind; a video with a readable poster shows it; and DejaVu’s own private-use glyphs are no longer drawn — list an icon font under[build.pdf] fontsfor them.New warnings may appear on an unchanged project. Beside the codes above, each failing
ubc checkand both builders on the default gate unless listed under [lint] ignore:needs.filter(afilter=once skipped fails on some needs);config.variant_data_location([variants]and[needs] variant_data*both set, the latter then ignored);config.variants_unknown_key(another key in[variants]);config(a field named after a link type);needs.uml_partially_converted(see Improvements); and inubc build pdf,build.mermaid_render_failed(a diagram Mermaid rejects) andbuild.pdf_glyph_unavailable(a character missing inside a picture, or a private-use one). Variant-data findings move toconfig.variant_data_invalid, beyond a bare"config"ignore, and values the schema never allowed, such as a date-time in[parse.extensions], are refused.
🐛 Fixes¶
A link type’s own
defaultandpredicatesare read. On[needs.links.<name>]or[[needs.extra_links]]they were ignored without a word; they now fill the link as in Sphinx-Needs, as[needs.global_options]already did.A
:delete:need is gone, body and all. A need whose:delete:is truthy no longer draws an empty card holding its body inubc build htmlandubc build pdf, nothing written inside it takes effect, its links are not checked and its images not copied, and the editor fades the whole directive.A warm build gives a clean build’s answers. After an edit,
[[copy('lineno')]]andlinenoconditions on aneedimportorsrc-traceneed, amodificationscount after a second matchingneedextendand a backlink read throughneed.links_backare recomputed; a whole-projectcalc_sumadds in need-id order, so a decimal total no longer differs between builds; and a need’s references keep one order.links_from_content()and linked sums. An emptylinks_from_content()gives the empty list without a finding and aneed_idnaming no need is reported by name;calc_sum(…, links_only=True)andcheck_linked_valuesskip a dead link, which the dead-link finding already reports, instead of failing.Variant data and render context are read as written. A
-c "needs.variant_data.<key> = …"override, or an inline table in a file that extends another, merges key by key instead of replacing the table, an invalid value in the data file is reported at its key, and a TOML date-time in the variant data or in[needs.render_context]is reported instead of being read as a nested table.network_backschema messages list incoming needs in id order, so a message no longer changes order between runs of an unchanged project.Children of an orphan page are not “not included”.
toctree.not_includedno longer reports the pages a toctree lists on a page no toctree reaches — the children of an:orphan:hub — inubc check,ubc build htmlandubc build pdf, as in Sphinx;ubc build pdfwarnsbuild.pdf_documents_excludedonly for a page that is neither listed,:orphan:nor included, and names the others on aLeft out:line.A
contentsinside a need’s card no longer moves the page’s anchors. A.. contents::in the content aneedextract,needimport,needreporttemplate orsrc-tracecard shows numbered its entries in the page’s ownid1,id2… sequence, so a:ref:to a later target on that page could land on another element; its entries now take ids of the card’s own.A class and a tab set see through
ifandchoose. A content-lessrst-classjust before aniforchooselands on the first element the branch taken shows, instead of reportingdirective.class_unsupported_target, and atab-setholding them renders thetab-itemdirectives they show instead of reportingdirective.tab_structure_invalid, as in Sphinx-Needs.In a PDF, a character is drawn from the face prose would use, and a missing one is reported, in pictures and code too. An inline literal, a listing or a
:kbd:draws a character the monospaced face lacks from the sans face, keeping its chip; prose falls back to the monospaced face; a font listed under[build.pdf] fontsdraws its private-use icons; and a character no face has inside a chart, a gantt, a drawn diagram or an SVG image is reported asbuild.pdf_glyph_unavailableinstead of drawn as a box with no finding.In a PDF, references and titles read as on the site. A named reference, an embedded
<#label>fragment and a target pointing at#REQ_1are links to what they name; a source line break inside a reference title, a need title, an:abbr:explanation or a:math:span is a space, and a need card’s title is one line.In a PDF, a generic admonition draws its title band and icon, and
.. admonition:: Titlewith:class: warning(or another kind’s name) is drawn as that kind, as on the site. Lines next to an inline literal, a:kbd:or inline maths are evenly spaced, and a character from a fallback font no longer moves its line or grows a literal’s box.VS Code no longer offers ubCode as the formatter for source files. C/C++, Python, Rust, C#, Go, YAML, shell, Markdown and TypeScript files were offered a formatter that changed nothing; the language server now registers formatting for reStructuredText documents only, which format as before.