terminalhire 0.42.36 → 0.42.37

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.
@@ -26207,12 +26207,22 @@ function reportedCounts(classified, reparsed) {
26207
26207
  function readCounts(stdout, stderr = "") {
26208
26208
  const out = `${stdout}
26209
26209
  ${stderr}`;
26210
+ const matched = [];
26210
26211
  for (const reader of READERS) {
26211
26212
  const counts = reader.read(out);
26212
26213
  if (counts)
26213
- return { ...counts, runner: reader.runner };
26214
+ matched.push({ reader, counts });
26214
26215
  }
26215
- return null;
26216
+ const first = matched[0];
26217
+ if (first === void 0)
26218
+ return null;
26219
+ const legs = matched.filter(({ reader }) => !matched.some((m) => reader.shadowedBy?.includes(m.reader.runner)));
26220
+ const partial = legs.length > 1 || (first.reader.summaries?.(out) ?? 1) > 1;
26221
+ return {
26222
+ ...first.counts,
26223
+ runner: first.reader.runner,
26224
+ ...partial ? { coverage: "partial" } : {}
26225
+ };
26216
26226
  }
26217
26227
  function coverageThresholdUnmet(facts, counts) {
26218
26228
  if (counts === null || counts.runner !== "node-tap")
@@ -26371,7 +26381,7 @@ function isGreen(outcome) {
26371
26381
  function isOurFault(outcome) {
26372
26382
  return LIMIT_OWNER[outcome] === "ours";
26373
26383
  }
26374
- var VERIFICATION_OUTCOMES, int, withSuiteFailures, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, LIMIT_OWNER;
26384
+ var VERIFICATION_OUTCOMES, int, withSuiteFailures, countLines, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, LIMIT_OWNER;
26375
26385
  var init_classify2 = __esm({
26376
26386
  "../../packages/envrun/dist/classify.js"() {
26377
26387
  "use strict";
@@ -26390,6 +26400,7 @@ var init_classify2 = __esm({
26390
26400
  return counts;
26391
26401
  return { ...counts, tests_failed: int(/(\d+) failed/.exec(suiteLine[1])) };
26392
26402
  };
26403
+ countLines = (out, re) => (out.match(re) ?? []).length;
26393
26404
  READERS = [
26394
26405
  {
26395
26406
  // `node --test` TAP. Anchored to line start, which is what makes it a
@@ -26401,7 +26412,10 @@ var init_classify2 = __esm({
26401
26412
  if (!pass || !fail2)
26402
26413
  return null;
26403
26414
  return { tests_passed: int(pass), tests_failed: int(fail2) };
26404
- }
26415
+ },
26416
+ // One `# pass N` per invocation, so a script that runs `node --test` once per file
26417
+ // prints many. envrun's own offline leg printed 67 and was read as the first one's 4.
26418
+ summaries: (out) => countLines(out, /^# pass \d+$/gm)
26405
26419
  },
26406
26420
  {
26407
26421
  // node-tap's runner summary, which is NOT the `# pass N` / `# fail N` pair
@@ -26428,7 +26442,8 @@ var init_classify2 = __esm({
26428
26442
  tests_passed: int(/\bpass: (\d+)/.exec(line[1])),
26429
26443
  tests_failed: int(/\bfail: (\d+)/.exec(line[1]))
26430
26444
  };
26431
- }
26445
+ },
26446
+ summaries: (out) => countLines(out, /^# \{ total: \d+[^}\n]* \}$/gm)
26432
26447
  },
26433
26448
  {
26434
26449
  // jest: `Tests: 1 failed, 2 passed, 3 total`
@@ -26441,7 +26456,8 @@ var init_classify2 = __esm({
26441
26456
  tests_passed: int(/(\d+) passed/.exec(line[1])),
26442
26457
  tests_failed: int(/(\d+) failed/.exec(line[1]))
26443
26458
  }, /^Test Suites:\s+(.+?)\s*$/m.exec(out));
26444
- }
26459
+ },
26460
+ summaries: (out) => countLines(out, /^Tests:\s+[^\n]*\btotal\b/gm)
26445
26461
  },
26446
26462
  {
26447
26463
  // vitest: `Tests 3 passed (3)` / `Tests 1 failed | 2 passed (3)`
@@ -26458,7 +26474,12 @@ var init_classify2 = __esm({
26458
26474
  if (!passed && !failed)
26459
26475
  return null;
26460
26476
  return withSuiteFailures({ tests_passed: int(passed), tests_failed: int(failed) }, files);
26461
- }
26477
+ },
26478
+ // One invocation prints one `Test Files` line, and a numeric `Tests` line only when a
26479
+ // spec loaded (the every-spec-failed shape above has none). The larger of the two
26480
+ // counts invocations without counting a paired block twice; reading `Tests` alone
26481
+ // missed an all-failed run that followed a passing one (Codex review).
26482
+ summaries: (out) => Math.max(countLines(out, /^\s*Tests\s+[^\n]*\(\d+\)\s*$/gm), countLines(out, /^\s*Test Files\s+[^\n]*\(\d+\)\s*$/gm))
26462
26483
  },
26463
26484
  {
26464
26485
  // pytest summary rule: `===== 297 passed in 0.54s =====`,
@@ -26481,7 +26502,10 @@ var init_classify2 = __esm({
26481
26502
  if (!passed && !failed && !errors)
26482
26503
  return null;
26483
26504
  return { tests_passed: int(passed), tests_failed: int(failed) + int(errors) };
26484
- }
26505
+ },
26506
+ // Only FINAL tallies, which carry pytest's `in 0.54s`. The interim banners the reader
26507
+ // skips above carry no duration, and counting them would mark every red run partial.
26508
+ summaries: (out) => Math.max(1, countLines(out, /^=+ [^\n]*?(?:passed|failed|error|no tests ran)[^\n]*? in [\d.]+s[^\n]*? =+$/gm))
26485
26509
  },
26486
26510
  {
26487
26511
  // Maven surefire's per-module summary (TERM-1122), measured against surefire
@@ -26543,7 +26567,8 @@ var init_classify2 = __esm({
26543
26567
  return null;
26544
26568
  const failed = Number(m[1]);
26545
26569
  return { tests_passed: Number(m[2]) - failed, tests_failed: failed };
26546
- }
26570
+ },
26571
+ summaries: (out) => Math.max(1, countLines(out, /^\d+% tests passed, \d+ tests? failed out of \d+$/gm))
26547
26572
  },
26548
26573
  {
26549
26574
  // mocha: ` 440 passing (1s)` and ` 2 failing`
@@ -26554,7 +26579,8 @@ var init_classify2 = __esm({
26554
26579
  if (!passing && !failing)
26555
26580
  return null;
26556
26581
  return { tests_passed: int(passing), tests_failed: int(failing) };
26557
- }
26582
+ },
26583
+ summaries: (out) => Math.max(countLines(out, /^\s*\d+ passing\b/gm), countLines(out, /^\s*\d+ failing\b/gm))
26558
26584
  },
26559
26585
  {
26560
26586
  // ava: ` 3 tests passed` / ` 1 test failed`
@@ -26565,7 +26591,8 @@ var init_classify2 = __esm({
26565
26591
  if (!passed && !failed)
26566
26592
  return null;
26567
26593
  return { tests_passed: int(passed), tests_failed: int(failed) };
26568
- }
26594
+ },
26595
+ summaries: (out) => Math.max(countLines(out, /^\s*\d+ tests? passed\b/gm), countLines(out, /^\s*\d+ tests? failed\b/gm))
26569
26596
  },
26570
26597
  {
26571
26598
  // uvu: a `Total:` / `Passed:` / `Skipped:` block.
@@ -26578,7 +26605,8 @@ var init_classify2 = __esm({
26578
26605
  const t = int(total);
26579
26606
  const p = int(passed);
26580
26607
  return { tests_passed: p, tests_failed: Math.max(0, t - p) };
26581
- }
26608
+ },
26609
+ summaries: (out) => countLines(out, /^\s*Total:\s+\d+/gm)
26582
26610
  },
26583
26611
  {
26584
26612
  /**
@@ -26613,6 +26641,7 @@ var init_classify2 = __esm({
26613
26641
  * those would double them against their parent.
26614
26642
  */
26615
26643
  runner: "bare TAP",
26644
+ shadowedBy: ["node --test (TAP)", "node-tap"],
26616
26645
  read: (out) => {
26617
26646
  let n = 0;
26618
26647
  let failed = 0;
@@ -30358,9 +30387,9 @@ function renderVerdictLine(r) {
30358
30387
  const t = fmtMs(r.wallMs);
30359
30388
  switch (r.outcome) {
30360
30389
  case "completed":
30361
- return `GREEN ${String(r.counts?.tests_passed ?? 0)} test(s) passed, none failed \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
30390
+ return `GREEN ${String(r.counts?.tests_passed ?? 0)} test(s) passed, none failed ` + (r.counts?.coverage === "partial" ? `(${PARTIAL_COUNTS_NOTE}) ` : "") + `\u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
30362
30391
  case "tests-failed":
30363
- return `RED ${r.counts ? `${String(r.counts.tests_failed)} test(s) failed` : `exit ${String(r.exitCode)}`} \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
30392
+ return `RED ${r.counts ? `${String(r.counts.tests_failed)} test(s) failed` : `exit ${String(r.exitCode)}`}` + (r.counts?.coverage === "partial" ? ` (${PARTIAL_COUNTS_NOTE})` : "") + ` \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
30364
30393
  case "no-tests-observed":
30365
30394
  return `NOTHING RAN the suite reported zero tests passed and zero failed \u2014 ${t}`;
30366
30395
  case "counts-unparsed":
@@ -30414,7 +30443,7 @@ function exitCodeFor(r) {
30414
30443
  return 2;
30415
30444
  return exitCodeForOutcome(r.outcome);
30416
30445
  }
30417
- var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS;
30446
+ var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, PARTIAL_COUNTS_NOTE, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS;
30418
30447
  var init_result = __esm({
30419
30448
  "../../packages/envrun/dist/result.js"() {
30420
30449
  "use strict";
@@ -30437,6 +30466,7 @@ var init_result = __esm({
30437
30466
  "founder-declared"
30438
30467
  ];
30439
30468
  RUN_RESULT_SCHEMA = "terminalhire.verification-run/1";
30469
+ PARTIAL_COUNTS_NOTE = "from one of several test summaries in the output, so not a total for the whole run";
30440
30470
  RUN_RESULT_FIELDS = [
30441
30471
  "schema",
30442
30472
  "runId",
@@ -30482,7 +30512,7 @@ var init_result = __esm({
30482
30512
  testCommand: (r) => r.testCommand === null ? null : `test command ${r.testCommand}`,
30483
30513
  testOutputTail: RENDER_NONE,
30484
30514
  // printed as a block below the fields, when red
30485
- counts: (r) => r.counts === null ? null : `tests ${String(r.counts.tests_passed)} passed, ${String(r.counts.tests_failed)} failed (${r.counts.runner})`,
30515
+ counts: (r) => r.counts === null ? null : `tests ${String(r.counts.tests_passed)} passed, ${String(r.counts.tests_failed)} failed (${r.counts.runner})` + (r.counts.coverage === "partial" ? ` \u2014 ${PARTIAL_COUNTS_NOTE}` : ""),
30486
30516
  wallMs: (r) => `round trip ${fmtMs(r.wallMs)}`,
30487
30517
  installMs: (r) => r.installMs === void 0 ? null : `install step ${fmtMs(r.installMs)}`,
30488
30518
  testMs: (r) => r.testMs === void 0 ? null : `test step ${fmtMs(r.testMs)}`,
@@ -30581,7 +30611,11 @@ function toTestRunResult(result, outputSha256) {
30581
30611
  return {
30582
30612
  exit_code: result.exitCode ?? -1,
30583
30613
  ...outputSha256 === void 0 ? {} : { output_sha256: outputSha256 },
30584
- ...result.counts === null ? {} : { tests_passed: result.counts.tests_passed, tests_failed: result.counts.tests_failed }
30614
+ ...result.counts === null ? {} : {
30615
+ tests_passed: result.counts.tests_passed,
30616
+ tests_failed: result.counts.tests_failed,
30617
+ ...result.counts.coverage === "partial" ? { tests_coverage: "partial" } : {}
30618
+ }
30585
30619
  };
30586
30620
  }
30587
30621
  function toAcceptancePredicate(pair, opts = {}) {
@@ -38033,6 +38067,7 @@ __export(dist_exports, {
38033
38067
  LabelWatch: () => LabelWatch,
38034
38068
  MIN_GIT_VERSION_FOR_END_OF_OPTIONS: () => MIN_GIT_VERSION_FOR_END_OF_OPTIONS,
38035
38069
  OUTCOME_TO_BUDGET: () => OUTCOME_TO_BUDGET,
38070
+ PARTIAL_COUNTS_NOTE: () => PARTIAL_COUNTS_NOTE,
38036
38071
  PATH_REFUSAL_CODES: () => PATH_REFUSAL_CODES,
38037
38072
  PLACEMENTS: () => PLACEMENTS,
38038
38073
  PLACEMENT_ALIASES: () => PLACEMENT_ALIASES,
@@ -31982,12 +31982,22 @@ function reportedCounts(classified, reparsed) {
31982
31982
  function readCounts(stdout, stderr = "") {
31983
31983
  const out = `${stdout}
31984
31984
  ${stderr}`;
31985
+ const matched = [];
31985
31986
  for (const reader of READERS) {
31986
31987
  const counts = reader.read(out);
31987
31988
  if (counts)
31988
- return { ...counts, runner: reader.runner };
31989
+ matched.push({ reader, counts });
31989
31990
  }
31990
- return null;
31991
+ const first = matched[0];
31992
+ if (first === void 0)
31993
+ return null;
31994
+ const legs = matched.filter(({ reader }) => !matched.some((m) => reader.shadowedBy?.includes(m.reader.runner)));
31995
+ const partial2 = legs.length > 1 || (first.reader.summaries?.(out) ?? 1) > 1;
31996
+ return {
31997
+ ...first.counts,
31998
+ runner: first.reader.runner,
31999
+ ...partial2 ? { coverage: "partial" } : {}
32000
+ };
31991
32001
  }
31992
32002
  function coverageThresholdUnmet(facts, counts) {
31993
32003
  if (counts === null || counts.runner !== "node-tap")
@@ -32146,7 +32156,7 @@ function isGreen(outcome) {
32146
32156
  function isOurFault(outcome) {
32147
32157
  return LIMIT_OWNER[outcome] === "ours";
32148
32158
  }
32149
- var VERIFICATION_OUTCOMES, int, withSuiteFailures, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, LIMIT_OWNER;
32159
+ var VERIFICATION_OUTCOMES, int, withSuiteFailures, countLines, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, LIMIT_OWNER;
32150
32160
  var init_classify2 = __esm({
32151
32161
  "../../packages/envrun/dist/classify.js"() {
32152
32162
  "use strict";
@@ -32165,6 +32175,7 @@ var init_classify2 = __esm({
32165
32175
  return counts;
32166
32176
  return { ...counts, tests_failed: int(/(\d+) failed/.exec(suiteLine[1])) };
32167
32177
  };
32178
+ countLines = (out, re) => (out.match(re) ?? []).length;
32168
32179
  READERS = [
32169
32180
  {
32170
32181
  // `node --test` TAP. Anchored to line start, which is what makes it a
@@ -32176,7 +32187,10 @@ var init_classify2 = __esm({
32176
32187
  if (!pass || !fail2)
32177
32188
  return null;
32178
32189
  return { tests_passed: int(pass), tests_failed: int(fail2) };
32179
- }
32190
+ },
32191
+ // One `# pass N` per invocation, so a script that runs `node --test` once per file
32192
+ // prints many. envrun's own offline leg printed 67 and was read as the first one's 4.
32193
+ summaries: (out) => countLines(out, /^# pass \d+$/gm)
32180
32194
  },
32181
32195
  {
32182
32196
  // node-tap's runner summary, which is NOT the `# pass N` / `# fail N` pair
@@ -32203,7 +32217,8 @@ var init_classify2 = __esm({
32203
32217
  tests_passed: int(/\bpass: (\d+)/.exec(line[1])),
32204
32218
  tests_failed: int(/\bfail: (\d+)/.exec(line[1]))
32205
32219
  };
32206
- }
32220
+ },
32221
+ summaries: (out) => countLines(out, /^# \{ total: \d+[^}\n]* \}$/gm)
32207
32222
  },
32208
32223
  {
32209
32224
  // jest: `Tests: 1 failed, 2 passed, 3 total`
@@ -32216,7 +32231,8 @@ var init_classify2 = __esm({
32216
32231
  tests_passed: int(/(\d+) passed/.exec(line[1])),
32217
32232
  tests_failed: int(/(\d+) failed/.exec(line[1]))
32218
32233
  }, /^Test Suites:\s+(.+?)\s*$/m.exec(out));
32219
- }
32234
+ },
32235
+ summaries: (out) => countLines(out, /^Tests:\s+[^\n]*\btotal\b/gm)
32220
32236
  },
32221
32237
  {
32222
32238
  // vitest: `Tests 3 passed (3)` / `Tests 1 failed | 2 passed (3)`
@@ -32233,7 +32249,12 @@ var init_classify2 = __esm({
32233
32249
  if (!passed && !failed)
32234
32250
  return null;
32235
32251
  return withSuiteFailures({ tests_passed: int(passed), tests_failed: int(failed) }, files);
32236
- }
32252
+ },
32253
+ // One invocation prints one `Test Files` line, and a numeric `Tests` line only when a
32254
+ // spec loaded (the every-spec-failed shape above has none). The larger of the two
32255
+ // counts invocations without counting a paired block twice; reading `Tests` alone
32256
+ // missed an all-failed run that followed a passing one (Codex review).
32257
+ summaries: (out) => Math.max(countLines(out, /^\s*Tests\s+[^\n]*\(\d+\)\s*$/gm), countLines(out, /^\s*Test Files\s+[^\n]*\(\d+\)\s*$/gm))
32237
32258
  },
32238
32259
  {
32239
32260
  // pytest summary rule: `===== 297 passed in 0.54s =====`,
@@ -32256,7 +32277,10 @@ var init_classify2 = __esm({
32256
32277
  if (!passed && !failed && !errors)
32257
32278
  return null;
32258
32279
  return { tests_passed: int(passed), tests_failed: int(failed) + int(errors) };
32259
- }
32280
+ },
32281
+ // Only FINAL tallies, which carry pytest's `in 0.54s`. The interim banners the reader
32282
+ // skips above carry no duration, and counting them would mark every red run partial.
32283
+ summaries: (out) => Math.max(1, countLines(out, /^=+ [^\n]*?(?:passed|failed|error|no tests ran)[^\n]*? in [\d.]+s[^\n]*? =+$/gm))
32260
32284
  },
32261
32285
  {
32262
32286
  // Maven surefire's per-module summary (TERM-1122), measured against surefire
@@ -32318,7 +32342,8 @@ var init_classify2 = __esm({
32318
32342
  return null;
32319
32343
  const failed = Number(m[1]);
32320
32344
  return { tests_passed: Number(m[2]) - failed, tests_failed: failed };
32321
- }
32345
+ },
32346
+ summaries: (out) => Math.max(1, countLines(out, /^\d+% tests passed, \d+ tests? failed out of \d+$/gm))
32322
32347
  },
32323
32348
  {
32324
32349
  // mocha: ` 440 passing (1s)` and ` 2 failing`
@@ -32329,7 +32354,8 @@ var init_classify2 = __esm({
32329
32354
  if (!passing && !failing)
32330
32355
  return null;
32331
32356
  return { tests_passed: int(passing), tests_failed: int(failing) };
32332
- }
32357
+ },
32358
+ summaries: (out) => Math.max(countLines(out, /^\s*\d+ passing\b/gm), countLines(out, /^\s*\d+ failing\b/gm))
32333
32359
  },
32334
32360
  {
32335
32361
  // ava: ` 3 tests passed` / ` 1 test failed`
@@ -32340,7 +32366,8 @@ var init_classify2 = __esm({
32340
32366
  if (!passed && !failed)
32341
32367
  return null;
32342
32368
  return { tests_passed: int(passed), tests_failed: int(failed) };
32343
- }
32369
+ },
32370
+ summaries: (out) => Math.max(countLines(out, /^\s*\d+ tests? passed\b/gm), countLines(out, /^\s*\d+ tests? failed\b/gm))
32344
32371
  },
32345
32372
  {
32346
32373
  // uvu: a `Total:` / `Passed:` / `Skipped:` block.
@@ -32353,7 +32380,8 @@ var init_classify2 = __esm({
32353
32380
  const t = int(total);
32354
32381
  const p = int(passed);
32355
32382
  return { tests_passed: p, tests_failed: Math.max(0, t - p) };
32356
- }
32383
+ },
32384
+ summaries: (out) => countLines(out, /^\s*Total:\s+\d+/gm)
32357
32385
  },
32358
32386
  {
32359
32387
  /**
@@ -32388,6 +32416,7 @@ var init_classify2 = __esm({
32388
32416
  * those would double them against their parent.
32389
32417
  */
32390
32418
  runner: "bare TAP",
32419
+ shadowedBy: ["node --test (TAP)", "node-tap"],
32391
32420
  read: (out) => {
32392
32421
  let n = 0;
32393
32422
  let failed = 0;
@@ -36133,9 +36162,9 @@ function renderVerdictLine(r) {
36133
36162
  const t = fmtMs(r.wallMs);
36134
36163
  switch (r.outcome) {
36135
36164
  case "completed":
36136
- return `GREEN ${String(r.counts?.tests_passed ?? 0)} test(s) passed, none failed \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
36165
+ return `GREEN ${String(r.counts?.tests_passed ?? 0)} test(s) passed, none failed ` + (r.counts?.coverage === "partial" ? `(${PARTIAL_COUNTS_NOTE}) ` : "") + `\u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
36137
36166
  case "tests-failed":
36138
- return `RED ${r.counts ? `${String(r.counts.tests_failed)} test(s) failed` : `exit ${String(r.exitCode)}`} \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
36167
+ return `RED ${r.counts ? `${String(r.counts.tests_failed)} test(s) failed` : `exit ${String(r.exitCode)}`}` + (r.counts?.coverage === "partial" ? ` (${PARTIAL_COUNTS_NOTE})` : "") + ` \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
36139
36168
  case "no-tests-observed":
36140
36169
  return `NOTHING RAN the suite reported zero tests passed and zero failed \u2014 ${t}`;
36141
36170
  case "counts-unparsed":
@@ -36189,7 +36218,7 @@ function exitCodeFor(r) {
36189
36218
  return 2;
36190
36219
  return exitCodeForOutcome(r.outcome);
36191
36220
  }
36192
- var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS;
36221
+ var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, PARTIAL_COUNTS_NOTE, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS;
36193
36222
  var init_result = __esm({
36194
36223
  "../../packages/envrun/dist/result.js"() {
36195
36224
  "use strict";
@@ -36212,6 +36241,7 @@ var init_result = __esm({
36212
36241
  "founder-declared"
36213
36242
  ];
36214
36243
  RUN_RESULT_SCHEMA = "terminalhire.verification-run/1";
36244
+ PARTIAL_COUNTS_NOTE = "from one of several test summaries in the output, so not a total for the whole run";
36215
36245
  RUN_RESULT_FIELDS = [
36216
36246
  "schema",
36217
36247
  "runId",
@@ -36257,7 +36287,7 @@ var init_result = __esm({
36257
36287
  testCommand: (r) => r.testCommand === null ? null : `test command ${r.testCommand}`,
36258
36288
  testOutputTail: RENDER_NONE,
36259
36289
  // printed as a block below the fields, when red
36260
- counts: (r) => r.counts === null ? null : `tests ${String(r.counts.tests_passed)} passed, ${String(r.counts.tests_failed)} failed (${r.counts.runner})`,
36290
+ counts: (r) => r.counts === null ? null : `tests ${String(r.counts.tests_passed)} passed, ${String(r.counts.tests_failed)} failed (${r.counts.runner})` + (r.counts.coverage === "partial" ? ` \u2014 ${PARTIAL_COUNTS_NOTE}` : ""),
36261
36291
  wallMs: (r) => `round trip ${fmtMs(r.wallMs)}`,
36262
36292
  installMs: (r) => r.installMs === void 0 ? null : `install step ${fmtMs(r.installMs)}`,
36263
36293
  testMs: (r) => r.testMs === void 0 ? null : `test step ${fmtMs(r.testMs)}`,
@@ -36356,7 +36386,11 @@ function toTestRunResult(result, outputSha256) {
36356
36386
  return {
36357
36387
  exit_code: result.exitCode ?? -1,
36358
36388
  ...outputSha256 === void 0 ? {} : { output_sha256: outputSha256 },
36359
- ...result.counts === null ? {} : { tests_passed: result.counts.tests_passed, tests_failed: result.counts.tests_failed }
36389
+ ...result.counts === null ? {} : {
36390
+ tests_passed: result.counts.tests_passed,
36391
+ tests_failed: result.counts.tests_failed,
36392
+ ...result.counts.coverage === "partial" ? { tests_coverage: "partial" } : {}
36393
+ }
36360
36394
  };
36361
36395
  }
36362
36396
  function toAcceptancePredicate(pair, opts = {}) {
@@ -43808,6 +43842,7 @@ __export(dist_exports, {
43808
43842
  LabelWatch: () => LabelWatch,
43809
43843
  MIN_GIT_VERSION_FOR_END_OF_OPTIONS: () => MIN_GIT_VERSION_FOR_END_OF_OPTIONS,
43810
43844
  OUTCOME_TO_BUDGET: () => OUTCOME_TO_BUDGET,
43845
+ PARTIAL_COUNTS_NOTE: () => PARTIAL_COUNTS_NOTE,
43811
43846
  PATH_REFUSAL_CODES: () => PATH_REFUSAL_CODES,
43812
43847
  PLACEMENTS: () => PLACEMENTS,
43813
43848
  PLACEMENT_ALIASES: () => PLACEMENT_ALIASES,
@@ -26748,12 +26748,22 @@ function reportedCounts(classified, reparsed) {
26748
26748
  function readCounts(stdout, stderr = "") {
26749
26749
  const out = `${stdout}
26750
26750
  ${stderr}`;
26751
+ const matched = [];
26751
26752
  for (const reader of READERS) {
26752
26753
  const counts = reader.read(out);
26753
26754
  if (counts)
26754
- return { ...counts, runner: reader.runner };
26755
+ matched.push({ reader, counts });
26755
26756
  }
26756
- return null;
26757
+ const first = matched[0];
26758
+ if (first === void 0)
26759
+ return null;
26760
+ const legs = matched.filter(({ reader }) => !matched.some((m) => reader.shadowedBy?.includes(m.reader.runner)));
26761
+ const partial2 = legs.length > 1 || (first.reader.summaries?.(out) ?? 1) > 1;
26762
+ return {
26763
+ ...first.counts,
26764
+ runner: first.reader.runner,
26765
+ ...partial2 ? { coverage: "partial" } : {}
26766
+ };
26757
26767
  }
26758
26768
  function coverageThresholdUnmet(facts, counts) {
26759
26769
  if (counts === null || counts.runner !== "node-tap")
@@ -26912,7 +26922,7 @@ function isGreen(outcome) {
26912
26922
  function isOurFault(outcome) {
26913
26923
  return LIMIT_OWNER[outcome] === "ours";
26914
26924
  }
26915
- var VERIFICATION_OUTCOMES, int, withSuiteFailures, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, LIMIT_OWNER;
26925
+ var VERIFICATION_OUTCOMES, int, withSuiteFailures, countLines, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, LIMIT_OWNER;
26916
26926
  var init_classify2 = __esm({
26917
26927
  "../../packages/envrun/dist/classify.js"() {
26918
26928
  "use strict";
@@ -26931,6 +26941,7 @@ var init_classify2 = __esm({
26931
26941
  return counts;
26932
26942
  return { ...counts, tests_failed: int(/(\d+) failed/.exec(suiteLine[1])) };
26933
26943
  };
26944
+ countLines = (out, re) => (out.match(re) ?? []).length;
26934
26945
  READERS = [
26935
26946
  {
26936
26947
  // `node --test` TAP. Anchored to line start, which is what makes it a
@@ -26942,7 +26953,10 @@ var init_classify2 = __esm({
26942
26953
  if (!pass || !fail2)
26943
26954
  return null;
26944
26955
  return { tests_passed: int(pass), tests_failed: int(fail2) };
26945
- }
26956
+ },
26957
+ // One `# pass N` per invocation, so a script that runs `node --test` once per file
26958
+ // prints many. envrun's own offline leg printed 67 and was read as the first one's 4.
26959
+ summaries: (out) => countLines(out, /^# pass \d+$/gm)
26946
26960
  },
26947
26961
  {
26948
26962
  // node-tap's runner summary, which is NOT the `# pass N` / `# fail N` pair
@@ -26969,7 +26983,8 @@ var init_classify2 = __esm({
26969
26983
  tests_passed: int(/\bpass: (\d+)/.exec(line[1])),
26970
26984
  tests_failed: int(/\bfail: (\d+)/.exec(line[1]))
26971
26985
  };
26972
- }
26986
+ },
26987
+ summaries: (out) => countLines(out, /^# \{ total: \d+[^}\n]* \}$/gm)
26973
26988
  },
26974
26989
  {
26975
26990
  // jest: `Tests: 1 failed, 2 passed, 3 total`
@@ -26982,7 +26997,8 @@ var init_classify2 = __esm({
26982
26997
  tests_passed: int(/(\d+) passed/.exec(line[1])),
26983
26998
  tests_failed: int(/(\d+) failed/.exec(line[1]))
26984
26999
  }, /^Test Suites:\s+(.+?)\s*$/m.exec(out));
26985
- }
27000
+ },
27001
+ summaries: (out) => countLines(out, /^Tests:\s+[^\n]*\btotal\b/gm)
26986
27002
  },
26987
27003
  {
26988
27004
  // vitest: `Tests 3 passed (3)` / `Tests 1 failed | 2 passed (3)`
@@ -26999,7 +27015,12 @@ var init_classify2 = __esm({
26999
27015
  if (!passed && !failed)
27000
27016
  return null;
27001
27017
  return withSuiteFailures({ tests_passed: int(passed), tests_failed: int(failed) }, files);
27002
- }
27018
+ },
27019
+ // One invocation prints one `Test Files` line, and a numeric `Tests` line only when a
27020
+ // spec loaded (the every-spec-failed shape above has none). The larger of the two
27021
+ // counts invocations without counting a paired block twice; reading `Tests` alone
27022
+ // missed an all-failed run that followed a passing one (Codex review).
27023
+ summaries: (out) => Math.max(countLines(out, /^\s*Tests\s+[^\n]*\(\d+\)\s*$/gm), countLines(out, /^\s*Test Files\s+[^\n]*\(\d+\)\s*$/gm))
27003
27024
  },
27004
27025
  {
27005
27026
  // pytest summary rule: `===== 297 passed in 0.54s =====`,
@@ -27022,7 +27043,10 @@ var init_classify2 = __esm({
27022
27043
  if (!passed && !failed && !errors)
27023
27044
  return null;
27024
27045
  return { tests_passed: int(passed), tests_failed: int(failed) + int(errors) };
27025
- }
27046
+ },
27047
+ // Only FINAL tallies, which carry pytest's `in 0.54s`. The interim banners the reader
27048
+ // skips above carry no duration, and counting them would mark every red run partial.
27049
+ summaries: (out) => Math.max(1, countLines(out, /^=+ [^\n]*?(?:passed|failed|error|no tests ran)[^\n]*? in [\d.]+s[^\n]*? =+$/gm))
27026
27050
  },
27027
27051
  {
27028
27052
  // Maven surefire's per-module summary (TERM-1122), measured against surefire
@@ -27084,7 +27108,8 @@ var init_classify2 = __esm({
27084
27108
  return null;
27085
27109
  const failed = Number(m[1]);
27086
27110
  return { tests_passed: Number(m[2]) - failed, tests_failed: failed };
27087
- }
27111
+ },
27112
+ summaries: (out) => Math.max(1, countLines(out, /^\d+% tests passed, \d+ tests? failed out of \d+$/gm))
27088
27113
  },
27089
27114
  {
27090
27115
  // mocha: ` 440 passing (1s)` and ` 2 failing`
@@ -27095,7 +27120,8 @@ var init_classify2 = __esm({
27095
27120
  if (!passing && !failing)
27096
27121
  return null;
27097
27122
  return { tests_passed: int(passing), tests_failed: int(failing) };
27098
- }
27123
+ },
27124
+ summaries: (out) => Math.max(countLines(out, /^\s*\d+ passing\b/gm), countLines(out, /^\s*\d+ failing\b/gm))
27099
27125
  },
27100
27126
  {
27101
27127
  // ava: ` 3 tests passed` / ` 1 test failed`
@@ -27106,7 +27132,8 @@ var init_classify2 = __esm({
27106
27132
  if (!passed && !failed)
27107
27133
  return null;
27108
27134
  return { tests_passed: int(passed), tests_failed: int(failed) };
27109
- }
27135
+ },
27136
+ summaries: (out) => Math.max(countLines(out, /^\s*\d+ tests? passed\b/gm), countLines(out, /^\s*\d+ tests? failed\b/gm))
27110
27137
  },
27111
27138
  {
27112
27139
  // uvu: a `Total:` / `Passed:` / `Skipped:` block.
@@ -27119,7 +27146,8 @@ var init_classify2 = __esm({
27119
27146
  const t = int(total);
27120
27147
  const p = int(passed);
27121
27148
  return { tests_passed: p, tests_failed: Math.max(0, t - p) };
27122
- }
27149
+ },
27150
+ summaries: (out) => countLines(out, /^\s*Total:\s+\d+/gm)
27123
27151
  },
27124
27152
  {
27125
27153
  /**
@@ -27154,6 +27182,7 @@ var init_classify2 = __esm({
27154
27182
  * those would double them against their parent.
27155
27183
  */
27156
27184
  runner: "bare TAP",
27185
+ shadowedBy: ["node --test (TAP)", "node-tap"],
27157
27186
  read: (out) => {
27158
27187
  let n = 0;
27159
27188
  let failed = 0;
@@ -30899,9 +30928,9 @@ function renderVerdictLine(r) {
30899
30928
  const t = fmtMs(r.wallMs);
30900
30929
  switch (r.outcome) {
30901
30930
  case "completed":
30902
- return `GREEN ${String(r.counts?.tests_passed ?? 0)} test(s) passed, none failed \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
30931
+ return `GREEN ${String(r.counts?.tests_passed ?? 0)} test(s) passed, none failed ` + (r.counts?.coverage === "partial" ? `(${PARTIAL_COUNTS_NOTE}) ` : "") + `\u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
30903
30932
  case "tests-failed":
30904
- return `RED ${r.counts ? `${String(r.counts.tests_failed)} test(s) failed` : `exit ${String(r.exitCode)}`} \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
30933
+ return `RED ${r.counts ? `${String(r.counts.tests_failed)} test(s) failed` : `exit ${String(r.exitCode)}`}` + (r.counts?.coverage === "partial" ? ` (${PARTIAL_COUNTS_NOTE})` : "") + ` \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
30905
30934
  case "no-tests-observed":
30906
30935
  return `NOTHING RAN the suite reported zero tests passed and zero failed \u2014 ${t}`;
30907
30936
  case "counts-unparsed":
@@ -30955,7 +30984,7 @@ function exitCodeFor(r) {
30955
30984
  return 2;
30956
30985
  return exitCodeForOutcome(r.outcome);
30957
30986
  }
30958
- var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS;
30987
+ var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, PARTIAL_COUNTS_NOTE, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS;
30959
30988
  var init_result = __esm({
30960
30989
  "../../packages/envrun/dist/result.js"() {
30961
30990
  "use strict";
@@ -30978,6 +31007,7 @@ var init_result = __esm({
30978
31007
  "founder-declared"
30979
31008
  ];
30980
31009
  RUN_RESULT_SCHEMA = "terminalhire.verification-run/1";
31010
+ PARTIAL_COUNTS_NOTE = "from one of several test summaries in the output, so not a total for the whole run";
30981
31011
  RUN_RESULT_FIELDS = [
30982
31012
  "schema",
30983
31013
  "runId",
@@ -31023,7 +31053,7 @@ var init_result = __esm({
31023
31053
  testCommand: (r) => r.testCommand === null ? null : `test command ${r.testCommand}`,
31024
31054
  testOutputTail: RENDER_NONE,
31025
31055
  // printed as a block below the fields, when red
31026
- counts: (r) => r.counts === null ? null : `tests ${String(r.counts.tests_passed)} passed, ${String(r.counts.tests_failed)} failed (${r.counts.runner})`,
31056
+ counts: (r) => r.counts === null ? null : `tests ${String(r.counts.tests_passed)} passed, ${String(r.counts.tests_failed)} failed (${r.counts.runner})` + (r.counts.coverage === "partial" ? ` \u2014 ${PARTIAL_COUNTS_NOTE}` : ""),
31027
31057
  wallMs: (r) => `round trip ${fmtMs(r.wallMs)}`,
31028
31058
  installMs: (r) => r.installMs === void 0 ? null : `install step ${fmtMs(r.installMs)}`,
31029
31059
  testMs: (r) => r.testMs === void 0 ? null : `test step ${fmtMs(r.testMs)}`,
@@ -31122,7 +31152,11 @@ function toTestRunResult(result, outputSha256) {
31122
31152
  return {
31123
31153
  exit_code: result.exitCode ?? -1,
31124
31154
  ...outputSha256 === void 0 ? {} : { output_sha256: outputSha256 },
31125
- ...result.counts === null ? {} : { tests_passed: result.counts.tests_passed, tests_failed: result.counts.tests_failed }
31155
+ ...result.counts === null ? {} : {
31156
+ tests_passed: result.counts.tests_passed,
31157
+ tests_failed: result.counts.tests_failed,
31158
+ ...result.counts.coverage === "partial" ? { tests_coverage: "partial" } : {}
31159
+ }
31126
31160
  };
31127
31161
  }
31128
31162
  function toAcceptancePredicate(pair, opts = {}) {
@@ -38574,6 +38608,7 @@ __export(dist_exports, {
38574
38608
  LabelWatch: () => LabelWatch,
38575
38609
  MIN_GIT_VERSION_FOR_END_OF_OPTIONS: () => MIN_GIT_VERSION_FOR_END_OF_OPTIONS,
38576
38610
  OUTCOME_TO_BUDGET: () => OUTCOME_TO_BUDGET,
38611
+ PARTIAL_COUNTS_NOTE: () => PARTIAL_COUNTS_NOTE,
38577
38612
  PATH_REFUSAL_CODES: () => PATH_REFUSAL_CODES,
38578
38613
  PLACEMENTS: () => PLACEMENTS,
38579
38614
  PLACEMENT_ALIASES: () => PLACEMENT_ALIASES,
@@ -582,12 +582,22 @@ function reportedCounts(classified, reparsed) {
582
582
  function readCounts(stdout, stderr = "") {
583
583
  const out = `${stdout}
584
584
  ${stderr}`;
585
+ const matched = [];
585
586
  for (const reader of READERS) {
586
587
  const counts = reader.read(out);
587
588
  if (counts)
588
- return { ...counts, runner: reader.runner };
589
+ matched.push({ reader, counts });
589
590
  }
590
- return null;
591
+ const first = matched[0];
592
+ if (first === void 0)
593
+ return null;
594
+ const legs = matched.filter(({ reader }) => !matched.some((m) => reader.shadowedBy?.includes(m.reader.runner)));
595
+ const partial = legs.length > 1 || (first.reader.summaries?.(out) ?? 1) > 1;
596
+ return {
597
+ ...first.counts,
598
+ runner: first.reader.runner,
599
+ ...partial ? { coverage: "partial" } : {}
600
+ };
591
601
  }
592
602
  function coverageThresholdUnmet(facts, counts) {
593
603
  if (counts === null || counts.runner !== "node-tap")
@@ -746,7 +756,7 @@ function isGreen(outcome) {
746
756
  function isOurFault(outcome) {
747
757
  return LIMIT_OWNER[outcome] === "ours";
748
758
  }
749
- var VERIFICATION_OUTCOMES, int, withSuiteFailures, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, LIMIT_OWNER;
759
+ var VERIFICATION_OUTCOMES, int, withSuiteFailures, countLines, READERS, SUPPORTED_RUNNERS, COVERAGE_TABLE, EXEC_FAILURE, SUITE_REPORTED_FAILURE, MISSING_SYSTEM_DEPENDENCY, OFFLINE_BUILD_GAP, LIMIT_OWNER;
750
760
  var init_classify = __esm({
751
761
  "../../packages/envrun/dist/classify.js"() {
752
762
  "use strict";
@@ -765,6 +775,7 @@ var init_classify = __esm({
765
775
  return counts;
766
776
  return { ...counts, tests_failed: int(/(\d+) failed/.exec(suiteLine[1])) };
767
777
  };
778
+ countLines = (out, re) => (out.match(re) ?? []).length;
768
779
  READERS = [
769
780
  {
770
781
  // `node --test` TAP. Anchored to line start, which is what makes it a
@@ -776,7 +787,10 @@ var init_classify = __esm({
776
787
  if (!pass || !fail2)
777
788
  return null;
778
789
  return { tests_passed: int(pass), tests_failed: int(fail2) };
779
- }
790
+ },
791
+ // One `# pass N` per invocation, so a script that runs `node --test` once per file
792
+ // prints many. envrun's own offline leg printed 67 and was read as the first one's 4.
793
+ summaries: (out) => countLines(out, /^# pass \d+$/gm)
780
794
  },
781
795
  {
782
796
  // node-tap's runner summary, which is NOT the `# pass N` / `# fail N` pair
@@ -803,7 +817,8 @@ var init_classify = __esm({
803
817
  tests_passed: int(/\bpass: (\d+)/.exec(line[1])),
804
818
  tests_failed: int(/\bfail: (\d+)/.exec(line[1]))
805
819
  };
806
- }
820
+ },
821
+ summaries: (out) => countLines(out, /^# \{ total: \d+[^}\n]* \}$/gm)
807
822
  },
808
823
  {
809
824
  // jest: `Tests: 1 failed, 2 passed, 3 total`
@@ -816,7 +831,8 @@ var init_classify = __esm({
816
831
  tests_passed: int(/(\d+) passed/.exec(line[1])),
817
832
  tests_failed: int(/(\d+) failed/.exec(line[1]))
818
833
  }, /^Test Suites:\s+(.+?)\s*$/m.exec(out));
819
- }
834
+ },
835
+ summaries: (out) => countLines(out, /^Tests:\s+[^\n]*\btotal\b/gm)
820
836
  },
821
837
  {
822
838
  // vitest: `Tests 3 passed (3)` / `Tests 1 failed | 2 passed (3)`
@@ -833,7 +849,12 @@ var init_classify = __esm({
833
849
  if (!passed && !failed)
834
850
  return null;
835
851
  return withSuiteFailures({ tests_passed: int(passed), tests_failed: int(failed) }, files);
836
- }
852
+ },
853
+ // One invocation prints one `Test Files` line, and a numeric `Tests` line only when a
854
+ // spec loaded (the every-spec-failed shape above has none). The larger of the two
855
+ // counts invocations without counting a paired block twice; reading `Tests` alone
856
+ // missed an all-failed run that followed a passing one (Codex review).
857
+ summaries: (out) => Math.max(countLines(out, /^\s*Tests\s+[^\n]*\(\d+\)\s*$/gm), countLines(out, /^\s*Test Files\s+[^\n]*\(\d+\)\s*$/gm))
837
858
  },
838
859
  {
839
860
  // pytest summary rule: `===== 297 passed in 0.54s =====`,
@@ -856,7 +877,10 @@ var init_classify = __esm({
856
877
  if (!passed && !failed && !errors)
857
878
  return null;
858
879
  return { tests_passed: int(passed), tests_failed: int(failed) + int(errors) };
859
- }
880
+ },
881
+ // Only FINAL tallies, which carry pytest's `in 0.54s`. The interim banners the reader
882
+ // skips above carry no duration, and counting them would mark every red run partial.
883
+ summaries: (out) => Math.max(1, countLines(out, /^=+ [^\n]*?(?:passed|failed|error|no tests ran)[^\n]*? in [\d.]+s[^\n]*? =+$/gm))
860
884
  },
861
885
  {
862
886
  // Maven surefire's per-module summary (TERM-1122), measured against surefire
@@ -918,7 +942,8 @@ var init_classify = __esm({
918
942
  return null;
919
943
  const failed = Number(m[1]);
920
944
  return { tests_passed: Number(m[2]) - failed, tests_failed: failed };
921
- }
945
+ },
946
+ summaries: (out) => Math.max(1, countLines(out, /^\d+% tests passed, \d+ tests? failed out of \d+$/gm))
922
947
  },
923
948
  {
924
949
  // mocha: ` 440 passing (1s)` and ` 2 failing`
@@ -929,7 +954,8 @@ var init_classify = __esm({
929
954
  if (!passing && !failing)
930
955
  return null;
931
956
  return { tests_passed: int(passing), tests_failed: int(failing) };
932
- }
957
+ },
958
+ summaries: (out) => Math.max(countLines(out, /^\s*\d+ passing\b/gm), countLines(out, /^\s*\d+ failing\b/gm))
933
959
  },
934
960
  {
935
961
  // ava: ` 3 tests passed` / ` 1 test failed`
@@ -940,7 +966,8 @@ var init_classify = __esm({
940
966
  if (!passed && !failed)
941
967
  return null;
942
968
  return { tests_passed: int(passed), tests_failed: int(failed) };
943
- }
969
+ },
970
+ summaries: (out) => Math.max(countLines(out, /^\s*\d+ tests? passed\b/gm), countLines(out, /^\s*\d+ tests? failed\b/gm))
944
971
  },
945
972
  {
946
973
  // uvu: a `Total:` / `Passed:` / `Skipped:` block.
@@ -953,7 +980,8 @@ var init_classify = __esm({
953
980
  const t = int(total);
954
981
  const p = int(passed);
955
982
  return { tests_passed: p, tests_failed: Math.max(0, t - p) };
956
- }
983
+ },
984
+ summaries: (out) => countLines(out, /^\s*Total:\s+\d+/gm)
957
985
  },
958
986
  {
959
987
  /**
@@ -988,6 +1016,7 @@ var init_classify = __esm({
988
1016
  * those would double them against their parent.
989
1017
  */
990
1018
  runner: "bare TAP",
1019
+ shadowedBy: ["node --test (TAP)", "node-tap"],
991
1020
  read: (out) => {
992
1021
  let n = 0;
993
1022
  let failed = 0;
@@ -4733,9 +4762,9 @@ function renderVerdictLine(r) {
4733
4762
  const t = fmtMs(r.wallMs);
4734
4763
  switch (r.outcome) {
4735
4764
  case "completed":
4736
- return `GREEN ${String(r.counts?.tests_passed ?? 0)} test(s) passed, none failed \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
4765
+ return `GREEN ${String(r.counts?.tests_passed ?? 0)} test(s) passed, none failed ` + (r.counts?.coverage === "partial" ? `(${PARTIAL_COUNTS_NOTE}) ` : "") + `\u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
4737
4766
  case "tests-failed":
4738
- return `RED ${r.counts ? `${String(r.counts.tests_failed)} test(s) failed` : `exit ${String(r.exitCode)}`} \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
4767
+ return `RED ${r.counts ? `${String(r.counts.tests_failed)} test(s) failed` : `exit ${String(r.exitCode)}`}` + (r.counts?.coverage === "partial" ? ` (${PARTIAL_COUNTS_NOTE})` : "") + ` \u2014 ${t}${r.preview ? ` \u2014 ${r.preview.url}` : ""}`;
4739
4768
  case "no-tests-observed":
4740
4769
  return `NOTHING RAN the suite reported zero tests passed and zero failed \u2014 ${t}`;
4741
4770
  case "counts-unparsed":
@@ -4789,7 +4818,7 @@ function exitCodeFor(r) {
4789
4818
  return 2;
4790
4819
  return exitCodeForOutcome(r.outcome);
4791
4820
  }
4792
- var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS;
4821
+ var RUN_TEST_COMMAND_SOURCES, RUN_IMAGE_SOURCES, RUN_RESULT_SCHEMA, PARTIAL_COUNTS_NOTE, RUN_RESULT_FIELDS, RENDER_NONE, FIELD_VIEWS;
4793
4822
  var init_result = __esm({
4794
4823
  "../../packages/envrun/dist/result.js"() {
4795
4824
  "use strict";
@@ -4812,6 +4841,7 @@ var init_result = __esm({
4812
4841
  "founder-declared"
4813
4842
  ];
4814
4843
  RUN_RESULT_SCHEMA = "terminalhire.verification-run/1";
4844
+ PARTIAL_COUNTS_NOTE = "from one of several test summaries in the output, so not a total for the whole run";
4815
4845
  RUN_RESULT_FIELDS = [
4816
4846
  "schema",
4817
4847
  "runId",
@@ -4857,7 +4887,7 @@ var init_result = __esm({
4857
4887
  testCommand: (r) => r.testCommand === null ? null : `test command ${r.testCommand}`,
4858
4888
  testOutputTail: RENDER_NONE,
4859
4889
  // printed as a block below the fields, when red
4860
- counts: (r) => r.counts === null ? null : `tests ${String(r.counts.tests_passed)} passed, ${String(r.counts.tests_failed)} failed (${r.counts.runner})`,
4890
+ counts: (r) => r.counts === null ? null : `tests ${String(r.counts.tests_passed)} passed, ${String(r.counts.tests_failed)} failed (${r.counts.runner})` + (r.counts.coverage === "partial" ? ` \u2014 ${PARTIAL_COUNTS_NOTE}` : ""),
4861
4891
  wallMs: (r) => `round trip ${fmtMs(r.wallMs)}`,
4862
4892
  installMs: (r) => r.installMs === void 0 ? null : `install step ${fmtMs(r.installMs)}`,
4863
4893
  testMs: (r) => r.testMs === void 0 ? null : `test step ${fmtMs(r.testMs)}`,
@@ -4956,7 +4986,11 @@ function toTestRunResult(result, outputSha256) {
4956
4986
  return {
4957
4987
  exit_code: result.exitCode ?? -1,
4958
4988
  ...outputSha256 === void 0 ? {} : { output_sha256: outputSha256 },
4959
- ...result.counts === null ? {} : { tests_passed: result.counts.tests_passed, tests_failed: result.counts.tests_failed }
4989
+ ...result.counts === null ? {} : {
4990
+ tests_passed: result.counts.tests_passed,
4991
+ tests_failed: result.counts.tests_failed,
4992
+ ...result.counts.coverage === "partial" ? { tests_coverage: "partial" } : {}
4993
+ }
4960
4994
  };
4961
4995
  }
4962
4996
  function toAcceptancePredicate(pair, opts = {}) {
@@ -12583,6 +12617,7 @@ __export(dist_exports, {
12583
12617
  LabelWatch: () => LabelWatch,
12584
12618
  MIN_GIT_VERSION_FOR_END_OF_OPTIONS: () => MIN_GIT_VERSION_FOR_END_OF_OPTIONS,
12585
12619
  OUTCOME_TO_BUDGET: () => OUTCOME_TO_BUDGET,
12620
+ PARTIAL_COUNTS_NOTE: () => PARTIAL_COUNTS_NOTE,
12586
12621
  PATH_REFUSAL_CODES: () => PATH_REFUSAL_CODES,
12587
12622
  PLACEMENTS: () => PLACEMENTS,
12588
12623
  PLACEMENT_ALIASES: () => PLACEMENT_ALIASES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalhire",
3
- "version": "0.42.36",
3
+ "version": "0.42.37",
4
4
  "description": "Developer jobs and paid bounties, matched in the terminal — ambient matches in the Claude Code spinner. Claim a scoped bounty and get paid when the poster accepts it.",
5
5
  "repository": {
6
6
  "type": "git",