Uh oh!
There was an error while loading. Please reload this page.
fix(docs): type-check apps/docs with next typegen, and delete the dead types:check - #10879
Conversation
…e dead `types:check` `apps/docs` declared two near-identical type-check scripts: "types:check": "fumadocs-mdx && next typegen && tsc --noEmit" "typecheck": "tsc --noEmit" `types:check` -- the thorough one -- was invoked by nothing (`git grep` over `.github/`, `scripts/`, `turbo.json` and the root `package.json` returns only its own declaration). The one CI actually runs is `typecheck`, reached through `turbo run typecheck --filter='./apps/*'`. That mattered because `tsconfig.json` includes `.next/types/**/*.ts`, which only `next typegen` produces. So the program CI type-checked was set by whether some earlier, unrelated command had populated `.next` -- and on a fresh checkout it had not. Measured with `.next` deleted, the bare script exits 0 while compiling none of the generated route types. Wiring the thorough command under the name CI already runs makes the check a function of source: it generates `.next/types/**` and `next-env.d.ts` itself before tsc reads them, and short-circuits loudly if typegen fails. Typegen costs ~1s and needs no build. Measured coverage delta (empty `.next`, `tsc --listFiles`): 1225 -> 1231 files. The six are `.next/types/validator.ts` (160 lines validating 13 route entry points), `routes.d.ts`, `root-params.d.ts`, `cache-life.d.ts`, `next-env.d.ts` and, through it, `next/image-types/global.d.ts` -- so the bare run was also type-checking a Next app without Next's own ambient declarations. Ablation, to show the added coverage is real: giving `app/[lang]/docs/layout.tsx` a `params` shape the route cannot supply leaves the old script green (exit 0, 0 errors) and makes the new one fail with TS2344 at `.next/types/validator.ts`, naming the layout and the missing param. `include` is deliberately left alone. Next writes both `.next` globs itself and re-adds either one on the next `next dev` / `next build`; running `writeConfigurationDefaults` against a narrowed copy put the `dev` glob straight back and rewrote the file. The rationale is recorded in `tsconfig.json` where the next reader would try to tidy it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
✅ ACCEPT — reviewer of record: |
| leg | result |
|---|---|
bare tsc --noEmit — today's CI | EXIT=0, 0 error lines — green over a broken route signature |
| typegen'd | EXIT=2 — .next/types/validator.ts(139,31): error TS2344 … Property 'nonexistentParam' is missing in type '{ lang: string; }' |
That is not "typegen adds files", it is "today's CI passes a broken route." The coverage delta measured it (1225 → 1231 program files, 6 named), and the ablation showed what those six are for. Cost: 1 second, no build.
Card's required proof also run: rm -rf .next next-env.d.ts then typecheck ⇒ exit 0 in 4s with .next/types regenerated — with a negative control (OLD_BARE_TSC_EXIT=0 on the same tree), so the difference is the script and not the tree. Filter verified per #10853.
#10880 — filed with the right reason for not riding along
deliberately kept out of the PR because it would edit the 3790-line gate that #10756 is already dispatched against (same file, different invariant; needs sequencing, not parallel work)
⭐ That is a serial constraint you worked out yourself from the state of the lane, not from anything in your brief.
Flipping ready and arming.
Generated by Claude Code
Uh oh!
There was an error while loading. Please reload this page.
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 32497999764 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Fixes#10871
apps/docsdeclared two near-identical type-check scripts, and the thorough one was dead:git grep types:checkover.github/,scripts/,turbo.jsonand the rootpackage.jsonreturns only its own declaration. CI reaches the other one throughturbo run typecheck --filter='./apps/*'(lint.yml,Type Check · workspace).That mattered because
apps/docs/tsconfig.jsonincludes.next/types/**/*.ts, which onlynext typegenproduces. The program CI type-checked was therefore set by whether some earlier, unrelated command had populated.next— and on a fresh CI checkout nothing had.The judgement: typegen materially changes coverage, so the thorough command wins
Measured on this branch's base,
.nextdeleted, comparingtsc --noEmit --listFileswith and without typegen:tsc --noEmit(what CI ran)fumadocs-mdx && next typegen && tsc --noEmitThe six added files are
.next/types/validator.ts(160 lines validating 13 route entry points — every page, layout and route handler against the route's real param map),routes.d.ts,root-params.d.ts,cache-life.d.ts,next-env.d.ts, and through that last onenext/image-types/global.d.ts. So the bare run was also type-checking a Next app without Next's own ambient declarations.Both were green, so the delta is coverage rather than a live bug. To show the added coverage is real rather than decorative, an ablation — giving
app/[lang]/docs/layout.tsxaparamsobject with one extra required key the route cannot supply, an edit that is internally consistent and so invisible to a plain compile:1 file changed, 1 insertion(+), 1 deletion(-)tsc, empty.next)TS2344at.next/types/validator.ts(139,31), naming the layout and the missing paramgit status --porcelainempty for the file, re-run exit 0⛔ Both scripts are not left standing.
types:checkis deleted and its command becomestypecheck— the nameturbo run typecheckandcheck:type-check-coveragealready require, so no turbo or CI wiring moves.How the empty-
.nextcase becomes honestThe check now produces the generated inputs it reads, from source, on every run:
fumadocs-mdxwrites.source/,next typegenwrites.next/types/**andnext-env.d.ts, and only then doestscrun. It can no longer pass because someone else populated.next, because it populates.nextitself; and if typegen ever fails,&&short-circuits andtscnever runs, so the failure is loud rather than a quietly smaller program.Proof, run on this commit with
.nextandnext-env.d.tsdeleted first:and the negative control on the same tree — the old command is still green over an empty
.next, which is the trap being removed:The cost of the trade, named rather than absorbed
next typegenneeds no build. Measured at ~1 second; it reads theapp/directory and writes route types, it does not compile the app. The turbotypechecktask keepsdependsOn: ["^build"]exactly as before (that builds@objectstack/spec, not this app), andpnpm buildstill excludes docs. So this buys a whole class of route-signature checking for about a second, and does not turn the type check into something that requires a full build.Why
includeis left alone — the other option in the card is not availableThe card floated narrowing
includeinstead. Measured: that change is self-reverting. Next owns this array —writeConfigurationDefaultswrites both.nextglobs and re-adds either one that is missing. Running Next's own routine against a narrowed copy of the file, exactly asnext buildandnext devcall it:So a hand-narrowed
includecomes back as an unrequested modification to a tracked file on the nextnext devornext build. The rationale is recorded as a comment intsconfig.json, at the spot where the next reader would try to tidy the glob away.The residual is named there too, and it is the harmless direction:
.next/dev/types/**is written only bynext dev, and Next's own build-mode type check filters that directory out of the program (getDevTypesPath,lib/typescript/runTypeCheck.js) "to prevent stale dev types from causing errors when routes have been deleted since the last dev session". Plaintschas no such filter, so a leftover dev session can only add a local false red — never a false green, which is the failure this card is about. Remedy:rm -rf apps/docs/.next.No changeset
skip-changeset.apps/docsisprivate: trueand the diff is two files — a package script and a config comment. Nothing user-visible ships and no published package changes;privatePackages.tagisfalse, so there is no tag or release to describe either.Gates
Derived with
node scripts/pm/dispatch-gates.mjs(2 paths vs merge base7d483e1e5), run at8140f71b39— the final commit — each quoting its own verdict line:check:override-consistency✓ 8 published-manifest declaration(s) covered by pnpm-workspace.yaml overrides all resolve to their override targets.check:test-source-aliascheck-test-source-alias OK — 72 packages with tests scannedcheck:type-source-resolutioncheck-type-source-resolution OK — 76 packages with a tsconfig.json scannedscripts/check-changeset-fixed.mjs✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.scripts/check-osv-exemptions.mjs✓ osv-scanner.toml holds zero OSV exemptions (the intended steady state).check:nul-bytescheck-nul-bytes: OK (scanned 6294 text file(s) ... no raw ASCII control bytes).check:type-check-coveragecheck-type-check-coverage: OK — 64/77 workspace packages type-checked (plus the root)check:nul-bytesis the any-edit gate;check:type-check-coverageis not path-derived but reads exactly the two files this diff changes (itsREALinvariant is about whether a declaredtypecheckscript means anything), so it was run deliberately.Also run:
turbo run typecheck --filter='./apps/*' --force—Tasks: 2 successful, 2 total— which is the CI lane's own command over this diff.Generated by Claude Code