sdkproof 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +192 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +201 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/core/classify.d.ts +4 -0
  7. package/dist/core/classify.js +72 -0
  8. package/dist/core/classify.js.map +1 -0
  9. package/dist/core/model.d.ts +30 -0
  10. package/dist/core/model.js +27 -0
  11. package/dist/core/model.js.map +1 -0
  12. package/dist/core/prompt.d.ts +4 -0
  13. package/dist/core/prompt.js +43 -0
  14. package/dist/core/prompt.js.map +1 -0
  15. package/dist/core/score.d.ts +3 -0
  16. package/dist/core/score.js +37 -0
  17. package/dist/core/score.js.map +1 -0
  18. package/dist/core/stats.d.ts +99 -0
  19. package/dist/core/stats.js +121 -0
  20. package/dist/core/stats.js.map +1 -0
  21. package/dist/core/types.d.ts +162 -0
  22. package/dist/core/types.js +11 -0
  23. package/dist/core/types.js.map +1 -0
  24. package/dist/core/verify.d.ts +33 -0
  25. package/dist/core/verify.js +191 -0
  26. package/dist/core/verify.js.map +1 -0
  27. package/dist/drift.d.ts +87 -0
  28. package/dist/drift.js +105 -0
  29. package/dist/drift.js.map +1 -0
  30. package/dist/index.d.ts +19 -0
  31. package/dist/index.js +19 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/models.d.ts +33 -0
  34. package/dist/models.js +186 -0
  35. package/dist/models.js.map +1 -0
  36. package/dist/registry.d.ts +65 -0
  37. package/dist/registry.js +98 -0
  38. package/dist/registry.js.map +1 -0
  39. package/dist/report.d.ts +26 -0
  40. package/dist/report.js +240 -0
  41. package/dist/report.js.map +1 -0
  42. package/dist/run.d.ts +36 -0
  43. package/dist/run.js +188 -0
  44. package/dist/run.js.map +1 -0
  45. package/dist/surface.d.ts +95 -0
  46. package/dist/surface.js +281 -0
  47. package/dist/surface.js.map +1 -0
  48. package/dist/tarball.d.ts +7 -0
  49. package/dist/tarball.js +66 -0
  50. package/dist/tarball.js.map +1 -0
  51. package/dist/tasks.d.ts +41 -0
  52. package/dist/tasks.js +199 -0
  53. package/dist/tasks.js.map +1 -0
  54. package/dist/workspace.d.ts +42 -0
  55. package/dist/workspace.js +251 -0
  56. package/dist/workspace.js.map +1 -0
  57. package/package.json +58 -0
