AI coding assistants write library code from memory. When a library ships a big release that renames or removes things, the assistant keeps writing the old version. It reads fine & it doesn't build.
SDKProof measures how often that happens. It gives a model real coding jobs for one library, then compiles every answer against the real installed package with tsc, the TypeScript compiler. A task passes only if it compiles — no AI judges another AI.
This page is TanStack React Table. Twelve ordinary table jobs — build one, sort it, paginate it — one shot each, no docs, no retries.
0 of the 12 compiled. That is the lowest score on this board. The model wrote the v8 API every time, and v9 renamed nearly every piece it reached for.
This measures the model, not TanStack Table.
v9 renamed the main hook. useReactTable is now useTable. It also moved the row-model factories: getCoreRowModel is no longer exported from the package root, and createCoreRowModel is. Same for the sorted, filtered, grouped and paginated ones.
In 11 of its 12 answers the model imported createTable from the package root, and two of those name useReactTable in a comment. The v9 root exports neither one.
Both panes below are real. Left is the answer the model wrote, shortened. Right is code that compiles against the same installed copy.
@tanstack/react-table 9.1.2
import {
createTable,
getCoreRowModel,
type ColumnDef,
type Table,
type TableState,
type Updater,
} from "@tanstack/react-table";
export function buildUserTable(data: User[]): Table<User> {
const columns: ColumnDef<User, any>[] = [
{ id: "id", accessorKey: "id", header: "ID" },
{ id: "name", accessorKey: "name", header: "Name" },
];
const table: Table<User> = createTable<User>({
data,
columns,
getCoreRowModel: getCoreRowModel<User>(),
state,
onStateChange: (updater: Updater<TableState>) => { … },
renderFallbackValue: null,
});
return table;
}error TS2724: '"@tanstack/react-table"' has no exported member named 'createTable'. Did you mean 'ReactTable'? error TS2724: '"@tanstack/react-table"' has no exported member named 'getCoreRowModel'. Did you mean 'createCoreRowModel'?
@tanstack/react-table 9.1.2
import {
useTable,
createColumnHelper,
type ColumnDef,
} from "@tanstack/react-table";
type User = { id: string; name: string };
const features = {};
type Features = typeof features;
const helper = createColumnHelper<Features, User>();
const columns: ColumnDef<Features, User, any>[] = [
helper.accessor("id", { header: "ID" }),
helper.accessor("name", { header: "Name" }),
];
export function buildUserTable(data: User[]) {
return useTable<Features, User>({ features, columns, data });
}tsc --noEmit — 0 errors
Reading the error. TS2724 is the compiler saying "this import does not exist, but something close does". It names the right replacement for getCoreRowModel. For createTable it does not, and that is the next section.
Every one of those 11 answers gets the same hint: Did you mean 'ReactTable'?
ReactTable is a type, not a function. It is what useTable returns. Swap the name in and the import resolves, then the call fails, because you cannot call a type.
So the one automatic repair path a developer or an agent would try first leads nowhere. The name it should have landed on is useTable.
This is not TanStack's doing. TypeScript picks the nearest exported name by spelling, and ReactTable is simply closer to createTable than useTable is. It is worth writing down because it changes what the failure costs. A wrong import you can fix straight from the error message costs a minute. This one doesn't offer that.
createColumnHelper survived into v9 with the same name and gained a type parameter in front of the data type — the table's feature set. So createColumnHelper<User>(), which was correct in v8, now hands User to the slot that wants features.
The compiler reports it as Type 'User' has no properties in common with type 'TableFeatures'. 11 of the 12 answers hit that error, 12 times in total.
TableFeatures appears nowhere in the code that produced it. You get told your own type is wrong against a name you have never typed, which is a hard error to act on without reading the release notes first.
@tanstack/react-table 9.1.2
import { createColumnHelper, type ColumnDef } from "@tanstack/react-table";
const columnHelper = createColumnHelper<User>();
export function userColumns(): ColumnDef<User, any>[] {
return [
columnHelper.accessor("id", {
id: "id",
header: () => "ID",
cell: (info) => info.getValue(),
}),
columnHelper.accessor("name", { … }),
];
}error TS2558: Expected 2 type arguments, but got 1. error TS2559: Type 'User' has no properties in common with type 'TableFeatures'.
@tanstack/react-table 9.1.2
import { createColumnHelper, type ColumnDef } from "@tanstack/react-table";
type User = { id: string; name: string };
const features = {};
type Features = typeof features;
const columnHelper = createColumnHelper<Features, User>();
export function userColumns(): ColumnDef<Features, User, any>[] {
return [
columnHelper.accessor("id", { header: () => "ID" }),
columnHelper.accessor("name", { header: () => "Name" }),
];
}tsc --noEmit — 0 errors
The feature set goes first, the data type second, and every table type carries the same pair from then on. That is one edit per generic, and the model made it zero times.
This is the part a 0 hides. v9 publishes a legacy entry point at @tanstack/react-table/legacy, and it holds the v8 shapes: useLegacyTable, getCoreRowModel and the rest of the getXRowModel family, plus a column helper that still takes the data type on its own.
Import from there and the same job compiles clean. So the upgrade is not a cliff — there is a supported landing spot, and it works.
All 12 answers imported from the package root. Not one reached for /legacy. That is the finding, and it is a finding about the model: the escape hatch is there, and it goes unused.
@tanstack/react-table 9.1.2
import {
useLegacyTable,
getCoreRowModel,
legacyCreateColumnHelper,
type LegacyColumnDef,
} from "@tanstack/react-table/legacy";
type User = { id: string; name: string };
const helper = legacyCreateColumnHelper<User>();
const columns: LegacyColumnDef<User, any>[] = [
helper.accessor("id", { header: "ID" }),
helper.accessor("name", { header: "Name" }),
];
export function buildUserTable(data: User[]) {
return useLegacyTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
});
}tsc --noEmit — 0 errors
Compiled against the same installed 9.1.2 as everything else on this page.
The package also ships skill files for agents — six of them under skills/, including one on moving from v8 to v9. This run hands the model nothing at all, so nothing here says whether those files work. That question needs its own measurement, and scoring a library twice is not it.
Every failing answer produced more than one error. They sort into four groups, and the first two are the rename and the column helper you have just read.
| What the compiler objected to | Count | One real example |
|---|---|---|
| an import that no longer exists | 30 | has no exported member named 'createTable' |
| a type in the wrong slot | 12 | Type 'User' has no properties in common with type 'TableFeatures' |
| an option that isn't there | 3 | 'enableSorting' does not exist in type 'ColumnDef<User, any>' |
| everything else | 68 | Expected 2 type arguments, but got 1. |
The last group is mostly knock-on: once the feature set is missing, every Table, Row and TableState in the file is short a type argument, so one wrong assumption bills several times. Counts are read straight out of data/react-table.result.json.
Twelve realistic table tasks, written by claude-opus-5, each dropped into a small project with @tanstack/react-table 9.1.2 actually installed, then run through tsc --noEmit. A task passes only if it compiles. Every failure quoted above is the compiler's own error text.
claude-opus-5. Another model will score differently.AGENTS.md, no editor rules, no skill files, no web. That's the point: it measures what a model reaches for unaided.tsc, the TypeScript compiler, checks that the API exists and the types line up. It never runs the code, so nothing here says the code does the right thing.claude-opus-5 remembers of this library, not about the library's design. A number this low mostly means the release is very recent.Breaks the moment a callback gets an explicit type.
Queries are clean. Both misses are client setup.
One miss: revalidateTag() takes two arguments now.
Every v4 to v5 rename written unprompted.
Name any TypeScript package & I'll run it. Or do it yourself — it's all open source.
There's no npm package. Clone the repo, point it at a library, run it. The compiler is the judge.