Skip to content

fix(deps): adopt the Starlight 0.42 line and the upstream selector-parser fix, with no forced override - #187

Merged
github-actions[bot] merged 1 commit into
mainfrom
fix/starlight-042-upstream-deps
Oct 26, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
fix/starlight-042-upstream-deps

Conversation

@aitrawog

@aitrawog aitrawog commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Head 757dbad488fb650394bd2f6ddff0f4147f0f4dda — two files, package.json and package-lock.json; the size is engine-dependent and is given under How tested.

Adopts the @astrojs/starlight 0.42 line and, through the same dependency update, the upstream Expressive Code release that fixes the selector-parser advisory — with no forced overrides entry and no astro/sharp/wrangler downgrade.

Two files, and each one is named here.

file what it carries
package.json one line: "@astrojs/starlight": "^0.41.11" becomes "^0.42.7"
package-lock.json the regenerated resolution: astro-expressive-code 0.44.3 → postcss-nested 7.0.2 → postcss-selector-parser 7.1.7

What it fixes.

  • Starlight mobile menu leaves focusable button aria-expanded stale #160 — the mobile-menu button's aria-expanded went stale, because StarlightMenuButton.setExpanded() updated the wrapper and not the button. The 0.42 line rewrites that button onto the native Popover API (popovertarget), so the browser owns the expanded state and it cannot fall out of sync.
  • Track unfixed Expressive Code selector-parser build dependency advisory #159 — GHSA-rj75-hqrm-r3gf (PostCSS quadratic complexity in flat selector parsing) is still open on the base tree, at postcss-selector-parser 6.1.4. That issue's own acceptance asks for the upstream release — "update through upstream releases of Expressive Code/postcss-nested that permit a fixed selector-parser, then regenerate the docs lockfile. Do not suppress the advisory, add forced overrides, or downgrade Starlight." — and that release now exists: @expressive-code/core 0.44.3 declares postcss-nested: ^7.0.2, which resolves postcss-selector-parser 7.1.7.

What this change does not carry forward. The predecessor's overrides block is absent here: its js-yaml entry addressed GHSA-2883-xcg3-v3hh, and that advisory no longer appears in the tree at all (js-yaml resolves to 4.3.2 and is not flagged); its postcss-selector-parser entry is replaced by the upstream release the issue asks for, and a forced override is what the issue forbids. The predecessor's astro/sharp/wrangler downgrades are absent because nothing here needs them.

Supersedes #161. That PR's branch is CONFLICTING against today's main (mergeStateStatus: DIRTY) and cannot be repaired without the force-push the rulebook forbids, and its evidence was measured on a tree that has since moved. The scope is re-derived here on today's main (ed442f90895fa2786497c0f06f02bbb6b5e24507), where the upstream fix has since been published. This body names its predecessor; #161 carries the closure record.

How tested

The size of the change, and why this body states it without a line total. The two files are named above and each one is accounted for; the line total is not stated, because for a lockfile change of this shape it is engine-dependent: the two diff engines do not align the same moved blocks. Both figures are given here, each with the command that produced it, so a reader can reproduce either.

The platform's figure — the PR's own file diff, i.e. what the reviewer sees on the Files tab:

$ gh api repos/opencharly/docs/pulls/187/files --jq '.[] | "\(.filename) +\(.additions) -\(.deletions)"'
package-lock.json +254 -2036
package.json +1 -1

$ gh api repos/opencharly/docs/pulls/187 --jq '"additions=\(.additions) deletions=\(.deletions) changed_files=\(.changed_files)"'
additions=255 deletions=2037 changed_files=2

The local figure — git diff --numstat, and GNU diff and Python's difflib agree with it:

$ git diff --numstat origin/main...HEAD
134	1916	package-lock.json
1	1	package.json

Why they differ, measured by comparing the two renderings line by line (git.diff is git diff origin/main...HEAD; platform.diff is gh api -H 'Accept: application/vnd.github.v3.diff' repos/opencharly/docs/pulls/187):

$ python3 - <<'PY'
from collections import Counter
def sets(path):
    a, d = [], []
    for line in open(path, encoding='utf-8', errors='replace'):
        if line.startswith(('+++', '---')):
            continue
        if line.startswith('+'):
            a.append(line[1:].rstrip('\n'))
        elif line.startswith('-'):
            d.append(line[1:].rstrip('\n'))
    return Counter(a), Counter(d)
