Skip to content

feat(scripts): check-doc-links 扫描面第三扩 CONTRIBUTING/ROADMAP/docs (#3572) - #3589

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-3572-scan-roots-third-ext
Aug 7, 2026
Merged

feat(scripts): check-doc-links 扫描面第三扩 CONTRIBUTING/ROADMAP/docs (#3572)#3589
yinlianghui merged 1 commit into
mainfrom
claude/issue-3572-scan-roots-third-ext

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #3572

SCAN_ROOTS 追加三行,全部沿用既有的 disk 规则,不新增规则类 / reason / hint:

{ path: 'CONTRIBUTING.md', rule: 'disk' },
{ path: 'ROADMAP.md',      rule: 'disk' },
{ path: 'docs',            rule: 'disk' },

这三面正是 #3536 头注释里 "Still not bought" 点名的下一批,当时标价「one SCAN_ROOTS row each — plus fixing what that turns red」。红账已由 #3545 / PR #3571 单独付清(3 条死链),所以本次是纯三行

实测(与 #3571 的预告逐项吻合)

先预告再跑,四项全中:

文件 可见链接 可判定 被围栏隐藏 死链
CONTRIBUTING.md 1 15 1 10 0
ROADMAP.md 1 6 4 0 0
docs/** 15 49 47 0 0
合计 17 70 52 10 0

node scripts/check-doc-links.mjsLinks are valid across 6 scan roots.,exit 0。零清理清单——对照 #3479(16 个死目标)、#3490(18 个)那种「门禁与欠账同时到货」。

docs/** 是三者中从未被量过的一面。Lychee 的扫描范围虽然列了它,但那个工作流只有 schedule + workflow_dispatch —— pull_request / push#3213 ruling B 被刻意注释掉,拦不住任何 PR。它的 49 条链接里 47 条此前从未被任何能让构建失败的东西解析过

已知边界:围栏内的链接看不见(如实写进头注释)

stripCode() 在扫描前抹掉围栏与行内代码(这正是相对链接检查得以安全开启的前提,#3536 已论证),所以围栏内的链接对本门禁不可见,死活都不可见

不是假想:CONTRIBUTING.md 共 25 条链接,10 条在围栏内,门禁真正判定的只有 1 条(其余 14 条是 #anchor 与外链)。那 10 条正是 #3570 的实例类——一段演示 docs 链接写法的围栏,其"正确示例"路由自己是死的。修那段文字是 #3570 的事;指出没有门禁够得着它是本注释的事。头注释同时写明为什么不该靠放宽 stripCode() 来补:围栏内合法地存在不是链接的 […](…),区分「示意路由」与「可执行路由」是另一个门禁的活。

#3570 的并行协调

本 PR 未触碰 CONTRIBUTING.md(#3570 正在改其内容)。真仓跑对该文件报出 0 条——与预期一致:它要修的三条死"示例"路由都在围栏内,stripCode 抹掉了。围栏边界测试用的是 fixture 而非真实文件内容,不与 #3570 的编辑抢同一段文本。

测试(+8,57 → 65)

pin 测试是否自动覆盖新行?一半是,一半不是 —— 这正是 #3542 自述里的 anti-vacuous-green 教训:

  • 两个既有 pin 测试用 toEqual 硬钉 SCAN_ROOTS / Object.keys,新行让它们响亮地红,已按新表扩写;
  • 逐根的 count 断言那一半不会自我扩展。原来只有前三根有下限,新行若打错字导致整棵树扫空,上面「无死链」的测试照绿。三行各自补了下限(CONTRIBUTING.md/ROADMAP.md 为 1,docs 为 ≥ 15)。

新 describe 覆盖三面的 reject/accept:#3545 真实死链形状、docs/ 递归遍历(15 个文件里 13 个在子目录,只开顶层会静默全绿)、离开 docs/ 的链接照收(无 collection 可逃)、无扩展名拼写与绝对 /... 照拒。

两处 control 链接:围栏边界测试与 docs/ vs content/docs/ 对照测试如果只断言「期望的那一条」,删掉扫描行它们会因什么都没扫而空绿。各加一条必被报出的 control 后,删行即红——已实测(见下)。

逆向验证(先定方向,再跑;四项全中)

# 动作 预告 实际
1 ROADMAP.md(单文件根)植入死链 红,example-relative [example-relative] ROADMAP.md:1900 -> ./docs/NOWHERE-3572.md,exit 1
2 同一条链接改放进围栏 绿(这就是边界) ✅ exit 0
3 docs/adr/ 嵌套文件(目录根)植入死链 [example-relative] docs/adr/9999-reverse-check.md:3,exit 1
4 死链保留,撤掉三行 绿——证明是这三行在干活 Links are valid across 3 scan roots.,exit 0

测试层同做一次:撤掉三行后 7 个新增/扩写的测试变红,其中包含围栏边界测试——证明那条 control 链接确实让它无法空绿。全部植入项已还原,工作树只剩三个目标文件。

ci-cd-pipeline.md:三处被证伪,做最小事实订正(披露)

  1. 扫描面清单(第 254 行)——补三面;
  2. disk 规则适用文件的段落——补三面,并把「若改用 docs 规则会拒掉 61 条」按新扫描面重算为 111(旧 61 已复核确实准确,只是随扫描面变宽而过时);
  3. 双检查器对照表那一行——补三面,并标注 except 围栏内内容。

另加一段说明围栏边界,以免读者把该表读成全覆盖。该文件对 Lychee「deliberately not a PR gate」的描述本身正确,未动。

顺手发现(未在本 PR 修,已另立单)

#3587(observation-class,finding,未指派):scripts/check-doc-links.mjs:157 与测试文件 :853 两处称 Lychee 是 "weekly cron with continue-on-error",而 check-links.yml 实际是 fail: true根本没有 PR 触发器。结论「gates nothing」对,机制说反了——照该理由行事的人会去摘一行不存在的 continue-on-error,而真取消注释 pull_request: 会立刻变成 #3213 明令不要的硬门禁。属 #3536 既有注释,不在 #3572 改动面内,故只订正本 PR 新写的那句(按正确机制表述),旧两处留给 #3587

验证

pnpm exec vitest run scripts/     16 files / 283 tests passed(check-doc-links 65,+8)
node scripts/check-doc-links.mjs  Links are valid across 6 scan roots. (exit 0)
pnpm type-check:scripts           exit 0
pnpm exec eslint (changed files)  0 errors
node scripts/check-control-bytes.mjs  OK (3631 tracked text files)
grep -naP 控制字符自扫(超出门禁扫描面)  clean

无 changeset:纯 CI 门禁改动,非用户可见特性。未触碰 content/docs/releases/


Generated by Claude Code

`SCAN_ROOTS` 追加三行,全部沿用既有的 `disk` 规则:

    { path: 'CONTRIBUTING.md', rule: 'disk' },
    { path: 'ROADMAP.md',      rule: 'disk' },
    { path: 'docs',            rule: 'disk' },

这三面正是 #3536 头注释里「Still not bought」点名的下一批,当时标价
「one SCAN_ROOTS row each — plus fixing what that turns red」。红账已由
#3545 / PR #3571 单独付清(3 条死链),所以本次是纯三行:实测 70 条链接、
52 条可判定、落地当日零死链——扩展该有的形状,对照 #3479(16 个死目标)
与 #3490(18 个)那种「门禁与欠账同时到货」。

`docs/**` 是三者中从未被量过的一面:Lychee 的扫描范围虽然列了它,但那个
工作流只有 schedule + workflow_dispatch,`pull_request`/`push` 按 #3213
ruling B 被刻意注释掉,拦不住任何 PR;它的 49 条链接里有 47 条此前从未被
任何能让构建失败的东西解析过。

不新增规则类、不新增 reason、不新增 hint。

头注释如实写下已知边界:`stripCode()` 在扫描前抹掉围栏与行内代码,所以
**围栏内的链接对本门禁不可见,死活都不可见**。这不是假想——CONTRIBUTING.md
的 25 条链接里 10 条在围栏内,门禁真正判定的只有 1 条。那 10 条正是 #3570
的实例类(演示 docs 链接写法的围栏,其"正确示例"路由自己是死的);修那段
文字是 #3570 的事,指出没有门禁够得着它是本注释的事。

测试(+8):
- 两个既有 pin 测试用 toEqual 硬钉 SCAN_ROOTS,新行会让它们**响亮地红**
  ——不是自动覆盖。逐根的 count 断言那一半才是不会自我扩展的:补齐三行
  各自的下限,否则某行打错字导致整棵树扫空时上面的测试仍会绿。
- 新 describe 覆盖三面的 reject/accept,含 docs/ 递归遍历、离开 docs/ 的
  链接照收(无 collection 可逃)、无扩展名拼写与绝对 `/...` 照拒。
- 围栏边界作为**决定**钉住,而非留成日后被误读为覆盖的静默缺口。该测试
  与 docs/ vs content/docs 对照测试都带一条 control 链接:否则删掉扫描行
  它们会因「什么都没扫」而空绿——本文件存在的意义就是防这个。

ci-cd-pipeline.md 三处被证伪,做最小事实订正:扫描面清单、disk 规则的
适用文件、双检查器对照表那一行;并把「若改用 docs 规则会拒掉 61 条」按新
扫描面重算为 111。另加一段说明围栏边界,以免读者把该表读成全覆盖。

Fixes #3572

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 7, 2026 2:53pm

Request Review

@github-actions github-actions Bot added the tests label Aug 7, 2026
@yinlianghui
yinlianghui marked this pull request as ready for review August 7, 2026 14:57
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 6632114 Aug 7, 2026
17 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-3572-scan-roots-third-ext branch August 7, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-doc-links 扫描面第三扩:CONTRIBUTING.md + ROADMAP.md + docs/**(实测 70 链接、今日零死链、纯配置三行)

2 participants