@bilalimamoglu/sift 0.3.3 → 0.4.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.js CHANGED
@@ -61,7 +61,125 @@ function evaluateGate(args) {
61
61
 
62
62
  // src/core/testStatusDecision.ts
63
63
  import { z } from "zod";
64
- var TEST_STATUS_DIAGNOSE_JSON_CONTRACT = '{"status":"ok|insufficient","diagnosis_complete":boolean,"raw_needed":boolean,"additional_source_read_likely_low_value":boolean,"read_raw_only_if":string|null,"decision":"stop|zoom|read_source|read_raw","primary_suspect_kind":"test|app_code|config|environment|tooling|unknown","confidence_reason":string,"dominant_blocker_bucket_index":number|null,"provider_used":boolean,"provider_confidence":number|null,"provider_failed":boolean,"raw_slice_used":boolean,"raw_slice_strategy":"none|bucket_evidence|traceback_window|head_tail","resolved_summary":{"count":number,"families":[{"prefix":string,"count":number}]},"remaining_summary":{"count":number,"families":[{"prefix":string,"count":number}]},"remaining_subset_available":boolean,"main_buckets":[{"bucket_index":number,"label":string,"count":number,"root_cause":string,"suspect_kind":"test|app_code|config|environment|tooling|unknown","fix_hint":string,"evidence":string[],"bucket_confidence":number,"root_cause_confidence":number,"dominant":boolean,"secondary_visible_despite_blocker":boolean,"mini_diff":{"added_paths"?:number,"removed_models"?:number,"changed_task_mappings"?:number}|null}],"read_targets":[{"file":string,"line":number|null,"why":string,"bucket_index":number,"context_hint":{"start_line":number|null,"end_line":number|null,"search_hint":string|null}}],"next_best_action":{"code":"fix_dominant_blocker|read_source_for_bucket|read_raw_for_exact_traceback|insufficient_signal","bucket_index":number|null,"note":string},"resolved_tests"?:string[],"remaining_tests"?:string[]}';
64
+
65
+ // src/core/testStatusTargets.ts
66
+ function unique(values) {
67
+ return [...new Set(values)];
68
+ }
69
+ function normalizeTestId(value) {
70
+ return value.replace(/\\/g, "/").replace(/\s+/g, " ").trim();
71
+ }
72
+ function stripMatcherProse(value) {
73
+ return value.replace(/\s+-\s+.*$/, "").trim();
74
+ }
75
+ function extractJsFile(value) {
76
+ const match = value.match(/([A-Za-z0-9_./-]+\.(?:test|spec)\.[cm]?[jt]sx?)/i);
77
+ return match ? normalizeTestId(match[1]) : null;
78
+ }
79
+ function normalizeFailingTarget(label, runner) {
80
+ const normalized = normalizeTestId(label).replace(/^['"]|['"]$/g, "");
81
+ if (runner === "pytest") {
82
+ return stripMatcherProse(normalized);
83
+ }
84
+ if (runner === "vitest" || runner === "jest") {
85
+ const compact = normalized.replace(/^FAIL\s+/i, "").replace(/^[❯×]\s*/, "").replace(/\s+\[[^\]]+\]\s*$/, "").trim();
86
+ const file = extractJsFile(compact);
87
+ if (!file) {
88
+ return stripMatcherProse(compact);
89
+ }
90
+ const fileIndex = compact.indexOf(file);
91
+ const suffix = compact.slice(fileIndex + file.length).trim();
92
+ if (!suffix) {
93
+ return file;
94
+ }
95
+ if (suffix.startsWith(">")) {
96
+ const testName = stripMatcherProse(suffix.replace(/^>\s*/, ""));
97
+ return testName.length > 0 ? `${file} > ${testName}` : file;
98
+ }
99
+ return file;
100
+ }
101
+ return normalized;
102
+ }
103
+ function extractFamilyPrefix(value) {
104
+ const normalized = normalizeTestId(value);
105
+ const filePart = normalized.split("::")[0]?.split(" > ")[0]?.trim() ?? normalized;
106
+ const workflowMatch = filePart.match(/^(\.github\/workflows\/)/);
107
+ if (workflowMatch) {
108
+ return workflowMatch[1];
109
+ }
110
+ const testsMatch = filePart.match(/^((?:test|tests)\/[^/]+\/)/);
111
+ if (testsMatch) {
112
+ return testsMatch[1];
113
+ }
114
+ const srcMatch = filePart.match(/^(src\/[^/]+\/)/);
115
+ if (srcMatch) {
116
+ return srcMatch[1];
117
+ }
118
+ const configMatch = filePart.match(
119
+ /^((?:[^/]+\/)*(?:package\.json|pytest\.ini|pyproject\.toml|tox\.ini|conftest\.py|(?:vitest|jest)\.config\.[^/]+|tsconfig(?:\.[^/]+)?\.json|[^/]*config[^/]*\.(?:json|ya?ml)))$/i
120
+ );
121
+ if (configMatch) {
122
+ return configMatch[1];
123
+ }
124
+ const segments = filePart.replace(/^\/+/, "").split("/").filter(Boolean);
125
+ if (segments.length >= 2) {
126
+ return `${segments[0]}/${segments[1]}/`;
127
+ }
128
+ if (segments.length === 1) {
129
+ return segments[0];
130
+ }
131
+ return "other";
132
+ }
133
+ function buildTestTargetSummary(values) {
134
+ const uniqueValues = unique(values);
135
+ const counts = /* @__PURE__ */ new Map();
136
+ for (const value of uniqueValues) {
137
+ const prefix = extractFamilyPrefix(value);
138
+ counts.set(prefix, (counts.get(prefix) ?? 0) + 1);
139
+ }
140
+ const families = [...counts.entries()].map(([prefix, count]) => ({
141
+ prefix,
142
+ count
143
+ })).sort((left, right) => {
144
+ if (right.count !== left.count) {
145
+ return right.count - left.count;
146
+ }
147
+ return left.prefix.localeCompare(right.prefix);
148
+ }).slice(0, 5);
149
+ return {
150
+ count: uniqueValues.length,
151
+ families
152
+ };
153
+ }
154
+ function formatTargetSummary(summary) {
155
+ if (summary.count === 0) {
156
+ return "count=0";
157
+ }
158
+ const families = summary.families.length > 0 ? summary.families.map((family) => `${family.prefix}${family.count}`).join(", ") : "none";
159
+ return `count=${summary.count}; families=${families}`;
160
+ }
161
+ function joinFamilies(families) {
162
+ if (families.length === 0) {
163
+ return "";
164
+ }
165
+ if (families.length === 1) {
166
+ return families[0];
167
+ }
168
+ if (families.length === 2) {
169
+ return `${families[0]} and ${families[1]}`;
170
+ }
171
+ return `${families.slice(0, -1).join(", ")}, and ${families.at(-1)}`;
172
+ }
173
+ function describeTargetSummary(summary) {
174
+ if (summary.count === 0 || summary.families.length === 0) {
175
+ return null;
176
+ }
177
+ const families = summary.families.map((family) => `${family.prefix} (${family.count})`);
178
+ return `across ${joinFamilies(families)}`;
179
+ }
180
+
181
+ // src/core/testStatusDecision.ts
182
+ var TEST_STATUS_DIAGNOSE_JSON_CONTRACT = '{"status":"ok|insufficient","diagnosis_complete":boolean,"raw_needed":boolean,"additional_source_read_likely_low_value":boolean,"read_raw_only_if":string|null,"decision":"stop|zoom|read_source|read_raw","remaining_mode":"none|subset_rerun|full_rerun_diff","primary_suspect_kind":"test|app_code|config|environment|tooling|unknown","confidence_reason":string,"dominant_blocker_bucket_index":number|null,"provider_used":boolean,"provider_confidence":number|null,"provider_failed":boolean,"raw_slice_used":boolean,"raw_slice_strategy":"none|bucket_evidence|traceback_window|head_tail","resolved_summary":{"count":number,"families":[{"prefix":string,"count":number}]},"remaining_summary":{"count":number,"families":[{"prefix":string,"count":number}]},"remaining_subset_available":boolean,"main_buckets":[{"bucket_index":number,"label":string,"count":number,"root_cause":string,"suspect_kind":"test|app_code|config|environment|tooling|unknown","fix_hint":string,"evidence":string[],"bucket_confidence":number,"root_cause_confidence":number,"dominant":boolean,"secondary_visible_despite_blocker":boolean,"mini_diff":{"added_paths"?:number,"removed_models"?:number,"changed_task_mappings"?:number}|null}],"read_targets":[{"file":string,"line":number|null,"why":string,"bucket_index":number,"context_hint":{"start_line":number|null,"end_line":number|null,"search_hint":string|null}}],"next_best_action":{"code":"fix_dominant_blocker|read_source_for_bucket|read_raw_for_exact_traceback|insufficient_signal","bucket_index":number|null,"note":string},"resolved_tests"?:string[],"remaining_tests"?:string[]}';
65
183
  var TEST_STATUS_PROVIDER_SUPPLEMENT_JSON_CONTRACT = '{"diagnosis_complete":boolean,"raw_needed":boolean,"additional_source_read_likely_low_value":boolean,"read_raw_only_if":string|null,"decision":"stop|zoom|read_source|read_raw","provider_confidence":number|null,"bucket_supplements":[{"label":string,"count":number,"root_cause":string,"anchor":{"file":string|null,"line":number|null,"search_hint":string|null},"fix_hint":string|null,"confidence":number}],"next_best_action":{"code":"fix_dominant_blocker|read_source_for_bucket|read_raw_for_exact_traceback|insufficient_signal","bucket_index":number|null,"note":string}}';
66
184
  var nextBestActionSchema = z.object({
67
185
  code: z.enum([
@@ -103,6 +221,7 @@ var testStatusDiagnoseContractSchema = z.object({
103
221
  additional_source_read_likely_low_value: z.boolean(),
104
222
  read_raw_only_if: z.string().nullable(),
105
223
  decision: z.enum(["stop", "zoom", "read_source", "read_raw"]),
224
+ remaining_mode: z.enum(["none", "subset_rerun", "full_rerun_diff"]),
106
225
  primary_suspect_kind: z.enum([
107
226
  "test",
108
227
  "app_code",
@@ -436,54 +555,127 @@ function extractReasonDetail(reason, prefix) {
436
555
  function formatCount(count, singular, plural = `${singular}s`) {
437
556
  return `${count} ${count === 1 ? singular : plural}`;
438
557
  }
439
- function unique(values) {
558
+ function unique2(values) {
440
559
  return [...new Set(values)];
441
560
  }
442
- function normalizeTestId(value) {
561
+ function normalizeTestId2(value) {
443
562
  return value.replace(/\\/g, "/").trim();
444
563
  }
445
- function extractTestFamilyPrefix(value) {
446
- const normalized = normalizeTestId(value);
447
- const testsMatch = normalized.match(/^(tests\/[^/]+\/)/);
448
- if (testsMatch) {
449
- return testsMatch[1];
564
+ function normalizePathCandidate(value) {
565
+ if (!value) {
566
+ return null;
450
567
  }
451
- const filePart = normalized.split("::")[0]?.trim() ?? "";
452
- if (!filePart.includes("/")) {
453
- return "other";
568
+ let normalized = value.replace(/\\/g, "/").trim();
569
+ normalized = normalized.replace(/^[("'`<\[]+/, "").replace(/[>"'`\]),:;]+$/, "");
570
+ normalized = normalized.replace(/^<repo>\//, "").replace(/^\.\//, "");
571
+ if (normalized.includes("::")) {
572
+ normalized = normalized.split("::")[0]?.trim() ?? normalized;
454
573
  }
455
- const segments = filePart.replace(/^\/+/, "").split("/").filter(Boolean);
456
- if (segments.length === 0) {
457
- return "other";
574
+ if (normalized.startsWith("/") && !normalized.startsWith("/tmp/") && !normalized.startsWith("/var/tmp/")) {
575
+ return null;
576
+ }
577
+ if (/^\.github\/workflows\/.+\.(?:yml|yaml)$/i.test(normalized)) {
578
+ return normalized;
579
+ }
580
+ if (/^(?:src|test|tests)\/.+\.[A-Za-z0-9._-]+$/i.test(normalized)) {
581
+ return normalized;
582
+ }
583
+ if (/^(?:package\.json|pytest\.ini|pyproject\.toml|tox\.ini|(?:[A-Za-z0-9._/-]+\/)?conftest\.py)$/i.test(
584
+ normalized
585
+ )) {
586
+ return normalized;
587
+ }
588
+ if (/^(?:[A-Za-z0-9._/-]+\/)?(?:vitest|jest)\.config\.[A-Za-z0-9._-]+$/i.test(normalized)) {
589
+ return normalized;
590
+ }
591
+ if (/^(?:[A-Za-z0-9._/-]+\/)?tsconfig(?:\.[A-Za-z0-9_-]+)?\.json$/i.test(normalized)) {
592
+ return normalized;
593
+ }
594
+ if (/^[A-Za-z0-9._/-]*config[A-Za-z0-9._/-]*\.(?:json|yml|yaml)$/i.test(normalized)) {
595
+ return normalized;
458
596
  }
459
- return `${segments[0]}/`;
597
+ return null;
460
598
  }
461
- function buildTestTargetSummary(values) {
462
- const counts = /* @__PURE__ */ new Map();
463
- for (const value of values) {
464
- const prefix = extractTestFamilyPrefix(value);
465
- counts.set(prefix, (counts.get(prefix) ?? 0) + 1);
599
+ function addPathCandidatesFromText(target, text) {
600
+ if (!text) {
601
+ return;
466
602
  }
467
- const families = [...counts.entries()].map(([prefix, count]) => ({
468
- prefix,
469
- count
470
- })).sort((left, right) => {
471
- if (right.count !== left.count) {
472
- return right.count - left.count;
603
+ const pattern = /(?:^|[\s("'`])((?:\.github\/workflows\/[A-Za-z0-9._/-]+\.(?:yml|yaml)|(?:src|test|tests)\/[A-Za-z0-9._/-]+\.[A-Za-z0-9._-]+|package\.json|pytest\.ini|pyproject\.toml|tox\.ini|(?:[A-Za-z0-9._/-]+\/)?conftest\.py|(?:[A-Za-z0-9._/-]+\/)?(?:vitest|jest)\.config\.[A-Za-z0-9._-]+|(?:[A-Za-z0-9._/-]+\/)?tsconfig(?:\.[A-Za-z0-9_-]+)?\.json|[A-Za-z0-9._/-]*config[A-Za-z0-9._/-]*\.(?:json|yml|yaml)))/g;
604
+ for (const match of text.matchAll(pattern)) {
605
+ const normalized = normalizePathCandidate(match[1] ?? null);
606
+ if (normalized) {
607
+ target.add(normalized);
608
+ }
609
+ }
610
+ }
611
+ function extractBucketPathCandidates(args) {
612
+ const candidates = /* @__PURE__ */ new Set();
613
+ const push = (value) => {
614
+ const normalized = normalizePathCandidate(value);
615
+ if (normalized) {
616
+ candidates.add(normalized);
473
617
  }
474
- return left.prefix.localeCompare(right.prefix);
475
- }).slice(0, 5);
476
- return {
477
- count: values.length,
478
- families
479
618
  };
619
+ push(args.readTarget?.file);
620
+ for (const item of args.bucket.representativeItems) {
621
+ push(item.file);
622
+ addPathCandidatesFromText(candidates, item.label);
623
+ addPathCandidatesFromText(candidates, item.reason);
624
+ }
625
+ addPathCandidatesFromText(candidates, args.bucket.reason);
626
+ addPathCandidatesFromText(candidates, args.bucket.headline);
627
+ for (const line of args.bucket.summaryLines) {
628
+ addPathCandidatesFromText(candidates, line);
629
+ }
630
+ return [...candidates];
480
631
  }
481
- function formatTargetSummary(summary) {
482
- if (summary.count === 0) {
483
- return "count=0";
632
+ function isConfigPathCandidate(path4) {
633
+ return /^\.github\/workflows\/.+\.(?:yml|yaml)$/i.test(path4) || /^(?:package\.json|pytest\.ini|pyproject\.toml|tox\.ini|(?:[A-Za-z0-9._/-]+\/)?conftest\.py)$/i.test(
634
+ path4
635
+ ) || /^(?:[A-Za-z0-9._/-]+\/)?(?:vitest|jest)\.config\.[A-Za-z0-9._-]+$/i.test(path4) || /^(?:[A-Za-z0-9._/-]+\/)?tsconfig(?:\.[A-Za-z0-9_-]+)?\.json$/i.test(path4) || /^[A-Za-z0-9._/-]*config[A-Za-z0-9._/-]*\.(?:json|yml|yaml)$/i.test(path4);
636
+ }
637
+ function isAppPathCandidate(path4) {
638
+ return path4.startsWith("src/");
639
+ }
640
+ function isTestPathCandidate(path4) {
641
+ return path4.startsWith("test/") || path4.startsWith("tests/");
642
+ }
643
+ function looksLikeMatcherLiteralComparison(detail) {
644
+ return /\bexpected\b[\s\S]*\bto (?:be|contain)\b/i.test(detail);
645
+ }
646
+ function looksLikeGoldenLiteralDrift(detail) {
647
+ return /\\n/.test(detail) || /-\s+(?:Tests|Decision|Likely owner|Next|Stop signal)\b/.test(detail) || /\b(?:node-version|workflow_dispatch|run-name|matrix|registry-url)\b/i.test(detail);
648
+ }
649
+ function isGoldenOutputDriftBucket(bucket) {
650
+ if (bucket.type !== "assertion_failure") {
651
+ return false;
484
652
  }
485
- const families = summary.families.length > 0 ? summary.families.map((family) => `${family.prefix}${family.count}`).join(", ") : "none";
486
- return `count=${summary.count}; families=${families}`;
653
+ const detail = extractReasonDetail(bucket.reason, "assertion failed:") ?? bucket.reason;
654
+ if (!looksLikeMatcherLiteralComparison(detail)) {
655
+ return false;
656
+ }
657
+ if (bucket.reason.startsWith("snapshot mismatch:")) {
658
+ return false;
659
+ }
660
+ if (!looksLikeGoldenLiteralDrift(detail)) {
661
+ return false;
662
+ }
663
+ const candidates = extractBucketPathCandidates({
664
+ bucket
665
+ });
666
+ return candidates.some((candidate) => isConfigPathCandidate(candidate) || isTestPathCandidate(candidate));
667
+ }
668
+ function specializeBucket(bucket) {
669
+ if (!isGoldenOutputDriftBucket(bucket)) {
670
+ return bucket;
671
+ }
672
+ return {
673
+ ...bucket,
674
+ type: "golden_output_drift",
675
+ reason: "golden output drift: expected literal or golden output no longer matches current output",
676
+ labelOverride: "golden output drift",
677
+ hint: "Update the expected literal or golden output if the new output is intentional; otherwise fix the generated output and rerun."
678
+ };
487
679
  }
488
680
  function classifyGenericBucketType(reason) {
489
681
  const extended = findExtendedBucketSpec(reason);
@@ -508,6 +700,9 @@ function classifyGenericBucketType(reason) {
508
700
  if (reason.startsWith("missing module:")) {
509
701
  return "import_dependency_failure";
510
702
  }
703
+ if (reason.startsWith("golden output drift:")) {
704
+ return "golden_output_drift";
705
+ }
511
706
  if (reason.startsWith("assertion failed:")) {
512
707
  return "assertion_failure";
513
708
  }
@@ -660,7 +855,7 @@ function mergeBucketDetails(existing, incoming) {
660
855
  count,
661
856
  confidence: Math.max(existing.confidence, incoming.confidence),
662
857
  representativeItems,
663
- entities: unique([...existing.entities, ...incoming.entities]),
858
+ entities: unique2([...existing.entities, ...incoming.entities]),
664
859
  hint: existing.hint ?? incoming.hint,
665
860
  overflowCount: Math.max(
666
861
  existing.overflowCount,
@@ -852,6 +1047,9 @@ function labelForBucket(bucket) {
852
1047
  if (bucket.type === "import_dependency_failure") {
853
1048
  return "import dependency failure";
854
1049
  }
1050
+ if (bucket.type === "golden_output_drift") {
1051
+ return "golden output drift";
1052
+ }
855
1053
  if (bucket.type === "assertion_failure") {
856
1054
  return "assertion failure";
857
1055
  }
@@ -886,6 +1084,9 @@ function rootCauseConfidenceFor(bucket) {
886
1084
  if (bucket.type === "contract_snapshot_drift") {
887
1085
  return bucket.entities.length > 0 ? 0.92 : 0.76;
888
1086
  }
1087
+ if (bucket.type === "golden_output_drift") {
1088
+ return 0.78;
1089
+ }
889
1090
  if (bucket.source === "provider") {
890
1091
  return Math.max(0.6, Math.min(bucket.confidence, 0.82));
891
1092
  }
@@ -960,6 +1161,9 @@ function buildReadTargetWhy(args) {
960
1161
  if (args.bucket.type === "import_dependency_failure") {
961
1162
  return "it is the first visible failing module in this missing dependency bucket";
962
1163
  }
1164
+ if (args.bucket.type === "golden_output_drift") {
1165
+ return "it is the first visible golden or literal drift anchor for this bucket";
1166
+ }
963
1167
  if (args.bucket.type === "assertion_failure") {
964
1168
  return "it is the first visible failing test in this bucket";
965
1169
  }
@@ -1037,6 +1241,9 @@ function buildReadTargetSearchHint(bucket, anchor) {
1037
1241
  if (assertionText) {
1038
1242
  return assertionText;
1039
1243
  }
1244
+ if (bucket.type === "golden_output_drift") {
1245
+ return bucket.representativeItems.map((item) => item.reason.match(/^assertion failed:\s+(.+)$/)?.[1] ?? item.reason).find(Boolean) ?? anchor.label.split("::")[1]?.trim() ?? null;
1246
+ }
1040
1247
  if (bucket.reason.startsWith("unknown ")) {
1041
1248
  return anchor.reason;
1042
1249
  }
@@ -1091,18 +1298,36 @@ function buildConcreteNextNote(args) {
1091
1298
  }
1092
1299
  const lead = primaryTarget.context_hint.start_line !== null && primaryTarget.context_hint.end_line !== null ? `Read ${primaryTarget.file} lines ${primaryTarget.context_hint.start_line}-${primaryTarget.context_hint.end_line} first; ${primaryTarget.why}.` : primaryTarget.context_hint.search_hint ? `Search for ${primaryTarget.context_hint.search_hint} in ${primaryTarget.file} first; ${primaryTarget.why}.` : `Read ${formatReadTargetLocation(primaryTarget)} first; ${primaryTarget.why}.`;
1093
1300
  if (args.nextBestAction.code === "fix_dominant_blocker") {
1301
+ if (args.remainingMode === "subset_rerun") {
1302
+ return "Fix the remaining bucket first, then refresh the full-suite truth with sift rerun.";
1303
+ }
1304
+ if (args.remainingMode === "full_rerun_diff") {
1305
+ return "Fix the remaining bucket first. The cached full-suite baseline is still preserved; use sift rerun when you want to refresh it.";
1306
+ }
1094
1307
  if (args.nextBestAction.bucket_index === 1 && args.hasSecondaryVisibleBucket) {
1095
1308
  return "Fix bucket 1 first, then rerun the full suite at standard. Secondary buckets are already visible behind it.";
1096
1309
  }
1097
1310
  return `Fix bucket ${args.nextBestAction.bucket_index ?? 1} first, then rerun the full suite at standard.`;
1098
1311
  }
1099
1312
  if (args.nextBestAction.code === "read_source_for_bucket") {
1313
+ if (args.remainingMode === "subset_rerun") {
1314
+ return "Fix the remaining bucket first, then refresh the full-suite truth with sift rerun.";
1315
+ }
1316
+ if (args.remainingMode === "full_rerun_diff") {
1317
+ return "Fix the remaining bucket first. The cached full-suite baseline is still preserved; use sift rerun when you want to refresh it.";
1318
+ }
1100
1319
  return lead;
1101
1320
  }
1102
1321
  if (args.nextBestAction.code === "insufficient_signal") {
1103
- if (args.nextBestAction.note.startsWith("Provider follow-up failed")) {
1322
+ if (args.nextBestAction.note.startsWith("Provider follow-up")) {
1104
1323
  return args.nextBestAction.note;
1105
1324
  }
1325
+ if (args.remainingMode === "subset_rerun") {
1326
+ return "Fix the remaining bucket first, then refresh the full-suite truth with sift rerun.";
1327
+ }
1328
+ if (args.remainingMode === "full_rerun_diff") {
1329
+ return "Fix the remaining bucket first. The cached full-suite baseline is still preserved; use sift rerun when you want to refresh it.";
1330
+ }
1106
1331
  return `${lead} Then take one deeper sift pass before raw traceback.`;
1107
1332
  }
1108
1333
  return args.nextBestAction.note;
@@ -1111,13 +1336,13 @@ function extractMiniDiff(input, bucket) {
1111
1336
  if (bucket.type !== "contract_snapshot_drift") {
1112
1337
  return null;
1113
1338
  }
1114
- const addedPaths = unique(
1339
+ const addedPaths = unique2(
1115
1340
  [...input.matchAll(/[+-]\s+'(\/api\/[^']+)'/g)].map((match) => match[1])
1116
1341
  ).length;
1117
- const removedModels = unique(
1342
+ const removedModels = unique2(
1118
1343
  [...input.matchAll(/[+-]\s+'([A-Za-z0-9._/-]+-[A-Za-z0-9._-]+)'/g)].map((match) => match[1])
1119
1344
  ).length;
1120
- const changedTaskMappings = unique(
1345
+ const changedTaskMappings = unique2(
1121
1346
  [...input.matchAll(/[+-]\s+'([a-z]+(?:_[a-z0-9]+)+)'/g)].map((match) => match[1])
1122
1347
  ).length;
1123
1348
  if (addedPaths === 0 && removedModels === 0 && changedTaskMappings === 0) {
@@ -1218,7 +1443,7 @@ function pickUnknownAnchor(args) {
1218
1443
  }
1219
1444
  const label = args.kind === "error" ? args.analysis.visibleErrorLabels[0] : args.analysis.visibleFailedLabels[0];
1220
1445
  if (label) {
1221
- const normalizedLabel = normalizeTestId(label);
1446
+ const normalizedLabel = normalizeTestId2(label);
1222
1447
  const fileMatch = normalizedLabel.match(/^([A-Za-z0-9_./-]+\.[A-Za-z0-9]+)\b/);
1223
1448
  const file = fileMatch?.[1] ?? normalizedLabel.split("::")[0] ?? null;
1224
1449
  return {
@@ -1346,16 +1571,29 @@ function buildDecisionLine(contract) {
1346
1571
  }
1347
1572
  return "- Decision: raw only if exact traceback is required.";
1348
1573
  }
1574
+ function buildRemainingPassLine(contract) {
1575
+ if (contract.remaining_mode === "subset_rerun") {
1576
+ return "- Remaining pass: showing only what is still failing from the cached baseline.";
1577
+ }
1578
+ if (contract.remaining_mode === "full_rerun_diff") {
1579
+ return "- Remaining pass: full rerun analyzed against the cached baseline because narrowed rerun is not available for this runner.";
1580
+ }
1581
+ return null;
1582
+ }
1349
1583
  function buildComparisonLines(contract) {
1350
1584
  const lines = [];
1585
+ const resolvedSummary = buildTestTargetSummary(contract.resolved_tests);
1586
+ const remainingSummary = buildTestTargetSummary(contract.remaining_tests);
1351
1587
  if (contract.resolved_tests.length > 0) {
1588
+ const summaryText = describeTargetSummary(resolvedSummary);
1352
1589
  lines.push(
1353
- `- Resolved in this rerun: ${formatCount(contract.resolved_tests.length, "test")} dropped out of the failing set.`
1590
+ `- Resolved in this rerun: ${formatCount(contract.resolved_tests.length, "test")} dropped out of the failing set${summaryText ? ` ${summaryText}` : ""}.`
1354
1591
  );
1355
1592
  }
1356
- if (contract.resolved_tests.length > 0 && contract.remaining_tests.length > 0) {
1593
+ if (contract.remaining_tests.length > 0 && (contract.resolved_tests.length > 0 || contract.remaining_mode !== "none")) {
1594
+ const summaryText = describeTargetSummary(remainingSummary);
1357
1595
  lines.push(
1358
- `- Remaining failing targets: ${formatCount(contract.remaining_tests.length, "test/module", "tests/modules")}.`
1596
+ `- Remaining failing targets: ${formatCount(contract.remaining_tests.length, "test/module", "tests/modules")}${summaryText ? ` ${summaryText}` : ""}.`
1359
1597
  );
1360
1598
  }
1361
1599
  return lines;
@@ -1427,6 +1665,13 @@ function resolveBucketFixHint(args) {
1427
1665
  return "Inspect the first visible anchor for this bucket, apply the smallest fix that explains it, then rerun the full suite at standard.";
1428
1666
  }
1429
1667
  function deriveBucketSuspectKind(args) {
1668
+ const pathCandidates = extractBucketPathCandidates({
1669
+ bucket: args.bucket,
1670
+ readTarget: args.readTarget
1671
+ });
1672
+ const hasConfigCandidate = pathCandidates.some((candidate) => isConfigPathCandidate(candidate));
1673
+ const hasAppCandidate = pathCandidates.some((candidate) => isAppPathCandidate(candidate));
1674
+ const hasTestCandidate = pathCandidates.some((candidate) => isTestPathCandidate(candidate));
1430
1675
  if (args.bucket.type === "shared_environment_blocker" || args.bucket.type === "fixture_guard_failure" || args.bucket.type === "permission_denied_failure" || args.bucket.type === "django_db_access_denied" || args.bucket.type === "network_failure" || args.bucket.type === "service_unavailable" || args.bucket.type === "db_connection_failure" || args.bucket.type === "auth_bypass_absent" || args.bucket.type === "fixture_teardown_failure") {
1431
1676
  return "environment";
1432
1677
  }
@@ -1436,6 +1681,18 @@ function deriveBucketSuspectKind(args) {
1436
1681
  if (args.bucket.type === "contract_snapshot_drift" || args.bucket.type === "snapshot_mismatch" || args.bucket.type === "flaky_test_detected" || args.bucket.type === "xfail_strict_unexpected_pass") {
1437
1682
  return "test";
1438
1683
  }
1684
+ if (args.bucket.type === "golden_output_drift") {
1685
+ if (hasConfigCandidate) {
1686
+ return "config";
1687
+ }
1688
+ if (hasAppCandidate) {
1689
+ return "app_code";
1690
+ }
1691
+ if (hasTestCandidate) {
1692
+ return "test";
1693
+ }
1694
+ return "unknown";
1695
+ }
1439
1696
  if (args.bucket.type === "xdist_worker_crash" || args.bucket.type === "timeout_failure" || args.bucket.type === "async_event_loop_failure" || args.bucket.type === "subprocess_crash_segfault" || args.bucket.type === "memory_error" || args.bucket.type === "resource_leak_warning" || args.bucket.type === "interrupted_run") {
1440
1697
  return "tooling";
1441
1698
  }
@@ -1443,11 +1700,13 @@ function deriveBucketSuspectKind(args) {
1443
1700
  return "unknown";
1444
1701
  }
1445
1702
  if (args.bucket.type === "assertion_failure" || args.bucket.type === "runtime_failure" || args.bucket.type === "type_error_failure" || args.bucket.type === "serialization_encoding_failure") {
1446
- const file = args.readTarget?.file ?? "";
1447
- if (file.startsWith("src/")) {
1703
+ if (hasConfigCandidate) {
1704
+ return "config";
1705
+ }
1706
+ if (hasAppCandidate) {
1448
1707
  return "app_code";
1449
1708
  }
1450
- if (file.startsWith("test/") || file.startsWith("tests/")) {
1709
+ if (hasTestCandidate) {
1451
1710
  return "test";
1452
1711
  }
1453
1712
  return "unknown";
@@ -1500,6 +1759,10 @@ function buildStandardBucketSupport(args) {
1500
1759
  }
1501
1760
  function renderStandard(args) {
1502
1761
  const lines = [...buildOutcomeLines(args.analysis), ...buildComparisonLines(args.contract)];
1762
+ const remainingPassLine = buildRemainingPassLine(args.contract);
1763
+ if (remainingPassLine) {
1764
+ lines.push(remainingPassLine);
1765
+ }
1503
1766
  if (args.contract.main_buckets.length > 0) {
1504
1767
  for (const bucket of args.contract.main_buckets.slice(0, 3)) {
1505
1768
  const rawBucket = args.buckets[bucket.bucket_index - 1];
@@ -1527,13 +1790,19 @@ function renderStandard(args) {
1527
1790
  }
1528
1791
  }
1529
1792
  lines.push(buildDecisionLine(args.contract));
1530
- lines.push(`- Likely owner: ${formatSuspectKindLabel(args.contract.primary_suspect_kind)}`);
1793
+ if (args.contract.main_buckets.length > 0 && args.contract.primary_suspect_kind !== "unknown") {
1794
+ lines.push(`- Likely owner: ${formatSuspectKindLabel(args.contract.primary_suspect_kind)}`);
1795
+ }
1531
1796
  lines.push(`- Next: ${args.contract.next_best_action.note}`);
1532
1797
  lines.push(buildStopSignal(args.contract));
1533
1798
  return lines.join("\n");
1534
1799
  }
1535
1800
  function renderFocused(args) {
1536
1801
  const lines = [...buildOutcomeLines(args.analysis), ...buildComparisonLines(args.contract)];
1802
+ const remainingPassLine = buildRemainingPassLine(args.contract);
1803
+ if (remainingPassLine) {
1804
+ lines.push(remainingPassLine);
1805
+ }
1537
1806
  for (const bucket of args.contract.main_buckets) {
1538
1807
  const rawBucket = args.buckets[bucket.bucket_index - 1];
1539
1808
  lines.push(
@@ -1553,6 +1822,10 @@ function renderFocused(args) {
1553
1822
  }
1554
1823
  function renderVerbose(args) {
1555
1824
  const lines = [...buildOutcomeLines(args.analysis), ...buildComparisonLines(args.contract)];
1825
+ const remainingPassLine = buildRemainingPassLine(args.contract);
1826
+ if (remainingPassLine) {
1827
+ lines.push(remainingPassLine);
1828
+ }
1556
1829
  for (const bucket of args.contract.main_buckets) {
1557
1830
  const rawBucket = args.buckets[bucket.bucket_index - 1];
1558
1831
  lines.push(
@@ -1602,7 +1875,9 @@ function buildTestStatusDiagnoseContract(args) {
1602
1875
  count: residuals.remainingFailed
1603
1876
  })
1604
1877
  ].filter((bucket) => Boolean(bucket));
1605
- const buckets = prioritizeBuckets([...combinedBuckets, ...unknownBuckets]).slice(0, 3);
1878
+ const buckets = prioritizeBuckets(
1879
+ [...combinedBuckets, ...unknownBuckets].map((bucket) => specializeBucket(bucket))
1880
+ ).slice(0, 3);
1606
1881
  const simpleCollectionFailure = args.analysis.collectionErrorCount !== void 0 && args.analysis.collectionItems.length === 0 && buckets.length === 0;
1607
1882
  const dominantBucket = buckets.map((bucket, index) => ({
1608
1883
  bucket,
@@ -1650,9 +1925,9 @@ function buildTestStatusDiagnoseContract(args) {
1650
1925
  mini_diff: extractMiniDiff(args.input, bucket)
1651
1926
  };
1652
1927
  });
1653
- const resolvedTests = unique(args.resolvedTests ?? []);
1654
- const remainingTests = unique(
1655
- args.remainingTests ?? unique([...args.analysis.visibleErrorLabels, ...args.analysis.visibleFailedLabels])
1928
+ const resolvedTests = unique2(args.resolvedTests ?? []);
1929
+ const remainingTests = unique2(
1930
+ args.remainingTests ?? unique2([...args.analysis.visibleErrorLabels, ...args.analysis.visibleFailedLabels])
1656
1931
  );
1657
1932
  const primarySuspectKind = derivePrimarySuspectKind({
1658
1933
  mainBuckets,
@@ -1702,6 +1977,7 @@ function buildTestStatusDiagnoseContract(args) {
1702
1977
  raw_needed: rawNeeded,
1703
1978
  additional_source_read_likely_low_value: diagnosisComplete && !rawNeeded,
1704
1979
  read_raw_only_if: rawNeeded ? "you still need exact traceback lines after focused or verbose detail" : null,
1980
+ remaining_mode: args.remainingMode ?? "none",
1705
1981
  dominant_blocker_bucket_index: dominantBlockerBucketIndex,
1706
1982
  primary_suspect_kind: primarySuspectKind,
1707
1983
  confidence_reason: "Unknown or low-confidence buckets remain; one deeper sift pass is justified.",
@@ -1732,7 +2008,8 @@ function buildTestStatusDiagnoseContract(args) {
1732
2008
  readTargets,
1733
2009
  hasSecondaryVisibleBucket: mainBuckets.some(
1734
2010
  (bucket) => bucket.secondary_visible_despite_blocker
1735
- )
2011
+ ),
2012
+ remainingMode: args.contractOverrides?.remaining_mode ?? baseContract.remaining_mode
1736
2013
  })
1737
2014
  }
1738
2015
  };
@@ -1797,6 +2074,7 @@ function buildTestStatusAnalysisContext(args) {
1797
2074
  `- diagnosis_complete=${args.contract.diagnosis_complete}`,
1798
2075
  `- raw_needed=${args.contract.raw_needed}`,
1799
2076
  `- decision=${args.contract.decision}`,
2077
+ `- remaining_mode=${args.contract.remaining_mode}`,
1800
2078
  `- provider_used=${args.contract.provider_used}`,
1801
2079
  `- provider_failed=${args.contract.provider_failed}`,
1802
2080
  `- raw_slice_strategy=${args.contract.raw_slice_strategy}`,
@@ -2031,7 +2309,7 @@ function detectTestRunner(input) {
2031
2309
  if (/^\s*Test Suites:\s+\d+\s+failed,\s+\d+\s+passed(?:,\s+\d+\s+total)?/m.test(input) || /^\s*Tests:\s+\d+\s+failed,\s+\d+\s+passed(?:,\s+\d+\s+total)?/m.test(input)) {
2032
2310
  return "jest";
2033
2311
  }
2034
- if (/\bpytest\b/i.test(input) || /^\s*=+.*\b\d+\s+failed\b.*=+\s*$/m.test(input) || /\bcollected\s+\d+\s+items\b/i.test(input)) {
2312
+ if (/\bpytest\b/i.test(input) || /^\s*(?:FAILED|ERROR)\s+[A-Za-z0-9_./-]+::[^\n]+$/m.test(input) || /^\s*=+.*\b\d+\s+failed\b.*=+\s*$/m.test(input) || /\bcollected\s+\d+\s+items\b/i.test(input)) {
2035
2313
  return "pytest";
2036
2314
  }
2037
2315
  return "unknown";
@@ -3236,6 +3514,9 @@ function classifyBucketTypeFromReason(reason) {
3236
3514
  if (reason.startsWith("missing module:")) {
3237
3515
  return "import_dependency_failure";
3238
3516
  }
3517
+ if (reason.startsWith("golden output drift:")) {
3518
+ return "golden_output_drift";
3519
+ }
3239
3520
  if (reason.startsWith("assertion failed:")) {
3240
3521
  return "assertion_failure";
3241
3522
  }
@@ -5069,7 +5350,7 @@ function prepareInput(raw, config) {
5069
5350
  function escapeRegExp(value) {
5070
5351
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5071
5352
  }
5072
- function unique2(values) {
5353
+ function unique3(values) {
5073
5354
  return [...new Set(values)];
5074
5355
  }
5075
5356
  var genericBucketSearchTerms = /* @__PURE__ */ new Set([
@@ -5170,7 +5451,7 @@ function extractBucketSearchTerms(args) {
5170
5451
  ...args.bucket.evidence,
5171
5452
  ...args.readTargets.filter((target) => target.bucket_index === args.bucket.bucket_index).flatMap((target) => [target.context_hint.search_hint ?? "", target.file])
5172
5453
  ];
5173
- const prioritized = unique2(
5454
+ const prioritized = unique3(
5174
5455
  sources.flatMap((value) => collectCandidateSearchTerms(value)).filter(isHighSignalSearchTerm)
5175
5456
  ).sort((left, right) => {
5176
5457
  const delta = scoreSearchTerm(right) - scoreSearchTerm(left);
@@ -5182,7 +5463,7 @@ function extractBucketSearchTerms(args) {
5182
5463
  if (prioritized.length > 0) {
5183
5464
  return prioritized.slice(0, 6);
5184
5465
  }
5185
- const fallbackTerms = unique2(
5466
+ const fallbackTerms = unique3(
5186
5467
  [...args.bucket.evidence, args.bucket.root_cause].flatMap((value) => value.split(/->|:/).map((part) => normalizeSearchTerm(part))).filter(isHighSignalSearchTerm)
5187
5468
  );
5188
5469
  return fallbackTerms.slice(0, 4);
@@ -5220,7 +5501,7 @@ function buildLineWindows(args) {
5220
5501
  return [...selected].sort((left, right) => left - right).map((index) => args.lines[index]);
5221
5502
  }
5222
5503
  function buildPriorityLineGroup(args) {
5223
- return unique2([
5504
+ return unique3([
5224
5505
  ...args.indexes.map((index) => args.lines[index]).filter(Boolean),
5225
5506
  ...buildLineWindows(args)
5226
5507
  ]);
@@ -5229,7 +5510,7 @@ function collapseSelectedLines(args) {
5229
5510
  if (args.lines.length === 0) {
5230
5511
  return args.fallback();
5231
5512
  }
5232
- const joined = unique2(args.lines).join("\n").trim();
5513
+ const joined = unique3(args.lines).join("\n").trim();
5233
5514
  if (joined.length === 0) {
5234
5515
  return args.fallback();
5235
5516
  }
@@ -5380,7 +5661,7 @@ function buildTestStatusRawSlice(args) {
5380
5661
  const indexes = lines.map(
5381
5662
  (line, index) => bucketTerms.some((term) => new RegExp(escapeRegExp(term), "i").test(line)) ? index : -1
5382
5663
  ).filter((index) => index >= 0);
5383
- return unique2([
5664
+ return unique3([
5384
5665
  ...indexes.map((index) => lines[index]).filter(Boolean),
5385
5666
  ...buildPriorityLineGroup({
5386
5667
  lines,
@@ -5423,7 +5704,7 @@ function buildTestStatusRawSlice(args) {
5423
5704
  return [
5424
5705
  buildPriorityLineGroup({
5425
5706
  lines,
5426
- indexes: unique2([...searchHintIndexes, ...fileIndexes]),
5707
+ indexes: unique3([...searchHintIndexes, ...fileIndexes]),
5427
5708
  radius,
5428
5709
  maxLines
5429
5710
  })
@@ -5442,7 +5723,7 @@ function buildTestStatusRawSlice(args) {
5442
5723
  const selected = collapseSelectedLineGroups({
5443
5724
  groups: [
5444
5725
  ...targetGroups,
5445
- unique2([
5726
+ unique3([
5446
5727
  ...summaryIndexes.map((index) => lines[index]).filter(Boolean),
5447
5728
  ...buildLineWindows({
5448
5729
  lines,
@@ -5640,6 +5921,34 @@ function hasRecognizableTestStatusSignal(input) {
5640
5921
  const analysis = analyzeTestStatus(input);
5641
5922
  return analysis.collectionErrorCount !== void 0 || analysis.noTestsCollected || analysis.interrupted || analysis.failed > 0 || analysis.errors > 0 || analysis.passed > 0 || analysis.inlineItems.length > 0 || analysis.buckets.length > 0;
5642
5923
  }
5924
+ function shouldUseCompactTestStatusBypass(args) {
5925
+ if (args.request.policyName !== "test-status") {
5926
+ return false;
5927
+ }
5928
+ if (args.request.detail && args.request.detail !== "standard") {
5929
+ return false;
5930
+ }
5931
+ if (args.request.goal === "diagnose" && args.request.format === "json") {
5932
+ return false;
5933
+ }
5934
+ if (args.request.testStatusContext?.resolvedTests?.length || args.request.testStatusContext?.remainingTests?.length || args.request.testStatusContext?.remainingSubsetAvailable || args.request.testStatusContext?.remainingMode && args.request.testStatusContext.remainingMode !== "none") {
5935
+ return false;
5936
+ }
5937
+ return args.analysis.failed === 0 && args.analysis.errors === 0 && args.analysis.passed > 0 || args.analysis.collectionErrorCount !== void 0 && args.analysis.collectionItems.length === 0 && args.analysis.inlineItems.length === 0 && args.analysis.buckets.length === 0 || args.analysis.noTestsCollected || args.analysis.interrupted && args.analysis.failed === 0 && args.analysis.errors === 0;
5938
+ }
5939
+ function sanitizeProviderFailureReason(reason) {
5940
+ const normalized = reason.trim();
5941
+ const httpStatus = normalized.match(/\bHTTP\s+(\d{3})\b/i)?.[1];
5942
+ if (httpStatus) {
5943
+ return `provider follow-up unavailable (HTTP ${httpStatus})`;
5944
+ }
5945
+ if (/unterminated string|invalid json|unexpected token|json at position|schema|zod|parse/i.test(
5946
+ normalized
5947
+ )) {
5948
+ return "provider follow-up returned unusable structured output";
5949
+ }
5950
+ return "provider follow-up failed";
5951
+ }
5643
5952
  function renderTestStatusDecisionOutput(args) {
5644
5953
  if (args.request.goal === "diagnose" && args.request.format === "json") {
5645
5954
  return JSON.stringify(
@@ -5661,6 +5970,7 @@ function renderTestStatusDecisionOutput(args) {
5661
5970
  return args.decision.standardText;
5662
5971
  }
5663
5972
  function buildTestStatusProviderFailureDecision(args) {
5973
+ const sanitizedReason = sanitizeProviderFailureReason(args.reason);
5664
5974
  const concreteReadTarget = args.baseDecision.contract.read_targets.find(
5665
5975
  (target) => Boolean(target.file)
5666
5976
  );
@@ -5673,6 +5983,7 @@ function buildTestStatusProviderFailureDecision(args) {
5673
5983
  analysis: args.analysis,
5674
5984
  resolvedTests: args.baseDecision.contract.resolved_tests,
5675
5985
  remainingTests: args.baseDecision.contract.remaining_tests,
5986
+ remainingMode: args.request.testStatusContext?.remainingMode,
5676
5987
  contractOverrides: {
5677
5988
  ...args.baseDecision.contract,
5678
5989
  diagnosis_complete: false,
@@ -5688,7 +5999,9 @@ function buildTestStatusProviderFailureDecision(args) {
5688
5999
  next_best_action: {
5689
6000
  code: "read_source_for_bucket",
5690
6001
  bucket_index: args.baseDecision.contract.dominant_blocker_bucket_index ?? concreteReadTarget.bucket_index,
5691
- note: `Provider follow-up failed (${args.reason}). The heuristic anchor is concrete enough to inspect source for the current bucket before reading raw traceback.`
6002
+ note: `${sanitizedReason[0].toUpperCase()}${sanitizedReason.slice(
6003
+ 1
6004
+ )}. The heuristic anchor is concrete enough to inspect source for the current bucket before reading raw traceback.`
5692
6005
  }
5693
6006
  }
5694
6007
  });
@@ -5699,6 +6012,7 @@ function buildTestStatusProviderFailureDecision(args) {
5699
6012
  analysis: args.analysis,
5700
6013
  resolvedTests: args.baseDecision.contract.resolved_tests,
5701
6014
  remainingTests: args.baseDecision.contract.remaining_tests,
6015
+ remainingMode: args.request.testStatusContext?.remainingMode,
5702
6016
  contractOverrides: {
5703
6017
  ...args.baseDecision.contract,
5704
6018
  diagnosis_complete: false,
@@ -5714,7 +6028,11 @@ function buildTestStatusProviderFailureDecision(args) {
5714
6028
  next_best_action: {
5715
6029
  code: shouldZoomFirst ? "insufficient_signal" : "read_raw_for_exact_traceback",
5716
6030
  bucket_index: args.baseDecision.contract.dominant_blocker_bucket_index ?? args.baseDecision.contract.main_buckets[0]?.bucket_index ?? null,
5717
- note: shouldZoomFirst ? `Provider follow-up failed (${args.reason}). Use one deeper sift pass on the same cached output before reading raw traceback lines.` : `Provider follow-up failed (${args.reason}). Read raw traceback only if exact stack lines are still needed.`
6031
+ note: shouldZoomFirst ? `${sanitizedReason[0].toUpperCase()}${sanitizedReason.slice(
6032
+ 1
6033
+ )}. Use one deeper sift pass on the same cached output before reading raw traceback lines.` : `${sanitizedReason[0].toUpperCase()}${sanitizedReason.slice(
6034
+ 1
6035
+ )}. Read raw traceback only if exact stack lines are still needed.`
5718
6036
  }
5719
6037
  }
5720
6038
  });
@@ -5735,23 +6053,28 @@ async function runSiftCore(request, recorder) {
5735
6053
  const provider = createProvider(request.config);
5736
6054
  const hasTestStatusSignal = request.policyName === "test-status" && hasRecognizableTestStatusSignal(heuristicInput);
5737
6055
  const testStatusAnalysis = hasTestStatusSignal ? analyzeTestStatus(heuristicInput) : null;
5738
- const testStatusDecision = hasTestStatusSignal && testStatusAnalysis ? buildTestStatusDiagnoseContract({
6056
+ const useCompactTestStatusOutput = hasTestStatusSignal && testStatusAnalysis ? shouldUseCompactTestStatusBypass({
6057
+ request,
6058
+ analysis: testStatusAnalysis
6059
+ }) : false;
6060
+ const testStatusDecision = hasTestStatusSignal && testStatusAnalysis && !useCompactTestStatusOutput ? buildTestStatusDiagnoseContract({
5739
6061
  input: heuristicInput,
5740
6062
  analysis: testStatusAnalysis,
5741
6063
  resolvedTests: request.testStatusContext?.resolvedTests,
5742
- remainingTests: request.testStatusContext?.remainingTests
6064
+ remainingTests: request.testStatusContext?.remainingTests,
6065
+ remainingMode: request.testStatusContext?.remainingMode
5743
6066
  }) : null;
5744
6067
  const testStatusHeuristicOutput = testStatusDecision ? renderTestStatusDecisionOutput({
5745
6068
  request,
5746
6069
  decision: testStatusDecision
5747
- }) : null;
6070
+ }) : useCompactTestStatusOutput ? applyHeuristicPolicy("test-status", heuristicInput, "standard") : null;
5748
6071
  if (request.config.runtime.verbose) {
5749
6072
  process.stderr.write(
5750
6073
  `${pc.dim("sift")} provider=${provider.name} model=${request.config.provider.model} base_url=${request.config.provider.baseUrl} input_chars=${prepared.meta.finalLength}
5751
6074
  `
5752
6075
  );
5753
6076
  }
5754
- const heuristicOutput = request.policyName === "test-status" ? testStatusDecision?.contract.diagnosis_complete ? testStatusHeuristicOutput : null : applyHeuristicPolicy(request.policyName, heuristicInput, request.detail);
6077
+ const heuristicOutput = request.policyName === "test-status" ? useCompactTestStatusOutput ? testStatusHeuristicOutput : testStatusDecision?.contract.diagnosis_complete ? testStatusHeuristicOutput : null : applyHeuristicPolicy(request.policyName, heuristicInput, request.detail);
5755
6078
  if (heuristicOutput) {
5756
6079
  if (request.config.runtime.verbose) {
5757
6080
  process.stderr.write(`${pc.dim("sift")} heuristic=${request.policyName}
@@ -5875,6 +6198,7 @@ async function runSiftCore(request, recorder) {
5875
6198
  analysis: testStatusAnalysis,
5876
6199
  resolvedTests: request.testStatusContext?.resolvedTests,
5877
6200
  remainingTests: request.testStatusContext?.remainingTests,
6201
+ remainingMode: request.testStatusContext?.remainingMode,
5878
6202
  providerBucketSupplements: supplement.bucket_supplements,
5879
6203
  contractOverrides: {
5880
6204
  diagnosis_complete: supplement.diagnosis_complete,
@@ -6120,6 +6444,7 @@ var failureBucketTypeSchema = z2.enum([
6120
6444
  "import_dependency_failure",
6121
6445
  "collection_failure",
6122
6446
  "assertion_failure",
6447
+ "golden_output_drift",
6123
6448
  "runtime_failure",
6124
6449
  "interrupted_run",
6125
6450
  "no_tests_collected",
@@ -6160,7 +6485,19 @@ var cachedPytestStateSchema = z2.object({
6160
6485
  failingNodeIds: z2.array(z2.string()),
6161
6486
  remainingNodeIds: z2.array(z2.string()).optional()
6162
6487
  }).optional();
6163
- var cachedRunSchema = z2.object({
6488
+ var testRunnerSchema = z2.enum(["pytest", "vitest", "jest", "unknown"]);
6489
+ var cachedRunnerSubsetSchema = z2.object({
6490
+ available: z2.boolean(),
6491
+ strategy: z2.enum(["pytest-node-ids", "none"]),
6492
+ baseArgv: z2.array(z2.string()).min(1).optional()
6493
+ });
6494
+ var cachedRunnerStateSchema = z2.object({
6495
+ name: testRunnerSchema,
6496
+ failingTargets: z2.array(z2.string()),
6497
+ baselineCommand: cachedCommandSchema,
6498
+ subset: cachedRunnerSubsetSchema
6499
+ });
6500
+ var cachedRunV1Schema = z2.object({
6164
6501
  version: z2.literal(1),
6165
6502
  timestamp: z2.string(),
6166
6503
  presetName: z2.literal("test-status"),
@@ -6178,6 +6515,25 @@ var cachedRunSchema = z2.object({
6178
6515
  analysis: cachedAnalysisSchema,
6179
6516
  pytest: cachedPytestStateSchema
6180
6517
  });
6518
+ var cachedRunV2Schema = z2.object({
6519
+ version: z2.literal(2),
6520
+ timestamp: z2.string(),
6521
+ presetName: z2.literal("test-status"),
6522
+ cwd: z2.string(),
6523
+ commandKey: z2.string(),
6524
+ commandPreview: z2.string(),
6525
+ command: cachedCommandSchema,
6526
+ detail: detailSchema,
6527
+ exitCode: z2.number().int(),
6528
+ rawOutput: z2.string(),
6529
+ capture: z2.object({
6530
+ originalChars: countSchema,
6531
+ truncatedApplied: z2.boolean()
6532
+ }),
6533
+ analysis: cachedAnalysisSchema,
6534
+ runner: cachedRunnerStateSchema
6535
+ });
6536
+ var cachedRunSchema = z2.discriminatedUnion("version", [cachedRunV1Schema, cachedRunV2Schema]);
6181
6537
  var MissingCachedTestStatusRunError = class extends Error {
6182
6538
  constructor() {
6183
6539
  super(
@@ -6226,6 +6582,37 @@ function isPytestExecutable(value) {
6226
6582
  function isPythonExecutable(value) {
6227
6583
  return basenameMatches(value, /^python(?:\d+(?:\.\d+)*)?(?:\.exe)?$/i);
6228
6584
  }
6585
+ function detectRunnerFromCommand(command) {
6586
+ if (!command) {
6587
+ return "unknown";
6588
+ }
6589
+ if (command.mode === "argv") {
6590
+ const [first, second, third] = command.argv;
6591
+ if (first && isPytestExecutable(first)) {
6592
+ return "pytest";
6593
+ }
6594
+ if (first && isPythonExecutable(first) && second === "-m" && third === "pytest") {
6595
+ return "pytest";
6596
+ }
6597
+ if (first && basenameMatches(first, /^vitest(?:\.exe)?$/i)) {
6598
+ return "vitest";
6599
+ }
6600
+ if (first && basenameMatches(first, /^jest(?:\.exe)?$/i)) {
6601
+ return "jest";
6602
+ }
6603
+ return "unknown";
6604
+ }
6605
+ if (/\bpython(?:\d+(?:\.\d+)*)?\s+-m\s+pytest\b|\bpytest\b/i.test(command.shellCommand)) {
6606
+ return "pytest";
6607
+ }
6608
+ if (/\bvitest\b/i.test(command.shellCommand)) {
6609
+ return "vitest";
6610
+ }
6611
+ if (/\bjest\b/i.test(command.shellCommand)) {
6612
+ return "jest";
6613
+ }
6614
+ return "unknown";
6615
+ }
6229
6616
  var shortPytestOptionsWithValue = /* @__PURE__ */ new Set([
6230
6617
  "-c",
6231
6618
  "-k",
@@ -6320,26 +6707,52 @@ function buildCachedCommand(args) {
6320
6707
  }
6321
6708
  return void 0;
6322
6709
  }
6323
- function buildFailingNodeIds(analysis) {
6710
+ function buildFailingTargets(analysis) {
6711
+ const runner = analysis.runner;
6324
6712
  const values = [];
6325
6713
  for (const value of [...analysis.visibleErrorLabels, ...analysis.visibleFailedLabels]) {
6326
- if (value.length > 0 && !values.includes(value)) {
6327
- values.push(value);
6714
+ const normalized = normalizeFailingTarget(value, runner);
6715
+ if (normalized.length > 0 && !values.includes(normalized)) {
6716
+ values.push(normalized);
6328
6717
  }
6329
6718
  }
6330
6719
  return values;
6331
6720
  }
6332
- function buildCachedPytestState(args) {
6721
+ function buildCachedRunnerState(args) {
6333
6722
  const baseArgv = args.command?.mode === "argv" && isSubsetCapablePytestArgv(args.command.argv) ? [...args.command.argv] : void 0;
6723
+ const runnerName = args.analysis.runner !== "unknown" ? args.analysis.runner : detectRunnerFromCommand(args.command);
6334
6724
  return {
6335
- subsetCapable: Boolean(baseArgv),
6336
- baseArgv,
6337
- failingNodeIds: buildFailingNodeIds(args.analysis),
6338
- remainingNodeIds: args.remainingNodeIds
6725
+ name: runnerName,
6726
+ failingTargets: buildFailingTargets(args.analysis),
6727
+ baselineCommand: args.command,
6728
+ subset: {
6729
+ available: runnerName === "pytest" && Boolean(baseArgv),
6730
+ strategy: runnerName === "pytest" && baseArgv ? "pytest-node-ids" : "none",
6731
+ ...runnerName === "pytest" && baseArgv ? { baseArgv } : {}
6732
+ }
6339
6733
  };
6340
6734
  }
6735
+ function normalizeCwd(value) {
6736
+ return path2.resolve(value).replace(/\\/g, "/");
6737
+ }
6738
+ function buildTestStatusBaselineIdentity(args) {
6739
+ const cwd = normalizeCwd(args.cwd);
6740
+ const command = args.command ?? buildCachedCommand({
6741
+ shellCommand: args.shellCommand,
6742
+ command: args.shellCommand ? void 0 : args.commandPreview?.split(" ")
6743
+ });
6744
+ const mode = command?.mode ?? (args.shellCommand ? "shell" : "argv");
6745
+ const normalizedCommand = command?.mode === "argv" ? command.argv.join("") : command?.mode === "shell" ? command.shellCommand.trim().replace(/\s+/g, " ") : (args.commandPreview ?? "").trim().replace(/\s+/g, " ");
6746
+ return [cwd, args.runner, mode, normalizedCommand].join("");
6747
+ }
6341
6748
  function buildTestStatusCommandKey(args) {
6342
- return `${args.shellCommand ? "shell" : "argv"}:${args.commandPreview}`;
6749
+ return buildTestStatusBaselineIdentity({
6750
+ cwd: args.cwd ?? process.cwd(),
6751
+ runner: args.runner ?? "unknown",
6752
+ command: args.command,
6753
+ commandPreview: args.commandPreview,
6754
+ shellCommand: args.shellCommand
6755
+ });
6343
6756
  }
6344
6757
  function snapshotTestStatusAnalysis(analysis) {
6345
6758
  return {
@@ -6365,13 +6778,22 @@ function createCachedTestStatusRun(args) {
6365
6778
  command: args.command,
6366
6779
  shellCommand: args.shellCommand
6367
6780
  });
6781
+ const runnerName = args.analysis.runner !== "unknown" ? args.analysis.runner : detectRunnerFromCommand(command);
6782
+ const commandPreview = args.commandPreview ?? args.shellCommand ?? (args.command ?? []).join(" ");
6783
+ const commandKey = args.commandKey ?? buildTestStatusBaselineIdentity({
6784
+ cwd: args.cwd,
6785
+ runner: runnerName,
6786
+ command,
6787
+ commandPreview,
6788
+ shellCommand: args.shellCommand
6789
+ });
6368
6790
  return {
6369
- version: 1,
6791
+ version: 2,
6370
6792
  timestamp: args.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
6371
6793
  presetName: "test-status",
6372
6794
  cwd: args.cwd,
6373
- commandKey: args.commandKey,
6374
- commandPreview: args.commandPreview,
6795
+ commandKey,
6796
+ commandPreview,
6375
6797
  command,
6376
6798
  detail: args.detail,
6377
6799
  exitCode: args.exitCode,
@@ -6381,13 +6803,61 @@ function createCachedTestStatusRun(args) {
6381
6803
  truncatedApplied: args.truncatedApplied
6382
6804
  },
6383
6805
  analysis: snapshotTestStatusAnalysis(args.analysis),
6384
- pytest: buildCachedPytestState({
6806
+ runner: buildCachedRunnerState({
6385
6807
  command,
6386
- analysis: args.analysis,
6387
- remainingNodeIds: args.remainingNodeIds
6808
+ analysis: args.analysis
6388
6809
  })
6389
6810
  };
6390
6811
  }
6812
+ function migrateCachedTestStatusRun(state) {
6813
+ if (state.version === 2) {
6814
+ return state;
6815
+ }
6816
+ const runnerFromOutput = detectTestRunner(state.rawOutput);
6817
+ const runner = runnerFromOutput !== "unknown" ? runnerFromOutput : detectRunnerFromCommand(state.command);
6818
+ const storedCommand = state.command;
6819
+ const fallbackBaseArgv = !storedCommand && state.pytest?.baseArgv ? {
6820
+ mode: "argv",
6821
+ argv: [...state.pytest.baseArgv]
6822
+ } : void 0;
6823
+ const baselineCommand = storedCommand ?? fallbackBaseArgv;
6824
+ const commandPreview = state.commandPreview ?? (baselineCommand?.mode === "argv" ? baselineCommand.argv.join(" ") : baselineCommand?.mode === "shell" ? baselineCommand.shellCommand : "");
6825
+ const commandKey = buildTestStatusBaselineIdentity({
6826
+ cwd: state.cwd,
6827
+ runner,
6828
+ command: baselineCommand,
6829
+ commandPreview
6830
+ });
6831
+ return {
6832
+ version: 2,
6833
+ timestamp: state.timestamp,
6834
+ presetName: state.presetName,
6835
+ cwd: state.cwd,
6836
+ commandKey,
6837
+ commandPreview,
6838
+ command: state.command,
6839
+ detail: state.detail,
6840
+ exitCode: state.exitCode,
6841
+ rawOutput: state.rawOutput,
6842
+ capture: state.capture,
6843
+ analysis: state.analysis,
6844
+ runner: {
6845
+ name: runner,
6846
+ failingTargets: [...new Set((state.pytest?.failingNodeIds ?? []).map(
6847
+ (target) => normalizeFailingTarget(target, runner)
6848
+ ))],
6849
+ baselineCommand,
6850
+ subset: {
6851
+ available: runner === "pytest" && Boolean(state.pytest?.baseArgv),
6852
+ strategy: runner === "pytest" && state.pytest?.baseArgv ? "pytest-node-ids" : "none",
6853
+ ...runner === "pytest" && state.pytest?.baseArgv ? {
6854
+ baseArgv: [...state.pytest.baseArgv]
6855
+ } : {}
6856
+ }
6857
+ },
6858
+ ...fallbackBaseArgv ? { runnerMigrationFallbackUsed: true } : {}
6859
+ };
6860
+ }
6391
6861
  function readCachedTestStatusRun(statePath = getDefaultTestStatusStatePath()) {
6392
6862
  let raw = "";
6393
6863
  try {
@@ -6399,7 +6869,7 @@ function readCachedTestStatusRun(statePath = getDefaultTestStatusStatePath()) {
6399
6869
  throw new InvalidCachedTestStatusRunError();
6400
6870
  }
6401
6871
  try {
6402
- return cachedRunSchema.parse(JSON.parse(raw));
6872
+ return migrateCachedTestStatusRun(cachedRunSchema.parse(JSON.parse(raw)));
6403
6873
  } catch {
6404
6874
  throw new InvalidCachedTestStatusRunError();
6405
6875
  }
@@ -6419,15 +6889,7 @@ function writeCachedTestStatusRun(state, statePath = getDefaultTestStatusStatePa
6419
6889
  `, "utf8");
6420
6890
  }
6421
6891
  function buildTargetDelta(args) {
6422
- if (args.previous.presetName !== "test-status" || args.current.presetName !== "test-status" || args.previous.cwd !== args.current.cwd || args.previous.commandKey !== args.current.commandKey) {
6423
- return {
6424
- comparable: false,
6425
- resolved: [],
6426
- remaining: [],
6427
- introduced: []
6428
- };
6429
- }
6430
- if (!args.previous.pytest || !args.current.pytest) {
6892
+ if (args.previous.presetName !== "test-status" || args.current.presetName !== "test-status" || args.previous.cwd !== args.current.cwd || args.previous.commandKey !== args.current.commandKey || args.previous.runner.name !== args.current.runner.name || args.previous.runner.name === "unknown") {
6431
6893
  return {
6432
6894
  comparable: false,
6433
6895
  resolved: [],
@@ -6435,8 +6897,8 @@ function buildTargetDelta(args) {
6435
6897
  introduced: []
6436
6898
  };
6437
6899
  }
6438
- const previousTargets = args.previous.pytest.failingNodeIds;
6439
- const currentTargets = args.current.pytest.failingNodeIds;
6900
+ const previousTargets = args.previous.runner.failingTargets;
6901
+ const currentTargets = args.current.runner.failingTargets;
6440
6902
  const currentTargetSet = new Set(currentTargets);
6441
6903
  const previousTargetSet = new Set(previousTargets);
6442
6904
  return {
@@ -6449,6 +6911,9 @@ function buildTargetDelta(args) {
6449
6911
  function diffTestStatusTargets(args) {
6450
6912
  return buildTargetDelta(args);
6451
6913
  }
6914
+ function isRemainingSubsetAvailable(state) {
6915
+ return state.runner.name === "pytest" && state.runner.subset.available;
6916
+ }
6452
6917
  function diffTestStatusRuns(args) {
6453
6918
  const targetDelta = buildTargetDelta(args);
6454
6919
  const previousBuckets = new Map(
@@ -6458,21 +6923,45 @@ function diffTestStatusRuns(args) {
6458
6923
  args.current.analysis.buckets.map((bucket) => [buildBucketSignature(bucket), bucket])
6459
6924
  );
6460
6925
  const lines = [];
6461
- if (targetDelta.resolved.length > 0) {
6462
- lines.push(
6463
- `- Resolved: ${formatCount3(targetDelta.resolved.length, "failing test/module", "failing tests/modules")} no longer appear${appendPreview(targetDelta.resolved)}.`
6464
- );
6465
- }
6466
- if (targetDelta.remaining.length > 0) {
6467
- lines.push(
6468
- `- Remaining: ${formatCount3(targetDelta.remaining.length, "failing test/module", "failing tests/modules")} still appear${appendPreview(targetDelta.remaining)}.`
6469
- );
6470
- }
6471
- if (targetDelta.introduced.length > 0) {
6926
+ const resolvedSummary = buildTestTargetSummary(targetDelta.resolved);
6927
+ const remainingSummary = buildTestTargetSummary(targetDelta.remaining);
6928
+ const introducedSummary = buildTestTargetSummary(targetDelta.introduced);
6929
+ const pushTargetLine = (args2) => {
6930
+ if (args2.summary.count === 0) {
6931
+ return;
6932
+ }
6933
+ const summaryText = describeTargetSummary(args2.summary);
6934
+ if (summaryText) {
6935
+ lines.push(
6936
+ `- ${args2.kind}: ${formatCount3(args2.summary.count, args2.countLabel, `${args2.countLabel}s`)} ${args2.verb} ${summaryText}.`
6937
+ );
6938
+ return;
6939
+ }
6472
6940
  lines.push(
6473
- `- New: ${formatCount3(targetDelta.introduced.length, "failing test/module", "failing tests/modules")} appeared${appendPreview(targetDelta.introduced)}.`
6941
+ `- ${args2.kind}: ${formatCount3(args2.summary.count, args2.countLabel, `${args2.countLabel}s`)} ${args2.verb}${appendPreview(args2.fallbackValues)}.`
6474
6942
  );
6475
- }
6943
+ };
6944
+ pushTargetLine({
6945
+ kind: "Resolved",
6946
+ summary: resolvedSummary,
6947
+ countLabel: "failing target",
6948
+ fallbackValues: targetDelta.resolved,
6949
+ verb: "no longer appear"
6950
+ });
6951
+ pushTargetLine({
6952
+ kind: "Remaining",
6953
+ summary: remainingSummary,
6954
+ countLabel: "failing target",
6955
+ fallbackValues: targetDelta.remaining,
6956
+ verb: "still appear"
6957
+ });
6958
+ pushTargetLine({
6959
+ kind: "New",
6960
+ summary: introducedSummary,
6961
+ countLabel: "failing target",
6962
+ fallbackValues: targetDelta.introduced,
6963
+ verb: "appeared"
6964
+ });
6476
6965
  for (const bucket of args.current.analysis.buckets) {
6477
6966
  const signature = buildBucketSignature(bucket);
6478
6967
  const previous = previousBuckets.get(signature);
@@ -6500,8 +6989,7 @@ function diffTestStatusRuns(args) {
6500
6989
  }
6501
6990
  }
6502
6991
  return {
6503
- lines: lines.slice(0, 4),
6504
- remainingNodeIds: targetDelta.comparable ? targetDelta.remaining : void 0
6992
+ lines: lines.slice(0, 4)
6505
6993
  };
6506
6994
  }
6507
6995
 
@@ -6634,8 +7122,9 @@ async function runTestStatusWatch(request, cycles) {
6634
7122
  testStatusContext: {
6635
7123
  ...request.testStatusContext,
6636
7124
  resolvedTests: targetDelta?.resolved ?? request.testStatusContext?.resolvedTests,
6637
- remainingTests: targetDelta?.remaining ?? currentRun.pytest?.failingNodeIds ?? request.testStatusContext?.remainingTests,
6638
- remainingSubsetAvailable: request.testStatusContext?.remainingSubsetAvailable ?? (Boolean(currentRun.pytest?.subsetCapable) && (currentRun.pytest?.failingNodeIds.length ?? 0) > 0)
7125
+ remainingTests: targetDelta?.remaining ?? currentRun.runner.failingTargets ?? request.testStatusContext?.remainingTests,
7126
+ remainingSubsetAvailable: request.testStatusContext?.remainingSubsetAvailable ?? (isRemainingSubsetAvailable(currentRun) && currentRun.runner.failingTargets.length > 0),
7127
+ remainingMode: request.testStatusContext?.remainingMode ?? "none"
6639
7128
  }
6640
7129
  });
6641
7130
  if (request.goal === "diagnose" && request.format === "json") {
@@ -6782,8 +7271,10 @@ async function runExec(request) {
6782
7271
  const shellPath = process.env.SHELL || "/bin/bash";
6783
7272
  const commandPreview = buildCommandPreview(request);
6784
7273
  const commandCwd = request.cwd ?? process.cwd();
6785
- const shouldCacheTestStatusBase = request.presetName === "test-status" && !request.skipCacheWrite;
6786
- const previousCachedRun = shouldCacheTestStatusBase ? tryReadCachedTestStatusRun() : null;
7274
+ const isTestStatusPreset = request.presetName === "test-status";
7275
+ const readCachedBaseline = isTestStatusPreset && (request.readCachedBaseline ?? true);
7276
+ const writeCachedBaselineRequested = isTestStatusPreset && (request.writeCachedBaseline ?? (request.skipCacheWrite ? false : true));
7277
+ const previousCachedRun = readCachedBaseline ? tryReadCachedTestStatusRun() : null;
6787
7278
  if (request.config.runtime.verbose) {
6788
7279
  process.stderr.write(
6789
7280
  `${pc3.dim("sift")} exec mode=${hasShellCommand ? "shell" : "argv"} command=${commandPreview}
@@ -6841,7 +7332,8 @@ async function runExec(request) {
6841
7332
  const capturedOutput = capture.render();
6842
7333
  const autoWatchDetected = !request.watch && looksLikeWatchStream(capturedOutput);
6843
7334
  const useWatchFlow = Boolean(request.watch) || autoWatchDetected;
6844
- const shouldCacheTestStatus = shouldCacheTestStatusBase && !useWatchFlow;
7335
+ const shouldBuildTestStatusState = isTestStatusPreset && !useWatchFlow;
7336
+ const shouldWriteCachedBaseline = writeCachedBaselineRequested && !useWatchFlow;
6845
7337
  if (request.config.runtime.verbose) {
6846
7338
  process.stderr.write(
6847
7339
  `${pc3.dim("sift")} child_exit=${exitCode} captured_chars=${capture.getTotalChars()} capture_truncated=${capture.wasTruncated()}
@@ -6904,10 +7396,19 @@ async function runExec(request) {
6904
7396
  `);
6905
7397
  return exitCode;
6906
7398
  }
6907
- const analysis = shouldCacheTestStatus ? analyzeTestStatus(capturedOutput) : null;
6908
- let currentCachedRun = shouldCacheTestStatus && analysis ? createCachedTestStatusRun({
7399
+ const analysis = shouldBuildTestStatusState ? analyzeTestStatus(capturedOutput) : null;
7400
+ let currentCachedRun = shouldBuildTestStatusState && analysis ? createCachedTestStatusRun({
6909
7401
  cwd: commandCwd,
6910
7402
  commandKey: buildTestStatusCommandKey({
7403
+ cwd: commandCwd,
7404
+ runner: analysis.runner,
7405
+ command: Array.isArray(request.command) && request.command.length > 0 ? {
7406
+ mode: "argv",
7407
+ argv: [...request.command]
7408
+ } : request.shellCommand ? {
7409
+ mode: "shell",
7410
+ shellCommand: request.shellCommand
7411
+ } : void 0,
6911
7412
  commandPreview,
6912
7413
  shellCommand: request.shellCommand
6913
7414
  }),
@@ -6921,31 +7422,32 @@ async function runExec(request) {
6921
7422
  truncatedApplied: capture.wasTruncated(),
6922
7423
  analysis
6923
7424
  }) : null;
6924
- const targetDelta = request.diff && !request.dryRun && previousCachedRun && currentCachedRun ? diffTestStatusTargets({
7425
+ const targetDelta = (request.diff || request.testStatusContext?.remainingMode === "subset_rerun" || request.testStatusContext?.remainingMode === "full_rerun_diff") && !request.dryRun && previousCachedRun && currentCachedRun ? diffTestStatusTargets({
6925
7426
  previous: previousCachedRun,
6926
7427
  current: currentCachedRun
6927
7428
  }) : null;
6928
7429
  const result = await runSiftWithStats({
6929
7430
  ...request,
6930
7431
  stdin: capturedOutput,
6931
- analysisContext: request.skipCacheWrite && request.presetName === "test-status" ? [
7432
+ analysisContext: request.testStatusContext?.remainingMode && request.testStatusContext.remainingMode !== "none" && request.presetName === "test-status" ? [
6932
7433
  request.analysisContext,
6933
7434
  "Zoom context:",
6934
7435
  "- This pass is remaining-only.",
6935
7436
  "- The full-suite truth already exists from the cached full run.",
6936
7437
  "- Do not reintroduce resolved tests into the diagnosis."
6937
7438
  ].filter((value) => Boolean(value)).join("\n") : request.analysisContext,
6938
- testStatusContext: shouldCacheTestStatus && analysis ? {
7439
+ testStatusContext: shouldBuildTestStatusState && analysis ? {
6939
7440
  ...request.testStatusContext,
6940
7441
  resolvedTests: targetDelta?.resolved ?? request.testStatusContext?.resolvedTests,
6941
- remainingTests: targetDelta?.remaining ?? currentCachedRun?.pytest?.failingNodeIds ?? request.testStatusContext?.remainingTests,
7442
+ remainingTests: targetDelta?.remaining ?? currentCachedRun?.runner.failingTargets ?? request.testStatusContext?.remainingTests,
6942
7443
  remainingSubsetAvailable: request.testStatusContext?.remainingSubsetAvailable ?? Boolean(
6943
- currentCachedRun?.pytest?.subsetCapable && (targetDelta?.remaining ?? currentCachedRun?.pytest?.failingNodeIds ?? []).length > 0
6944
- )
7444
+ currentCachedRun && isRemainingSubsetAvailable(currentCachedRun) && (targetDelta?.remaining ?? currentCachedRun?.runner.failingTargets ?? []).length > 0
7445
+ ),
7446
+ remainingMode: request.testStatusContext?.remainingMode ?? "none"
6945
7447
  } : request.testStatusContext
6946
7448
  });
6947
7449
  let output = result.output;
6948
- if (shouldCacheTestStatus) {
7450
+ if (shouldBuildTestStatusState) {
6949
7451
  if (isInsufficientSignalOutput(output)) {
6950
7452
  output = buildInsufficientSignalOutput({
6951
7453
  presetName: request.presetName,
@@ -6960,26 +7462,12 @@ async function runExec(request) {
6960
7462
  previous: previousCachedRun,
6961
7463
  current: currentCachedRun
6962
7464
  });
6963
- currentCachedRun = createCachedTestStatusRun({
6964
- cwd: commandCwd,
6965
- commandKey: currentCachedRun.commandKey,
6966
- commandPreview,
6967
- command: request.command,
6968
- shellCommand: request.shellCommand,
6969
- detail: request.detail ?? "standard",
6970
- exitCode,
6971
- rawOutput: capturedOutput,
6972
- originalChars: capture.getTotalChars(),
6973
- truncatedApplied: capture.wasTruncated(),
6974
- analysis,
6975
- remainingNodeIds: delta.remainingNodeIds
6976
- });
6977
7465
  if (delta.lines.length > 0) {
6978
7466
  output = `${delta.lines.join("\n")}
6979
7467
  ${output}`;
6980
7468
  }
6981
7469
  }
6982
- if (currentCachedRun) {
7470
+ if (currentCachedRun && shouldWriteCachedBaseline) {
6983
7471
  try {
6984
7472
  writeCachedTestStatusRun(currentCachedRun);
6985
7473
  } catch (error) {