@plune-ai/cli 0.17.0 → 0.18.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.
package/dist/index.cjs CHANGED
@@ -35,19 +35,22 @@ __export(src_exports, {
35
35
  SyncFileError: () => SyncFileError,
36
36
  SyncHttpError: () => SyncHttpError,
37
37
  SyncNetworkError: () => SyncNetworkError,
38
+ TEST_CASE_SCHEMA_VERSION: () => TEST_CASE_SCHEMA_VERSION,
38
39
  TokenRejectedError: () => TokenRejectedError,
39
40
  assertionConfigSchema: () => assertionConfigSchema,
40
- assertionResultRecordSchema: () => assertionResultRecordSchema,
41
+ assertionResultRecordSchema: () => assertionResultRecordSchema2,
41
42
  binaryVerdictSchema: () => binaryVerdictSchema,
42
43
  formatIngestResult: () => formatIngestResult,
43
44
  ingest: () => handleIngest,
44
45
  parseRunResult: () => parseRunResult,
46
+ parseTestCase: () => parseTestCase,
45
47
  readCairnRun: () => readCairnRun,
46
48
  reportIngestFailure: () => reportIngestFailure,
47
49
  reportSyncFailure: () => reportSyncFailure,
48
50
  run: () => handleRun,
49
51
  runResultSchema: () => runResultSchema,
50
- sync: () => handleSync
52
+ sync: () => handleSync,
53
+ testCaseSchema: () => testCaseSchema
51
54
  });
52
55
  module.exports = __toCommonJS(src_exports);
53
56
 
@@ -1269,6 +1272,64 @@ async function mapLimit(items, limit, fn) {
1269
1272
  return results;
1270
1273
  }
1271
1274
 
1275
+ // src/types/test-case-schema.ts
1276
+ var import_zod2 = require("zod");
1277
+ var TEST_CASE_SCHEMA_VERSION = 2;
1278
+ var externalKeySchema = import_zod2.z.object({
1279
+ kind: import_zod2.z.enum(["playwright-id", "path-title", "allure-history", "cairn-stable", "eval-id", "qase", "testrail"]),
1280
+ value: import_zod2.z.string().min(1)
1281
+ });
1282
+ var manualStepSchema = import_zod2.z.object({
1283
+ action: import_zod2.z.string().min(1),
1284
+ expected: import_zod2.z.string().min(1).optional()
1285
+ });
1286
+ var assertionResultRecordSchema = import_zod2.z.object({
1287
+ type: import_zod2.z.string().min(1),
1288
+ passed: import_zod2.z.boolean(),
1289
+ score: import_zod2.z.number().optional(),
1290
+ reason: import_zod2.z.string().optional()
1291
+ });
1292
+ var manualCaseSchema = import_zod2.z.object({
1293
+ type: import_zod2.z.literal("manual"),
1294
+ steps: import_zod2.z.array(manualStepSchema).min(1),
1295
+ expected: import_zod2.z.string().min(1).optional()
1296
+ });
1297
+ var automatedCaseSchema = import_zod2.z.object({
1298
+ type: import_zod2.z.literal("automated"),
1299
+ specRef: import_zod2.z.string().min(1)
1300
+ });
1301
+ var evalCaseSchema = import_zod2.z.object({
1302
+ type: import_zod2.z.literal("eval"),
1303
+ prompt: import_zod2.z.string().min(1),
1304
+ goldenSetId: import_zod2.z.string().min(1).optional(),
1305
+ assertions: import_zod2.z.array(assertionResultRecordSchema).min(1)
1306
+ });
1307
+ var testCaseBaseSchema = import_zod2.z.object({
1308
+ schemaVersion: import_zod2.z.literal(TEST_CASE_SCHEMA_VERSION),
1309
+ id: import_zod2.z.string().min(1),
1310
+ externalKeys: import_zod2.z.array(externalKeySchema).optional(),
1311
+ /** ADR 0045 / plune#1246 — optional, purely additive; schemaVersion stays 2. */
1312
+ is_flaky: import_zod2.z.boolean().optional(),
1313
+ /** ADR 0045 / plune#1246 — optional, purely additive; schemaVersion stays 2. */
1314
+ is_muted: import_zod2.z.boolean().optional()
1315
+ });
1316
+ var testCaseSchema = import_zod2.z.discriminatedUnion("type", [
1317
+ testCaseBaseSchema.merge(manualCaseSchema),
1318
+ testCaseBaseSchema.merge(automatedCaseSchema),
1319
+ testCaseBaseSchema.merge(evalCaseSchema)
1320
+ ]);
1321
+ function parseTestCase(value) {
1322
+ return testCaseSchema.parse(value);
1323
+ }
1324
+
1325
+ // src/types/results.ts
1326
+ var ROW_INCONCLUSIVE = ["blocked", "skipped"];
1327
+ function rowStatus(row) {
1328
+ if (row.inconclusive !== void 0) return row.inconclusive;
1329
+ if (row.error !== void 0) return "errored";
1330
+ return row.assertions.every((a) => a.passed) ? "passed" : "failed";
1331
+ }
1332
+
1272
1333
  // src/orchestrator/run.ts
