Skip to content

docs(links): scan the app READMEs and the rest of the repo root (#4148) - #4173

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-4148-doclinks-scan-roots
Aug 10, 2026
Merged

docs(links): scan the app READMEs and the rest of the repo root (#4148)#4173
yinlianghui merged 1 commit into
mainfrom
claude/issue-4148-doclinks-scan-roots

Conversation

@yinlianghui

@yinlianghuiyinlianghui commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes#4148

Ordering

PR #4159 (which owns scripts/__tests__/quick-reference-commands-4149.test.ts, edited here) merged as 521a37bd0 before this started, so this branches off main rather than stacking on claude/issue-4149-quick-reference-dead-commands.

Premise: valid, with one correction that decides the fix

Both dead links reproduce at the tip, and so does the blind spot:

$ node scripts/check-doc-links.mjs # before the rows
Links are valid across 7 scan roots.

The card's second half is the one that changed. It states "the roadmap content is gone, not moved — there is no successor path to repoint at", and that is where its two links-vs-delete options came from. The history says otherwise:

CommitDateWhat happened
c988277ff2026-02-14apps/console/CONSOLE_ROADMAP.md renamed to a root ROADMAP_CONSOLE.md (a pure rename, git show --stat reports it as such)
3e814e07a2026-02-21"consolidate 4 ROADMAP files into single ROADMAP.md" — ROADMAP_CONSOLE.md, ROADMAP_DESIGNER.md and ROADMAP_SPEC.md folded into ROADMAP.md

Today's ROADMAP.md still carries the Console work as its own phases (P1.2 Console — Forms & Data Collection, P1.3 Console — Import/Export Excellence, P1.4, P1.5, P1.6, P1.7, and more). So there IS a successor, both links are repointed at it, and the delete-versus-repoint decision the card deliberately left open does not need a judgement call — it needs the rename history. The links were dead for about six months.

1. Two SCAN_ROOTS rows

Both disk, because both surfaces are read on GitHub and never served by the site.

One README per directory under apps/. objectui#3622's package-README purchase one directory over, and the rule choice is stronger here than "same shape": @object-ui/console declares no private, so it is a published package and its README is read on npm and on GitHub in exactly the two places a package README is. A wildcard row for the same reason as the package one — the surface is one file per directory rather than a tree — which also means the next app to land is scanned on arrival with no row to remember.

The rest of the root-level markdown: AGENTS.md, CHANGELOG.md, CLAUDE.md, LICENSE-THIRD-PARTY.md, QUICK_REFERENCE.md, joining the README.md / CONTRIBUTING.md / ROADMAP.md already in the table. Per-file rows, not a glob — expandWildcard() expands a whole path SEGMENT by design, so *.md is not a spelling this table has and would throw.

LICENSE-THIRD-PARTY.md was not on the header's list of four and is bought anyway. That is the point: it makes the root complete, which converts a list of names (a thing that goes stale the next time someone adds a page) into an invariant a test can hold. See the reverse assertion below.

2. What the rows cost — measured before landing

RowFilesLinksDecidable hereDead
app READMEs21362
root-level files51190

The 2 are the CONSOLE_ROADMAP.md pair, repaired in this PR, so the rows land green — the objectui#3572 shape (a check arriving with its backlog already paid), not the #3479/#3490 shape.

Two of the five root files carry nothing to judge today: AGENTS.md has no markdown links at all (its paths are code spans, which stripCode() blanks) and both of CHANGELOG.md's are external. They are bought regardless — a row over a file with no decidable links is the row that judges the FIRST dead link written into it, and buying a surface while it is empty is the cheapest this ever gets.

3. The #4159 stopgap, deleted

scripts/__tests__/quick-reference-commands-4149.test.ts resolved that one page's links itself, and said so in its own words: a stopgap for one file, "it should be deleted when a SCAN_ROOTS row makes it redundant, rather than left as a second implementation". The QUICK_REFERENCE.md row is that replacement, so the block and its header claim both go. Two implementations of one check is one more than gets maintained.

4. Stale counts swept (the #3212 family)

The header did not merely omit these surfaces — it documented them as unscanned, so the prose had to move with the rows:

The success line was already SCAN_ROOTS.length, so it needed nothing — it reads 13 scan roots on its own.

Reverse verification — predicted first, three directions

Direction 1 (the point of the card): the widened scan on the pre-fix tree. Predicted red naming exactly the two roadmap links and nothing else, since the root rows were measured at zero. Measured:

$ node scripts/check-doc-links.mjs # rows added, README not yet fixed
Found 2 broken links (1 distinct target):
- [example-relative] apps/console/README.md:5 -> ./CONSOLE_ROADMAP.md
- [example-relative] apps/console/README.md:126 -> ./CONSOLE_ROADMAP.md
EXIT=1
$ node scripts/check-doc-links.mjs # after the repair
Links are valid across 13 scan roots.
EXIT=0

Direction 2: delete the apps row from the repaired tree. Predicted 5 red — the two whole-table pins plus the three app fixtures — and the repo-wide scan staying GREEN, because the README is fixed by then and its links resolve whether or not anything looks at them. That last part is the interesting half: it is precisely why a fixture pair is needed and a repo scan is not sufficient evidence that a row exists. Measured Tests 5 failed | 82 passed (87), exactly those five.

Direction 3: delete one root-file row. The self-extending assertion, which no fixture can prove:

AssertionError: every root-level markdown file needs its own SCAN_ROOTS row — the root has no glob
spelling (`expandWildcard()` expands a whole path segment, never `*.md`), so completeness here is
per-file: expected [ 'LICENSE-THIRD-PARTY.md' ] to deeply equal []

It names the file, and it is derived from git ls-files rather than readdir on purpose: an untracked stray at the root — a scratch note, or a generated AGENTS.md of the kind #4159 caught next dev writing into apps/site/ — is not this gate's business and must not redden the suite.

Both mutations reverted from a patch file. Never git stash — that stack is shared across worktrees (objectui#3430).

Gates

$ pnpm exec vitest run scripts/ # whole scripts suite, from repo root
Test Files 30 passed (30)
Tests 582 passed (582)
$ node scripts/check-doc-links.mjs # Links are valid across 13 scan roots. → exit 0
$ pnpm run type-check:scripts # tsc -p tsconfig.scripts.json → exit 0
$ pnpm run check:control-bytes
✅ check-control-bytes: OK (scanned 3847 tracked text file(s); skipped 85 binary).
$ pnpm exec eslint [the 4 changed script files] # exit 0

Changed files were also swept for control characters beyond the gate's scan surface: clean.

Changeset

None owed, arbitrated by the script rather than by judgement:

$ node scripts/check-changeset-presence.mjs
Compared the working tree with 521a37bd0 (merge-base with origin/main): 5 file(s) changed,
0 of them under the src/ of a package the release covers, 0 under a package changesets ignores,
0 changeset(s) added.
✅ No source of a released package changed in this range, so no changeset is owed.

No skip-changeset label: it is decorative in this repo (objectui#3724 deleted .github/WORKFLOWS.md for documenting, among other phantoms, "a changeset gate skippable with a skip-changeset label; neither the workflow nor the label was ever real").

Out-of-scope findings

None new. The one thing worth flagging for whoever reads the card: its "two decisions, deliberately not made here" are both made here, and the first was made by the rename history rather than by a judgement call — the option set in the card ("deleting both links, or repointing them at the root ROADMAP.md") had assumed repointing "asserts something about content nobody has checked". Someone has now checked: the content is literally the same document, moved twice.


Generated by Claude Code

`apps/console/README.md` linked twice to `./CONSOLE_ROADMAP.md`, a file that
left that directory in c988277 (renamed to a root ROADMAP_CONSOLE.md) and was
folded into the root ROADMAP.md by 3e814e0. Both links sat dead for about six
months while `pnpm docs:check-links` reported green on every push, because
`apps/` matched no SCAN_ROOTS row.
Two rows, both `disk`, both surfaces the script's own header had been
DOCUMENTING as unscanned:
- one README per directory under `apps/` — objectui#3622's package-README
purchase one directory over, and `@object-ui/console` is itself a published
package, so its README is read on npm and GitHub in exactly the same two
places. A wildcard row, so the next app is scanned on arrival.
- the rest of the root-level markdown (AGENTS.md, CHANGELOG.md, CLAUDE.md,
LICENSE-THIRD-PARTY.md, QUICK_REFERENCE.md), joining README.md,
CONTRIBUTING.md and ROADMAP.md. Per-file rows: the root has no glob spelling,
since `expandWildcard()` expands a whole path segment and never `*.md`.
Price: 2 dead links from the apps row (the CONSOLE_ROADMAP pair, repointed at
the root ROADMAP.md — the content moved there, so repointing is an answer
rather than a guess), and zero from the root row.
The header no longer documents the gap it used to; the stale forward-references
to that list, and a positional "scan roots 1 and 7" in doc-version-claims, go
with it.
Deletes objectui#4149's in-test link check
(`scripts/__tests__/quick-reference-commands-4149.test.ts`), which was written
as an explicit stopgap for one page "to be deleted when a SCAN_ROOTS row makes
it redundant". The `QUICK_REFERENCE.md` row above is that replacement, and it
judges the page under the same rule as every other GitHub-read file.
The self-test gains the new rows' red/green plus a reverse assertion that every
TRACKED root-level markdown file has a row — so the surface this card reopened
cannot silently close again.
Fixes#4148
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
@vercel

vercelBot commented Aug 10, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 10, 2026 3:50pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)28.0 KB350 KB
Entry fileindex-DGe1i1xz.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.66KB3.13KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)22.10KB4.37KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.13KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.64KB2.21KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)35.76KB9.11KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.91KB0.87KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)26.07KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.65KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)485.06KB107.21KB
core (index.js)3.04KB1.15KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)140.66KB36.25KB
fields (index.js)229.40KB56.93KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.65KB1.06KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)9.48KB3.27KB
i18n (useObjectLabel.js)27.59KB6.63KB
i18n (useSafeTranslation.js)4.52KB1.96KB
layout (index.js)38.87KB10.80KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.71KB3.79KB
plugin-calendar (index.js)45.23KB12.45KB
plugin-charts (index.js)61.52KB17.49KB
plugin-chatbot (index.js)180.33KB42.79KB
plugin-dashboard (index.js)118.52KB30.68KB
plugin-designer (index.js)210.51KB42.51KB
plugin-detail (index.js)237.80KB59.48KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)114.58KB27.68KB
plugin-gantt (index.js)162.81KB39.67KB
plugin-grid (index.js)188.04KB49.91KB
plugin-kanban (index.js)48.60KB13.41KB
plugin-list (index.js)110.04KB26.67KB
plugin-map (index.js)17.00KB5.32KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)40.58KB10.58KB
plugin-timeline (index.js)26.21KB7.52KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.03KB20.55KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)23.71KB7.95KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.23KB0.66KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)2.71KB1.34KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@yinlianghui
yinlianghui marked this pull request as ready for review August 10, 2026 15:58
@yinlianghui
yinlianghui added this pull request to the merge queueAug 10, 2026
Merged via the queue into main with commit da81093Aug 10, 2026
20 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4148-doclinks-scan-roots branch August 10, 2026 15:58
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@yinlianghui@claude