Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

check-doc-links 扫描面漏掉包内非 README markdown:15 个文件的链接从未被任何门禁解析过(实测 1 条死链) #4938

Closed

Description

@yinlianghui

现象

scripts/check-doc-links.mjs 的 SCAN_ROOTS 里,包级两行是精确文件名匹配:

{ path: 'packages/*/README.md', rule: 'disk' },
{ path: 'apps/*/README.md',     rule: 'disk' },

于是包目录(及其子目录)下任何不叫 README.md 的 markdown 都在扫描面外。该脚本自己的注释把「包内非 README markdown 未被扫描」记为已知边界,但边界的代价一直没被测过。

排除 CHANGELOG.md(生成物)后,当前有 15 个这样的文件:

apps/console/docs/UI_IMPROVEMENT_PROPOSAL.md
apps/console/docs/deployment.md
apps/console/docs/error-tracking.md
packages/cli/MIGRATION.md
packages/components/README_SHADCN_SYNC.md
packages/components/TESTING.md
packages/components/docs/FilterBuilder.md
packages/components/src/renderers/complex/README-KANBAN.md
packages/components/src/renderers/complex/TIMELINE.md
packages/plugin-dashboard/SKILL.md
packages/plugin-gantt/ROADMAP.md
packages/vscode-extension/DESIGN.md
packages/vscode-extension/ICON.md
packages/vscode-extension/PUBLISHING.md
packages/vscode-extension/SUMMARY.md

实测入场价(按先例的口径先量后报)

这 15 个文件里共 4 条仓内相对 markdown 链接,其中 1 条死链:

  • packages/components/src/renderers/complex/TIMELINE.md:349
    See the [prototype app](../../examples/prototype/src/App.tsx) …
    → 解析到 packages/components/src/examples/prototype/src/App.tsx,不存在;仓根 examples/prototype/ 也不存在。

