0.39.x¶
0.39.0¶
- Released:
04.11.2026
The story of this release, in five arcs:
Numbering, as in Sphinx (beta) — sections, figures, tables, code blocks and equations are numbered from one model,
[build.numbering], and:numref:and:eq:print the number, in the preview, the editor and both builds.With Sphinx-Needs 9.2.0: where needs come from, and per-type models — a filter reads where a need comes from (
c.source) and followsinherits(c.is_a); a need type grades its declarations, counts the links it receives and shapes its ids; summary link types.Code coverage and semantic codelinks (alpha) — every marker names the code entity it documents,
ubc codelinks coveragelists the functions that trace to no need, in the editor too, andubc difflists the needs whose code changed since it was linked.Directives of your own (experimental), with sphinx-ub-directives 0.1.0 — directive presets and template directives, declared in
ubproject.tomland read alike bysphinx-build.Test reports, with sphinx-test-reports 4.0.0 and ub-test-reports 2.0.0 — a test-report type takes an entry of its own in
[needs.types], and pytest’s lines are read from the right base.
Also in this release:
The language server counts columns in UTF-16, so navigation on a line with non-ASCII text lands on its target.
Every needs.json reader checks
format_versionfirst, and a need that is not an object no longer drops the file.Directive option names match ignoring case, as in docutils.
Typos in field and link-type entries are reported.
ubc serve mcpexits promptly when its client goes away, and release binaries are about 7 % smaller on Linux.
Numbers appear and new warnings can fire on an unchanged project (see Breaking Changes).
This release is the ubCode half of a lockstep with Sphinx-Needs 9.2.0: c.source, c.is_a, summary,
copy and collect on a link type, empty, where on copy, and severity, back_links and
id_regex on a need type are read by both tools from this release and 9.2.0 onward, with the same answers on the
shared conformance corpora. Marker attachment and generated ids follow sphinx-codelinks 2.1.0, test-report types and
line_base sphinx-test-reports 4.0.0 and ub-test-reports 2.0.0, presets and templates sphinx-ub-directives 0.1.0.
✨ Numbering (beta)¶
Sections, figures, tables, code blocks and equations are numbered. One model,
[build.numbering], decides which items carry a number, how it is written and what a numbered reference prints. With nothing configured, ubCode numbers what Sphinx numbers by default: the sections of the documents a:numbered:toctree lists, and every labelled display equation. Captions (Fig. 1.2,Table 1,Listing 1) are numbered oncefigure,tableorcodeisenabled;sections.from = "all"numbers every chapter andequation.allevery display equation. A table gives the ubCode key for each Sphinx setting. See Numbering.Numbered references.
:numref:({numref}) and:eq:/:math:numref:({eq}) print the target’s number and link to it. A missing label isstd.numreforstd.eq; a target with no number still links and says why (std.numref_not_numbered,std.eq_not_numbered); a document two numbered toctrees both number istoctree.secnum. In reStructuredText, hover shows the number and go to definition opens an equation’smathblock; MCPresolve_referenceanswers withnumber. See In the editor and for agents.The site and the PDF, each with its own switches.
[build.html.numbering]and[build.pdf.numbering]inherit every key they leave unset and takesuffixandshow(on the sitesidebar,prev_nextandtitleare off by default). Pages listed in[build.pdf] appendicesare lettered (A.1,Fig. A.1), and a:numref:to one readsAppendix A. Each build checks a reference against the numbers it prints, so one into an appendix is reported byubc checkandubc build html, not by the PDF build. See Numbering sections, figures and equations, Numbering and Appendices.docutils
sectnum... sectnum::(.. section-numbering::, MyST{sectnum}) numbers its page’s headings, with:depth:,:start:,:prefix:and:suffix:, and a.. contents::on the page lists the same numbers. See Numbers for the page’s headings (sectnum).
✨ With Sphinx-Needs 9.2.0: where needs come from, and per-type models¶
Filter needs by where they come from:
c.source.c.source.kind(page,import,external,code,report),c.source.pathandc.source.line(the file the need is written in, an included file rather than the page),c.source.paths(every file it came through, innermost first) andc.source.document/c.source.sections, in every filter that selects needs; refused, naming the place, in a field’spredicates, aneedimport:filter:and a variant condition. See Where a need comes from: c.source.Filters and Cypher follow
inherits.c.is_a('req')is true for areqand for any type inheriting from it;type == 'req'still compares the name. Ac.is_aliteral naming no need type isneeds.filter_type, with a did-you-mean, so a misspelt type there no longer empties a view in silence. In Cypher asys_reqneed is(:sys_req:req), andn.typestays its own type. See Special functions and Need types and inherits.A need type grades, counts and shapes.
severity = "violation" | "warning" | "info"on a type’sfieldsorlinksentry grades its failures (severity);[needs.types.<t>.back_links.<l>] schema = { minItems = 1 }counts the links a need of the type must receive (incoming links);id_regexon a type is matched beside[needs] id_regexand each ancestor’s (the ids of a type).Summary link types, and a trace’s far ends as links.
summary = truemarks a link type whose links are shortcuts across the trace: a needflow without:link_types:,ubc agent’s orphan gate and impact walks andubc diff’s impact chains (unless--allow-linknames it) leave it out, and the Need Viewer tree shows it one hop deep.copyofidon the first rung andcollectbelow give every need of a trace the requirements it reaches as links. See Summary link types and A chain as links: copy and collect on a link type.Derived fields:
emptyandwhereoncopy.empty = falseon anallrule makes a requirement that nothing tests readfalse; acopywithoverorjointakes awhere. See any and all and copy.Imported and traced needs stand where their directive does: a need from
needimportorsrc-tracehas its directive’ssectionsand the need it is written inside as itsparent_need. See Importing needs.
✨ Code coverage and semantic codelinks (alpha)¶
A marker names the code it documents. Every one-line need, multi-line need and
@need-ids:reference is attached to at most one code entity by the seven rules of sphinx-codelinks 2.1.0, never across code, and the entity’s qualified name is the filter facetc.source.symbol(c.source.symbol == 'Engine.start'). See Which code a marker is attached to.Find the code no requirement asks for.
ubc codelinks coveragelists, per codelinks source file, the traced percentage and the functions and methods that trace to no need, in C, C++, Rust, Python, TypeScript and JavaScript, libclang’s preprocessor included. A marker on a unit, its class, module or file traces it; acodelinks-exclude:comment or a ledger justifies it;symbol_fieldslets a requirement name the code it covers. The policy is the new[codelinks_coverage]table. Withgap = truean untraced unit is acode_untracedgap forubc agent, and the MCP tooluntraced_symbolsgives an assistant the same report, suggesting the nearest files for apath_prefixthat names none.--changed-since <REF>(MCPchanged_since) lists only the code a change touches, and-f lcovexports an lcov tracefile for a coverage service. The command and the tool need a licence. See Finding code that traces to no need and untraced_symbols.Coverage in the editor. With
[codelinks_coverage]and a licence, the language server marks each untraced unit with an Information diagnostic and each unknown one with a Hint (sourceubcode-coverage), as of the last index pass; in VS Code, ubCode: Show Code Trace Coverage shows the report as test coverage, per file and function and in the gutter. See See it in the editor.Code changes since linked. When both sides declare
[codelinks_coverage],ubc diffandubc diff gitprint aCODE DIFFsection: each need whose traced functionchanged, waslinkedorunlinked, or wasremoved. Reformatting and comment edits are not listed. An entry makesubc diffexit1;--no-codekeeps the needs-only exit code. See Code changes since linked.
✨ Directives of your own (experimental)¶
Directive presets.
[parse.directive_presets.<name>]declares a directive that is an existing one with preset options (options,append_options,force_options,argument); a preset named like its base gives that directive project-wide defaults. A use renders, is indexed and is checked as its base, and the editor completes it. See Directive presets.Template directives.
.. ub-template::renders a Jinja template with data from[parse.template_contexts]and inserts the result in the page; generated needs are ordinary needs.[parse.directive_templates.<name>]declares a template directive of your own. Templates are sandboxed and bounded, and see the variant data asvar... ub-template::is switched on with[parse] ub_template_enabled = true; a declared template directive needs no switch. See Templates: generating content from data.The same tables build with Sphinx: sphinx-ub-directives 0.1.0, a new Sphinx extension, reads them for
sphinx-build, and both tools pass its two conformance corpora. See With Sphinx.
✨ Test reports, with sphinx-test-reports 4.0.0 and ub-test-reports 2.0.0¶
A test-report type takes an entry of its own.
[needs.types.testcase](ortestfile,testsuite), bare or with atitleand aprefix, narrows the type[test_reports]adds: itsfields,links,inherits,back_links,id_regexandseverityapply, so a link type or a rule can sit on the test cases themselves. On the Sphinx side this needs Sphinx-Needs 9.2.0 and sphinx-test-reports 4.0.0. See What the section adds.pytest’s lines, read from the right base.
case_lineis the test’s own line for pytest’s JUnit reports, which count from 0; aub_test_reports:line_basemark or[test_reports] line_base("auto",0,1) decides the base. See Lines of pytest reports.
👌 Improvements¶
A diagram that falls back to its placeholder keeps its number, so later figure numbers and a
:numref:to it match the PDF.One number per equation. KaTeX and the PDF’s typesetter no longer number an unlabelled
alignor an unstarredequationon their own, as with Sphinx’s MathJax; an explicit\tag{…}is still drawn. See Numbered equations.needs.json readers check
format_versionfirst, refusing a later format as Sphinx-Needs’ reader does; a need that is not an object is skipped and reported, where it dropped the file; a need with noidhas its key asid; and a needs.json older than Sphinx-Needs 4.0 imports its needs’descriptionas content. See External needs.Configuration typos are reported: an unknown key in a field or link-type entry, or an unknown
schemakeyword, is aconfigwarning, where it was dropped in silence, and a name close to a declared one isconfig.type_near_name. See Fields.Hover on a link option shows its
target_types, andubc query filterreads what a view reads (back-link fields, the dead-link flags,var.*,[needs.filter_data]). See Backlink fields.A variants data-file override swaps the file, never the table:
-c "variants.data_file = '…'"and-c "needs.variant_data_file = '…'"are one override, as-D needs_variant_data_fileis in Sphinx-Needs. See A data-file override swaps the file, never the table.The editor’s agent views follow
ubcode.server.configOverride(alpha), Tailor workflow splices[workflow]asubc agent install --detectdoes, andubc agent nextsays which open streams can continue (in_graph,actionable) and names itsarm, the one actionrunandprompttake (author,review,refix,fix_diagnosticsornone). See Settings and Ask for the next step.ubc agent reverse status --summary(alpha) prints a compact answer that stays small on very large projects: single-line JSON, each recovery unit’s counts once, and dropped lists replaced by counts. See ubc agent reverse status.ubc serve mcp --tools NAMESserves only the tools it names (--tools query_cypher,get_graph_schema); the others are neither listed nor answered, and an unknown name stops the server at startup. See Serving fewer tools.ubc serve mcpexits within about six seconds when its client goes away, where it waited for every abandoned tool call.Smaller release binaries (about 7 % on Linux), from whole-program link-time optimisation and compressed PDF fonts.
Dependencies with security advisories are updated: DOMPurify 3.4.17 (GHSA-cg2g-xh64-v4q7, critical, and two more) and KaTeX (GHSA-238p-pmpm-9mq7) in the Mermaid bundle;
zstd0.14 (RUSTSEC-2026-0391, -0398, -0403, -0410), which decompresses the embedded assets;ctutils0.4.5 (RUSTSEC-2026-0385), under the licence check; andtokio1.54.1 (RUSTSEC-2026-0386), under the MCP server.
♻️ Changes¶
Directive option names match ignoring case, as in docutils and Sphinx-Needs:
:Status: opensets the status, on need directives,needextendand the test-report directives, in reStructuredText and MyST; of two MyST options matching one name the exact spelling wins. Declared names that differ only in case are aconfigwarning. See Components and markup.[needs] id_regexis applied as Sphinx-Needs applies it: from the start of the id (Python’sre.match), in Python’s syntax; a pattern ubCode cannot read is reported.ubc schema validatetags a violationsn_schema_violation(wassn_schema_error).A map iterates in the order it was written, as a Python dict does, on every Jinja surface:
ubc reporttemplates and insights,needreport,needumlandneedarch, need templates, an external source’starget_url, string links andubc agent’s prompt templates. Output that iterated a map may reorder;|dictsortrestores the sorted order. Templates also gain{% break %},{% continue %}andurlencode.
‼️ Breaking Changes¶
One-time full re-index and re-render on upgrade. The cache format moved (6.256 → 6.287) and the build manifest with it (147 → 154): the first run after upgrading re-indexes every project and
ubc build htmlre-renders every page; nothing is lost and nothing needs doing. The link-check result cache and fetched intersphinx inventories are not discarded.New warnings can fire on an unchanged project, and fail the default gate of
ubc checkand the builders until ignored or fixed:std.numref,std.numref_not_numbered,std.eq,std.eq_not_numbered,toctree.secnum,build.numbering_mismatch,build.pdf_numbering_mismatchandbuild.pdf_equation_number_kept;needs.filter_type;configfor an unknown entry key or schema keyword, anenumon a boolean, or names that differ only in case;config.type_near_name, deliberate plurals included; andtemplate.switched_offfor a.. ub-template::a page already held. The likeliest is a:numref:to a figure while figure numbering is off, the default, as Sphinx warns:[build.numbering.figure] enabled = true. See Configuration diagnostics (config and config.*).Numbers appear on an unchanged project: a
:numbered:toctree numbers its sections, a labelled display equation prints(N)and a.. sectnum::numbers its page, in the site and the PDF. An unlabelled equation loses the number its engine drew: label it, or set[build.numbering.equation] all = true.[build.numbering] enabled = falseprints none (in a builder table, for one output;showdrops one surface).Cypher: a need’s labels are its type lineage, so
MATCH (n:req)also matches the types inheritingreq;WHERE n.type = 'req'keeps the old answer.A failed per-type declaration at
violationis an error, whereneeds.invalid_field_valueandneeds.invalid_link_valuewere warnings:--deny errornow fails on them, and--max-warningsno longer counts them.severity = "warning"restores the old grade.Test-report directives without
:id:fail the default gate: their generated ids, in lower case, fail the default[needs] id_regex(needs.id_regex), as a Sphinx build refuses those needs. Give them an:id:, set anid_regexthey match (a test-report type’s ownid_regexchecks them too), or ignore the code.Test-report fields belong to the test-report types: a need of another type that sets
case_nameorresultgetsneeds.undeclared_for_type; declare the field on that type or under[needs.fields].case_parameter,case_fileandcase_lineare null where the report gives none (case_line is None, not== '').pytest test cases move one line down:
case_linefrom a plainpytest --junitxmlreport is one more.[test_reports] line_base = 1reads unmarked reports as before.Some
[test_reports] extra_optionsnames are refused, as sphinx-test-reports refuses them (config.test_reports_extra_option_reserved), and a directive writing one makes no need: remove the name. See the refused names.Codelinks markers without an id become needs (see Fixes): two with one title are
needs.duplicate; give them distinct titles or ids. A marker is no longer attached across code to a later entity.A need’s
parent_needsis its nesting: an imported record’s statedparent_needsis no longer kept; carry the relation in a link type of your own.A needs.json older than Sphinx-Needs 4.0 imports content, which can raise
needs.import.denied_assetorneeds.link_ref: allow the assets withallow_assets = trueon the source (assets_rootscopes them), or fix the references.Answers change on an unchanged project: an option that was ignored for its case now sets its field; two data-file overrides naming different files are refused (pass one);
docnamein a field’s or link type’s default predicate names the page for asrc-traceneed;docnamein a link condition names the target’s page; a[needs] id_regexthat matched anywhere may report ids;json_remove_defaultsleaves out links equal to a link type’s default; a template that iterated a map may reorder; and the commandsubc agent(alpha) writes out for an agent carry more flags, and its editor views refuse a malformed[workflow]override.An unchanged project’s PDF comes out differently (beta): numbered headings, captions and equations, and lettered appendices; the
[build.pdf.numbering]keys switch each off.Lockstep with Sphinx-Needs 9.2.0: a project adopting
c.source,c.is_a,summary,copyorcollecton a link type,empty,whereoncopy,back_links,id_regexon a type orseverityneeds Sphinx-Needs 9.2.0 on the Sphinx side, and a[needs.types.testcase]entry sphinx-test-reports 4.0.0 too.
🐛 Fixes¶
A codelinks marker written without an id is a need, with the id sphinx-codelinks 2.1.0 gives it, where it was dropped without a word; a marker whose id slot is empty makes no need. See generated ids.
Google Test’s
value_paramandtype_paramreachcase_parameter, where it was null.Navigation on lines with non-ASCII text: go to definition, references, hover and completion count columns in UTF-16 units, as the editor does, and
initializedeclarespositionEncoding: "utf-16"; diagnostic columns are not converted yet. See Use ubCode from any editor.Navigation to an imported need in an included file lands in that file, not the host page, and a need there whose id fails
needs.id_regexis reported on its own:id:.An external source’s
target_urlthat renders an absolute URL is used as given, andversion = ""reads itscurrent_version;ubc build validate-jsonandubc diffreport a need whose"id"is not its key.ubc agent(alpha):config-validateanddoctorread need and link types as a build does; the commands it writes out for an agent carry the call’s-p,-cand--config-name, quoted for the shell;install --detectkeeps the tables after its[workflow]block; and change requests no longer crowd the needs graph view (its CR button shows them) or the stream lists.Run QA scores the answer, not the reasoning before it (alpha): a model’s reasoning object before its answer no longer hides the scores, and a response with unknown or repeated axis IDs or conflicting scored objects is refused rather than mis-attributed, the existing verdict kept. See Running a review.
Deeply nested documents no longer crash
ubcon Windows: every input-driven thread runs on an 8 MiB stack.A template that calls
'…'.count('')(an empty substring) now gives a template error instead of hanging the render forever, on every Jinja surface: template directives (in the editor too),ubc report, string links,needreport,needumlandneedarch, need templates andubc agent’s prompt templates.An imported or external need whose string field holds a JSON number is kept, the value as text (
3becomes"3"), where the need was dropped with a warning;ubc build validate-jsonstays strict.ubc codelinks coverageno longer crashes on a non-ASCII identifier (über,straße) (alpha).