From e140642ed0ebf80bed3bdfb7c56890e3c39d6b53 Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Wed, 14 Oct 2026 17:15:31 +0200 Subject: [PATCH] fix: the manual's figures carry a description, and the check that would have caught them Three pages opened in nothing in Yelp -- overview (twice), math, trigger -- while every check passed. They are exactly the pages whose
had a sentence for a and no <desc>: a Mallard <figure> is title?, desc?, media, and the page is refused without the description. The idiomatic shape, as the shipped sets on this machine write it, moves the sentence into <desc> and leaves the <title> a name. Note trigger-capture.png is referenced from both overview and trigger, which is why those two pages are the ones that were noticed. The gap was in the check, not in its reach: tools/check_help.sh ran links/media/orphans/ids and an HTML render, and all four are silent about a page that is well linked, well parsed and unrenderable. `yelp-check validate` is the one that would have caught it, and it cannot run here: it generates the RelaxNG schema with xsltproc, but the schema includes the base grammar over the network and this machine's libxml cannot fetch. So the structural rules this project has actually needed are checked with xmllint's XPath, offline, each written after a page needed it. One trap, paid for while writing it: a Mallard page declares xmlns="http://projectmallard.org/1.0/", XPath 1.0 has no default namespace, so `//figure` matches nothing and every rule passes -- the first version of this check reported "ok" for the very defect it was written for. Every expression uses local-name() for that reason, and the script says so where the next reader will be. Verified: on the committed pages the rules exit 1 and name the three figures, which is the failing-before case kept as the regression test; on the fixed pages all five rules pass and yelp-build renders all 15; make -C build test-help goes from red to green. --- help/C/org.udpscope.app/math.page | 5 +-- help/C/org.udpscope.app/overview.page | 11 ++++--- help/C/org.udpscope.app/trigger.page | 5 +-- tools/check_help.sh | 47 ++++++++++++++++++++++++--- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/help/C/org.udpscope.app/math.page b/help/C/org.udpscope.app/math.page index c2aecfa..0e207ec 100644 --- a/help/C/org.udpscope.app/math.page +++ b/help/C/org.udpscope.app/math.page @@ -89,8 +89,9 @@ are rebuilt whenever it changes.</p> <figure> - <title>The math channel editor: nodes as boxes on a canvas, the orange dot marking the - output, and the selected node's parameters beside it. + The math channel editor + Nodes as boxes on a canvas, the orange dot marking the output, and the selected + node's parameters beside it.

A sine source wired through a gain into a filter, with the output on the last node.

diff --git a/help/C/org.udpscope.app/overview.page b/help/C/org.udpscope.app/overview.page index 5b6b6df..76da886 100644 --- a/help/C/org.udpscope.app/overview.page +++ b/help/C/org.udpscope.app/overview.page @@ -33,8 +33,9 @@ is still there when you need it — an export writes it as a second column.

- The udpscope window on a live source: the header bar above the plots, the side - panel on the right, and the status bar along the bottom. + The udpscope window + The header bar above the plots, the side panel on the right, and the status bar + along the bottom.

One figure of three rows with four signals enabled, the Measure tab open in the side panel, and the status bar reading live.

@@ -95,9 +96,9 @@
- A held capture: the trigger has latched, the state reads - <gui>event held</gui>, and the axis is pinned to the event rather than to the live - edge. + A held capture + The trigger has latched, the state reads event held, and the axis is + pinned to the event rather than to the live edge.

The trigger marker sits at x = 0 and the pre-trigger share of the window is drawn to its left.

diff --git a/help/C/org.udpscope.app/trigger.page b/help/C/org.udpscope.app/trigger.page index 47923e5..1a76622 100644 --- a/help/C/org.udpscope.app/trigger.page +++ b/help/C/org.udpscope.app/trigger.page @@ -41,8 +41,9 @@
- A held capture: <gui>event held</gui> in the status bar, the axis pinned to the - event, and the trigger marker at x = 0. + A held capture + event held in the status bar, the axis pinned to the event, and the + trigger marker at x = 0.

The pre-trigger share is drawn to the left of the marker.

diff --git a/tools/check_help.sh b/tools/check_help.sh index 7a41627..cbedb59 100755 --- a/tools/check_help.sh +++ b/tools/check_help.sh @@ -3,12 +3,26 @@ # SPDX-FileCopyrightText: 2026 Martino Ferrari # # The manual's check, the way tools/check_ui.py is the .ui files' check: it runs the four -# yelp-check subcommands that need no network, then renders the whole set to HTML with -# yelp-build so a page that only fails at the transform step is caught too. +# yelp-check subcommands that need no network, checks the structural rules below, then renders +# the whole set to HTML with yelp-build so a page that only fails at the transform step is +# caught too. # -# `yelp-check validate` is deliberately absent. It fetches the Mallard RelaxNG schema over -# the network and fails on an offline machine for that reason rather than for a fault in the -# pages -- links/media/orphans/ids are the offline checks. +# `yelp-check validate` is deliberately absent, and it is worth being exact about why: it +# builds the RelaxNG schema locally with `xsltproc` and `mal-rng.xsl`, but the schema it +# generates includes the base grammar over the network, so on a machine where fetching is +# blocked it fails for that reason rather than for a fault in the pages. +# +# What that leaves unverified is *structure*, and that is not a small gap: three pages +# carrying a `
` whose `` was a sentence and which had no `<desc>` opened in +# nothing, while links, media, orphans, ids and the HTML render all passed. So the rules this +# project has actually needed are checked here, with xmllint's XPath and no schema at all. +# Add a rule when a page *teaches* one -- and do not add one you have not seen broken: a rule +# that fails a legal page is worse than no rule. +# +# Every expression uses local-name() on purpose. A Mallard page declares +# `xmlns="http://projectmallard.org/1.0/"`, and XPath 1.0 has no default namespace, so +# `//figure` matches *nothing* and every rule below silently passes. Written naively, this +# check reported "ok" for the very defect it was written for. # # Exit status is nonzero on the first finding. set -eu @@ -27,6 +41,29 @@ for sub in links media orphans ids; do yelp-check "$sub" "$dir" >/dev/null done +failed=0 +for page in "$dir"/*.page; do + while IFS='|' read -r what expr; do + [ -n "$what" ] || continue + n=$(xmllint --xpath "$expr" "$page" 2>/dev/null || echo "?") + if [ "$n" != "0" ]; then + printf ' %s: %s (%s)\n' "$(basename "$page")" "$what" "$n" >&2 + failed=1 + fi + done <<'RULES' +a figure describes itself|count(//*[local-name()='figure'][not(*[local-name()='desc'])]) +a section has a title|count(//*[local-name()='section'][not(*[local-name()='title'])]) +info is the page's first child|count(/*[local-name()='page']/*[1][local-name()!='info']) +no text sits directly under page|count(/*[local-name()='page']/text()[normalize-space()]) +a media names its src|count(//*[local-name()='media'][not(@src)]) +RULES +done +if [ "$failed" != 0 ]; then + echo "check_help: structural findings above" >&2 + exit 1 +fi +printf 'structure %s: ok\n' "$dir" + mkdir -p "$tmp/html" yelp-build html -o "$tmp/html" "$dir" pages=$(find "$tmp/html" -maxdepth 1 -name '*.html' | wc -l)