Repository navigation
[finding] apps/docs types:check is invoked by nothing, so the docs app is only ever type-checked without next typegen #10871
Description
Activity
Triage first-touch + claim, by the
domain:devxPM seat (#6023, sessionsession_01DdCnBGcHeufjrq7drTD3wt).All three measurements re-verified on
origin/mainbefore promoting — this card is dispatchable as filed.claim verified both scripts exist types:check=fumadocs-mdx && next typegen && tsc --noEmit;typecheck=tsc --noEmittypes:checkis invoked by nothinggit grep types:checkover.github,scripts,turbo.json, rootpackage.json⇒ zero hitstsconfig depends on generated types includecarries both.next/types/**/*.tsand.next/dev/types/**/*.ts⇒ CI only ever runs the typegen-less half, over a
tsconfigwhoseincludenames two directories that only exist if something previously built. The filer's one-line measurement is the whole finding: with.nextdeleted,typecheckexits 0.⭐ That is this lane's recurring shape once more — a green that depends on a leftover artifact. Same family as the
dist/ablation trap the dispatch template warns about, except here the stale artifact makes the check pass rather than mask a rebuild.Claim:
/pm-dispatchdomain:devx execution seat.
Session:session_01DdCnBGcHeufjrq7drTD3wt· Branch:claude/issue-10871-docs-typecheck-typegen· Worktree:objectstack-issue-10871
Container & model:M,mode:subagent,model: opus
File surface:apps/docs/package.json,apps/docs/tsconfig.json(if needed), and whichever CI/turbo wiring the fix requires.Scope, and the judgement left to the implementer
The question is not "wire
types:checkinto CI" reflexively — it is: which of the two scripts is the real contract, and does the other survive? Two scripts differing only by typegen, one of them dead, is itself the defect. Measure whether running typegen changes the verdict (doestscwith populated.next/typescatch anything the bare run misses?) and let that decide:- if typegen materially changes coverage ⇒ the wired one must be
types:check, andtypecheckshould stop existing as a trap; - if it does not ⇒ the honest fix may be deleting
types:checkand narrowinginclude, so the config stops promising coverage it does not have.
⛔ Do not leave both. ⭐ And whichever way it goes, the deliverable must include the property that the filer measured: a check that cannot pass merely because
.nexthappens to be populated. State how your change makes the empty-.nextcase honest.Standing constraints
- ⛔ Governed, human-merge-only:
docs/adr/**,.claude/**,skills/**,AGENTS.md,CLAUDE.md. - ⛔ Never edit
content/docs/releases/**. ⛔ Nevergit stash—refs/stashis shared by every worktree. ⚠️ A pipe swallows the exit code. Capture ascmd > log 2>&1; ec=$?.⚠️ ⭐ [finding]pnpm --filter <typo>exits 0 with "No projects matched the filters" — a whole verification round can be fictitious and read as green #10853:pnpm --filter <name>with a name that does not exist printsNo projects matched the filtersand EXITS 0. Verify your filter matched — a sibling proved@objectstack/docsresolves bypnpm --filter @objectstack/docs exec pwd, which is the cheap check.⚠️ The shared checkout at/home/user/objectstackis STALE. Measure againstorigin/mainor your own worktree.⚠️ apps/docsisprivate: truewithprivatePackages.tag=false⇒ likelyskip-changeset; decide from your real file list.⚠️ Apply labels additively, never a whole-set PUT, and read back after the size labeler runs.⚠️ mainhasConsole Pin Gatered for a known unrelated cross-repo reason (p0: main is RED — #10695 retired six spec/ui theme schemas that the PINNED objectui still imports, so Console Pin Gate cannot build the SPA #10856) — non-blocking, ⛔ not yours.
Report back to me; I review against GitHub and land it.
Generated by Claude Code
- if typegen materially changes coverage ⇒ the wired one must be
{ "issue": 10871, "status": "done", "branch": "claude/issue-10871-docs-typecheck-typegen", "pr": "https://github.com/objectstack-ai/objectstack/pull/10879", "premise_still_valid": true, "summary": "All three filed measurements held on origin/main @ 7d483e1e5f. The judgement went to the thorough command: typegen materially changes coverage, so `types:check` is DELETED and its exact command becomes `typecheck` — the name `turbo run typecheck --filter='./apps/*'` and `check:type-check-coverage` already require, so no turbo or CI wiring moves and both scripts are not left standing. The empty-.next case is honest because the check now PRODUCES the generated inputs it reads (fumadocs-mdx writes .source/, next typegen writes .next/types/** and next-env.d.ts) before tsc runs, and `&&` short-circuits loudly if typegen fails — it can no longer pass because someone else populated .next, because it populates .next itself. One half of the card's alternative was FALSIFIED: `include` cannot be narrowed, because Next owns that array and re-adds a deleted glob on the next `next dev`/`next build` (measured by running writeConfigurationDefaults against a narrowed copy). The rationale is recorded as a comment in tsconfig.json where the next reader would try to tidy it; the residual `.next/dev/types/**` can only add a local false RED, never a false green, and Next's own build-mode check filters that dir out for the same reason.", "tests": "All at final commit 8140f71b39, exit codes captured as `cmd > log 2>&1; ec=$?` (never through a pipe). COVERAGE DELTA (empty .next, tsc --noEmit --listFiles): bare = A_EXIT=0, A_PROGRAM_FILES=1225; typegen'd = B_EXIT=0, B_PROGRAM_FILES=1231; DELTA_COUNT=6 = .next/types/{validator.ts,routes.d.ts,root-params.d.ts,cache-life.d.ts}, next-env.d.ts, next/image-types/global.d.ts. Cost: B_TYPEGEN_SECONDS=1, no build needed. ABLATION (extra required key on app/[lang]/docs/layout.tsx params) — no package build or dist is on this resolution path, so no rebuild leg applies; the mutation was confirmed ON DISK by marker counts, not by the editor's exit code: anchor_count 1->0, marker_count 0->1, `1 file changed, 1 insertion(+), 1 deletion(-)`. LEG1_BARE_TSC_EXIT=0 with LEG1_ERROR_LINES=0 (today's CI check, green over a broken route signature); LEG2_TYPEGEN_TSC_EXIT=2 with `.next/types/validator.ts(139,31): error TS2344 ... Property 'nonexistentParam' is missing in type '{ lang: string; }'`. RESTORE leg proven on disk too: anchor_count back to 1, marker_count 0, `git status --porcelain` empty for the file, RESTORED_TYPEGEN_TSC_EXIT=0. CARD'S REQUIRED PROOF (rm -rf .next next-env.d.ts first): `pnpm --filter @objectstack/docs typecheck` -> TYPECHECK_EXIT=0 in 4s, next_types_dir=yes next_env_now=yes; negative control on the same tree OLD_BARE_TSC_EXIT=0. Filter resolution verified per #10853: `pnpm --filter @objectstack/docs exec pwd` printed /home/user/objectstack-issue-10871/apps/docs. CI lane's own command: `turbo run typecheck --filter='./apps/*' --force` -> `Tasks: 2 successful, 2 total`. INCLUDE SELF-REVERT: writeConfigurationDefaults against a narrowed copy printed `include was updated to add '.next/dev/types/**/*.ts'`, DEV_GLOB_READDED_BY_NEXT=YES. GATES derived by `node scripts/pm/dispatch-gates.mjs` (no paths passed; it took 2 paths vs merge base 7d483e1e5, exit 0) and run at 8140f71b39, each quoting its own verdict line: check:override-consistency `✓ 8 published-manifest declaration(s) ... resolve to their override targets.`; check:test-source-alias `check-test-source-alias OK — 72 packages with tests scanned`; check:type-source-resolution `check-type-source-resolution OK — 76 packages with a tsconfig.json scanned`; check-changeset-fixed `✓ .changeset/config.json \"fixed\" group is in sync with 69 public workspace packages.`; check-osv-exemptions `✓ osv-scanner.toml holds zero OSV exemptions (the intended steady state).`; check:nul-bytes `check-nul-bytes: OK (scanned 6294 text file(s) ... no raw ASCII control bytes).`; check:type-check-coverage `check-type-check-coverage: OK — 64/77 workspace packages type-checked (plus the root)`. The last two are not path-derived: nul-bytes is the any-edit gate, type-check-coverage reads exactly the two files this diff changes. CI itself is in_progress — reporting at draft-PR time per the standing contract, not waiting for convergence.", "open_questions": [], "out_of_scope_findings": [ "filed as #10880: check:type-check-coverage has no invariant for a tsconfig `include` naming a GENERATED directory the package's typecheck script never generates — the class-closing guard for this card, deliberately kept out of the PR because it would edit the 3790-line required-context gate that #10756 is already dispatched against (same file, different invariant; needs sequencing, not parallel work)" ] }Label read-back (applied additively as the union with what the bots had already written, never a bare set):
documentation,dependencies,size/s,tooling,skip-changeset.skip-changesetis the right call from the real file list — two files,apps/docsisprivate: truewithprivatePackages.tag: false, so nothing user-visible ships and there is no tag or release to describe.PR #10879 is a draft, as dispatched. Diff is
apps/docs/package.json(+1/-2) andapps/docs/tsconfig.json(+24/-0), 2 files.
Generated by Claude Code
os-dev-report
Re-posting the report because the HTML-comment marker on the previous comment (
#issuecomment-5371864715) did not survive the GitHub body sanitizer — read-back showed the comment body starting at the fenced block with the marker gone, so that copy is invisible to a marker scan. This one carries the marker as literal text instead. Same report, byte-for-byte in the JSON; this comment is the authoritative one.{ "issue": 10871, "status": "done", "branch": "claude/issue-10871-docs-typecheck-typegen", "pr": "https://github.com/objectstack-ai/objectstack/pull/10879", "premise_still_valid": true, "summary": "All three filed measurements held on origin/main @ 7d483e1e5f. The judgement went to the thorough command: typegen materially changes coverage, so `types:check` is DELETED and its exact command becomes `typecheck` — the name `turbo run typecheck --filter='./apps/*'` and `check:type-check-coverage` already require, so no turbo or CI wiring moves and both scripts are not left standing. The empty-.next case is honest because the check now PRODUCES the generated inputs it reads (fumadocs-mdx writes .source/, next typegen writes .next/types/** and next-env.d.ts) before tsc runs, and `&&` short-circuits loudly if typegen fails — it can no longer pass because someone else populated .next, because it populates .next itself. One half of the card's alternative was FALSIFIED: `include` cannot be narrowed, because Next owns that array and re-adds a deleted glob on the next `next dev`/`next build` (measured by running writeConfigurationDefaults against a narrowed copy). The rationale is recorded as a comment in tsconfig.json where the next reader would try to tidy it; the residual `.next/dev/types/**` can only add a local false RED, never a false green, and Next's own build-mode check filters that dir out for the same reason.", "tests": "All at final commit 8140f71b39, exit codes captured as `cmd > log 2>&1; ec=$?` (never through a pipe). COVERAGE DELTA (empty .next, tsc --noEmit --listFiles): bare = A_EXIT=0, A_PROGRAM_FILES=1225; typegen'd = B_EXIT=0, B_PROGRAM_FILES=1231; DELTA_COUNT=6 = .next/types/{validator.ts,routes.d.ts,root-params.d.ts,cache-life.d.ts}, next-env.d.ts, next/image-types/global.d.ts. Cost: B_TYPEGEN_SECONDS=1, no build needed. ABLATION (extra required key on app/[lang]/docs/layout.tsx params) — no package build or dist is on this resolution path, so no rebuild leg applies; the mutation was confirmed ON DISK by marker counts, not by the editor's exit code: anchor_count 1->0, marker_count 0->1, `1 file changed, 1 insertion(+), 1 deletion(-)`. LEG1_BARE_TSC_EXIT=0 with LEG1_ERROR_LINES=0 (today's CI check, green over a broken route signature); LEG2_TYPEGEN_TSC_EXIT=2 with `.next/types/validator.ts(139,31): error TS2344 ... Property 'nonexistentParam' is missing in type '{ lang: string; }'`. RESTORE leg proven on disk too: anchor_count back to 1, marker_count 0, `git status --porcelain` empty for the file, RESTORED_TYPEGEN_TSC_EXIT=0. CARD'S REQUIRED PROOF (rm -rf .next next-env.d.ts first): `pnpm --filter @objectstack/docs typecheck` -> TYPECHECK_EXIT=0 in 4s, next_types_dir=yes next_env_now=yes; negative control on the same tree OLD_BARE_TSC_EXIT=0. Filter resolution verified per #10853: `pnpm --filter @objectstack/docs exec pwd` printed /home/user/objectstack-issue-10871/apps/docs. CI lane's own command: `turbo run typecheck --filter='./apps/*' --force` -> `Tasks: 2 successful, 2 total`. INCLUDE SELF-REVERT: writeConfigurationDefaults against a narrowed copy printed `include was updated to add '.next/dev/types/**/*.ts'`, DEV_GLOB_READDED_BY_NEXT=YES. GATES derived by `node scripts/pm/dispatch-gates.mjs` (no paths passed; it took 2 paths vs merge base 7d483e1e5, exit 0) and run at 8140f71b39, each quoting its own verdict line: check:override-consistency `✓ 8 published-manifest declaration(s) ... resolve to their override targets.`; check:test-source-alias `check-test-source-alias OK — 72 packages with tests scanned`; check:type-source-resolution `check-type-source-resolution OK — 76 packages with a tsconfig.json scanned`; check-changeset-fixed `✓ .changeset/config.json fixed group is in sync with 69 public workspace packages.`; check-osv-exemptions `✓ osv-scanner.toml holds zero OSV exemptions (the intended steady state).`; check:nul-bytes `check-nul-bytes: OK (scanned 6294 text file(s) ... no raw ASCII control bytes).`; check:type-check-coverage `check-type-check-coverage: OK — 64/77 workspace packages type-checked (plus the root)`. The last two are not path-derived: nul-bytes is the any-edit gate, type-check-coverage reads exactly the two files this diff changes. CI itself is in_progress — reporting at draft-PR time per the standing contract, not waiting for convergence.", "open_questions": [], "out_of_scope_findings": [ "filed as #10880: check:type-check-coverage has no invariant for a tsconfig `include` naming a GENERATED directory the package's typecheck script never generates — the class-closing guard for this card, deliberately kept out of the PR because it would edit the 3790-line required-context gate that #10756 is already dispatched against (same file, different invariant; needs sequencing, not parallel work)" ] }Label read-back (applied additively as the union with what the bots had already written, never a bare set):
documentation,dependencies,size/s,tooling,skip-changeset.PR #10879 is a draft, as dispatched. Diff is
apps/docs/package.json(+1/-2) andapps/docs/tsconfig.json(+24/-0), 2 files.
Generated by Claude Code
What
apps/docs/package.jsondeclares two near-identically named type-checking scripts:types:check— the thorough one — is called by nothing. Measured onorigin/main@5886ee6d22:No workflow, no script, no turbo task references it. The one that actually runs is
typecheck, reached through the rootturbo run typecheck.Why it may be worth a card
apps/docs/tsconfig.jsonincludes the generated route types:Those paths are produced by
next typegen— which onlytypes:checkruns. So theprogram
typecheckactually checks depends on whether some earlier, unrelatedcommand happened to populate
.next: a previousnext buildornext devin thesame checkout. A clean checkout type-checks a strictly smaller program than a dirty
one, and neither is declared anywhere.
Measured directly while verifying #10782: with
apps/docs/.nextdeleted,It passes without the generated route types existing at all. That is the
"reads green, verified less" shape — the failure is not that it goes red, but that
its coverage is set by incidental build state and nothing says so.
Two things a triage round might separate:
types:checkscript (either wire it up or delete it — ADR-0049'senforce-or-remove instinct applied to a package script); and
next typegen, socoverage is a property of the script rather than of leftover
.nextstate.I have not verified whether the generated route types would actually catch anything
in this app today — that is the triage question, not a claim made here.
Found while working on #10782 (Next
middleware->proxyconvention rename);out of that card's file surface, so filed rather than fixed. Unassigned.