ga, gd = sets('git.diff')
ha, hd = sets('platform.diff')
print('git:    +%d -%d' % (sum(ga.values()), sum(gd.values())))
print('github: +%d -%d' % (sum(ha.values()), sum(hd.values())))
print('git added set == platform added set ?', ga == ha)
print('shared added lines   (git & platform):', sum((ga & ha).values()))
print('shared removed lines (git & platform):', sum((gd & hd).values()))
print('in platform added   but NOT in git added:  ', sum((ha - ga).values()))
print('in platform removed but NOT in git removed:', sum((hd - gd).values()))
print('lines present in BOTH of the platform added and removed sets:', sum((ha & hd).values()))
PY
git:    +135 -1917
github: +255 -2037
git added set == platform added set ? False
shared added lines   (git & platform): 135
shared removed lines (git & platform): 1917
in platform added   but NOT in git added:   120
in platform removed but NOT in git removed: 120
lines present in BOTH of the platform added and removed sets: 176

git's added set is a strict subset of the platform's, and the same holds for the removed set: 120 lines that the platform renders as removed-then-added are rendered by git as unchanged. They belong to a block of 176 lines that appears in both of the platform's added and removed sets — package entries that changed position in the alphabetically sorted packages map (the @bruits/satteri-* platform packages and the entries hoisted with them). So the difference is an alignment choice on a moved block, not a disagreement about content, and a line total quoted without saying which engine produced it is a number the reader cannot check.

The org's two pr_body controls ask for different answers because they read different engines — the local linter compares the body's figure against git diff --numstat, the validator against the PR's own file diff. That control conflict is opencharly/marketplace#580; it is filed below and it is not this change's to fix.

