@rotifer/playground 0.25.1 → 0.26.1

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 (66) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +8 -8
  3. package/README.zh.md +9 -9
  4. package/dist/commands/arena-submit.d.ts.map +1 -1
  5. package/dist/commands/arena-submit.js +2 -57
  6. package/dist/commands/arena-submit.js.map +1 -1
  7. package/dist/commands/compile.d.ts.map +1 -1
  8. package/dist/commands/compile.js +22 -0
  9. package/dist/commands/compile.js.map +1 -1
  10. package/dist/commands/install.d.ts.map +1 -1
  11. package/dist/commands/install.js +18 -0
  12. package/dist/commands/install.js.map +1 -1
  13. package/dist/commands/publish.d.ts.map +1 -1
  14. package/dist/commands/publish.js +49 -0
  15. package/dist/commands/publish.js.map +1 -1
  16. package/dist/commands/test.d.ts.map +1 -1
  17. package/dist/commands/test.js +164 -0
  18. package/dist/commands/test.js.map +1 -1
  19. package/dist/scanner/rules.d.ts.map +1 -1
  20. package/dist/scanner/rules.js +14 -4
  21. package/dist/scanner/rules.js.map +1 -1
  22. package/dist/testsuite/gate.d.ts +82 -0
  23. package/dist/testsuite/gate.d.ts.map +1 -0
  24. package/dist/testsuite/gate.js +179 -0
  25. package/dist/testsuite/gate.js.map +1 -0
  26. package/dist/testsuite/generate.d.ts +12 -0
  27. package/dist/testsuite/generate.d.ts.map +1 -0
  28. package/dist/testsuite/generate.js +70 -0
  29. package/dist/testsuite/generate.js.map +1 -0
  30. package/dist/testsuite/load.d.ts +23 -0
  31. package/dist/testsuite/load.d.ts.map +1 -0
  32. package/dist/testsuite/load.js +121 -0
  33. package/dist/testsuite/load.js.map +1 -0
  34. package/dist/testsuite/run.d.ts +109 -0
  35. package/dist/testsuite/run.d.ts.map +1 -0
  36. package/dist/testsuite/run.js +298 -0
  37. package/dist/testsuite/run.js.map +1 -0
  38. package/dist/testsuite/scaffold.d.ts +20 -0
  39. package/dist/testsuite/scaffold.d.ts.map +1 -0
  40. package/dist/testsuite/scaffold.js +146 -0
  41. package/dist/testsuite/scaffold.js.map +1 -0
  42. package/dist/testsuite/types.d.ts +67 -0
  43. package/dist/testsuite/types.d.ts.map +1 -0
  44. package/dist/testsuite/types.js +19 -0
  45. package/dist/testsuite/types.js.map +1 -0
  46. package/dist/utils/artifact-consistency.d.ts +37 -0
  47. package/dist/utils/artifact-consistency.d.ts.map +1 -0
  48. package/dist/utils/artifact-consistency.js +43 -0
  49. package/dist/utils/artifact-consistency.js.map +1 -0
  50. package/genes/genesis-code-format/.cloud-manifest.json +4 -4
  51. package/genes/genesis-code-format/index.ts +24 -6
  52. package/genes/genesis-code-format/phenotype.json +36 -7
  53. package/genes/genesis-code-format/testsuite.json +25 -0
  54. package/genes/genesis-file-read/.cloud-manifest.json +4 -4
  55. package/genes/genesis-file-read/index.ts +21 -2
  56. package/genes/genesis-file-read/phenotype.json +32 -8
  57. package/genes/genesis-file-read/testsuite.json +41 -0
  58. package/genes/genesis-web-search/.cloud-manifest.json +4 -4
  59. package/genes/genesis-web-search/index.ts +23 -4
  60. package/genes/genesis-web-search/phenotype.json +36 -9
  61. package/genes/genesis-web-search/testsuite.json +82 -0
  62. package/genes/genesis-web-search-lite/.cloud-manifest.json +4 -4
  63. package/genes/genesis-web-search-lite/index.ts +14 -2
  64. package/genes/genesis-web-search-lite/phenotype.json +21 -5
  65. package/genes/genesis-web-search-lite/testsuite.json +24 -0
  66. package/package.json +12 -7
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_FUZZ_ITERATIONS = void 0;
7
+ exports.classify = classify;
8
+ exports.runPositive = runPositive;
9
+ exports.runNegative = runNegative;
10
+ exports.runProperty = runProperty;
11
+ exports.runT1 = runT1;
12
+ exports.newTestRunId = newTestRunId;
13
+ const ajv_1 = __importDefault(require("ajv"));
14
+ const node_crypto_1 = require("node:crypto");
15
+ const generate_js_1 = require("./generate.js");
16
+ /** §47.5 fixes the publishing gate's property test at 10 random inputs. */
17
+ exports.DEFAULT_FUZZ_ITERATIONS = 10;
18
+ /**
19
+ * Classify a case as positive or negative from the input alone.
20
+ *
21
+ * spec §4.2's TestCase has no field saying which it is, and §47.5 words the
22
+ * negative case as "gene can correctly handle illegal input" — legality being
23
+ * decided by the Gene's own inputSchema. So the classification is derived:
24
+ * an input the schema accepts is a positive case, one it rejects is negative.
25
+ *
26
+ * The consequence worth stating: a positive case whose input is accidentally
27
+ * malformed silently becomes a negative case. That is why the reason string
28
+ * carries the validator's complaint — an author who meant it to be positive
29
+ * sees exactly which field disqualified it.
30
+ */
31
+ function classify(testCases, inputValidator) {
32
+ return testCases.map((testCase, index) => {
33
+ if (!inputValidator) {
34
+ // No inputSchema to judge against. Everything is a positive case, and
35
+ // the §47.5 gate will report the missing negative rather than inventing
36
+ // one — see requirementsMet().
37
+ return {
38
+ index,
39
+ kind: "positive",
40
+ testCase,
41
+ reason: "gene declares no inputSchema, so no input can be illegal",
42
+ };
43
+ }
44
+ const isLegalInput = inputValidator(testCase.input);
45
+ if (isLegalInput) {
46
+ return { index, kind: "positive", testCase, reason: "input conforms to inputSchema" };
47
+ }
48
+ const complaint = (inputValidator.errors ?? [])
49
+ .map((e) => `${e.instancePath || "(root)"} ${e.message}`)
50
+ .join("; ");
51
+ return {
52
+ index,
53
+ kind: "negative",
54
+ testCase,
55
+ reason: `input violates inputSchema — ${complaint || "no detail"}`,
56
+ };
57
+ });
58
+ }
59
+ /**
60
+ * Serialize for comparison with object keys in a stable order.
61
+ *
62
+ * `expectedOutput` was compared with plain JSON.stringify, which is key-order
63
+ * sensitive: a case declaring `{formatted, changed, language}` failed against a
64
+ * Gene returning `{changed, formatted, language}` — same object by every
65
+ * meaning JavaScript gives the word, unequal as text. That is a property of the
66
+ * serializer, not of the Gene, and every author writing an expectedOutput by
67
+ * hand would hit it. Found by hitting it.
68
+ *
69
+ * Array order is preserved: in an array it carries meaning.
70
+ */
71
+ function canonical(value) {
72
+ const walk = (v) => {
73
+ if (Array.isArray(v))
74
+ return v.map(walk);
75
+ if (v && typeof v === "object") {
76
+ const out = {};
77
+ for (const k of Object.keys(v).sort()) {
78
+ out[k] = walk(v[k]);
79
+ }
80
+ return out;
81
+ }
82
+ return v;
83
+ };
84
+ return JSON.stringify(walk(value));
85
+ }
86
+ function fail(testId, started, reason, details) {
87
+ return {
88
+ testId,
89
+ level: "T1",
90
+ passed: false,
91
+ duration: Date.now() - started,
92
+ failureReason: reason,
93
+ ...(details ? { details } : {}),
94
+ };
95
+ }
96
+ function pass(testId, started, details) {
97
+ return { testId, level: "T1", passed: true, duration: Date.now() - started, details };
98
+ }
99
+ /**
100
+ * A positive case passes when the Gene runs and its output satisfies whatever
101
+ * the case asserts: `expectedOutput` exactly, `expectedSchema` structurally,
102
+ * or — when the case asserts neither — the Gene's declared outputSchema.
103
+ *
104
+ * The fallback matters. Without it a case carrying only an input would pass on
105
+ * any output at all, including one that violates the Gene's own contract, and
106
+ * the suite would look populated while asserting nothing.
107
+ */
108
+ function runPositive(c, run, outputValidator, ajv) {
109
+ const started = Date.now();
110
+ const id = `t1-positive-${c.index}`;
111
+ const outcome = run(c.testCase.input, c.testCase.timeout);
112
+ if (!outcome.success) {
113
+ return fail(id, started, `Gene rejected a legal input: ${outcome.errorMessage ?? "no message"}`, c.reason);
114
+ }
115
+ if ("expectedOutput" in c.testCase) {
116
+ const got = canonical(outcome.output);
117
+ const want = canonical(c.testCase.expectedOutput);
118
+ if (got !== want) {
119
+ return fail(id, started, "output does not equal expectedOutput", `expected ${want}, got ${got}`);
120
+ }
121
+ return pass(id, started, "output matches expectedOutput exactly");
122
+ }
123
+ if (c.testCase.expectedSchema) {
124
+ let validate;
125
+ try {
126
+ validate = ajv.compile(c.testCase.expectedSchema);
127
+ }
128
+ catch (e) {
129
+ return fail(id, started, `expectedSchema is not a valid JSON Schema: ${e.message}`);
130
+ }
131
+ if (!validate(outcome.output)) {
132
+ return fail(id, started, "output does not conform to expectedSchema", JSON.stringify(validate.errors));
133
+ }
134
+ return pass(id, started, "output conforms to expectedSchema");
135
+ }
136
+ if (outputValidator) {
137
+ if (!outputValidator(outcome.output)) {
138
+ return fail(id, started, "output does not conform to the Gene's outputSchema", JSON.stringify(outputValidator.errors));
139
+ }
140
+ return pass(id, started, "output conforms to the Gene's outputSchema");
141
+ }
142
+ return fail(id, started, "case asserts nothing: no expectedOutput, no expectedSchema, and the Gene declares no outputSchema");
143
+ }
144
+ /**
145
+ * A negative case: the Gene is fed input its own inputSchema forbids.
146
+ *
147
+ * §47.5 asks that the Gene "correctly handle illegal input" without saying
148
+ * what handling looks like, and measuring four real Genes shows why it cannot
149
+ * be decided from the outcome alone:
150
+ *
151
+ * grammar-checker throws — `not a function`, a TypeError from inside
152
+ * source-linker returns — echoes the illegal value straight back
153
+ * url-extractor returns — an empty result
154
+ * json-validator returns — `{ valid: false }`, which IS correct handling
155
+ *
156
+ * "Returned output" therefore cannot mean acceptance: for json-validator it is
157
+ * the right answer, and for source-linker it is the bug. The difference is not
158
+ * in the outcome, it is in what the author intended — so the author has to say,
159
+ * using the fields spec §4.2 already provides.
160
+ *
161
+ * The rules, in order:
162
+ *
163
+ * 1. Crashed — fail. Dying is not handling, whatever was intended.
164
+ * 2. Errored without crashing — pass. The Gene refused, deliberately.
165
+ * 3. Returned, and the case declares expectedOutput/expectedSchema that
166
+ * matches — pass. This is the author saying "rejection looks like this".
167
+ * 4. Returned, and the declaration does not match — fail.
168
+ * 5. Returned, and the case declares nothing — fail, and say why. Silent
169
+ * acceptance and correct handling are indistinguishable here; refusing to
170
+ * guess is the whole point, since guessing "pass" is what lets a
171
+ * source-linker through.
172
+ */
173
+ function runNegative(c, run, ajv) {
174
+ const started = Date.now();
175
+ const id = `t1-negative-${c.index}`;
176
+ const outcome = run(c.testCase.input, c.testCase.timeout);
177
+ if (outcome.crashed) {
178
+ // ADR-333: a crash fails not because it is necessarily a bug, but because
179
+ // it is necessarily unusable. A trap reaching the caller carries no
180
+ // information — the caller cannot tell deliberate refusal from defect, so
181
+ // nothing downstream can act on it correctly. §47.5 asks for "correctly
182
+ // handle", and a signal the caller cannot handle is not handling completed.
183
+ return fail(id, started, `Gene crashed on illegal input instead of handling it: ${outcome.errorMessage ?? "no message"}`, c.reason);
184
+ }
185
+ if (!outcome.success) {
186
+ return pass(id, started, `Gene refused illegal input: ${outcome.errorMessage ?? "no message"}`);
187
+ }
188
+ if ("expectedOutput" in c.testCase) {
189
+ const got = canonical(outcome.output);
190
+ const want = canonical(c.testCase.expectedOutput);
191
+ return got === want
192
+ ? pass(id, started, "Gene handled illegal input exactly as the case declares")
193
+ : fail(id, started, "output does not equal the declared expectedOutput", `expected ${want}, got ${got}`);
194
+ }
195
+ if (c.testCase.expectedSchema) {
196
+ let validate;
197
+ try {
198
+ validate = ajv.compile(c.testCase.expectedSchema);
199
+ }
200
+ catch (e) {
201
+ return fail(id, started, `expectedSchema is not a valid JSON Schema: ${e.message}`);
202
+ }
203
+ return validate(outcome.output)
204
+ ? pass(id, started, "Gene handled illegal input in the declared shape")
205
+ : fail(id, started, "output does not conform to the declared expectedSchema", JSON.stringify(validate.errors));
206
+ }
207
+ return fail(id, started, "Gene returned output for illegal input and the case declares no expected shape", `${c.reason}; output was ${JSON.stringify(outcome.output).slice(0, 200)}. ` +
208
+ `If returning this is correct handling, declare it with expectedOutput or expectedSchema; ` +
209
+ `otherwise the Gene is silently accepting input its own schema forbids.`);
210
+ }
211
+ /**
212
+ * §47.4 Schema Legality, run as §47.5's third T1 requirement: generated inputs
213
+ * must always produce output conforming to outputSchema.
214
+ *
215
+ * The seed is what makes a failure actionable — the same seed replays the same
216
+ * inputs, so a property failure can be reproduced rather than merely observed.
217
+ * When the suite gives no seed one is derived from nothing but a constant, so
218
+ * two runs of an unseeded suite still agree; randomness across runs would make
219
+ * the gate flaky and the flake would be blamed on the Gene.
220
+ */
221
+ function runProperty(suite, inputSchema, run, outputValidator) {
222
+ const started = Date.now();
223
+ const id = "t1-property-schema-legality";
224
+ const iterations = suite.config?.fuzzIterations ?? exports.DEFAULT_FUZZ_ITERATIONS;
225
+ const seed = suite.config?.seed ?? 1;
226
+ if (iterations === 0) {
227
+ return fail(id, started, "config.fuzzIterations is 0 — the property test asserts nothing");
228
+ }
229
+ if (!outputValidator) {
230
+ return fail(id, started, "Gene declares no outputSchema, so schema legality cannot be asserted");
231
+ }
232
+ for (let i = 0; i < iterations; i++) {
233
+ const input = (0, generate_js_1.generateTestInput)(inputSchema, seed + i);
234
+ const outcome = run(input);
235
+ if (!outcome.success) {
236
+ return fail(id, started, `iteration ${i + 1}/${iterations} did not produce output: ${outcome.errorMessage ?? "no message"}`, `reproduce with seed ${seed}, input ${JSON.stringify(input).slice(0, 200)}`);
237
+ }
238
+ if (!outputValidator(outcome.output)) {
239
+ return fail(id, started, `iteration ${i + 1}/${iterations} produced output violating outputSchema`, `reproduce with seed ${seed}, input ${JSON.stringify(input).slice(0, 200)}`);
240
+ }
241
+ }
242
+ return pass(id, started, `${iterations} generated inputs all produced schema-legal output (seed ${seed})`);
243
+ }
244
+ function runT1(suite, opts) {
245
+ const ajv = new ajv_1.default({ allErrors: true, strict: false });
246
+ let inputValidator = null;
247
+ let outputValidator = null;
248
+ try {
249
+ if (opts.inputSchema)
250
+ inputValidator = ajv.compile(opts.inputSchema);
251
+ }
252
+ catch {
253
+ inputValidator = null;
254
+ }
255
+ try {
256
+ if (opts.outputSchema)
257
+ outputValidator = ajv.compile(opts.outputSchema);
258
+ }
259
+ catch {
260
+ outputValidator = null;
261
+ }
262
+ const classified = classify(suite.testCases, inputValidator);
263
+ const results = [];
264
+ for (const c of classified) {
265
+ results.push(c.kind === "positive"
266
+ ? runPositive(c, opts.run, outputValidator, ajv)
267
+ : runNegative(c, opts.run, ajv));
268
+ }
269
+ const property = runProperty(suite, opts.inputSchema, opts.run, outputValidator);
270
+ results.push(property);
271
+ const hasPassingPositive = classified.some((c, i) => c.kind === "positive" && results[i].passed);
272
+ const hasPassingNegative = classified.some((c, i) => c.kind === "negative" && results[i].passed);
273
+ const requirements = {
274
+ positive: hasPassingPositive,
275
+ negative: hasPassingNegative,
276
+ property: property.passed,
277
+ };
278
+ // §47.5 sets a floor on coverage — "at least 1 positive", "at least 1
279
+ // negative" — not permission to ship failing assertions. A suite with three
280
+ // negatives where two fail still satisfies the letter of the requirement, and
281
+ // that is how a case an author wrote, and a Gene does not honour, could reach
282
+ // the registry. Every declared case has to pass as well.
283
+ const hasFailingCase = results.some((r) => !r.passed);
284
+ return {
285
+ results,
286
+ classified,
287
+ requirements,
288
+ gatePassed: requirements.positive &&
289
+ requirements.negative &&
290
+ requirements.property &&
291
+ !hasFailingCase,
292
+ };
293
+ }
294
+ /** Stable id for a report, for callers that persist results. */
295
+ function newTestRunId() {
296
+ return (0, node_crypto_1.randomUUID)();
297
+ }
298
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/testsuite/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { TestSuite } from "./types.js";
2
+ interface ScaffoldNote {
3
+ /** Which §47.5 requirement this case is there to satisfy. */
4
+ requirement: "positive" | "negative";
5
+ /** Why this input is legal or illegal, for the comment block. */
6
+ rationale: string;
7
+ }
8
+ export interface ScaffoldResult {
9
+ suite: TestSuite;
10
+ notes: ScaffoldNote[];
11
+ /** Cases the generator meant to be illegal but could not make illegal —
12
+ * see makeIllegalInputs. Empty is the normal case. */
13
+ warnings: string[];
14
+ }
15
+ export declare function scaffoldTestSuite(phenotype: {
16
+ inputSchema?: unknown;
17
+ outputSchema?: unknown;
18
+ }): ScaffoldResult;
19
+ export {};
20
+ //# sourceMappingURL=scaffold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/testsuite/scaffold.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAY,SAAS,EAAE,MAAM,YAAY,CAAC;AAmBtD,UAAU,YAAY;IACpB,6DAA6D;IAC7D,WAAW,EAAE,UAAU,GAAG,UAAU,CAAC;IACrC,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB;0DACsD;IACtD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAiED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GAAG,cAAc,CA4EjB"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.scaffoldTestSuite = scaffoldTestSuite;
7
+ const ajv_1 = __importDefault(require("ajv"));
8
+ const generate_js_1 = require("./generate.js");
9
+ const run_js_1 = require("./run.js");
10
+ /**
11
+ * §47.3 Phenotype-driven scaffolding, aimed at §47.5's shape.
12
+ *
13
+ * This exists because 2.1 turns §47.5 into a publishing gate, and a gate with
14
+ * no exit teaches people to route around it. Being told "you need a negative
15
+ * case" is only actionable if something can write the first one for you.
16
+ *
17
+ * What it cannot do is make a Gene correct. A scaffolded suite is a starting
18
+ * point whose assertions the author still has to mean — the generated negative
19
+ * cases in particular encode a guess about what rejection should look like, and
20
+ * that guess is left blank on purpose (see below).
21
+ */
22
+ const SCAFFOLD_SEED = 1;
23
+ /**
24
+ * Build inputs that the Gene's own inputSchema rejects.
25
+ *
26
+ * Each strategy targets a different way a caller gets it wrong, because a
27
+ * negative case that only ever tests one of them proves little:
28
+ *
29
+ * - a required field missing entirely
30
+ * - a field present with the wrong type
31
+ * - an empty object, the degenerate call
32
+ *
33
+ * Every candidate is then run through the validator before being kept. A
34
+ * "negative" case whose input the schema happens to accept is silently a
35
+ * positive case — classify() would file it as one, the suite would look like it
36
+ * covers §47.5 while covering half of it, and nothing would say so.
37
+ */
38
+ function makeIllegalInputs(inputSchema, isIllegal) {
39
+ const inputs = [];
40
+ const warnings = [];
41
+ const candidates = [];
42
+ const properties = (inputSchema?.properties ?? {});
43
+ const required = (inputSchema?.required ?? []);
44
+ const legal = (0, generate_js_1.generateTestInput)(inputSchema, SCAFFOLD_SEED);
45
+ if (required.length > 0) {
46
+ const dropped = { ...legal };
47
+ delete dropped[required[0]];
48
+ candidates.push({
49
+ value: dropped,
50
+ rationale: `required field "${required[0]}" is missing`,
51
+ });
52
+ }
53
+ const firstTyped = Object.entries(properties).find(([, p]) => typeof p.type === "string");
54
+ if (firstTyped) {
55
+ const [key, prop] = firstTyped;
56
+ // A value of a type the field cannot hold. Numbers are wrong for
57
+ // everything except number/integer, where a string is wrong instead.
58
+ const wrong = prop.type === "number" || prop.type === "integer" ? "not-a-number" : 42;
59
+ candidates.push({
60
+ value: { ...legal, [key]: wrong },
61
+ rationale: `"${key}" is declared ${String(prop.type)} but given ${JSON.stringify(wrong)}`,
62
+ });
63
+ }
64
+ candidates.push({ value: {}, rationale: "empty object — the degenerate call" });
65
+ for (const c of candidates) {
66
+ if (isIllegal(c.value)) {
67
+ inputs.push(c);
68
+ }
69
+ else {
70
+ warnings.push(`dropped a would-be negative case (${c.rationale}): the Gene's inputSchema accepts it, ` +
71
+ `so it would have been classified as a positive case`);
72
+ }
73
+ }
74
+ return { inputs, warnings };
75
+ }
76
+ function scaffoldTestSuite(phenotype) {
77
+ const inputSchema = phenotype.inputSchema ?? null;
78
+ const outputSchema = phenotype.outputSchema ?? null;
79
+ const ajv = new ajv_1.default({ allErrors: true, strict: false });
80
+ let isIllegal = () => false;
81
+ if (inputSchema) {
82
+ try {
83
+ const validate = ajv.compile(inputSchema);
84
+ isIllegal = (v) => !validate(v);
85
+ }
86
+ catch {
87
+ isIllegal = () => false;
88
+ }
89
+ }
90
+ const testCases = [];
91
+ const notes = [];
92
+ const warnings = [];
93
+ // Positive case. It asserts against outputSchema rather than a literal
94
+ // expectedOutput: a generated literal would be whatever the Gene happens to
95
+ // return today, which is an assertion that the Gene never changes, not that
96
+ // it is right.
97
+ const legal = (0, generate_js_1.generateTestInput)(inputSchema, SCAFFOLD_SEED);
98
+ testCases.push({
99
+ input: legal,
100
+ ...(outputSchema ? { expectedSchema: outputSchema } : {}),
101
+ });
102
+ // The generator is best-effort: for nested or object-typed required fields it
103
+ // can emit something the schema rejects. Left unchecked that case is filed as
104
+ // a *negative* by classify(), and the suite ships with no positive case at
105
+ // all — measured on json-validator, whose `data` and `schema` fields the
106
+ // generator could not satisfy.
107
+ //
108
+ // Negative candidates were already verified illegal; not verifying the
109
+ // positive one was an asymmetry, and it failed in the direction that is
110
+ // harder to notice: the suite looks complete and the gate reports "positive
111
+ // NOT met" without saying the scaffold is why.
112
+ const isPositiveLegal = !isIllegal(legal);
113
+ if (!isPositiveLegal) {
114
+ warnings.push(`the generated positive input does not satisfy this Gene's inputSchema, so it will be ` +
115
+ `classified as a negative case and the suite will have no positive case — ` +
116
+ `replace testCases[0].input with one your Gene actually accepts`);
117
+ }
118
+ notes.push({
119
+ requirement: "positive",
120
+ rationale: !isPositiveLegal
121
+ ? "GENERATED INPUT IS NOT ACCEPTED BY inputSchema — replace it, see the warning below"
122
+ : outputSchema
123
+ ? "generated from inputSchema; asserts the output conforms to outputSchema"
124
+ : "generated from inputSchema; the Gene declares no outputSchema, so replace this with an expectedOutput you mean",
125
+ });
126
+ const { inputs: illegal, warnings: negativeWarnings } = makeIllegalInputs(inputSchema, isIllegal);
127
+ warnings.push(...negativeWarnings);
128
+ for (const c of illegal) {
129
+ // No expectedOutput / expectedSchema on purpose. If the Gene answers
130
+ // illegal input rather than refusing it — json-validator returning
131
+ // { valid: false } is the honest version of this — the author has to say
132
+ // what that answer should be. Guessing here would hand out a case that
133
+ // passes on whatever the Gene already does, which is not a test.
134
+ testCases.push({ input: c.value });
135
+ notes.push({ requirement: "negative", rationale: c.rationale });
136
+ }
137
+ return {
138
+ suite: {
139
+ testCases,
140
+ config: { fuzzIterations: run_js_1.DEFAULT_FUZZ_ITERATIONS, seed: SCAFFOLD_SEED },
141
+ },
142
+ notes,
143
+ warnings,
144
+ };
145
+ }
146
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/testsuite/scaffold.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * TestSuite types, spec §4.2.
3
+ *
4
+ * The field set here is the spec's struct verbatim — no additions. That is
5
+ * deliberate: §47.5 requires a publishing gate to count "at least 1 positive
6
+ * case" and "at least 1 negative case", and the obvious way to support it
7
+ * would be a `kind: "positive" | "negative"` discriminator. The spec has no
8
+ * such field, and inventing one would put a concept in the file format that
9
+ * the protocol does not define.
10
+ *
11
+ * It is not needed. §47.5 words the negative case as "gene can correctly
12
+ * handle illegal input", and legality is already decided by the gene's own
13
+ * `inputSchema`. So the classification is *derived* rather than declared —
14
+ * see classify() in ./run.ts. A gene author writes test cases; whether each
15
+ * one is positive or negative follows from the input.
16
+ */
17
+ /** spec §4.2 TestConfig */
18
+ export interface TestConfig {
19
+ /** Fuzz testing iteration count. §47.5 fixes the publishing gate at 10. */
20
+ fuzzIterations?: number;
21
+ /** Random seed, so a property run can be reproduced exactly. */
22
+ seed?: number;
23
+ }
24
+ /** spec §4.2 TestCase */
25
+ export interface TestCase {
26
+ /** Test input. MUST conform to inputSchema for a positive case. */
27
+ input: unknown;
28
+ /** Expected output, for exact matching. */
29
+ expectedOutput?: unknown;
30
+ /** Expected output Schema, for structural validation. */
31
+ expectedSchema?: Record<string, unknown>;
32
+ /** Per-case timeout, milliseconds. */
33
+ timeout?: number;
34
+ }
35
+ /** spec §4.2 TestSuite */
36
+ export interface TestSuite {
37
+ testCases: TestCase[];
38
+ config?: TestConfig;
39
+ }
40
+ /** spec §47.1 test level */
41
+ export type TestLevel = "T1" | "T2" | "T3" | "T4";
42
+ /** spec §4.2 / §47.2 TestResult */
43
+ export interface TestResult {
44
+ testId: string;
45
+ level: TestLevel;
46
+ passed: boolean;
47
+ /** Milliseconds. */
48
+ duration: number;
49
+ details?: string;
50
+ failureReason?: string;
51
+ coverageHint?: number;
52
+ }
53
+ /**
54
+ * How a case was classified, and why. Not part of the file format — this is
55
+ * produced by the runner, and exists so `rotifer test` can report which of
56
+ * §47.5's three T1 requirements a given case satisfied.
57
+ */
58
+ export type CaseKind = "positive" | "negative";
59
+ export interface ClassifiedCase {
60
+ index: number;
61
+ kind: CaseKind;
62
+ testCase: TestCase;
63
+ /** Why it landed in that bucket — shown on failure so the author can tell
64
+ * an intentionally-illegal input from an accidentally-illegal one. */
65
+ reason: string;
66
+ }
67
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/testsuite/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,2BAA2B;AAC3B,MAAM,WAAW,UAAU;IACzB,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,yBAAyB;AACzB,MAAM,WAAW,QAAQ;IACvB,mEAAmE;IACnE,KAAK,EAAE,OAAO,CAAC;IACf,2CAA2C;IAC3C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,yDAAyD;IACzD,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,0BAA0B;AAC1B,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,4BAA4B;AAC5B,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAElD,mCAAmC;AACnC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,oBAAoB;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC;AAE/C,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB;0EACsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ /**
3
+ * TestSuite types, spec §4.2.
4
+ *
5
+ * The field set here is the spec's struct verbatim — no additions. That is
6
+ * deliberate: §47.5 requires a publishing gate to count "at least 1 positive
7
+ * case" and "at least 1 negative case", and the obvious way to support it
8
+ * would be a `kind: "positive" | "negative"` discriminator. The spec has no
9
+ * such field, and inventing one would put a concept in the file format that
10
+ * the protocol does not define.
11
+ *
12
+ * It is not needed. §47.5 words the negative case as "gene can correctly
13
+ * handle illegal input", and legality is already decided by the gene's own
14
+ * `inputSchema`. So the classification is *derived* rather than declared —
15
+ * see classify() in ./run.ts. A gene author writes test cases; whether each
16
+ * one is positive or negative follows from the input.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/testsuite/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Does a Gene's declared fidelity match what is actually on disk / in the
3
+ * registry record?
4
+ *
5
+ * spec §4.2 defines NATIVE as "core logic compiled to Rotifer IR". A record
6
+ * declaring Native with no artifact is therefore self-contradicting, and until
7
+ * 2026-09-07 both commands that meet one said nothing about it:
8
+ *
9
+ * install downloaded nothing (no else branch on `if (gene.wasm_url)`) and
10
+ * then reported "Gene installed! Fidelity: Native" — for a Gene that
11
+ * answers `rotifer run` with "No runnable source found".
12
+ * compile wrote `.compile-result.json` with `fidelity: "Wrapped"` while
13
+ * leaving phenotype.json saying Native, and reported *success*:
14
+ * "validated (Wrapped fidelity)". Two files in one directory
15
+ * disagreeing, and a green tick over a Gene that cannot run.
16
+ *
17
+ * The judgement lives here, apart from the commands, so it can be tested
18
+ * without a network or a compiler.
19
+ */
20
+ export type ArtifactVerdict = {
21
+ status: "consistent";
22
+ }
23
+ /** Declared Native, nothing to run. The Gene cannot do what it claims. */
24
+ | {
25
+ status: "native-without-artifact";
26
+ message: string;
27
+ hint: string[];
28
+ };
29
+ export declare function checkNativeArtifact(input: {
30
+ geneName: string;
31
+ fidelity: string | undefined;
32
+ /** A compiled artifact is present (installed .wasm, or a registry wasm_url). */
33
+ hasArtifact: boolean;
34
+ /** Source is present, so the Gene can still be compiled locally. */
35
+ hasSource: boolean;
36
+ }): ArtifactVerdict;
37
+ //# sourceMappingURL=artifact-consistency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-consistency.d.ts","sourceRoot":"","sources":["../../src/utils/artifact-consistency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE;AAC1B,0EAA0E;GACxE;IAAE,MAAM,EAAE,yBAAyB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE3E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,gFAAgF;IAChF,WAAW,EAAE,OAAO,CAAC;IACrB,oEAAoE;IACpE,SAAS,EAAE,OAAO,CAAC;CACpB,GAAG,eAAe,CAmBlB"}