1273
1334
  var DEFAULT_MAX_TOKENS2 = 1024;
1274
1335
  var PLUNE_VERSION = "0.1.0";
@@ -1411,10 +1472,25 @@ function resolveTemplate(ev, baseDir) {
1411
1472
  }
1412
1473
  throw new RunConfigError(`Eval "${ev.id}" has neither prompt nor prompt_file`);
1413
1474
  }
1414
- function classify(row) {
1415
- if (row.error !== void 0) return "errored";
1416
- if (row.assertions.some((a) => !a.passed)) return "failed";
1417
- return "passed";
1475
+ function summarize(evals, durationMs) {
1476
+ const summary = {
1477
+ total: 0,
1478
+ passed: 0,
1479
+ failed: 0,
1480
+ errored: 0,
1481
+ blocked: 0,
1482
+ skipped: 0,
1483
+ cost_usd: 0,
1484
+ duration_ms: durationMs
1485
+ };
1486
+ for (const ev of evals) {
1487
+ for (const row of ev.rows) {
1488
+ summary.total += 1;
1489
+ summary[rowStatus(row)] += 1;
1490
+ summary.cost_usd += row.usage?.cost_usd ?? 0;
1491
+ }
1492
+ }
1493
+ return summary;
1418
1494
  }
1419
1495
  async function runOrchestration(config, options, deps) {
1420
1496
  const started = deps.now();
@@ -1449,26 +1525,12 @@ async function runOrchestration(config, options, deps) {
1449
1525
  noCache
1450
1526
  })
1451
1527
  );
1452
- const passed = rowResults.every((r) => classify(r) === "passed");
1528
+ const passed = rowResults.every((r) => rowStatus(r) === "passed");
1453
1529
  evalResults.push({ id: ev.id, tags: ev.tags ?? [], rows: rowResults, passed });
1454
1530
  if (options.bail === true && !passed) break;
1455
1531
  }
1456
1532
  const finished = deps.now();
