fix(gate): read CommonMark autolinks in check:published-readme-links - #10764
Merged
Conversation
`extractLinks` matched only `[text](dest)` and `[label]: dest`, so a bare URI in angle brackets -- the CommonMark autolink, rendered as a live link by both GitHub and npm -- was read by none of the gate's four assertions. 13 docs-site links across 6 published READMEs sat in that hole, every one of them in the "Docs" / "API Reference" footer. All 13 resolve today, so nothing was broken through it; what was missing was the population the gate's own header claims. Adds AUTOLINK as a third recognizer, transcribed from the CommonMark scheme production. Autolinks are matched last and filtered by SPAN, because a pointy-bracket destination `[d](<https://x/y>)` that contains no space is also a well-formed autolink body -- a span-blind recognizer would count it twice and inflate the printed census by one per occurrence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
os-zhuang
marked this pull request as ready for review
August 21, 2026 10:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #10657
The gap
extractLinksinscripts/check-published-readme-links.mjsmatched exactly two shapes —the inline link
[text](dest)and the reference definition[label]: dest. It did notmatch the CommonMark autolink: a bare absolute URI wrapped in angle brackets, which
GitHub and npm both render as a live link.
The gate's own header states its subject as "every outbound link in a PUBLISHED markdown
file". An autolink is a link by that definition, so the shape was owed to the population
and missing from it: an autolinked docs URL was read by none of the four assertions —
no page resolution, no anchor check. A 404 written in that form shipped to npm green.
Nothing was broken through this: every autolink in the tree resolves today. This is
invariant restoration on a latent hole, not a live defect.
Derived population — 13 links in 6 files
Derived from the gate's own population helper (
publishedDocs) and its own proseextraction, rather than adopted from an estimate:
@objectstack/adapter-honopackages/adapters/hono/README.md@objectstack/corepackages/core/README.md@objectstack/driver-memorypackages/drivers/driver-memory/README.md@objectstack/plugin-securitypackages/plugins/plugin-security/README.md@objectstack/restpackages/rest/README.md@objectstack/service-packagepackages/services/service-package/README.mdAll 13 classify
docs-site, all 13 are already on the canonical origin, and all 13resolve — so the widened gate stays green.
How 13 reconciles with the two prior counts
The issue estimates ten; a repo-wide grep reported matches in seven files. Both
differ from the gate's own population, and both differences are fully explained:
service-package's three. Its line census lists five files(hono, core, driver-memory, plugin-security, rest) at 2 each. But its route list
includes
/docs/protocol— andservice-packageis the only file in the tree whoseautolink carries that route. So the card's route census saw the file its line census
dropped. 10 + 3 = 13.
examples/app-crm, which isprivate: truewith nofilesarray, so it is outside the published population by design. Its one autolink isa
localhostURL, which would classifyexternaland be read by no assertion even ifthe file were published. 14 − 1 = 13.
What changed
One file. A third recognizer,
AUTOLINK, transcribed from the CommonMark schemeproduction (2–32 chars, opening with an ASCII letter, then a colon, then a run of
characters that are not ASCII control, space, or either angle bracket).
Autolinks are matched last and filtered by SPAN, not by pattern. A pointy-bracket
destination — angle brackets inside an inline link's parentheses — is a different
construct that
INLINE_LINKhas always read. When such a destination happens to containno space it is also a well-formed autolink body, so a span-blind recognizer counts it
twice and inflates the census this gate prints by one per occurrence.
extractLinksnowrecords the range each link construct consumed and drops an autolink starting inside one.
The self-test pins this as a count, not a presence — presence alone cannot see a
double-count.
Verification
Positive control reproduced first. The shipped extractor, on the input the issue
reports, returned
[]:(
LT/GTstand in for the literal angle brackets, per the issue's spelling note.)Gate delta, measured before and after against the real tree — the
afterextractorreads exactly 13 more links, all of which resolve:
Reject side pinned, one case per shape that wears angle brackets without being a
link: bracketed prose with no scheme, an HTML tag, a tag with attributes, and CommonMark's
address-shaped email autolink (deliberately not matched — it can never be a docs-site
URL, so every assertion would classify it
externaland read no further; matching itwould widen the recognizer and change no verdict).
One finding worth naming: assertion 1 is structurally unreachable through this form.
A CommonMark autolink body must be an absolute URI, so a root-relative path in angle
brackets is not an autolink at all — GitHub and npm render it as literal text. My first
draft pinned "A1 fails on a root-relative autolink" and it went red, correctly. The pin is
now written in the direction that is actually true, with a comment explaining why, so a
later author does not read the missing A1 pin as a hole and "fix" it by dropping the
scheme requirement — which would start claiming every HTML tag in the tree. The self-test
summary line was corrected too: it now says assertions 2, 3 and 4 are re-observed failing
and A1 is pinned unreachable, rather than claiming all four.
Ablation, predicted before running. Neutralising the new pattern (
AUTOLINKreplacedwith a never-matching regex; mutation confirmed on disk by anchor count, removed 1→0 and
injected 0→1) was predicted to redden 11 of the 20 new pins — the 9 negative and
discrimination pins should survive, because a recognizer that matches nothing still
"correctly" stays silent on non-links. Measured: exactly 11, and the named 11 were the
predicted set. The gate itself fell back to
152 / 12under ablation, confirming all 13links come from the new pattern alone. The restore leg was verified in both directions —
ablation marker absent, real pattern present, tree byte-identical to the commit.
Gates
Derived with
node scripts/pm/dispatch-gates.mjs(no paths — it reads the change set fromthe merge base itself), then run at final commit
3713e89:check:published-readme-links165 outbound link(s) across 60 published markdown file(s): 0 root-relative, 0 non-canonical origin(s), 25 docs-site page(s) resolvedcheck:published-readme-links --self-testcheck:cross-package-test-inputsAll 90 self-test cases passed./OK: 13 package(s) read outside themselvescheck:parse-guardcheck:nul-bytesOK (scanned 6234 text file(s) ... no raw ASCII control bytes)check:entry-guard125 scripts/ file(s) — every entry guard goes through invoked-as.mjscheck:published-readme-exportsis red in this worktree, and it is not this branch:all 194 of its findings are the
dist/... does not exist. Build firstshape from anunbuilt fresh worktree. It does not import this module, and this branch does not touch it.
No changeset: gate tooling, publishes nothing. Labelled
skip-changeset.Generated by Claude Code