@@ -0,0 +1,37 @@
1
+ import { classify } from "./classify.js";
2
+ /** Aggregate verdicts into a per-model + overall score with ranked failures. */
3
+ export function score(library, libraryVersion, generatedAt, verdicts, refusals = [], contextArms = [], lost = []) {
4
+ const models = [...new Set(verdicts.map((v) => v.model))].sort();
5
+ const perModel = models.map((model) => {
6
+ const forModel = verdicts.filter((v) => v.model === model);
7
+ const passed = forModel.filter((v) => v.passed).length;
8
+ const total = forModel.length;
9
+ return {
10
+ model,
11
+ passed,
12
+ total,
13
+ score: total ? Math.round((100 * passed) / total) : 0,
14
+ };
15
+ });
16
+ const passedAll = verdicts.filter((v) => v.passed).length;
17
+ const overallScore = verdicts.length
18
+ ? Math.round((100 * passedAll) / verdicts.length)
19
+ : 0;
20
+ // Deltas are computed against the bare score on the SAME task subset, in the
21
+ // CLI, where the per-arm verdicts live. Not recomputed here against the
22
+ // headline score — those cover different task sets whenever an arm lost one.
23
+ const arms = contextArms;
24
+ return {
25
+ library,
26
+ libraryVersion,
27
+ generatedAt,
28
+ overallScore,
29
+ perModel,
30
+ failurePatterns: classify(verdicts),
31
+ verdicts,
32
+ refusals,
33
+ ...(lost.length ? { lost } : {}),
34
+ ...(arms.length ? { contextArms: arms } : {}),
35
+ };
36
+ }
37
+ //# sourceMappingURL=score.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/core/score.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,gFAAgF;AAChF,MAAM,UAAU,KAAK,CACnB,OAAe,EACf,cAAsB,EACtB,WAAmB,EACnB,QAAmB,EACnB,QAAQ,GAAc,EAAE,EACxB,WAAW,GAAe,EAAE,EAC5B,IAAI,GAAe,EAAE;IAErB,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjE,MAAM,QAAQ,GAAiB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAClD,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;QAC3D,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;QACvD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;QAC9B,OAAO;YACL,KAAK;YACL,MAAM;YACN,KAAK;YACL,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;SACtD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAC1D,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM;QAClC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,SAAS,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;QACjD,CAAC,CAAC,CAAC,CAAC;IAEN,6EAA6E;IAC7E,wEAAwE;IACxE,6EAA6E;IAC7E,MAAM,IAAI,GAAG,WAAW,CAAC;IAEzB,OAAO;QACL,OAAO;QACP,cAAc;QACd,WAAW;QACX,YAAY;QACZ,QAAQ;QACR,eAAe,EAAE,QAAQ,CAAC,QAAQ,CAAC;QACnC,QAAQ;QACR,QAAQ;QACR,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * The two things every rate on this site was missing (raised on dev.to,
3
+ * 2026-08-06, and both fair):
4
+ *
5
+ * 1. A rate with no interval invites the reader to treat 10/10 as certainty.
6
+ * At n=10 it is not — Wilson puts 10/10 at ">=72%" and 0/10 at "up to 28%".
7
+ * The refusal table had four 10/10 rows and three 0/10 rows presented as if
8
+ * they were the same strength of evidence.
9
+ * 2. One score over one denominator hides which denominator it used. A Stripe
10
+ * run is 100% of what it wrote and 67% of what it was asked — both true, and
11
+ * publishing only the first hides the actual failure mode.
12
+ *
13
+ * Wilson rather than the normal approximation because every interesting cell
14
+ * here is at or near an edge (0/10, 10/10), where the textbook interval is
15
+ * either degenerate or runs outside [0,1].
16
+ */
17
+ export interface Interval {
18
+ /** lower bound, 0-1 */
19
+ low: number;
20
+ /** upper bound, 0-1 */
21
+ high: number;
22
+ /** point estimate, 0-1 */
23
+ p: number;
24
+ /** the denominator the interval was computed over */
25
+ n: number;
26
+ }
27
+ /**
28
+ * Wilson score interval for a binomial proportion.
29
+ *
30
+ * Throws on an empty or impossible denominator rather than returning a
31
+ * confident-looking zero — the same rule exp-refusals.ts already applies when
32
+ * more than half its requests error out. A broken input must not render as a
33
+ * number.
34
+ */
35
+ export declare function wilson(k: number, n: number, z?: number): Interval;
36
+ /** "33.8–49.3%" — an en dash, so it never reads as a minus sign. */
37
+ export declare function fmtInterval(ci: Interval): string;
38
+ export interface RateSplit {
39
+ /** passes over usable, non-refused completions — "did it write the current API" */
40
+ conditional: {
41
+ passed: number;
42
+ n: number;
43
+ pct: number;
44
+ ci: Interval;
45
+ };
46
+ /** passes over every task asked — "did I get a usable answer at all" */
47
+ unconditional: {
48
+ passed: number;
49
+ n: number;
50
+ pct: number;
51
+ ci: Interval;
52
+ };
53
+ /** true when the two differ, i.e. something was refused and the split matters */
54
+ split: boolean;
55
+ }
56
+ /**
57
+ * Both top-line numbers for a run, side by side.
58
+ *
59
+ * `scored` is the count that produced code and reached the compiler; `refused`
60
+ * is the count that produced none. A refusal is not a failure of the library,
61
+ * which is why it stays out of the conditional denominator — and it is not a
62
+ * success either, which is why it stays in the unconditional one.
63
+ */
64
+ export declare function rates(run: {
65
+ passed: number;
66
+ scored: number;
67
+ refused: number;
68
+ }): RateSplit;
69
+ export interface Delta {
70
+ /** point estimate of arm − baseline, -1 to 1 */
71
+ diff: number;
72
+ /** lower bound on the difference */
73
+ low: number;
74
+ /** upper bound on the difference */
75
+ high: number;
76
+ /** true iff the 95% interval excludes zero, i.e. the sign is supported */
77
+ significant: boolean;
78
+ }
79
+ /**
80
+ * 95% interval for the difference between a context arm and its baseline,
81
+ * by Newcombe's method — the two Wilson intervals combined, rather than a
82
+ * normal approximation on the difference.
83
+ *
84
+ * Why this exists (2026-08-08): every context arm on the site was published as
85
+ * a bare delta. React Router's skill-only arm is 42/45 against a 41/45
86
+ * baseline and was rendered as "+2", which is one generation out of
87
+ * forty-five. Prisma's best arm is +6, which is three. Neither survives a
88
+ * moment's arithmetic, and a site whose argument is that unmeasured numbers
89
+ * mislead cannot ship a delta without an interval on it.
90
+ *
91
+ * Newcombe rather than the textbook two-proportion interval for the same
92
+ * reason wilson() is used above: the interesting arms sit at the edges
93
+ * (0/10 -> 10/10), where the normal approximation runs outside [-1, 1].
94
+ *
95
+ * `significant` is deliberately the only boolean here. Everything downstream
96
+ * must branch on it rather than on the sign of `diff`, so a direction can
97
+ * never be reported from noise.
98
+ */
99
+ export declare function armDelta(armPassed: number, armN: number, basePassed: number, baseN: number): Delta;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * The two things every rate on this site was missing (raised on dev.to,
3
+ * 2026-08-06, and both fair):
4
+ *
5
+ * 1. A rate with no interval invites the reader to treat 10/10 as certainty.
6
+ * At n=10 it is not — Wilson puts 10/10 at ">=72%" and 0/10 at "up to 28%".
7
+ * The refusal table had four 10/10 rows and three 0/10 rows presented as if
8
+ * they were the same strength of evidence.
9
+ * 2. One score over one denominator hides which denominator it used. A Stripe
10
+ * run is 100% of what it wrote and 67% of what it was asked — both true, and
11
+ * publishing only the first hides the actual failure mode.
12
+ *
13
+ * Wilson rather than the normal approximation because every interesting cell
14
+ * here is at or near an edge (0/10, 10/10), where the textbook interval is
15
+ * either degenerate or runs outside [0,1].
16
+ */
17
+ /** 95% two-sided normal quantile. */
18
+ const Z95 = 1.959963984540054;
19
+ /**
20
+ * Wilson score interval for a binomial proportion.
21
+ *
22
+ * Throws on an empty or impossible denominator rather than returning a
23
+ * confident-looking zero — the same rule exp-refusals.ts already applies when
24
+ * more than half its requests error out. A broken input must not render as a
25
+ * number.
26
+ */
27
+ export function wilson(k, n, z = Z95) {
28
+ if (!Number.isFinite(n) || n <= 0) {
29
+ throw new Error(`wilson: empty denominator (n=${n}) — no rate to report`);
30
+ }
31
+ if (!Number.isFinite(k) || k < 0 || k > n) {
32
+ throw new Error(`wilson: need 0 <= k <= n, got k=${k}, n=${n}`);
33
+ }
34
+ const p = k / n;
35
+ const z2 = z * z;
36
+ const denom = 1 + z2 / n;
37
+ const center = (p + z2 / (2 * n)) / denom;
38
+ const margin = (z / denom) * Math.sqrt((p * (1 - p)) / n + z2 / (4 * n * n));
39
+ return {
40
+ // Pinned at the edges rather than clamped: floating point leaves k===n at
41
+ // 0.9999999999999999, and an interval that excludes its own point estimate
42
+ // is worse than no interval.
43
+ low: k === 0 ? 0 : Math.max(0, center - margin),
44
+ high: k === n ? 1 : Math.min(1, center + margin),
45
+ p,
46
+ n,
47
+ };
48
+ }
49
+ const pct = (x) => {
50
+ const s = (100 * x).toFixed(1);
51
+ // 100.0% and 0.0% read as false precision; the bound is exact at the edges.
52
+ return s.endsWith(".0") ? s.slice(0, -2) : s;
53
+ };
54
+ /** "33.8–49.3%" — an en dash, so it never reads as a minus sign. */
55
+ export function fmtInterval(ci) {
56
+ return `${pct(ci.low)}–${pct(ci.high)}%`;
57
+ }
58
+ /**
59
+ * Both top-line numbers for a run, side by side.
60
+ *
61
+ * `scored` is the count that produced code and reached the compiler; `refused`
62
+ * is the count that produced none. A refusal is not a failure of the library,
63
+ * which is why it stays out of the conditional denominator — and it is not a
64
+ * success either, which is why it stays in the unconditional one.
65
+ */
66
+ export function rates(run) {
67
+ const attempted = run.scored + run.refused;
68
+ const conditional = wilson(run.passed, run.scored);
69
+ const unconditional = wilson(run.passed, attempted);
70
+ return {
71
+ conditional: {
72
+ passed: run.passed,
73
+ n: run.scored,
74
+ pct: Math.round(100 * conditional.p),
75
+ ci: conditional,
76
+ },
77
+ unconditional: {
78
+ passed: run.passed,
79
+ n: attempted,
80
+ pct: Math.round(100 * unconditional.p),
81
+ ci: unconditional,
82
+ },
83
+ split: run.refused > 0,
84
+ };
85
+ }
86
+ /**
87
+ * 95% interval for the difference between a context arm and its baseline,
88
+ * by Newcombe's method — the two Wilson intervals combined, rather than a
89
+ * normal approximation on the difference.
90
+ *
91
+ * Why this exists (2026-08-08): every context arm on the site was published as
92
+ * a bare delta. React Router's skill-only arm is 42/45 against a 41/45
93
+ * baseline and was rendered as "+2", which is one generation out of
94
+ * forty-five. Prisma's best arm is +6, which is three. Neither survives a
95
+ * moment's arithmetic, and a site whose argument is that unmeasured numbers
96
+ * mislead cannot ship a delta without an interval on it.
97
+ *
98
+ * Newcombe rather than the textbook two-proportion interval for the same
99
+ * reason wilson() is used above: the interesting arms sit at the edges
100
+ * (0/10 -> 10/10), where the normal approximation runs outside [-1, 1].
101
+ *
102
+ * `significant` is deliberately the only boolean here. Everything downstream
103
+ * must branch on it rather than on the sign of `diff`, so a direction can
104
+ * never be reported from noise.
105
+ */
106
+ export function armDelta(armPassed, armN, basePassed, baseN) {
107
+ const a = wilson(armPassed, armN);
108
+ const b = wilson(basePassed, baseN);
109
+ const diff = a.p - b.p;
110
+ // Newcombe method 10: each side takes the far bound of one interval and the
111
+ // near bound of the other, so the result inherits Wilson's edge behaviour.
112
+ const low = diff - Math.sqrt((a.p - a.low) ** 2 + (b.high - b.p) ** 2);
113
+ const high = diff + Math.sqrt((a.high - a.p) ** 2 + (b.p - b.low) ** 2);
114
+ return {
115
+ diff,
116
+ low: Math.max(-1, low),
117
+ high: Math.min(1, high),
118
+ significant: low > 0 || high < 0,
119
+ };
120
+ }
121
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/core/stats.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,162 @@
1
+ export type Difficulty = "easy" | "medium" | "hard";
2
+ export interface Task {
3
+ id: string;
4
+ /** feature area, e.g. "crud" | "relations" | "transactions" | "pagination" */
5
+ area: string;
6
+ difficulty: Difficulty;
7
+ /** natural-language instruction given to the model */
8
+ prompt: string;
9
+ /** TS skeleton the solution must complete; forces real library usage */
10
+ skeleton: string;
11
+ }
12
+ /** model identifier, resolved to a concrete API model id at generation time */
13
+ export type ModelId = string;
14
+ export interface Candidate {
15
+ taskId: string;
16
+ model: ModelId;
17
+ /** full TypeScript source produced by the model */
18
+ code: string;
19
+ }
20
+ export interface TscError {
21
+ /** diagnostic code, e.g. "TS2339" */
22
+ code: string;
23
+ message: string;
24
+ line: number;
25
+ column: number;
26
+ /** true if the error references the target library's API surface */
27
+ libraryRelated: boolean;
28
+ }
29
+ export interface Verdict {
30
+ taskId: string;
31
+ model: ModelId;
32
+ /** true iff the candidate type-checks clean against the real installed package */
33
+ passed: boolean;
34
+ errors: TscError[];
35
+ }
36
+ export type FailureCategory = "hallucinated-member" | "wrong-arguments" | "bad-import" | "type-mismatch" | "deprecated-or-removed" | "other";
37
+ export interface FailurePattern {
38
+ category: FailureCategory;
39
+ count: number;
40
+ example: {
41
+ taskId: string;
42
+ model: ModelId;
43
+ message: string;
44
+ snippet?: string;
45
+ };
46
+ }
47
+ export interface ModelScore {
48
+ model: ModelId;
49
+ passed: number;
50
+ total: number;
51
+ /** 0-100 */
52
+ score: number;
53
+ }
54
+ /**
55
+ * A task the model would not attempt. Refusals are stochastic on ordinary SDK
56
+ * tasks (measured 2026-08-04 on Stripe), and they are NOT library drift — the
57
+ * model never wrote code, so nothing was measured. They are excluded from the
58
+ * score and reported separately so a thin board is visibly thin.
59
+ */
60
+ export interface Refusal {
61
+ taskId: string;
62
+ model: ModelId;
63
+ /** how many times the identical prompt was re-sampled before giving up */
64
+ attempts: number;
65
+ }
66
+ /** A context arm's outcome, alongside the bare score it is compared against. */
67
+ export interface ArmScore {
68
+ name: string;
69
+ label: string;
70
+ passed: number;
71
+ total: number;
72
+ /** 0-100, over the comparable task subset */
73
+ score: number;
74
+ /** the bare score over that SAME subset — never the headline bare score */
75
+ baselineScore: number;
76
+ /** score minus baselineScore, the number a maintainer actually wants */
77
+ delta: number;
78
+ /** how many tasks every arm and the bare run all produced code for */
79
+ comparedOn: number;
80
+ /** trials per task; total = comparedOn x trials */
81
+ trials: number;
82
+ /** task ids this arm still fails — the list a maintainer's docs have not closed */
83
+ failed: string[];
84
+ /** task ids this arm fixes that the bare run got wrong — what the docs bought */
85
+ fixed: string[];
86
+ }
87
+ export interface Recommendation {
88
+ id: string;
89
+ severity: "high" | "medium" | "info";
90
+ title: string;
91
+ detail: string;
92
+ /** never empty — an item without a measurement behind it is not emitted */
93
+ evidence: string[];
94
+ }
95
+ export interface LostTask {
96
+ taskId: string;
97
+ model: string;
98
+ /** first line of the underlying error, e.g. a 529 overloaded_error */
99
+ reason: string;
100
+ }
101
+ export interface Result {
102
+ library: string;
103
+ libraryVersion: string;
104
+ /** ISO timestamp */
105
+ generatedAt: string;
106
+ /** 0-100 across all models */
107
+ overallScore: number;
108
+ perModel: ModelScore[];
109
+ failurePatterns: FailurePattern[];
110
+ verdicts: Verdict[];
111
+ /** tasks the model refused outright; excluded from every score above */
112
+ refusals: Refusal[];
113
+ /**
114
+ * Tasks that never produced a candidate because generation errored out —
115
+ * API overload, timeout, transport. NOT refusals: the model never said no,
116
+ * the request never landed. Recorded because a lost task silently shrinks
117
+ * the denominator, and a partial run that drops the HARD tasks scores
118
+ * higher than the real one. Seen 2026-08-05 (an overloaded arm scored high
119
+ * because overload took its hardest tasks away) and again 2026-08-18, when
120
+ * four react-table runs each lost half their tasks and the scorecard still
121
+ * printed "no task was refused, so both rates run over the same set".
122
+ */
123
+ lost?: LostTask[];
124
+ /** present only on a --with-context run: the same tasks scored with the library's own agent files */
125
+ contextArms?: ArmScore[];
126
+ /** derived changes a maintainer could make; every one carries its evidence */
127
+ recommendations?: Recommendation[];
128
+ }
129
+ /**
130
+ * What verify() needs to know about the thing being scored. The bench passes a
131
+ * full LibrarySpec (which is structurally assignable); the CLI passes a
132
+ * throwaway npm workspace it just built for an arbitrary package.
133
+ */
134
+ export interface VerifyTarget {
135
+ /** the import name models are expected to use, e.g. "@prisma/client" */
136
+ packageName: string;
137
+ /** absolute path to the directory holding tsconfig.json and the candidate file */
138
+ fixtureDir: string;
139
+ /**
140
+ * The file the candidate is written to, default `candidate.ts`.
141
+ *
142
+ * A React-facing package needs `candidate.tsx`, because a model asked for a
143
+ * table or a form writes a component and JSX is a syntax error in a `.ts`
144
+ * file — 125 diagnostics on the first @tanstack/react-table run, none of them
145
+ * about the library. It is not the default: in a `.tsx` file `<T>(x) => x`
146
+ * parses as JSX, so a non-React package would break the other way.
147
+ */
148
+ candidateFile?: string;
149
+ }
150
+ /**
151
+ * What buildUserPrompt() needs about the thing being scored. Kept structural
152
+ * for the same reason as VerifyTarget: the bench passes a LibrarySpec, the CLI
153
+ * passes a description it assembled from the npm registry.
154
+ */
155
+ export interface PromptTarget {
156
+ /** human name, e.g. "Prisma" */
157
+ displayName: string;
158
+ /** the import name models are expected to use, e.g. "@prisma/client" */
159
+ packageName: string;
160
+ /** one-line steer for generation — what the library is and what it covers */
161
+ docsHint: string;
162
+ }
@@ -0,0 +1,11 @@
1
+ // Shared types for the SDKProof measurement core.
2
+ // The pipeline flows: Task[] -> Candidate[] -> Verdict[] -> Result
3
+ //
4
+ // This file is the single source of truth for both the published CLI
5
+ // (packages/cli) and the research bench at the repo root — the root's
6
+ // src/types.ts re-exports it. Keeping one copy is not tidiness: the CLI and the
7
+ // bench publish numbers that are compared against each other, and two drifting
8
+ // copies of `API_SHAPE_CODES` or the pass rule would make those numbers
9
+ // incomparable while still looking identical.
10
+ export {};
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,mEAAmE;AACnE,EAAE;AACF,qEAAqE;AACrE,sEAAsE;AACtE,gFAAgF;AAChF,+EAA+E;AAC/E,wEAAwE;AACxE,8CAA8C"}
@@ -0,0 +1,33 @@
1
+ import type { Candidate, TscError, Verdict, VerifyTarget } from "./types.ts";
2
+ export declare const API_SHAPE_CODES: Set<string>;
3
+ export interface VerifyOptions {
4
+ /** JS entrypoint of the tsc compiler (see env.tscEntry) */
5
+ tscEntry: string;
6
+ }
7
+ /**
8
+ * Type-check a model-generated candidate against the real installed package,
9
+ * inside the library's sandbox fixture. A candidate "passes" iff it compiles
10
+ * clean under the fixture's strict tsconfig.
11
+ */
12
+ export declare function verify(candidate: Candidate, spec: VerifyTarget, opts: VerifyOptions): Promise<Verdict>;
13
+ /**
14
+ * Detect a candidate that augments the module it is supposed to be USING.
15
+ * Matches the package itself and any subpath entrypoint of it, so
16
+ * `declare module "@apollo/client/react"` is caught as well as
17
+ * `declare module "@apollo/client"`.
18
+ *
19
+ * Deliberately conservative: it fires on ANY augmentation of the library's own
20
+ * module, not only ones that redeclare a removed symbol. Telling those apart
21
+ * needs type introspection, and the cost of being wrong is asymmetric — a
22
+ * false positive costs one task, a false negative silently inflates a
23
+ * published score. Measured before shipping: `declare module` appears in 0 of
24
+ * 35 stored candidate files, so this fires on nothing that has ever been run.
25
+ */
26
+ export declare function augmentsLibrary(code: string, packageName: string): string | null;
27
+ /**
28
+ * Reject candidates that cannot possibly be an answer. Returns a reason string
29
+ * when the candidate is unusable, or null when it is worth compiling.
30
+ */
31
+ export declare function emptyCandidate(code: string): string | null;
32
+ /** Parse tsc "--pretty false" output into structured errors for the candidate. */
33
+ export declare function parseDiagnostics(output: string, spec: VerifyTarget): TscError[];