1457
- const summary = {
1458
- total: 0,
1459
- passed: 0,
1460
- failed: 0,
1461
- errored: 0,
1462
- cost_usd: 0,
1463
- duration_ms: finished - started
1464
- };
1465
- for (const ev of evalResults) {
1466
- for (const row of ev.rows) {
1467
- summary.total += 1;
1468
- summary[classify(row)] += 1;
1469
- summary.cost_usd += row.usage?.cost_usd ?? 0;
1470
- }
1471
- }
1533
+ const summary = summarize(evalResults, finished - started);
1472
1534
  return {
1473
1535
  schemaVersion: 1,
1474
1536
  plune_version: PLUNE_VERSION,
@@ -1552,59 +1614,64 @@ var fs6 = __toESM(require("fs"), 1);
1552
1614
  var path5 = __toESM(require("path"), 1);
1553
1615
 
1554
1616
  // src/types/run-result-schema.ts
1555
- var import_zod2 = require("zod");
1617
+ var import_zod3 = require("zod");
1556
1618
  var RUN_RESULT_SCHEMA_VERSION = 1;
1557
- var binaryVerdictSchema = import_zod2.z.object({
1558
- question: import_zod2.z.string().min(1),
1559
- passed: import_zod2.z.boolean()
1619
+ var binaryVerdictSchema = import_zod3.z.object({
1620
+ question: import_zod3.z.string().min(1),
1621
+ passed: import_zod3.z.boolean()
1560
1622
  });
1561
- var assertionResultRecordSchema = import_zod2.z.object({
1562
- type: import_zod2.z.string(),
1563
- passed: import_zod2.z.boolean(),
1564
- score: import_zod2.z.number().optional(),
1565
- reason: import_zod2.z.string().optional(),
1566
- binaryVerdicts: import_zod2.z.array(binaryVerdictSchema).optional()
1623
+ var assertionResultRecordSchema2 = import_zod3.z.object({
1624
+ type: import_zod3.z.string(),
1625
+ passed: import_zod3.z.boolean(),
1626
+ score: import_zod3.z.number().optional(),
1627
+ reason: import_zod3.z.string().optional(),
1628
+ binaryVerdicts: import_zod3.z.array(binaryVerdictSchema).optional()
1567
1629
  });
1568
- var usageSchema = import_zod2.z.object({
1569
- input_tokens: import_zod2.z.number(),
1570
- output_tokens: import_zod2.z.number(),
1571
- cost_usd: import_zod2.z.number()
1630
+ var usageSchema = import_zod3.z.object({
1631
+ input_tokens: import_zod3.z.number(),
1632
+ output_tokens: import_zod3.z.number(),
1633
+ cost_usd: import_zod3.z.number()
1572
1634
  });
1573
- var runErrorSchema = import_zod2.z.object({
1574
- code: import_zod2.z.string(),
1575
- message: import_zod2.z.string()
1635
+ var runErrorSchema = import_zod3.z.object({
1636
+ code: import_zod3.z.string(),
1637
+ message: import_zod3.z.string()
1576
1638
  });
1577
- var rowResultSchema = import_zod2.z.object({
1578
- vars: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()),
1579
- output: import_zod2.z.string().nullable(),
1580
- cached: import_zod2.z.boolean(),
1639
+ var rowResultSchema = import_zod3.z.object({
1640
+ vars: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()),
1641
+ output: import_zod3.z.string().nullable(),
1642
+ cached: import_zod3.z.boolean(),
1581
1643
  usage: usageSchema.optional(),
1582
- latency_ms: import_zod2.z.number().optional(),
1644
+ latency_ms: import_zod3.z.number().optional(),
1583
1645
  error: runErrorSchema.optional(),
1584
- assertions: import_zod2.z.array(assertionResultRecordSchema)
1646
+ assertions: import_zod3.z.array(assertionResultRecordSchema2),
1647
+ /** ADR 0045. Optional and additive — the same enum the platform validates on `POST /v1/runs`. */
1648
+ inconclusive: import_zod3.z.enum(ROW_INCONCLUSIVE).optional()
1585
1649
  });
1586
- var summarySchema = import_zod2.z.object({
1587
- total: import_zod2.z.number(),
1588
- passed: import_zod2.z.number(),
1589
- failed: import_zod2.z.number(),
1590
- errored: import_zod2.z.number(),
1591
- cost_usd: import_zod2.z.number(),
1592
- duration_ms: import_zod2.z.number()
1650
+ var summarySchema = import_zod3.z.object({
1651
+ total: import_zod3.z.number(),
1652
+ passed: import_zod3.z.number(),
1653
+ failed: import_zod3.z.number(),
1654
+ errored: import_zod3.z.number(),
1655
+ /** plune#1245. Optional and additive — a RunResult written before them validates unchanged. */
1656
+ blocked: import_zod3.z.number().optional(),
1657
+ skipped: import_zod3.z.number().optional(),
1658
+ cost_usd: import_zod3.z.number(),
1659
+ duration_ms: import_zod3.z.number()
1593
1660
  });
1594
- var evalResultSchema = import_zod2.z.object({
1595
- id: import_zod2.z.string(),
1596
- tags: import_zod2.z.array(import_zod2.z.string()),
1597
- rows: import_zod2.z.array(rowResultSchema),
1598
- passed: import_zod2.z.boolean()
1661
+ var evalResultSchema = import_zod3.z.object({
1662
+ id: import_zod3.z.string(),
1663
+ tags: import_zod3.z.array(import_zod3.z.string()),
1664
+ rows: import_zod3.z.array(rowResultSchema),
1665
+ passed: import_zod3.z.boolean()
1599
1666
  });
1600
- var runResultSchema = import_zod2.z.object({
1601
- schemaVersion: import_zod2.z.literal(RUN_RESULT_SCHEMA_VERSION),
1602
- plune_version: import_zod2.z.string(),
1603
- started_at: import_zod2.z.string(),
1604
- finished_at: import_zod2.z.string(),
1605
- config_hash: import_zod2.z.string(),
1667
+ var runResultSchema = import_zod3.z.object({
1668
+ schemaVersion: import_zod3.z.literal(RUN_RESULT_SCHEMA_VERSION),
1669
+ plune_version: import_zod3.z.string(),
1670
+ started_at: import_zod3.z.string(),
1671
+ finished_at: import_zod3.z.string(),
1672
+ config_hash: import_zod3.z.string(),
1606
1673
  summary: summarySchema,
1607
- evals: import_zod2.z.array(evalResultSchema)
1674
+ evals: import_zod3.z.array(evalResultSchema)
1608
1675
  });
1609
1676
  function parseRunResult(value) {
1610
1677
  return runResultSchema.parse(value);
@@ -1988,6 +2055,7 @@ function formatIngestResult(r) {
1988
2055
  SyncFileError,
1989
2056
  SyncHttpError,
1990
2057
  SyncNetworkError,
2058
+ TEST_CASE_SCHEMA_VERSION,
1991
2059
  TokenRejectedError,
1992
2060
  assertionConfigSchema,
1993
2061
  assertionResultRecordSchema,
@@ -1995,10 +2063,12 @@ function formatIngestResult(r) {
1995
2063
  formatIngestResult,
1996
2064
  ingest,
1997
2065
  parseRunResult,
2066
+ parseTestCase,
1998
2067
  readCairnRun,
1999
2068
  reportIngestFailure,
2000
2069
  reportSyncFailure,
2001
2070
  run,
2002
2071
  runResultSchema,
2003
- sync
2072
+ sync,
2073
+ testCaseSchema
2004
2074
  });
package/dist/index.d.cts CHANGED
@@ -5,6 +5,14 @@ interface Summary {
5
5
  passed: number;
6
6
  failed: number;
7
7
  errored: number;
8
+ /**
9
+ * plune#1245. Rows the runner said were never judged (`RowResult.inconclusive`), counted apart from
10
+ * `failed`. The runner always writes both; they are optional so a RunResult written before them is
11
+ * still valid and `schemaVersion` stays 1. With them, `passed + failed + errored + blocked + skipped`
12
+ * is `total`.
13
+ */
14
+ blocked?: number;
15
+ skipped?: number;
8
16
  cost_usd: number;
9
17
  duration_ms: number;
10
18
  }
@@ -23,6 +31,54 @@ interface RunError {
23
31
  code: string;
24
32
  message: string;
25
33
  }
34
+ interface ManualStep {
35
+ action: string;
36
+ expected?: string;
37
+ }
38
+ interface ExternalKey {
39
+ kind: 'playwright-id' | 'path-title' | 'allure-history' | 'cairn-stable' | 'eval-id' | 'qase' | 'testrail';
40
+ value: string;
41
+ }
42
+ type ManualCase = {
43
+ type: 'manual';
44
+ steps: {
45
+ action: string;
46
+ expected?: string;
47
+ }[];
48
+ expected?: string;
49
+ };
50
+ type AutomatedCase = {
51
+ type: 'automated';
52
+ specRef: string;
53
+ };
54
+ type EvalCase = {
55
+ type: 'eval';
56
+ prompt: string;
57
+ goldenSetId?: string;
58
+ assertions: AssertionResultRecord[];
59
+ };
60
+ interface TestCaseBase {
61
+ schemaVersion: 2;
62
+ id: string;
63
+ externalKeys?: ExternalKey[];
64
+ is_flaky?: boolean;
65
+ is_muted?: boolean;
66
+ }
67
+ type TestCase = TestCaseBase & (ManualCase | AutomatedCase | EvalCase);
68
+ /**
69
+ * The two words a row may use to say the product was never judged (ADR 0045).
70
+ *
71
+ * Both are already statuses this system has — they are `RESULT_STATUSES` entries — and neither was
72
+ * reachable through this contract: a row could only report `passed`, `failed` or `broken`. So a
73
+ * runner whose row was stopped by something outside the test had exactly one word left, `failed`,
74
+ * and the word was wrong. A red build because a dependency fell over reads identically to a red
75
+ * build because the code is broken, and those are two different pieces of work.
76
+ *
77
+ * `invalid` is deliberately absent: Qase's `invalid` is our `broken`, and a second word for one
78
+ * idea would fork the contract at both ends.
79
+ */
80
+ declare const ROW_INCONCLUSIVE: readonly ["blocked", "skipped"];
81
+ type RowInconclusive = (typeof ROW_INCONCLUSIVE)[number];
26
82
  interface RowResult {
27
83
  vars: Record<string, unknown>;
28
84
  output: string | null;
@@ -31,6 +87,14 @@ interface RowResult {
31
87
  latency_ms?: number;
32
88
  error?: RunError;
33
89
  assertions: AssertionResultRecord[];
90
+ /**
91
+ * ADR 0045. Say it when the test was never judged, rather than letting `failed` stand for both
92
+ * "the environment broke" and "the code is wrong".
93
+ *
94
+ * Optional and purely additive: a producer that does not send it validates exactly as before,
95
+ * which is why `schemaVersion` stays 1.
96
+ */
97
+ inconclusive?: RowInconclusive;
34
98
  }
35
99
  interface EvalResult {
36
100
  id: string;
@@ -374,6 +438,8 @@ declare const runResultSchema: z.ZodObject<{
374
438
  passed: z.ZodNumber;
375
439
  failed: z.ZodNumber;
376
440
  errored: z.ZodNumber;
441
+ blocked: z.ZodOptional<z.ZodNumber>;
442
+ skipped: z.ZodOptional<z.ZodNumber>;
377
443
  cost_usd: z.ZodNumber;
378
444
  duration_ms: z.ZodNumber;
379
445
  }, z.core.$strip>;
@@ -404,6 +470,10 @@ declare const runResultSchema: z.ZodObject<{
404
470
  passed: z.ZodBoolean;
405
471
  }, z.core.$strip>>>;
406
472
  }, z.core.$strip>>;
473
+ inconclusive: z.ZodOptional<z.ZodEnum<{
474
+ blocked: "blocked";
475
+ skipped: "skipped";
476
+ }>>;
407
477
  }, z.core.$strip>>;
408
478
  passed: z.ZodBoolean;
409
479
  }, z.core.$strip>>;
@@ -417,6 +487,133 @@ type BinaryVerdict = z.infer<typeof binaryVerdictSchema>;
417
487
  /** Parse + validate an unknown value as a RunResult. Throws ZodError on failure. */
418
488
  declare function parseRunResult(value: unknown): ParsedRunResult;
419
489
 
490
+ /**
491
+ * Runtime validation for the TestCase contract — the JSON that `plune run import` emits
492
+ * or that the platform accepts on `POST /v1/test-cases`.
493
+ *
494
+ * The canonical TypeScript shape is owned by `./results.ts` (`TEST_CASE_SCHEMA_VERSION: 2`).
495
+ * This module is its zod mirror: the validator for anything that has to *ingest* that
496
+ * output rather than produce it. `plune sync` validates the local case file with it
497
+ * before uploading, and the Plune platform validates the same body on
498
+ * `POST /v1/test-cases` — one schema, both ends.
499
+ */
500
+ declare const TEST_CASE_SCHEMA_VERSION: TestCase['schemaVersion'];
501
+ declare const testCaseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
502
+ schemaVersion: z.ZodLiteral<2>;
503
+ id: z.ZodString;
504
+ externalKeys: z.ZodOptional<z.ZodArray<z.ZodObject<{
505
+ kind: z.ZodEnum<{
506
+ "playwright-id": "playwright-id";
507
+ "path-title": "path-title";
508
+ "allure-history": "allure-history";
509
+ "cairn-stable": "cairn-stable";
510
+ "eval-id": "eval-id";
511
+ qase: "qase";
512
+ testrail: "testrail";
513
+ }>;
514
+ value: z.ZodString;
515
+ }, z.core.$strip>>>;
516
+ is_flaky: z.ZodOptional<z.ZodBoolean>;
517
+ is_muted: z.ZodOptional<z.ZodBoolean>;
518
+ type: z.ZodLiteral<"manual">;
519
+ steps: z.ZodArray<z.ZodObject<{
520
+ action: z.ZodString;
521
+ expected: z.ZodOptional<z.ZodString>;
522
+ }, z.core.$strip>>;
523
+ expected: z.ZodOptional<z.ZodString>;
524
+ }, z.core.$strip>, z.ZodObject<{
525
+ schemaVersion: z.ZodLiteral<2>;
526
+ id: z.ZodString;
527
+ externalKeys: z.ZodOptional<z.ZodArray<z.ZodObject<{
528
+ kind: z.ZodEnum<{
529
+ "playwright-id": "playwright-id";
530
+ "path-title": "path-title";
531
+ "allure-history": "allure-history";
532
+ "cairn-stable": "cairn-stable";
533
+ "eval-id": "eval-id";
534
+ qase: "qase";
535
+ testrail: "testrail";
536
+ }>;
537
+ value: z.ZodString;
538
+ }, z.core.$strip>>>;
539
+ is_flaky: z.ZodOptional<z.ZodBoolean>;
540
+ is_muted: z.ZodOptional<z.ZodBoolean>;
541
+ type: z.ZodLiteral<"automated">;
542
+ specRef: z.ZodString;
543
+ }, z.core.$strip>, z.ZodObject<{
544
+ schemaVersion: z.ZodLiteral<2>;
545
+ id: z.ZodString;
546
+ externalKeys: z.ZodOptional<z.ZodArray<z.ZodObject<{
547
+ kind: z.ZodEnum<{
548
+ "playwright-id": "playwright-id";
549
+ "path-title": "path-title";
550
+ "allure-history": "allure-history";
551
+ "cairn-stable": "cairn-stable";
552
+ "eval-id": "eval-id";
553
+ qase: "qase";
554
+ testrail: "testrail";
555
+ }>;
556
+ value: z.ZodString;
557
+ }, z.core.$strip>>>;
558
+ is_flaky: z.ZodOptional<z.ZodBoolean>;
559
+ is_muted: z.ZodOptional<z.ZodBoolean>;
560
+ type: z.ZodLiteral<"eval">;
561
+ prompt: z.ZodString;
562
+ goldenSetId: z.ZodOptional<z.ZodString>;
563
+ assertions: z.ZodArray<z.ZodObject<{
564
+ type: z.ZodString;
565
+ passed: z.ZodBoolean;
566
+ score: z.ZodOptional<z.ZodNumber>;
567
+ reason: z.ZodOptional<z.ZodString>;
568
+ }, z.core.$strip>>;
569
+ }, z.core.$strip>], "type">;
570
+ /** Parse + validate an unknown value as a TestCase. Throws ZodError on failure. */
571
+ declare function parseTestCase(value: unknown): {
572
+ schemaVersion: 2;
573
+ id: string;
574
+ type: "manual";
575
+ steps: {
576
+ action: string;
577
+ expected?: string | undefined;
578
+ }[];
579
+ externalKeys?: {
580
+ kind: "playwright-id" | "path-title" | "allure-history" | "cairn-stable" | "eval-id" | "qase" | "testrail";
581
+ value: string;
582
+ }[] | undefined;
583
+ is_flaky?: boolean | undefined;
584
+ is_muted?: boolean | undefined;
585
+ expected?: string | undefined;
586
+ } | {
587
+ schemaVersion: 2;
588
+ id: string;
589
+ type: "automated";
590
+ specRef: string;
591
+ externalKeys?: {
592
+ kind: "playwright-id" | "path-title" | "allure-history" | "cairn-stable" | "eval-id" | "qase" | "testrail";
593
+ value: string;
594
+ }[] | undefined;
595
+ is_flaky?: boolean | undefined;
596
+ is_muted?: boolean | undefined;
597
+ } | {
598
+ schemaVersion: 2;
599
+ id: string;
600
+ type: "eval";
601
+ prompt: string;
602
+ assertions: {
603
+ type: string;
604
+ passed: boolean;
605
+ score?: number | undefined;
606
+ reason?: string | undefined;
607
+ }[];
608
+ externalKeys?: {
609
+ kind: "playwright-id" | "path-title" | "allure-history" | "cairn-stable" | "eval-id" | "qase" | "testrail";
610
+ value: string;
611
+ }[] | undefined;
612
+ is_flaky?: boolean | undefined;
613
+ is_muted?: boolean | undefined;
614
+ goldenSetId?: string | undefined;
615
+ };
616
+
420
617
  interface IngestDeps {
421
618
  /** The Cairn run directory to read. Omit it and the newest run under `runsDir` is used. */
422
619
  dir?: string;
@@ -485,4 +682,4 @@ interface ReadCairnRunDeps {
485
682
  */
486
683
  declare function readCairnRun(dir: string, deps?: ReadCairnRunDeps): Promise<CairnIngestPayload>;
487
684
 
488
- export { type AssertionResultRecord, type BinaryVerdict, type CairnIngestCase, type CairnIngestPayload, type Config, type EvalResult, type IngestDeps, type IngestResult, NotLoggedInError, type ParsedRunResult, RUN_RESULT_SCHEMA_VERSION, type RowResult, type RunError, type RunOptions, type RunResult, type Summary, type SyncDeps, SyncFileError, SyncHttpError, SyncNetworkError, type SyncResult, TokenRejectedError, type Usage, assertionConfigSchema, assertionResultRecordSchema, binaryVerdictSchema, formatIngestResult, handleIngest as ingest, parseRunResult, readCairnRun, reportIngestFailure, reportSyncFailure, handleRun as run, runResultSchema, handleSync as sync };
685
+ export { type AssertionResultRecord, type BinaryVerdict, type CairnIngestCase, type CairnIngestPayload, type Config, type EvalResult, type ExternalKey, type IngestDeps, type IngestResult, type ManualStep, NotLoggedInError, type ParsedRunResult, RUN_RESULT_SCHEMA_VERSION, type RowResult, type RunError, type RunOptions, type RunResult, type Summary, type SyncDeps, SyncFileError, SyncHttpError, SyncNetworkError, type SyncResult, TEST_CASE_SCHEMA_VERSION, type TestCase, TokenRejectedError, type Usage, assertionConfigSchema, assertionResultRecordSchema, binaryVerdictSchema, formatIngestResult, handleIngest as ingest, parseRunResult, parseTestCase, readCairnRun, reportIngestFailure, reportSyncFailure, handleRun as run, runResultSchema, handleSync as sync, testCaseSchema };