所以 markdown 链接这一面很便宜(先例 #3603/#3622 的入场价是 9~11 条,这里是 1 条)。

真正大的那一面:反引号仓内路径

死链只是一半。#3881 实际被咬的是反引号包裹的仓内路径——README_SHADCN_SYNC.md 的 ## Files 声称 shadcn-sync.js 在 packages/components/ 目录下,而它一直在仓根 scripts/shadcn-sync.js。这类路径没有任何门禁看:scripts/check-skills-paths.mjs(#3735 的产物)只覆盖 skills/**,带 baseline;包内 markdown 一概不在其内。

两面的失效形状不同,建议分开定价:

  1. SCAN_ROOTS 加一行把包内非 README markdown 纳入(链接面,入场价实测 1 条);
  2. 反引号仓内路径的校验是否要从 skills/** 推广到包内 markdown(面更大,需另测)。

分级

按 finding 归档,不带 pm:queue,交分诊定级。今天没有 CI 会因它变红,读者付的成本是照着错误路径去找文件。归为观察类而非具体缺陷:那条死链本身是小事,值钱的是「这一整片文件从未被解析过」这个结构事实。

出处与去重

发现于 #3881 的实施过程(PR 见该卡)。#3881 的可选修法 3 曾建议「顺带考虑把包内 README* 纳入 check-doc-links 扫描面」,该 PR 刻意未取:它是独立的门禁扩面,有自己的入场价,按 #3536 → #3572 → #3603/#3622 → #4148 这条先例链,每次扩面都是一张独立卡。#3881 落地的是文档内容 + 成员集合钉,已把该文件的反引号路径在包内测试里局部钉住(packages/components/src/__tests__/readme-shadcn-sync-categories.test.ts),不构成全仓覆盖。

检索过开放卡:#3536 / #3572 / #3603 / #3622 / #4148 / #3735 均已关闭,无在途卡覆盖本条。

Activity

  1. os-support-ai commented on Aug 19, 2026

    @os-support-ai
    Collaborator

    Triage first-touch — concentrated round #2 (triage seat, session session_019ZyCHcjXeTFPiS8RWRk4nN, 2026-08-19): promoted finding → pm:queue · Task · tooling. Scope for dispatch = half 1 only: add the package-internal non-README markdown surface to SCAN_ROOTS (entry price measured at 1 dead link, which the same PR fixes — TIMELINE.md:349), excluding generated CHANGELOG.md. Half 2 (generalising backtick in-repo path checking beyond skills/**) is explicitly not in scope — it needs its own measurement and card per the one-expansion-one-card precedent chain the body cites; leave it recorded here until someone prices it. Size/model suggestion: S-mechanical, sonnet.


    Generated by Claude Code

  2. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Aug 21, 2026
  3. yinlianghui-tw commented on Aug 24, 2026

    @yinlianghui-tw
    Contributor

    Claim: PM loop round 9
    Session: session_019b5UBNMtTzKbVtZZGvFuxe · Branch: claude/issue-4938-doc-links-package-markdown · Worktree: objectui-issue-4938
    Domain: domain:devx (@ objectui, seat #5748) · Container & model: S–M, mode:subagent, model: sonnet · Clause-②: no
    File surface: scripts/check-doc-links.mjs (its SCAN_ROOTS) + whatever dead links the widened surface exposes
    Serial constraints cleared: in-flight 0. Disjoint from open PRs #5988 / #5989 / #5991 / #5995 and from co-dispatched R9 cards #5754 (a new workflow), #4964 (i18n gate), #4991 (packages/cli test), #4996 (shadcn patches). ⚠️ #5754 is adding a workflow this round — ⛔ do not add one here, and ⛔ do not touch content/docs/guide/ci-cd-pipeline.md; one inventory write per round and it is theirs.

    PM-verified premise on origin/main @ 53dc89db8

    scripts/check-doc-links.mjs scans the surfaces listed in its SCAN_ROOTS (declared at its :4 docblock). The card measures 15 package-internal non-README markdown files that no gate has ever parsed, with 1 confirmed dead link among them.

    ⚠️ Read that file's docblock before changing anything. It carries a long history of this exact surface being grown deliberately — #3536, #3490, #3486 — including a case where examples/hello-world/README.md pointed at directories deleted in another PR. The scan surface has been widened before, on purpose, with reasons recorded. Yours must join that record, not overwrite it.

    Zone 2 — the risk that actually matters here

    ⚠️ Widening a scan surface exposes every dead link on it, not the one the card measured. 15 files have never been parsed. The card's "1 measured" is a floor, ⛔ not a scope.

    So the first thing to report is: how many dead links does the widened surface actually find? Then, per link, either fix it or exclude it with a measured written reason — and ⛔ never exclude a whole file to avoid fixing one link. If the count is large enough that fixing them all exceeds this card, clear what you can and name exactly what you left, the same partial-delivery rule that let four #5174 batches land.

    ⛔ Do not narrow the gate's strictness to make the new surface pass. Weakening is maintainer-floor — stop and report.

    Gates

    Derive from your diff. At minimum check-doc-links.mjs itself over the whole repo (before/after counts), its test if one exists, pnpm type-check:scripts, Lint. Note the sibling Internal Docs Link Check and Docs Links workflows both run this family — say which your change affects. Run check-changeset-presence.mjs, follow its verdict; ⛔ no skip-changeset label (#4912/#3724).

    ⚠️ Restore idiom (objectstack#11648): use git checkout HEAD -- <path>, prove with git diff HEAD — the bare form reads the mutation back and exits 0.

    Draft PR, Fixes #4938, report as an issue comment with the <!-- os-dev-report --> marker (⚠️ sanitizer strips it — read back, repost as literal text if so). Report the before/after dead-link count.


    Generated by Claude Code

  4. added a commit that references this issue on Aug 24, 2026
    22396fc
  5. yinlianghui-tw commented on Aug 24, 2026

    @yinlianghui-tw
    Contributor

    <!-- os-dev-report -->

    {
      "issue": 4938,
      "status": "done",
      "branch": "claude/issue-4938-doc-links-package-markdown",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6029",
      "premise_still_valid": true,
      "summary": "Added two SCAN_ROOTS rows (packages/*, apps/*) to scripts/check-doc-links.mjs with a new row-level `exclude` mechanism (basenames skipped at every walk depth) so the surface covers every package/app-internal markdown file except the already-scanned top-level README.md and the generated per-package CHANGELOG.md. Widening exposed exactly 1 dead link (packages/components/src/renderers/complex/TIMELINE.md:349, pointing at an example app deleted in 3aa84cee0), fixed in the same PR by repointing at the live docs-site demo for the same content. Docblock and test suite (SCAN_ROOTS pin, per-root floors, 2 now-stale 'stays unscanned' tests, plus a new 7-test describe block) updated to match.",
      "tests": "node scripts/check-doc-links.mjs: before (13 old roots) 0 broken; widened to 15 roots before fix: 1 broken (TIMELINE.md:349 -> ../../examples/prototype/src/App.tsx, example-relative); after fix: 0 broken, 'Links are valid across 15 scan roots.' npx vitest run scripts/__tests__/check-doc-links.test.ts: 93/93 passed. npx vitest run scripts/__tests__/docs-links-workflow.test.ts: 7/7 passed (workflow pin unaffected -- only docs-links.yml/'Internal Docs Link Check' runs this script; ci.yml deliberately excludes it since #3448; check-links.yml/Lychee is a separate external-link tool). npx vitest run scripts/__tests__/ (full scripts suite): 1709/1709 passed. pnpm type-check:scripts (tsc -p tsconfig.scripts.json): clean -- caught and fixed one real bug (writing 'packages/*/README.md' literally inside a /** */ JSDoc comment closes the comment early via the '*/' substring, which the file's own header already warns about; rephrased in prose). npx eslint scripts/check-doc-links.mjs scripts/__tests__/check-doc-links.test.ts --no-inline-config: clean, exit 0. node scripts/check-changeset-presence.mjs: flagged TIMELINE.md as guarded @object-ui/components source -> added .changeset/4938-timeline-dead-example-link.md (empty frontmatter, doc-only fix) -> now passes. node scripts/check-changeset-no-major.mjs / check-changeset-fixed.mjs: clean. Head commit: 22396fc57.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #6026: check-doc-links' new packages/*/apps/* rows exclude README.md at every depth (by design, to avoid re-judging the top-level README row), which leaves 4 nested README.md files (packages/core/src/adapters/README.md, packages/components/src/__tests__/README.md, packages/plugin-gantt/docs/verification/README.md, packages/types/src/zod/README.md) unscanned by any gate; measured entry price 0 dead links"
      ]
    }

    Generated by Claude Code

  6. yinlianghui-tw commented on Aug 24, 2026

    @yinlianghui-tw
    Contributor

    ACCEPT — PR #6029. Landing on CI convergence.

    Reviewer of record: domain:devx @ objectui seat (#5748), round R9, PM session session_019b5UBNMtTzKbVtZZGvFuxe. Scope: 4 files.

    The "1 is a floor, not a scope" warning came back benign — and that is a result

    The dispatch's main worry was that widening a never-parsed surface would expose far more than the one measured dead link. It exposed exactly one, and it was fixed in the same PR:

    packages/components/src/renderers/complex/TIMELINE.md:349 → ../../examples/prototype/src/App.tsx, an example app deleted in 3aa84cee0.

    Verified independently: that path is absent from origin/main, and the control — examples/ itself still exists with live entries — makes the empty result a reading rather than a broken query. Repointed at the live docs-site demo for the same content, ⛔ not deleted.

    Roots 13 → 15; 0 broken before, 1 broken at the widened surface pre-fix, 0 broken after. The before/after triple is what makes this a measurement.

    The bug it caught in its own work

    pnpm type-check:scripts caught it writing packages/*/README.md literally inside a /** */ JSDoc block — the */ substring closes the comment early. ⚠️ The file's own header already warns about exactly that hazard. Fixed by rephrasing. Worth recording: the gate caught the author of a gate falling into the trap the gate's own docs describe.

    The new blind spot is disclosed, measured, and filed — ⛔ not hidden

    The new packages/* / apps/* rows exclude README.md at every walk depth (to avoid re-judging the existing top-level README row). That leaves 4 nested READMEs unscanned by any gate — core/src/adapters/, components/src/__tests__/, plugin-gantt/docs/verification/, types/src/zod/ — filed as #6026 with the entry price measured: 0 dead links today.

    ⚠️ Accepting, with a note for whoever takes #6026: a depth-aware exclusion (skip only the row's top-level README) would have closed this entirely rather than trading one blind spot for a smaller one. The basename-at-any-depth rule is simpler and its cost is zero today — but the simpler rule is why the gap exists, and that is the thing to weigh, not the four files.

    ⛔ Correctly did not add a workflow or touch ci-cd-pipeline.md — #5754 owned that write this round.

    Changeset

    check-changeset-presence.mjs flagged TIMELINE.md as guarded @object-ui/components source; an empty-frontmatter changeset was added because the gate demanded one, not by convention. Correct direction.

    Landing

    scripts/**, packages/components/src/** (a .md), .changeset/** — not governed surface. Maintainer's ruling, 2026-08-24, verbatim: 「授权 auto-merge(推荐)」 — every check green, ⛔ not the required subset.


    Generated by Claude Code

  7. added a commit that references this issue on Aug 25, 2026
  8. added 2 commits that reference this issue on Sep 1, 2026
    5a06bed
    7e63d2b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

documentationImprovements or additions to documentationdomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repopm:dispatchedtooling

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions