fix(docs): the review's four rendering defects, and the check that could see one

Review of PR #62 found four live defects and four smaller ones. The most
valuable is the first, because of what it says about the rest.

A TOC entry wrapping a heading whose rendered text already contained a link
emitted <a href="#anchor"><a href="github...">...</a></a>. The parser closes
the outer anchor early, so the entry became an empty unclickable stub and
clicking "src/draw.rs" in the TOC navigated to GitHub. 19 occurrences over 10
pages. Both hrefs resolved perfectly, so the link check, the whole-site link
check and a screenshot pass all missed it -- it answers "does every href
resolve" and this is not that question. So the fix is two things: the TOC keeps
a tag-free variant of each heading, AND a new check asserts no <a> appears
inside another <a>. Proved by reverting the first half: 66 nested anchors
reported, assembly exits 1. Clicking that TOC entry now lands on #src-draw-rs.

esc() escaped & < > but not the quote it was interpolated into title="..."
with. Five shipped bead titles contain one, and Chromium parsed the resulting
tag as having three junk attributes. Now escapes " and ' as well. Today's
corpus being friendly is not a reason to interpolate tracker text unescaped.

Seven pages overflowed at 390px by up to 92%. The reviewer attributed it to the
TOC; the TOC was real but not sufficient, and the first fix measured clean by
getBoundingClientRect while the page still overflowed, because the heading text
overflows its box without widening it. Only per-element scrollWidth names the
culprit: hundreds of snake_case test names as headings. overflow-wrap on the
headings and on .toc a, plus dropping display:table from .tickets, which had
been cancelling the mobile overflow-x guard. 0 of 23 pages overflow at 390 and
1280 now, measured.

table() split rows on every pipe before inline parsing, so `|Time Period|` --
this repo's own quoting syntax, in a corpus whose subject is that syntax -- was
shredded into extra columns. A row that still parses, so no column count could
see it. Now walked, respecting code spans and \|. Proved with a fixture: 9 tds
with the fix, 12 with the old splitter reinstated.

The four smaller ones: the link check counted 48 links it never verified behind
a comment promising they were "checked below" when there was no below -- those
are the links leaving the docs tree, and they are now resolved against the
filesystem, which is why assemble.sh runs the generator LAST, once its siblings
exist. The gotcha jump bar's hardcoded 48 is derived from the emitted anchors,
because it failed closed on deletion and silently on addition, and the list only
grows. context/code/doc-site.md cited improvise-xxxx and so broke the audit the
surrounding paragraph describes; the repo-wide audit is now 189 ids, all
resolving. And the Conventions paragraph's own example path now goes through
inline(), so the sentence demonstrates the linking instead of asserting it.

The ticket index keeps all 180 rows, open bugs and titles included, by the
user's ruling: the data is already public in .beads/issues.jsonl, the page only
makes it legible, and filtering it while the lede links the raw JSONL would be
theatre.

Gates: 1378 rust tests, 26 node --test, 16 playwright (3 skipped, webkit OPFS),
all three check-comment-runs modes, 3460 site links / 0 broken, 0 nested
anchors, 189 bead ids all resolving.

improvise-djoo

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Edward Langley
2026-09-20 13:56:56 -07:00
parent f30d512169
commit 1a0f04856e
3 changed files with 180 additions and 38 deletions
+7 -5
View File
@@ -42,11 +42,6 @@ mkdir -p "$out"
cp -f "$root/docs/index.html" "$out/index.html"
cp -rf "$root/docs/casts" "$out/casts"
# The prose site under `docs/`, generated from context/ by the same script the
# harness runs. It link-checks its own output and exits non-zero on a break, so
# a dead cross-reference fails the assembly rather than shipping.
node "$root/scripts/build-doc-site.mjs" "$out/docs" "$token"
app="$out/app"
mkdir -p "$app"
# shell.js and serial.js are imported by app.js and worker.js: leaving either
@@ -103,3 +98,10 @@ for entry in app.js worker.js; do
}
done
done
# The prose site at `docs/`, generated from context/ by the same script the
# harness serves. It runs LAST because its link check verifies the links that
# leave its own tree (`../index.html`, `../app/`) against the filesystem, which
# means their targets must already be here. It exits non-zero on a break, so a
# dead cross-reference fails the assembly rather than shipping.
node "$root/scripts/build-doc-site.mjs" "$out/docs" "$token"