The change-class gate, on the committed tree — the site build (this repo's npm ci && npm run build, which is also what the docs-site candy runs as its image build, and the failure mode its skill names: "a Starlight config a version bump invalidated"):

$ git rev-parse HEAD
757dbad488fb650394bd2f6ddff0f4147f0f4dda

$ rm -rf node_modules dist && npm ci --no-audit --no-fund
added 299 packages in 1s
npm warn install-scripts 2 packages have install scripts not yet covered by allowScripts:
npm warn install-scripts   esbuild@0.28.2 (postinstall: node install.js)
npm warn install-scripts   workerd@1.20261012.1 (postinstall: node install.js)
npm warn install-scripts
npm warn install-scripts Run `npm install-scripts ls` to review, or `npm install-scripts approve <pkg>` to allow.

$ npm run build
[starlight:pagefind] Found 1699 HTML files.
[build] 1699 page(s) built in 37.51s
[build] Complete!

$ npm run build 2>&1 | grep -icE '\b(error|warn|deprecat|fail)\b'
0

The one warning in that paste, said out loud. It is npm 11.20.0's install-scripts advisory — npm 11 asks which packages' install scripts it may run, and the local toolchain has no allowlist for this repo. It is not a build warning and not caused by this change: the two packages it names are the same two versions in the base tree's lockfile, and this diff changes neither.

$ for ref in origin/main HEAD; do git show "$ref:package-lock.json" | python3 -c "import json,sys; pk=json.load(sys.stdin)['packages']; print(sys.argv[1], pk['node_modules/esbuild']['version'], pk['node_modules/workerd']['version'])" "$ref"; done
origin/main 0.28.2 1.20261012.1
HEAD 0.28.2 1.20261012.1

The repo's CI installs with node 22's npm 10, which has no such policy, so the warning is local-toolchain output and the CI gate never sees it. The build in the next fence ran on exactly the tree npm ci produced above, and it succeeded — nothing the build needs was skipped.

The emitted artefact, at the boundary it is actually shipped from — the built site, not the source:

$ grep -c 'aria-expanded' dist/index.html
0
$ echo $?
1

$ grep -c 'popovertarget="starlight__sidebar"' dist/index.html
1

$ grep -rl 'aria-expanded' dist --include='*.html' | wc -l
0

$ grep -rl 'popovertarget="starlight__sidebar"' dist --include='*.html' | wc -l
1698

The same greps on the base build — the counter-measurement that shows the change is what moves them (the base tree is ed442f9, built with the same commands, and the greps below are run inside its dist):

$ grep -c 'aria-expanded' index.html
1

$ grep -rl 'aria-expanded' . --include='*.html' | wc -l
1698

$ grep -rl 'popovertarget="starlight__sidebar"' . --include='*.html' | wc -l
0

$ grep -o '<button aria-expanded="false"[^>]*>' index.html
<button aria-expanded="false" aria-label="Menu" aria-controls="starlight__sidebar" class="sl-flex md:sl-hidden astro-vvx7mffu">

and on this head:

$ grep -o '<button[^>]*popovertarget="starlight__sidebar"[^>]*>' dist/index.html
<button popovertarget="starlight__sidebar" class="sl-menu-button sl-flex md:sl-hidden print:hidden astro-r2osgdlg">

The rendered code blocks, compared between the two builds — the expressive-code path changes underneath them, so #159's acceptance asks for the code blocks to be checked:

$ python3 - <<'PY'
import os, re, hashlib
def blocks(root):
    out = {}
    for dp, _, fns in os.walk(root):
        for fn in fns:
            if fn.endswith('.html'):
                p = os.path.join(dp, fn)
                html = open(p, encoding='utf-8', errors='replace').read()
                out[os.path.relpath(p, root)] = [
                    hashlib.sha256(b.encode()).hexdigest()
                    for b in re.findall(r'<pre[\s\S]*?</pre>', html)
                ]
    return out
base, head = blocks('/tmp/pin904/dist-base'), blocks('dist')
print('pages compared:', len(base))
print('code blocks compared:', sum(len(v) for v in base.values()))
print('pages whose code blocks differ:', sum(1 for k in base if base[k] != head.get(k)))
PY
pages compared: 1699
code blocks compared: 1351
pages whose code blocks differ: 0

The whole rendered site, compared — because the lockfile change replaces the entire markdown pipeline. The code blocks above are one construct. The same lockfile change also replaces @mdx-js/mdx, remark-gfm/micromark-extension-gfm*, rehype-raw/hast-util-raw, remark-parse/rehype-stringify, so tables, raw HTML and links could in principle render differently without failing the build and without touching a <pre> block. The comparison that answers that is the rendered content region of every page, base build against this head:

$ python3 - <<'PY'
import os, re
BASE = '/tmp/pin904/dist-base'   # the base tree (ed442f9), built with the same commands
HEAD = 'dist'                    # this head's build
HASH = re.compile(r'astro-[0-9a-z]{8}')   # the per-build scoped-style hash
MAIN = re.compile(r'<main\b.*?</main>', re.S)
def pages(root):
    out = {}
    for dp, _, fns in os.walk(root):
        for fn in fns:
            if fn.endswith('.html'):
                p = os.path.join(dp, fn)
                out[os.path.relpath(p, root)] = open(p, encoding='utf-8', errors='replace').read()
    return out
b, h = pages(BASE), pages(HEAD)
def main_of(s):
    m = MAIN.search(s)
    return HASH.sub('astro-HASH', m.group(0)) if m else ''
def chrome(s):
    m = MAIN.search(s)
    return s[:m.start()] + s[m.end():] if m else s
same = 0
for k in sorted(set(b) & set(h)):
    if main_of(b[k]) == main_of(h[k]):
        same += 1
print('pages compared:', len(set(b) & set(h)))
print('pages whose rendered content region (<main>) is byte-identical:', same)
print('pages whose content region contains a rendered markdown table:',
      sum(1 for k in b if '<table' in main_of(b[k])))
print('pages whose content region contains a raw <figure>:',
      sum(1 for k in b if '<figure' in main_of(b[k])))
print('pages whose content region contains footnote (<sup>) markup:',
      sum(1 for k in b if '<sup' in main_of(b[k])))
def n(f):
    return (sum(1 for k in b if f(chrome(b[k]))), sum(1 for k in b if f(chrome(h[k]))))
print('pages whose chrome carries <starlight-menu-button>  base/head:',
      n(lambda s: '<starlight-menu-button' in s))
print('pages whose chrome carries toggleExpanded()          base/head:',
      n(lambda s: 'toggleExpanded' in s))
print('pages whose chrome carries sl-sidebar-pane popover   base/head:',
      n(lambda s: 'sl-sidebar-pane popover' in s))
PY
pages compared: 1699
pages whose rendered content region (<main>) is byte-identical: 1699
pages whose content region contains a rendered markdown table: 1619
pages whose content region contains a raw <figure>: 513
pages whose content region contains footnote (<sup>) markup: 0
pages whose chrome carries <starlight-menu-button>  base/head: (1698, 0)
pages whose chrome carries toggleExpanded()          base/head: (1698, 0)
pages whose chrome carries sl-sidebar-pane popover   base/head: (0, 1698)

What that says, precisely. Every one of the 1699 rendered pages carries a byte-identical content region — the part the markdown pipeline produces, tables and raw HTML included: 1619 of them render a markdown table, 513 carry a raw <figure>, and the site uses no GFM footnote syntax at all (0 pages render footnote <sup> markup), so no footnote rendering is left uncompared. Outside that region the differences are exactly the mobile-menu rewrite, and they are uniform: the <starlight-menu-button> custom element and its toggleExpanded() script are on 1698 pages in the base build and on none in this one, and the native popover sidebar (<sl-sidebar-pane popover>) is on 1698 pages here and on none in the base. (The 1699th page is 404.html, which carries no sidebar.) The toggleExpanded() script is the mechanism #160's RCA names — the browser owns the expanded state now.

Both audit trees, on this head:

$ npm audit --omit=dev
found 0 vulnerabilities

$ npm audit
3 vulnerabilities (2 moderate, 1 high)

The honest remainder of that last line: the production tree is clean, and the dev tree still carries the undici advisories through wrangler → miniflare → undici 7.29.1. npm's own remedy is npm audit fix --force, a major bump of a deploy tool. That is #163; it is separable from this change and it is filed below rather than folded in.

What was not run here, stated plainly. The Cloudflare Pages deploy is this repo's CI deploy check on the PR; a production deploy is not run from a workstation, so no deploy output is claimed here. The check-docs bed — a disposable: true pod deploy of docs-site-app — clones the docs repo at an immutable DOCS_REF and asserts that pin is the docs repo's main head, so it cannot be pointed at an unmerged docs commit: it is the charly-side leg that follows this landing, not a step this change could run.

Rulebook compliance

  • R0 — the dispatcher's two skills for this surface were loaded before any edit: /charly-tools:docs-site (the Astro/Starlight toolchain candy and the check-docs bed, and the "a Starlight config a version bump invalidated" failure mode this change had to disprove) and /charly-build:docs (the generation half, whose drift gate this change leaves a no-op). This repo's own AGENTS.md was read first.
  • R1 — the dependency state was measured before the change was written (both audit trees, the resolved versions, the base build's counter-measurement), the build output carries no warning or error line, and the one finding this change does not clear — the dev-tree undici family — is filed as the DEV dependency tree carries undici advisories through wrangler, whose fix is a major bump #163 rather than left implied. The one warning the install step printed is npm 11's install-scripts advisory: it is named, explained and shown to be present in the base tree too, under How tested. The predecessor's evidence was re-derived on today's tree instead of carried forward: a result measured on another tree is not evidence for this one. The change's size is stated qualitatively and both diff engines' figures are pasted with their provenance, because the line total of a bulk-reordering diff is engine-dependent: a number the diff does not reproduce is a defect whether it is stale, miscomputed, or quoted from the wrong engine. The whole rendered site — not only the code blocks — is compared against the base build, and the org's two disagreeing diffstat controls are filed as the two pr_body diffstat controls disagree on a bulk-reordering diff — the body that satisfies the validator fails the linter, and the one that satisfies the linter is BLOCKed (measured on docs#187) marketplace#580 rather than worked around in silence.
  • R2 — no duplication: the fix is the upstream release, not a local component copy, a patched node_modules, a patch-package shim, or a second mechanism alongside the dependency graph.
  • R3 — no workaround: no suppression, no audit --force, no forced override, no downgrade to dodge a version. The advisory is cleared by the release that fixes it.
  • R4 — product before prose: the fix is in the dependency graph, and no documentation was edited to route around the defect.
  • R5 — nothing legacy survives: the overrides block and the three downgrades the predecessor carried are absent here, and package.json carries one line.
  • R6 — git status was read before every change; one session worktree, one fresh branch off origin/main, no force-push, no amend after the push, no rebase, no hook bypass, no direct push to main.
  • R7 — the coverage fails without the change: the base build carries aria-expanded on 1698 pages and the popover invoker on 0, this head carries the invoker on 1698 pages and aria-expanded on 0. The changed path (the rendered menu markup) was inspected in the artefact that is actually shipped; the 1351 rendered code blocks are compared between the two builds; and the rendered content region of all 1699 pages is compared between the two builds and is byte-identical, with the residual differences enumerated and shown to be the menu rewrite alone.
  • R8 — the one service boundary, the Cloudflare Pages deploy, has no local credential, so it skips visibly: it is the CI deploy check on this PR, and no deploy output is claimed here. Nothing was mocked or faked.
  • R9 — no binary and no runtime OS dependency in this repo, so there is no build stamp to match and no packaging: entry to add.
  • R10 — the change class is a docs-site dependency + lockfile change, and its gate is the site build, run on the committed tree above. The disposable: true bed for this surface (check-docs) pins the docs repo's main head and therefore cannot exercise an unmerged docs commit; it is the charly-side leg that follows this landing, as stated under How tested.
  • HC — one cutover: the dependency, its lockfile and the two issues it closes travel together, and no half of it is deferred.
  • COORD — the predecessor's scope was taken over with the comment-first window procedure (the WAITING, the window, the TAKING OVER, and the withdrawal of a premature takeover that was posted on a misread clock — all on fix(deps): bump @astrojs/starlight to 0.42.6, which removes the stale mobile-menu aria-expanded #161), and this body names its predecessor.
  • QUAL — the build output carries no warning or error line, as pasted above, and the install step's one warning is accounted for under How tested.

Change classification

A runtime change, not a documentation one. The diff edits package.json and package-lock.json — dependency metadata that decides what the deploy job installs and what the published site renders — so the documentation reviewed tier is forbidden for it.

Tier: fully tested and validated. The change-class gate (the site build) passed on the final tree with zero warnings; the changed path was executed live and inspected in the built artefact; the rendered content region of every one of the 1699 pages is byte-identical to the base build, and the differences outside it are the menu rewrite and nothing else; both audit trees are pasted above. The live Cloudflare deploy is the CI's check on this PR and is not claimed as local proof.

What this change touches, and the issues it closes:

change files issues
the Starlight 0.42 adoption and the upstream selector-parser fix package.json, package-lock.json Closes #160, Closes #159

What it leaves to #163: the dev tree's undici advisories through wrangler → miniflare, which need a different change because undici is a runtime dependency of the tool that performs the deploy — an override that satisfies the audit can still break the deploy, so the audit is not the proof.

Filed issues


Agent: umbrella-prose-truth
Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)

…rser fix, with no forced override

The mobile menu button carried aria-expanded="false" and StarlightMenuButton.setExpanded()
updated the wrapper, not the button, so assistive technology read a stale expanded state on
every docs route (#160). The 0.42 line rewrites that button onto the native
Popover API (popovertarget) and drops the manual attribute, so the browser owns the state
and it cannot go stale.

The same dependency update carries #159's fix, and this time through the
upstream release that issue asks for: astro-expressive-code 0.44.3 declares
postcss-nested ^7.0.2, which resolves postcss-selector-parser 7.1.7 - above the <7.1.6
range of GHSA-rj75-hqrm-r3gf (PostCSS quadratic complexity in flat selector parsing).
The forced "overrides" entry the previous attempt needed is therefore NOT carried, and
neither is an astro, sharp or wrangler downgrade: the js-yaml advisory that override
addressed (GHSA-2883-xcg3-v3hh) no longer appears in the tree at all - js-yaml resolves to
4.3.2 and is not flagged. #159's acceptance requires exactly this - "update
through upstream releases of Expressive Code/postcss-nested that permit a fixed
selector-parser, then regenerate the docs lockfile. Do not suppress the advisory, add
forced overrides, or downgrade Starlight."

package.json moves exactly one line: @astrojs/starlight ^0.41.11 -> ^0.42.7. The lockfile
regenerates and now resolves astro-expressive-code 0.44.3 -> postcss-nested 7.0.2 ->
postcss-selector-parser 7.1.7. The dev tree is untouched by this change: the undici
advisories reached through wrangler -> miniflare -> undici 7.29.1 remain, and they are
tracked in #163 - a different change with a different blast radius.

Verified on the committed tree: npm ci rc=0 (299 packages); npm run build rc=0, 1699 pages,
no warning or error line; npm audit --omit=dev reports "found 0 vulnerabilities"; in
dist/index.html the menu button carries popovertarget="starlight__sidebar" and aria-expanded
occurs ZERO times, against 1698 pages that carry aria-expanded="false" on the base tree;
and all 1351 rendered code blocks are byte-identical to the base build, so the
expressive-code path change alters no rendered code.

Refs #159
Refs #160
Supersedes #161

Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
@aitrawog

Copy link
Copy Markdown
Contributor Author

STATUS — #187 — slug umbrella-prose-truth — the successor to #161 is open at head 757dbad488fb650394bd2f6ddff0f4147f0f4dda, and the takeover it rests on is now real.

The authority for the takeover. The WAITING on #161 went up at 2026-10-26T05:49:47Z and named a 60-minute window. The TAKING OVER — authority: window-expired went up at 2026-10-26T06:50:31Z, after the window closed at 06:49:47Z, with the clock in the comment and every fact re-verified at that instant: the head still 15541bc9ed5aa8c6aa18dfecc3bd2d186feb78e7 (DIRTY), the only two checks still the red runs from 2026-10-16, and no owner answer on the thread.

What is in this PR. @astrojs/starlight ^0.41.11 → ^0.42.7 and the regenerated lockfile, which resolves @expressive-code/core 0.44.3 → postcss-nested 7.0.2 → postcss-selector-parser 7.1.7 — the upstream release #159's own acceptance asks for, with no forced overrides entry and no downgrade. It closes #159 and #160.

The proof on the committed tree. The site build passes with zero warning lines; the built site carries the popover invoker on 1698 pages and aria-expanded on none, against the base build's 1698 pages carrying aria-expanded="false"; the 1351 rendered code blocks are byte-identical between the two builds; npm audit --omit=dev reports 0. The full paste is in the PR body.

What it deliberately leaves. The dev tree's undici advisories through wrangler → miniflare — #163, claimed by this slug, sequenced behind this landing because it edits the same two files. The overrides block the predecessor carried is not carried forward: one of its entries is redundant upstream now, the other is the forced override #159 forbids.

Next. The validator's verdict on this head. Every finding it raises is fixed in one commit per round, and the two issues are closed by hand when the merge lands.

Agent: umbrella-prose-truth
Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)

@github-actions

Copy link
Copy Markdown

Review — BLOCK

I re-derived every claim in the body and the one comment against the supplied diff. Most of it checks out; one headline claim does not.

What checks out

  • package.json — exactly one line: "@astrojs/starlight": "^0.41.11" → "^0.42.7". No overrides block, no astro/sharp/wrangler downgrade. Matches the body's table and R5.
  • package-lock.json — the resolution chain the body names is genuinely present: @astrojs/starlight 0.42.7, astro-expressive-code 0.44.3, @expressive-code/core 0.44.3 declaring "postcss-nested": "^7.0.2", postcss-nested 7.0.2, and postcss-selector-parser 7.1.7 (base was 6.1.4). The advisory-relevant edge is cleared by an upstream range, not a forced override — consistent with the body and with Track unfixed Expressive Code selector-parser build dependency advisory #159's stated acceptance.
  • No downgrades — astro stays 7.4.0, sharp ^0.35.5, workerd 1.20261012.1, esbuild 0.28.2 (the esbuild hunk is a pure relocation with byte-identical content), matching the body's for-loop evidence.
  • Scope/cleanliness — the lockfile changes are the coherent consequence of the Starlight 0.42 / @astrojs/mdx 8.1.1 / @astrojs/markdown-satteri 0.4.5 tree (dedupe of @astrojs/internal-helpers 0.13.0, hoisting of satteri 0.10.5, removal of the @mdx-js/mdx/remark-gfm/rehype-* stack). No leftover override, no patched node_modules, no second mechanism.
  • Security — no secrets, no unsafe input handling introduced; the change reduces the vulnerable transitive set. The dev-tree undici residue is correctly declared and deferred to the DEV dependency tree carries undici advisories through wrangler, whose fix is a major bump #163 rather than suppressed.
  • Comment (6410053036, @aitrawog) — it is the PR author's own status post, not a maintainer sign-off (no designated maintainers are configured for this run, so no comment can be a sign-off). Its substantive claims (head SHA, version chain, greps, audit) mirror the body and are consistent with the diff; it states no diffstat, so it does not independently carry the defect below.

Blocks

1. pr_body — the stated diffstat is contradicted by the diff.

  • Evidence: the body opens with “Head 757dbad… — 2 files changed, 135 insertions(+), 1917 deletions(-).” The supplied per-file counts are package-lock.json +254/−2036 and package.json +1/−1, i.e. 2 files, +255/−2037. Both numbers are wrong by ~120 lines in each direction. The file count (2) is right; the insertion/deletion figures are not reproduced by the diff at all, and this is the body's own bolded summary of the change's size.
  • Why it matters: this body is built on a claim of exact, re-derived measurement (“Two files, and each one is named here”; “the dependency state was measured before the change was written”; R1). A precise numeric claim that the diff does not reproduce is exactly the “file counts … actually reproduced by the diff” honesty check, and it is either stale (carried from the superseded fix(deps): bump @astrojs/starlight to 0.42.6, which removes the stale mobile-menu aria-expanded #161 tree the body itself says must not be imported) or miscomputed.
  • Required fix: correct the summary line to the diff's actual figures (2 files, +255/−2037), or drop the numeric claim and describe the change qualitatively. No code change is required — the dependency edit itself is sound.

Non-blocking observation

The body's artefact evidence covers the menu button (aria-expanded → popovertarget) and the 1351 <pre> blocks, but the same lockfile change replaces the entire markdown pipeline (@mdx-js/mdx, remark-gfm/micromark-extension-gfm*, rehype-raw/hast-util-raw, remark-parse/rehype-stringify). Tables, footnotes, autolinks, and raw-HTML-in-markdown could render differently without failing the build or altering a <pre> block, so the “fully tested and validated” tier rests on a narrower comparison than the change's blast radius. Not a blocker on its own — the build did produce all 1699 pages — but a base-vs-head comparison of one GFM/raw-HTML page would close the gap honestly.

Because the body makes a specific, checkable claim about the change's size that the diff does not support, and that claim is the first line of the description, this does not pass the honesty gate as written.

Verdict: BLOCK


ollama-cloud/deepseek-v4.1-flash — action-review.

View action run

@aitrawog

Copy link
Copy Markdown
Contributor Author

STATUS — #187 — slug umbrella-prose-truth — round 1: the validator's BLOCK is fixed, the body is rewritten, and the head is unchanged at 757dbad488fb650394bd2f6ddff0f4147f0f4dda.

The finding. The body's headline stated a diffstat of "2 files changed, 135 insertions(+), 1917 deletions(-)". The validator compares the body's figure against the PR's own file diff (package-lock.json +254/−2036, package.json +1/−1 → 2 files, +255/−2037) and BLOCKed the body: the stated figure is not reproduced by the diff.

The RCA — the mechanism. Both engines render the same commit pair (ed442f9 → 757dbad) and disagree because they align a moved block differently. This change is a lockfile reorganization: package entries are hoisted and deduped, and a block of them changes position in the alphabetically sorted packages map. Measured by comparing the two renderings line by line: git's added set is a strict subset of the platform's, and the same holds for the removed set — 120 lines that the platform renders as removed-then-added are rendered by git as unchanged; they belong to a block of 176 lines present in both of the platform's added and removed sets (the @bruits/satteri-* platform entries and the packages hoisted with them). GNU diff and Python's difflib agree with git. The two figures are therefore one diff under two alignment choices, and a line total quoted without naming its engine is a number the reader cannot check. The measurement is pasted in the body.

The RCA — the missed control. Two, and they are the same control in two places.

  1. In the body: the diffstat was derived from a local git diff --stat and never checked against the artifact the reader and the validator actually read — the PR's own file diff. The validator's check is that check, and it caught the gap. The root fix is in the body: the size is now stated qualitatively in the headline, and both engines' figures are pasted with the command that produced each, so either can be reproduced.
  2. In the tooling: the org's two pr_body diffstat controls read different engines — marketplace/scripts/pr_body_lint.py compares the body's figure against git diff --numstat, the validator against the platform's file diff — so on a bulk-reordering diff no body can satisfy both, and the only escape is to drop the number, which is the very check the DIFFSTAT rule exists to enforce. That control conflict is non-blocking for this change (it lives in two other repos and is genuinely separable), and it is filed with its measurement as the two pr_body diffstat controls disagree on a bulk-reordering diff — the body that satisfies the validator fails the linter, and the one that satisfies the linter is BLOCKed (measured on docs#187) marketplace#580, claimed by this slug, and listed under Filed issues in the body.

What this round changes. No code change — the validator's own words are "No code change is required — the dependency edit itself is sound", and the dependency edit is untouched. The body now:

  • states the size qualitatively in the headline and points at How tested for the figures;
  • pastes the platform's per-file counts, the local git diff --numstat, and the line-by-line comparison that explains the gap between them;
  • answers the validator's non-blocking observation by comparing the rendered content region of all 1699 pages, base build against this head: byte-identical on every one. That covers what the code-block comparison could not — the lockfile change replaces the whole markdown pipeline (@mdx-js/mdx, remark-gfm/micromark-extension-gfm*, rehype-raw/hast-util-raw, remark-parse/rehype-stringify), so tables, raw HTML and links are compared too: 1619 pages render a markdown table, 513 carry a raw <figure>, 0 use footnote <sup> markup, and the differences outside <main> are the mobile-menu rewrite alone, uniform on 1698 pages (the 1699th is 404.html, which carries no sidebar).

The mechanical control agrees. pr_body_lint.py on the rewritten body, run against the worktree with --base origin/main: 0 findings.

What is not claimed. The Cloudflare Pages deploy stays what it was — this repo's CI deploy check, which passed on this head (13m12s) — and no deploy output is claimed locally. The check-docs bed still cannot exercise an unmerged docs commit, for the reason stated in the body. The dev tree's undici advisories remain #163, unchanged and still sequenced behind this landing.

Next. The validator's verdict on the republished body. Every finding it raises is fixed in one commit per round, and the two issues are closed by hand when the merge lands.

Agent: umbrella-prose-truth

Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)

@github-actions

Copy link
Copy Markdown

Review — PASS

I re-derived every claim that the supplied diff can settle, and dispositioned all three comments against the current state.

What the diff establishes (checked line by line)

  • File count and scope. Exactly two files: package.json (+1/−1) and package-lock.json (+254/−2036). The body names both and states the file count as 2 — correct.
  • package.json. The single hunk is the one line the body claims: "@astrojs/starlight": "^0.41.11" → "^0.42.7". There is no overrides block anywhere in the file's full diff, and no astro/sharp/wrangler edit. sharp ^0.35.5 and astro ^7.4.0 are untouched.
  • The resolution chain the body names is really in the lockfile. @astrojs/starlight 0.42.7 → astro-expressive-code 0.44.3 → @expressive-code/core 0.44.3 declaring "postcss-nested": "^7.0.2" → postcss-nested 7.0.2 → postcss-selector-parser 7.1.7, with the base's postcss-selector-parser 6.1.4 entry removed. The advisory-relevant edge is cleared by an upstream range, not a forced override — matching the body and Track unfixed Expressive Code selector-parser build dependency advisory #159's stated acceptance.
  • No downgrades. astro 7.4.0 unchanged; esbuild 0.28.2 and workerd 1.20261012.1 appear as pure relocations/context with byte-identical content; the body's for-loop evidence about those two install-script packages is consistent with the diff.
  • Lockfile coherence. The removals are exactly the dropped markdown stack (@mdx-js/mdx, acorn/acorn-jsx, estree-*/recma-*, remark-gfm + micromark-extension-gfm*, rehype/rehype-raw/rehype-parse/rehype-stringify/rehype-format, hast-util-raw/hast-util-from-html/parse5/entities, mdast-util-mdx*, style-to-js/inline-style-parser, source-map, vfile-location, retext*/parse-latin, unist-util-* orphans), plus the dedupe/hoist of @astrojs/internal-helpers 0.13.0 and satteri 0.10.5 with its @bruits/satteri-* optionals moved to 0.10.5. Nothing left dangling that the diff shows, no patched node_modules, no second mechanism. This is the coherent consequence of the Starlight 0.42 / @astrojs/mdx 8.1.1 / @astrojs/markdown-satteri 0.4.5 tree.
  • Honesty of the size claim. The body no longer states a single headline line-total. It gives the platform figure (+254/−2036, +1/−1 → +255/−2037) and the local git diff --numstat figure (134/1916, 1/1 → +135/−1917), each with the command that produced it, and explains the 120-line-per-side gap as an alignment choice on a moved block. The platform figure it quotes matches the supplied per-file headers exactly; the internal arithmetic (135 shared additions = git total, 1917 shared removals = git total, 120 platform-only on each side, 176 lines in both platform sets) is self-consistent. Nothing here is contradicted by the diff.
  • Other numeric claims. js-yaml is not touched by the diff; since the removed @astrojs/internal-helpers 0.10.4 required js-yaml ^4.3.0, the body's "resolves to 4.3.2" is consistent, and the removed entry's presence explains why the predecessor's override was redundant. The dev-tree undici residue is declared and deferred, not suppressed.
  • Security. No secrets, no unsafe input handling, no privilege change; the change strictly reduces the vulnerable transitive set. The residual dev-tree undici family is disclosed and filed (the DEV dependency tree carries undici advisories through wrangler, whose fix is a major bump #163) rather than papered over with audit fix --force.

Comment dispositions

  • 6410053036 (@aitrawog) — author status post. Its head SHA matches the given head 757dbad…; its version-chain and grep claims mirror the body and are consistent with the diff. Not a sign-off: no designated maintainers are configured for this run, so no comment can approve.
  • 6410067988 (@github-actions[bot], action-review) — STALE, dismissed. Its block was that the body's headline stated "2 files changed, 135 insertions(+), 1917 deletions(-)" against the platform's +255/−2037. The current body contains no such headline: it states the size qualitatively and pastes both engines' figures with provenance, explaining the discrepancy the bot flagged as "wrong by ~120 lines in each direction." Its non-blocking observation (markdown pipeline not compared) is likewise answered by the added all-1699-pages <main> byte-identity comparison, which covers tables (1619 pages), raw <figure> (513), and footnotes (0). Both points are resolved in the current state.
  • 6410339709 (@aitrawog) — author round-1 status post. Head unchanged at 757dbad… (matches), body rewrite described accurately, and the "no code change required" framing is corroborated by the diff (the dependency edit is untouched). Not a sign-off.

Non-blocking observations

  1. Node engine floor rises silently. @astrojs/mdx 8.1.1 and @astrojs/prism 4.1.1 in the lockfile carry "engines": { "node": ">=22.23.4" } (base was >=22.12.0). The body discusses CI's npm 10 but never this floor. Worth confirming the deploy/CI Node version satisfies it; npm ci only warns unless engine-strict is set, so a stale CI image would surface later, not at install.
  2. Attested-but-unverifiable evidence. The build paste, grep counts, npm audit output, and the base-vs-head site comparison cannot be reproduced from the supplied diff. They are internally consistent and nothing in the diff contradicts them, so I treat them as attested rather than blocked — but they are not independently checkable from this review's inputs.

No demonstrable defect remains in the changed files, and the prior block is genuinely fixed in the current body. The dependency edit itself is sound.

Verdict: PASS


ollama-cloud/deepseek-v4.1-flash — action-review.

View action run

@github-actions
github-actions Bot enabled auto-merge (squash) October 26, 2026 07:12
@github-actions
github-actions Bot merged commit b5cb810 into main Oct 26, 2026
6 of 7 checks passed
charly-auto-merge Bot added a commit that referenced this pull request Oct 26, 2026
github-actions Bot pushed a commit that referenced this pull request Oct 26, 2026
…declares (#189)

#187's lockfile carries "engines": {"node": ">=22.23.4"} on
@astrojs/mdx 8.1.1 and @astrojs/prism 4.1.1. The repo declared its node as the
floating major "22", and actions/setup-node@v7 resolved that through its own
version manifest to v22.23.3 — one patch below the floor, although v22.23.4 had
been released six days earlier (2026-10-20). The deploy job therefore installed
the site's dependencies on a node they declare unsupported, and npm said so
twice (EBADENGINE) in the install log; the install still exits 0, so that warning
was the only signal.

A floating major cannot guarantee a floor: the floor was published ahead of the
alias's resolution. Pin the exact version the tree's dependencies require, so CI
installs a compliant node deterministically, and the declared toolchain and the
declared requirement stop being two files that never compare themselves.

Measured on the committed tree, same npm on both sides (10.9.9): node 22.23.3 ->
npm ci warns EBADENGINE twice (>=22.23.4 required, v22.23.3 current); node
22.23.4 -> 0 warnings, and the site builds 1699 pages with 0 warning lines.

Refs #188

Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant