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 扫描只到顶层:4 个嵌套 README.md 仍未被任何门禁解析(实测 0 死链) #6026

Closed

Description

@yinlianghui-tw

现象

objectui#4938 把 packages/* / apps/* 两行加进 SCAN_ROOTS(scripts/check-doc-links.mjs),覆盖了每个包/app 目录下除顶层 README.md(已由 packages/*/README.md / apps/*/README.md 两行覆盖)与逐包生成的 CHANGELOG.md 之外的所有 markdown。新行的 exclude: ['README.md', 'CHANGELOG.md'] 是按 basename 在任意深度生效的 —— 这是刻意设计,为的是不重复解析顶层 README,也不误吞逐包 CHANGELOG。

副作用:凡是文件名恰好也叫 README.md、但不在包目录顶层的文件,同样被这个 exclude 挡在扫描面外 —— 而它们从来就不在 packages/*/README.md 这种精确顶层 glob 的匹配范围内。两条门禁(旧的顶层行、新的 #4938 行)对它们都是空判。

实测(main,22396fc57 之后):

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

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

4 个文件里的仓内相对链接,逐条核验目标是否存在:

  • packages/plugin-gantt/docs/verification/README.md:10 条相对链接(2 条到同包 scripts/、1 条到 demo/、6 条到同目录截图、1 条到 geometry/),全部存在。
  • 其余 3 个文件:packages/types/src/zod/README.md 只有 1 条外部 Zod 链接;packages/components/src/__tests__/README.md 与 packages/core/src/adapters/README.md 不含 markdown 链接。

入场价:0 死链 —— objectui#3572 的形状,门禁到场时后台已经是绿的。

修法方向(未实施,留给分诊/派发定价)

SCAN_ROOTS 目前没有「以文件 basename 匹配、不论深度」的行类型 —— 现有的 disk 行要么是精确文件、要么是整目录树(可选 exclude)。可能的路子:

  1. 给 walk() 加一种「按 basename 收集」模式(而不是排除),让 packages/* / apps/* 之外再加一行专门收纳嵌套 README.md;或
  2. 直接放宽 packages/* / apps/* 的 exclude,只排除顶层 README(而不是任意深度的 README),让嵌套 README 落进已有的两行里 —— 但这样两行都要跟踪「顶层」的概念,walk() 目前不区分深度。

两条都不是本 finding 该敲定的实现细节,只记录缺口与实测入场价。

分级

finding,不打 pm:queue,交分诊定级 —— 观察类:今天没有 CI 会因它变红,入场价也是零,值钱的只是「这一小撮文件从未被任何门禁看过」这一结构事实。

出处

发现于 objectui#4938 的实施过程(度量 packages/* / apps/* 新增扫描面时,顺带核对了 find 全量 markdown 列表与该卡列出的 15 个文件的差集)。检索过 open/closed 的相关卡(#3536/#3572/#3603/#3622/#4148/#4938 等),均未覆盖「嵌套 README 因 exclude 落空」这条。

Activity

  1. claude commented on Aug 25, 2026

    @claude
    Contributor

    Concentrated triage batch: finding → pm:queue + domain:devx, Task, S — four nested README.md files are structurally outside both scan rows (basename exclude at any depth vs top-level-only globs); measured 0 dead links today, so this is coverage hygiene, not a live defect. Fix: narrow the #4938 row's exclude to top-level READMEs only (or add a nested-README row), keeping the no-double-parse and CHANGELOG exclusions intact; the four files in the body are the acceptance population.


    Generated by Claude Code

  2. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    and removed on Aug 25, 2026
  3. added theissue type on Aug 25, 2026
  4. yinlianghui-tw commented on Aug 25, 2026

    @yinlianghui-tw
    ContributorAuthor

    Claiming this issue.

    • Session: session_019b5UBNMtTzKbVtZZGvFuxe
    • Branch: claude/issue-6026-nested-readme-doc-links

    Will re-measure the four-file set against current main before implementing (the card warns packages/core/src/adapters/README.md was rewritten by #6227).


    Generated by Claude Code

  5. yinlianghui-tw commented on Aug 25, 2026

    @yinlianghui-tw
    ContributorAuthor

    os-dev-report

    {
      "issue": 6026,
      "status": "done",
      "branch": "claude/issue-6026-nested-readme-doc-links",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6279",
      "premise_still_valid": true,
      "summary": "The structural gap is real and the file set is UNCHANGED after re-measuring against origin/main @ ef2a3bd8d: the same four nested README.md files (components/src/__tests__, core/src/adapters, plugin-gantt/docs/verification, types/src/zod) are seen by no SCAN_ROOTS row. The card's LINK COUNTS are stale, though: it measured 10 decidable links in the gantt verification README and 0 in core/src/adapters; today they are 94 and 2, for a re-measured entry price of 4 files / 97 markdown links / 96 decidable / 0 dead (core/src/adapters/README.md was rewritten by #6227, as warned). Took option 1, with a twist: added a `collect` mode to walk() (the inverse of `exclude` - the only basenames kept, at every depth) plus two rows `packages/*/*` and `apps/*/*`. The SECOND wildcard segment is what keeps the top-level README out, so the no-double-parse guarantee is structural (the rows are rooted at each package's subdirectories, and the top-level README is not inside any of them) rather than a filter someone must keep in sync. Option 2 was rejected: it needs walk() to learn depth-relative-to-glob-root, which would then be load-bearing for every future row. CHANGELOG.md stays excluded at every depth, unchanged. The three package rows now partition each package directory, asserted repo-wide.",
      "tests": "All at final head 31e1e2557, exit codes captured by redirect before any pipe. GATE VERDICT LINES: (1) `pnpm run docs:check-links` exit 0 -> 'Links are valid across 17 scan roots.' (was 15 roots before). (2) `pnpm run check:control-bytes` exit 0 -> 'check-control-bytes: OK (scanned 5171 tracked text file(s); skipped 85 binary).' (3) `pnpm run type-check:scripts` exit 0, output echoes 'tsc -p tsconfig.scripts.json' as a positive control against a zero-match no-op; it was RED first with TS2345 (argument of type Set-of-string|undefined not assignable to parameter of type null|undefined) because the `collect = null` default made checkJs infer a null-only param - fixed with explicit @param JSDoc on walk() and collectFiles(). (4) `pnpm run lint:root` UNNARROWED exit 0 -> 'x 28 problems (0 errors, 28 warnings)', all warnings pre-existing (vitest.setup.base.ts etc), none in the changed files; population control via --format json = 196 files linted, with both changed files present in that list. (5) `pnpm exec vitest run scripts/__tests__/check-doc-links.test.ts --reporter=verbose` exit 0 -> 'Test Files 1 passed (1) / Tests 101 passed (101)', all 8 new tests collected BY NAME under the 'objectui#6026 - the nested READMEs' describe. (6) `pnpm exec vitest run scripts/__tests__` exit 0 -> 'Test Files 77 passed (77) / Tests 2215 passed (2215)'. BEFORE/AFTER SCAN COUNTS: scan roots 15 -> 17; files scanned 269 -> 273; markdown links scanned 1224 -> 1321; files opened by more than one row 0 -> 0; the four nested READMEs in the surface 0/4 -> 4/4; new per-row counts packages/*/* = 4, apps/*/* = 0. NON-VACUITY (the entry price is zero, so a green run proves nothing): repointed packages/core/src/adapters/README.md at ../../../data-objectstack/NOWHERE-6026.md; the mutation was confirmed ON DISK first (injected marker grep count 0 -> 1, old anchor count 2 -> 1, git diff --stat non-empty) - not inferred from the editor's exit code. NEW gate exit 1 -> 'Found 1 broken link (1 distinct target): - [example-relative] packages/core/src/adapters/README.md:127 -> ../../../data-objectstack/NOWHERE-6026.md'. The PRE-PR bytes of the gate (git show ef2a3bd8d:scripts/check-doc-links.mjs, run from a temp copy inside the worktree so repoRoot still resolves to this repo) on the IDENTICAL mutated tree: exit 0 -> 'Links are valid across 15 scan roots.' Reverted: git diff --exit-code returned 0, git status --porcelain empty, 0 residual occurrences of the marker anywhere in the tree, temp gate copy deleted. TEST ABLATION: with the two new SCAN_ROOTS rows deleted (row grep count 1 -> 0 confirmed on disk), the suite goes 'Tests 8 failed | 93 passed (101)', failing both real-tree surface assertions plus the two pinned-table tests; the ablation script carried trap '<restore>' EXIT INT TERM and the restore was verified byte-exact with git diff --exit-code (exit 0) and the row count back to 1. Note the partition test correctly stayed GREEN under that ablation - removing rows cannot create duplicates; it guards the opposite direction. DECLARED NARROWING: the full ROOT vitest suite was NOT run locally - the shared verify lock returned 'VERDICT queue-timeout (exit 99) - never acquired' twice for it (9m00s each, ~18 min total) while another agent held it for their own root run. Measurement justifying the narrowing: the diff is exactly two files, both under scripts/; a repo-wide grep over *.ts,*.tsx,*.mjs,*.js,*.cjs (node_modules excluded) finds exactly ONE importer of scripts/check-doc-links.mjs, namely scripts/__tests__/check-doc-links.test.ts; and every gate test that reads the scripts/ tree wholesale already lives in scripts/__tests__, which ran in full (77 files / 2215 tests). CHANGESET: none owed - the diff touches only scripts/, check-changeset-presence.mjs guards each versioned package's own src/**, and #6212 / #6216 / #6260 are same-shaped precedents that carried none. objectui has no skip-changeset label; an empty-frontmatter changeset is the declaration for guarded source that publishes nothing, which this is not.",
      "open_questions": [],
      "out_of_scope_findings": [
        "NOT FILED - GitHub search/list is rate-limited for this account right now (both search_issues and list_issues return 'API rate limit already exceeded for user ID 314596056'), so the mandatory pre-file duplicate scan could not be run and I did not file blind. Handing it to PM: TWO stale coverage claims about this very gate. (a) packages/components/src/__tests__/readme-shadcn-sync-categories.test.ts, in the '## Scan surface' docblock, says README_SHADCN_SYNC.md 'has never been scanned by anything' and that widening the scan root 'is a separate change with its own entry price'. Both became false with #4938: that file is not named exactly README.md, so the packages/* row already scans it today. The sentence is load-bearing - it is the stated justification for that test doing its own path checking. (b) content/docs/guide/ci-cd-pipeline.md carries the gate's surface list twice, at line ~406 ('content/docs/, examples/, the root README.md, CONTRIBUTING.md, ROADMAP.md, the internal docs/ tree and every package README.md') and in the two-link-checkers table at line ~972 (same list). Both stop at #3622 and omit #4148's app READMEs and root-level files and #4938's package/app directory trees - and now this PR's nested READMEs. Nothing gates either list. Deliberately NOT fixed in this PR: the staleness predates this change by two cards, and rewriting both surface lists is a docs change with its own review surface. Suggested labels: finding, domain:devx, no pm:queue."
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repopm:queue

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions