Problem class: typescript-test-tsconfig-semantic-gap
The repository was not present in the environment and the private GitHub remote rejected unauthenticated access, so the document below is derived entirely from the provided problem evidence. It is written to /workspace/solution.md.
GroundingCaseFamily + TS2322 constraint-guarded in test datasetProblem class: typescript-test-tsconfig-semantic-gap
Repo: dexdat/eduos.dexdat.com.co — apps/api pnpm monorepo
Task: BETA-GATE-005
Commit under repair: 0b3988f81726b22cafe3308053aafd3b13d90d74
File: apps/api/src/modules/ai-tutor/__tests__/grounding-eval-dataset.ts
Toolchain: pnpm, TypeScript, Vitest 4.1.11
Functional tests were green under Vitest, but a direct semantic compile of the test source failed:
TS2300 Duplicate identifier 'GroundingCaseFamily'.
TS2322 Type '"constraint-guarded"' is not assignable to type 'GroundingCaseFamily'.
Both errors came from a single test-only file. Nothing in application code failed, and the normal package build / tsc -p run stayed clean.
Three separate mechanisms combined into a false-green signal:
Vitest (like Jest with ts-jest in transpile mode) runs test files through esbuild / SWC transform, which strips types without checking them. isolatedModules-style transforms never evaluate type aliases, duplicate identifiers, or assignability. Therefore a type error in a test file cannot fail a Vitest run.
tsconfig excludes test sourcesapps/api/tsconfig.json owns include/exclude for the production program. Test directories (__tests__/, *.test.ts, *.spec.ts) are deliberately excluded so test-only dependencies never leak into the app build. Consequence:
pnpm --filter @eduos/api exec tsc -p tsconfig.json --noEmit # succeeds, but never sees the test file
The standard type-check gate never compiled grounding-eval-dataset.ts, so it was invisible to CI.
grounding-eval-dataset.ts declared the same exported type alias twice. The obsolete declaration was left in place when the family union was reworked, while the new canonical union added the "constraint-guarded" family:
// ── obsolete declaration that should have been deleted ──────────────
export type GroundingCaseFamily =
| "factual"
| "inference"
| "refusal";
// ── canonical declaration (new families added) ──────────────────────
export type GroundingCaseFamily =
| "factual"
| "inference"
| "refusal"
| "constraint-guarded";
// ...later in the dataset...
const evalCase = { family: "constraint-guarded" } as const;
TypeScript reports one TS2300 per duplicate declaration site, and because the duplicate name is ambiguous, the literal "constraint-guarded" is checked against a union that does not contain it → TS2322. The TS2322 is a downstream artifact of the TS2300, not an independent bug. Removing the obsolete alias leaves the canonical union (which already contains "constraint-guarded") and clears both errors.
| Gate | Sees test file? | Checks types? | Result |
|---|---|---|---|
| Vitest run | yes | no | green |
tsc -p apps/api/tsconfig.json |
no | yes | green |
direct tsc on test source |
yes | yes | TS2300 + TS2322 |
The single gate that would have caught it — semantic compilation of test sources — did not exist.
Find both declaration sites:
cd apps/api
rg -n "type GroundingCaseFamily" src/modules/ai-tutor/__tests__/grounding-eval-dataset.ts
Expected: two hits (plus usage sites). Delete the obsolete block — the one that is not referenced by the rest of the dataset and does not include "constraint-guarded" (or whichever canonical union is actually used by the case objects). Keep exactly one declaration.
Before
// obsolete — remove this entire block
export type GroundingCaseFamily =
| "factual"
| "inference"
| "refusal";
export type GroundingCaseFamily =
| "factual"
| "inference"
| "refusal"
| "constraint-guarded";
After
export type GroundingCaseFamily =
| "factual"
| "inference"
| "refusal"
| "constraint-guarded";
If the surviving canonical union is genuinely missing
"constraint-guarded"and the case value is correct, add the literal to the single surviving union instead of deleting it. Never keep two aliases. Do not// @ts-ignoreor cast the literal toany.
tsconfigSo the type error can never be invisible again, add a program that includes the tests. Create apps/api/tsconfig.test.json:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"types": ["node", "vitest/globals"]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules",
"dist",
"coverage"
]
}
Because it extends the base config, it inherits strict, moduleResolution, path alias/paths, and every other compiler option; overriding include/exclude is what pulls __tests__ back into the program. If apps/api has no base tsconfig paths for test imports, add the Vitest global types as shown.
apps/api/package.json:
{
"scripts": {
"typecheck": "tsc -p tsconfig.json --noEmit",
"typecheck:tests": "tsc -p tsconfig.test.json --noEmit",
"test": "vitest run"
}
}
CI (or the root typecheck aggregate):
pnpm --filter @eduos/api run typecheck
pnpm --filter @eduos/api run typecheck:tests # closure evidence alongside Vitest
pnpm --filter @eduos/api run test
The direct single-file invocation used to expose the bug, usable as an ad-hoc check:
cd apps/api
pnpm exec tsc --noEmit \
--strict \
--target ES2022 \
--module ESNext \
--moduleResolution bundler \
--types node,vitest/globals \
--skipLibCheck \
src/modules/ai-tutor/__tests__/grounding-eval-dataset.ts
--skipLibCheck only skips .d.ts checking; it still fully semantically checks the target .ts source, which is what surfaces duplicate identifiers and invalid literals.
Run from the repo root unless noted. All commands must exit 0.
# 1. The previously-hidden semantic compile of the test program.
cd apps/api
pnpm exec tsc -p tsconfig.test.json --noEmit # => exit 0, no output
# or the one-file form:
pnpm exec tsc --noEmit --strict --target ES2022 --module ESNext \
--moduleResolution bundler --types node,vitest/globals --skipLibCheck \
src/modules/ai-tutor/__tests__/grounding-eval-dataset.ts # => exit 0
# 2. Focused aggregate for the ai-tutor area.
cd ../..
pnpm --filter @eduos/api exec vitest run src/modules/ai-tutor # => 41/41 passed
# 3. Full root suite (functional closure).
pnpm -w exec vitest run # => 3434 passed, 33 skipped
# 4. Confirm exactly one alias declaration remains.
rg -n "^export type GroundingCaseFamily" \
apps/api/src/modules/ai-tutor/__tests__/grounding-eval-dataset.ts # => 1 match
Expected evidence (matches BETA-GATE-005 sign-off):
| Check | Expected |
|---|---|
direct tsc (test program / test file) |
exit 0, no TS2300 / TS2322 |
| focused aggregate | 41/41 passed |
| root suite | 3434 passed, 33 skipped |
rg alias count |
exactly 1 declaration |
"constraint-guarded", so the TS2322 disappears as a consequence.typecheck:tests runs real tsc over __tests__, so transpile-only Vitest can no longer hide type errors in test sources. The application tsconfig stays clean (tests still excluded from the production build).@ts-ignore, as any, or skipLibCheck scoped to project source. skipLibCheck in the one-file recipe only affects dependency .d.ts files.tsc) and runtime execution (Vitest focused + root suite) — which is exactly the closure evidence requested.rg "type GroundingCaseFamily" must return one hit.pnpm --filter @eduos/api run typecheck:tests.typecheck:tests to the same CI job/stage that runs Vitest so neither can pass alone.Caveat: I could not execute the verification commands because the repository checkout and remote credentials were unavailable in this environment. The fix, the tsconfig.test.json guard, and the expected evidence table are pinned to the reported BETA-GATE-005 results (direct tsc exit 0; focused 41/41; root 3434 passed / 33 skipped).
# Evidence - Problem class: typescript-test-tsconfig-semantic-gap - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-11T14:46:51.290Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Vitest transpile-only execution and an application tsconfig that excludes __tests__ allowed a duplicate exported type alias plus invalid union members to remain hidden while functional tests passed. A direct one-file tsc invocation exposed TS2300 duplicate identifier and TS2322 invalid literal errors. Remove the obsolete alias, then retain direct test-source semantic compilation as closure evidence alongside Vitest.", "environment": "EduOS pnpm monorepo on Linux; apps/api tsconfig excludes test sources", "language": "typescript", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "typescript-test-tsconfig-semantic-gap", "provider": "openrouter", "solved_at": "2026-09-11T14:46:51.290Z", "version": "TypeScript with Vitest 4.1.11"}The repository was not present in the environment and the private GitHub remote rejected unauthenticated access, so the document below is derived entirely from the provided problem evidence. It is written to /workspace/solution.md.
GroundingCaseFamily + TS2322 constraint-guarded in test datasetProblem class: typescript-test-tsconfig-semantic-gap
Repo: dexdat/eduos.dexdat.com.co — apps/api pnpm monorepo
Task: BETA-GATE-005
Commit under repair: 0b3988f81726b22cafe3308053aafd3b13d90d74
File: apps/api/src/modules/ai-tutor/__tests__/grounding-eval-dataset.ts
Toolchain: pnpm, TypeScript, Vitest 4.1.11
Functional tests were green under Vitest, but a direct semantic compile of the test source failed:
TS2300 Duplicate identifier 'GroundingCaseFamily'.
TS2322 Type '"constraint-guarded"' is not assignable to type 'GroundingCaseFamily'.
Both errors came from a single test-only file. Nothing in application code failed, and the normal package build / tsc -p run stayed clean.
Three separate mechanisms combined into a false-green signal:
Vitest (like Jest with ts-jest in transpile mode) runs test files through esbuild / SWC transform, which strips types without checking them. isolatedModules-style transforms never evaluate type aliases, duplicate identifiers, or assignability. Therefore a type error in a test file cannot fail a Vitest run.
tsconfig excludes test sourcesapps/api/tsconfig.json owns include/exclude for the production program. Test directories (__tests__/, *.test.ts, *.spec.ts) are deliberately excluded so test-only dependencies never leak into the app build. Consequence:
pnpm --filter @eduos/api exec tsc -p tsconfig.json --noEmit # succeeds, but never sees the test file
The standard type-check gate never compiled grounding-eval-dataset.ts, so it was invisible to CI.
grounding-eval-dataset.ts declared the same exported type alias twice. The obsolete declaration was left in place when the family union was reworked, while the new canonical union added the "constraint-guarded" family:
// ── obsolete declaration that should have been deleted ──────────────
export type GroundingCaseFamily =
| "factual"
| "inference"
| "refusal";
// ── canonical declaration (new families added) ──────────────────────
export type GroundingCaseFamily =
| "factual"
| "inference"
| "refusal"
| "constraint-guarded";
// ...later in the dataset...
const evalCase = { family: "constraint-guarded" } as const;
TypeScript reports one TS2300 per duplicate declaration site, and because the duplicate name is ambiguous, the literal "constraint-guarded" is checked against a union that does not contain it → TS2322. The TS2322 is a downstream artifact of the TS2300, not an independent bug. Removing the obsolete alias leaves the canonical union (which already contains "constraint-guarded") and clears both errors.
| Gate | Sees test file? | Checks types? | Result |
|---|---|---|---|
| Vitest run | yes | no | green |
tsc -p apps/api/tsconfig.json |
no | yes | green |
direct tsc on test source |
yes | yes | TS2300 + TS2322 |
The single gate that would have caught it — semantic compilation of test sources — did not exist.
Find both declaration sites:
cd apps/api
rg -n "type GroundingCaseFamily" src/modules/ai-tutor/__tests__/grounding-eval-dataset.ts
Expected: two hits (plus usage sites). Delete the obsolete block — the one that is not referenced by the rest of the dataset and does not include "constraint-guarded" (or whichever canonical union is actually used by the case objects). Keep exactly one declaration.
Before
// obsolete — remove this entire block
export type GroundingCaseFamily =
| "factual"
| "inference"
| "refusal";
export type GroundingCaseFamily =
| "factual"
| "inference"
| "refusal"
| "constraint-guarded";
After
export type GroundingCaseFamily =
| "factual"
| "inference"
| "refusal"
| "constraint-guarded";
If the surviving canonical union is genuinely missing
"constraint-guarded"and the case value is correct, add the literal to the single surviving union instead of deleting it. Never keep two aliases. Do not// @ts-ignoreor cast the literal toany.
tsconfigSo the type error can never be invisible again, add a program that includes the tests. Create apps/api/tsconfig.test.json:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"types": ["node", "vitest/globals"]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx"
],
"exclude": [
"node_modules",
"dist",
"coverage"
]
}
Because it extends the base config, it inherits strict, moduleResolution, path alias/paths, and every other compiler option; overriding include/exclude is what pulls __tests__ back into the program. If apps/api has no base tsconfig paths for test imports, add the Vitest global types as shown.
apps/api/package.json:
{
"scripts": {
"typecheck": "tsc -p tsconfig.json --noEmit",
"typecheck:tests": "tsc -p tsconfig.test.json --noEmit",
"test": "vitest run"
}
}
CI (or the root typecheck aggregate):
pnpm --filter @eduos/api run typecheck
pnpm --filter @eduos/api run typecheck:tests # closure evidence alongside Vitest
pnpm --filter @eduos/api run test
The direct single-file invocation used to expose the bug, usable as an ad-hoc check:
cd apps/api
pnpm exec tsc --noEmit \
--strict \
--target ES2022 \
--module ESNext \
--moduleResolution bundler \
--types node,vitest/globals \
--skipLibCheck \
src/modules/ai-tutor/__tests__/grounding-eval-dataset.ts
--skipLibCheck only skips .d.ts checking; it still fully semantically checks the target .ts source, which is what surfaces duplicate identifiers and invalid literals.
Run from the repo root unless noted. All commands must exit 0.
# 1. The previously-hidden semantic compile of the test program.
cd apps/api
pnpm exec tsc -p tsconfig.test.json --noEmit # => exit 0, no output
# or the one-file form:
pnpm exec tsc --noEmit --strict --target ES2022 --module ESNext \
--moduleResolution bundler --types node,vitest/globals --skipLibCheck \
src/modules/ai-tutor/__tests__/grounding-eval-dataset.ts # => exit 0
# 2. Focused aggregate for the ai-tutor area.
cd ../..
pnpm --filter @eduos/api exec vitest run src/modules/ai-tutor # => 41/41 passed
# 3. Full root suite (functional closure).
pnpm -w exec vitest run # => 3434 passed, 33 skipped
# 4. Confirm exactly one alias declaration remains.
rg -n "^export type GroundingCaseFamily" \
apps/api/src/modules/ai-tutor/__tests__/grounding-eval-dataset.ts # => 1 match
Expected evidence (matches BETA-GATE-005 sign-off):
| Check | Expected |
|---|---|
direct tsc (test program / test file) |
exit 0, no TS2300 / TS2322 |
| focused aggregate | 41/41 passed |
| root suite | 3434 passed, 33 skipped |
rg alias count |
exactly 1 declaration |
"constraint-guarded", so the TS2322 disappears as a consequence.typecheck:tests runs real tsc over __tests__, so transpile-only Vitest can no longer hide type errors in test sources. The application tsconfig stays clean (tests still excluded from the production build).@ts-ignore, as any, or skipLibCheck scoped to project source. skipLibCheck in the one-file recipe only affects dependency .d.ts files.tsc) and runtime execution (Vitest focused + root suite) — which is exactly the closure evidence requested.rg "type GroundingCaseFamily" must return one hit.pnpm --filter @eduos/api run typecheck:tests.typecheck:tests to the same CI job/stage that runs Vitest so neither can pass alone.Caveat: I could not execute the verification commands because the repository checkout and remote credentials were unavailable in this environment. The fix, the tsconfig.test.json guard, and the expected evidence table are pinned to the reported BETA-GATE-005 results (direct tsc exit 0; focused 41/41; root 3434 passed / 33 skipped).
# Evidence - Problem class: typescript-test-tsconfig-semantic-gap - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-11T14:46:51.290Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Vitest transpile-only execution and an application tsconfig that excludes __tests__ allowed a duplicate exported type alias plus invalid union members to remain hidden while functional tests passed. A direct one-file tsc invocation exposed TS2300 duplicate identifier and TS2322 invalid literal errors. Remove the obsolete alias, then retain direct test-source semantic compilation as closure evidence alongside Vitest.", "environment": "EduOS pnpm monorepo on Linux; apps/api tsconfig excludes test sources", "language": "typescript", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "typescript-test-tsconfig-semantic-gap", "provider": "openrouter", "solved_at": "2026-09-11T14:46:51.290Z", "version": "TypeScript with Vitest 4.1.11"}