@skyramp/mcp 0.3.6 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/build/prompts/code-reuse.js +17 -2
  2. package/build/prompts/enhance-assertions/sharedAssertionRules.js +1 -1
  3. package/build/prompts/modularization/ui-test-modularization.js +9 -6
  4. package/build/prompts/pom-aware-code-reuse.js +1 -1
  5. package/build/prompts/shared-helper-policy.js +5 -5
  6. package/build/prompts/test-recommendation/diffExecutionPlan.js +40 -9
  7. package/build/prompts/test-recommendation/fullRepoCatalog.js +3 -2
  8. package/build/prompts/test-recommendation/recommendationSections.js +3 -3
  9. package/build/prompts/test-recommendation/scopeAssessment.d.ts +2 -2
  10. package/build/prompts/test-recommendation/scopeAssessment.js +58 -76
  11. package/build/prompts/test-recommendation/test-recommendation-prompt.js +14 -1
  12. package/build/prompts/testbot/testbot-prompts.js +25 -8
  13. package/build/recommendation/budgeters/diversityBalancedBudgeter.js +39 -5
  14. package/build/recommendation/budgeters/shared.d.ts +21 -8
  15. package/build/recommendation/budgeters/shared.js +134 -56
  16. package/build/recommendation/planRanker.d.ts +5 -3
  17. package/build/recommendation/planRanker.js +1 -1
  18. package/build/services/TestGenerationService.js +15 -1
  19. package/build/tools/code-refactor/assertion-state.d.ts +91 -0
  20. package/build/tools/code-refactor/assertion-state.js +375 -0
  21. package/build/tools/code-refactor/codeReuseTool.js +6 -4
  22. package/build/tools/code-refactor/enhanceAssertionsTool.js +73 -18
  23. package/build/tools/code-refactor/retrofit-state.d.ts +53 -0
  24. package/build/tools/code-refactor/retrofit-state.js +162 -0
  25. package/build/tools/code-refactor/reuse-outcome.d.ts +7 -0
  26. package/build/tools/code-refactor/reuse-state.d.ts +9 -0
  27. package/build/tools/code-refactor/reuse-state.js +42 -4
  28. package/build/tools/code-refactor/utils-verify-gates.js +69 -15
  29. package/build/tools/executeSkyrampTestTool.js +19 -14
  30. package/build/tools/submitReportTool.js +87 -9
  31. package/build/tools/test-management/registerTestPlanTool.d.ts +3 -3
  32. package/build/tools/test-management/registerTestPlanTool.js +29 -8
  33. package/build/types/AssertionOutcome.d.ts +68 -0
  34. package/build/types/AssertionOutcome.js +1 -0
  35. package/build/types/RepositoryAnalysis.d.ts +10 -10
  36. package/build/types/ReuseOutcome.d.ts +16 -0
  37. package/build/types/TestRecommendation.d.ts +11 -1
  38. package/build/types/TestRecommendation.js +34 -0
  39. package/build/types/TestTypes.d.ts +4 -0
  40. package/build/types/TestTypes.js +8 -0
  41. package/build/types/TestbotReport.d.ts +13 -0
  42. package/build/types/index.d.ts +1 -1
  43. package/build/utils/AnalysisStateManager.d.ts +20 -7
  44. package/build/utils/assertion-verify/api-shared-lints.d.ts +5 -0
  45. package/build/utils/assertion-verify/api-shared-lints.js +315 -0
  46. package/build/utils/assertion-verify/contract-lints.d.ts +3 -0
  47. package/build/utils/assertion-verify/contract-lints.js +87 -0
  48. package/build/utils/assertion-verify/format.d.ts +5 -0
  49. package/build/utils/assertion-verify/format.js +65 -0
  50. package/build/utils/assertion-verify/helper-imports.d.ts +6 -0
  51. package/build/utils/assertion-verify/helper-imports.js +178 -0
  52. package/build/utils/assertion-verify/index.d.ts +3 -0
  53. package/build/utils/assertion-verify/index.js +7 -0
  54. package/build/utils/assertion-verify/integration-lints.d.ts +3 -0
  55. package/build/utils/assertion-verify/integration-lints.js +36 -0
  56. package/build/utils/assertion-verify/js-regex-blank.d.ts +1 -0
  57. package/build/utils/assertion-verify/js-regex-blank.js +153 -0
  58. package/build/utils/assertion-verify/lint-types.d.ts +33 -0
  59. package/build/utils/assertion-verify/lint-types.js +57 -0
  60. package/build/utils/assertion-verify/marker.d.ts +27 -0
  61. package/build/utils/assertion-verify/marker.js +61 -0
  62. package/build/utils/assertion-verify/metrics.d.ts +30 -0
  63. package/build/utils/assertion-verify/metrics.js +341 -0
  64. package/build/utils/assertion-verify/python-strip.d.ts +6 -0
  65. package/build/utils/assertion-verify/python-strip.js +75 -0
  66. package/build/utils/assertion-verify/strip-dispatch.d.ts +19 -0
  67. package/build/utils/assertion-verify/strip-dispatch.js +42 -0
  68. package/build/utils/assertion-verify/ui-lints.d.ts +8 -0
  69. package/build/utils/assertion-verify/ui-lints.js +244 -0
  70. package/build/utils/assertion-verify/verify.d.ts +61 -0
  71. package/build/utils/assertion-verify/verify.js +215 -0
  72. package/build/utils/featureFlags.d.ts +12 -2
  73. package/build/utils/featureFlags.js +33 -3
  74. package/build/utils/pathSignatures.d.ts +4 -1
  75. package/build/utils/pathSignatures.js +14 -2
  76. package/build/utils/planOnlyMode.d.ts +33 -0
  77. package/build/utils/planOnlyMode.js +40 -0
  78. package/build/utils/reportVerification.d.ts +9 -0
  79. package/build/utils/reportVerification.js +39 -6
  80. package/build/utils/scenarioDrafting.d.ts +1 -1
  81. package/build/utils/scenarioDrafting.js +1 -1
  82. package/build/utils/utils-verify/allow.d.ts +22 -4
  83. package/build/utils/utils-verify/allow.js +8 -2
  84. package/build/utils/utils-verify/call-sites.d.ts +40 -1
  85. package/build/utils/utils-verify/call-sites.js +196 -30
  86. package/build/utils/utils-verify/importers.d.ts +31 -0
  87. package/build/utils/utils-verify/importers.js +78 -0
  88. package/build/utils/utils-verify/index.d.ts +1 -0
  89. package/build/utils/utils-verify/index.js +1 -0
  90. package/build/utils/utils-verify/language-spec.d.ts +13 -2
  91. package/build/utils/utils-verify/language-spec.js +12 -2
  92. package/build/utils/utils-verify/parse.d.ts +31 -3
  93. package/build/utils/utils-verify/parse.js +190 -9
  94. package/build/utils/utils-verify/retrofit-equivalence.d.ts +43 -0
  95. package/build/utils/utils-verify/retrofit-equivalence.js +218 -0
  96. package/build/utils/utils-verify/stage.d.ts +6 -0
  97. package/build/utils/utils-verify/stage.js +12 -2
  98. package/build/utils/utils-verify/verify.d.ts +54 -4
  99. package/build/utils/utils-verify/verify.js +224 -12
  100. package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
  101. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-CZfp96qZ.js → codeMirrorModule-LNgEKtdV.js} +1 -1
  102. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-gpLo02E0.js → defaultSettingsView-Bwr1eMKC.js} +135 -135
  103. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.Bq1r1URj.js → index.-Id052Lr.js} +1 -1
  104. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
  105. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.VEfqi1qN.js → uiMode.BPopbasy.js} +1 -1
  106. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
  107. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  108. package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
  109. package/node_modules/playwright/package.json +1 -1
  110. package/package.json +2 -2
  111. package/build/tools/code-refactor/enhance-state.d.ts +0 -49
  112. package/build/tools/code-refactor/enhance-state.js +0 -109
@@ -12,9 +12,12 @@ import { StateManager, runArtifactDir, getTestsRepoDir, } from "../utils/Analysi
12
12
  import { toolError, testFileMatches } from "../utils/utils.js";
13
13
  import { matchesApprovedPlan } from "../utils/planMatchKeys.js";
14
14
  import { isTestbotEnabled } from "../utils/featureFlags.js";
15
- import { findInvalidSourceCitations, findUnchangedFileClaims, listChangedFiles, listChangedFilesAcross, } from "../utils/reportVerification.js";
15
+ import { findInvalidSourceCitations, findUnchangedFileClaims, listChangedFiles, listChangedFilesAcross, listChangedFilesAbs, } from "../utils/reportVerification.js";
16
+ import { isPlanOnlyMode } from "../utils/planOnlyMode.js";
16
17
  import { getReportLanguage, isEnforcedReportLanguage, findLanguageViolations, findLanguageNearMisses, reportLanguageDisplayName, } from "../utils/reportLanguage.js";
17
- import { rederiveReuseOutcome, reuseChainSkipped, } from "./code-refactor/reuse-state.js";
18
+ import { canonicalTestPath, findAssertionRecordByFileName, rederiveAssertionOutcome, } from "./code-refactor/assertion-state.js";
19
+ import { rederiveReuseOutcome, reuseChainSkipped, samePath, } from "./code-refactor/reuse-state.js";
20
+ import { retrofitGate, } from "./code-refactor/retrofit-state.js";
18
21
  // SKYR-3879 Path B: which testTypes the register-plan checkpoint gates. Mirrors
19
22
  // the generation tools actually wired to planGuard (batch-scenario/integration,
20
23
  // contract) — UI and E2E are on a separate blueprint-grounded pipeline and are
@@ -341,7 +344,8 @@ const issueFoundSchema = z
341
344
  .optional()
342
345
  .describe("Issue severity. critical = feature broken/unusable (e.g. page doesn't load, data corruption). " +
343
346
  "high = incorrect behavior (e.g. wrong calculation, stale data returned). " +
344
- "medium = minor functional gap. low = cosmetic or informational."),
347
+ "medium = minor functional gap. low = cosmetic or informational. " +
348
+ "Floor: a requirement conflict — the PR title/description (or a requirements file it references) states a requirement the implemented behavior contradicts — is 'high' or 'critical', never 'medium' or below."),
345
349
  category: z
346
350
  .nativeEnum(IssueFoundCategory)
347
351
  .describe("Issue classification. bug = a product/code defect, e.g. found by a test or in the diff. " +
@@ -641,7 +645,26 @@ function computeReportMetrics(params) {
641
645
  * gets `chainSkipped: true` (SKYR-4220), the one claim that is exactly about the
642
646
  * reuse tool NOT having run. Applies to every test type: UI tests carry the POM
643
647
  * fields, API tests the shared-helper ones. */
644
- async function attachReuseOutcome(test, outcomes, handOffs) {
648
+ /** Attach the assertion-enhancement summary to a report row.
649
+ *
650
+ * How the row finds its record: records are keyed by the test file's full
651
+ * path, but a report row only carries a file NAME. So the match compares
652
+ * basenames, then checks testType and repository. When more than one record
653
+ * still matches, the row gets NO summary — attaching the wrong spec's
654
+ * numbers is worse than attaching none.
655
+ *
656
+ * The numbers are server-derived and re-computed from the delivered file at
657
+ * report time — the report narrative is LLM-authored, these numbers are not.
658
+ * What a reader can conclude: `executionCount: 0` = generated but never
659
+ * executed; no `assertions` field at all = the enhance tool never ran for
660
+ * the file and it never executed. */
661
+ async function attachAssertionOutcome(test, outcomes, checkouts) {
662
+ const record = findAssertionRecordByFileName(outcomes, test, checkouts);
663
+ if (!record)
664
+ return test;
665
+ return { ...test, assertions: await rederiveAssertionOutcome(record) };
666
+ }
667
+ async function attachReuseOutcome(test, outcomes, handOffs, retrofits = []) {
645
668
  // POM records describe browser specs; a basename collision with an API test's
646
669
  // fileName must not attach them there. A utils-path record is attachable anywhere.
647
670
  const record = outcomes?.[path.basename(test.fileName)];
@@ -652,6 +675,18 @@ async function attachReuseOutcome(test, outcomes, handOffs) {
652
675
  ? record
653
676
  : undefined;
654
677
  const derived = found ? await rederiveReuseOutcome(found) : undefined;
678
+ // Pre-existing generated tests this spec's reuse pass rewired (SKYR-4276 A4): the
679
+ // report names each with its recorded execution, so a reviewer sees that the
680
+ // module became a dependency of code they already owned — and that it still runs.
681
+ if (derived?.helpers && found?.testFilePath) {
682
+ const specPath = found.testFilePath;
683
+ const mine = retrofits.filter((r) => samePath(r.testFile, specPath));
684
+ if (mine.length > 0)
685
+ derived.helpers.retrofits = mine.map((r) => ({
686
+ file: path.basename(r.file),
687
+ ...(r.execution ? { execution: r.execution } : {}),
688
+ }));
689
+ }
655
690
  // The RAW record: a colliding record of any kind means reuse ran for this basename,
656
691
  // and the chain claim must not be made on the filtered view.
657
692
  const chainSkipped = await reuseChainSkipped(test.fileName, test.testType, record, handOffs);
@@ -1036,6 +1071,11 @@ export function registerSubmitReportTool(server) {
1036
1071
  "Add a testMaintenanceDetails entry with the missing field(s), drafted from the execution output you already saw.");
1037
1072
  return errorResult;
1038
1073
  }
1074
+ // SKYR-4276 A4: retrofits (pre-existing generated tests the reuse pass edited)
1075
+ // that still stand in the working tree, resolved once here and attached to the
1076
+ // rows below; an unexecuted one refuses the report (inside the SKYR-3883 block,
1077
+ // which already enumerates the working tree).
1078
+ let retrofitViews = [];
1039
1079
  // SKYR-3883: in a testbot run, refuse to ship a report that claims file work
1040
1080
  // the working tree doesn't reflect. The delivery step can only commit what the
1041
1081
  // agent actually created/edited, so a report claiming otherwise erodes trust
@@ -1071,6 +1111,9 @@ export function registerSubmitReportTool(server) {
1071
1111
  newTests: dedupedNewTests,
1072
1112
  verdicts: stateData.maintenanceVerdicts ?? [],
1073
1113
  primaryRepository: fullState?.metadata?.repository,
1114
+ // SKYR-4250: captured at prompt-render time, since planOnly is a
1115
+ // prompt-only argument (mirrors the SKYR-4185 language capture).
1116
+ planOnly: isPlanOnlyMode(),
1074
1117
  });
1075
1118
  if (unbacked.length > 0) {
1076
1119
  // Offer the attribution-correction path explicitly and show what DID change:
@@ -1096,6 +1139,17 @@ export function registerSubmitReportTool(server) {
1096
1139
  `Files with actual working-tree changes:\n${changedList || " (none)"}`);
1097
1140
  return errorResult;
1098
1141
  }
1142
+ const changedFilesAbs = await listChangedFilesAbs([
1143
+ repoRoot,
1144
+ getTestsRepoDir(),
1145
+ ...Object.values(fullState?.relatedRepos ?? {}).map((section) => section.repositoryPath),
1146
+ ]);
1147
+ const gate = await retrofitGate(stateData, changedFilesAbs, repoRoot);
1148
+ retrofitViews = gate.views;
1149
+ if (gate.refusal) {
1150
+ errorResult = toolError(gate.refusal);
1151
+ return errorResult;
1152
+ }
1099
1153
  }
1100
1154
  catch (err) {
1101
1155
  // Never block a valid report because verification itself failed (path not a
@@ -1133,7 +1187,16 @@ export function registerSubmitReportTool(server) {
1133
1187
  // generation — downstream scoring scripts don't expect them and fail if
1134
1188
  // they encounter these string fields while traversing the object.
1135
1189
  // Also normalize each item's `repository` (blank → undefined).
1136
- const sanitizedNewTests = await Promise.all(dedupedNewTests.map(({ scenarioFile: _sf, traceFile: _tf, frontendTrace: _ft, ...rest }) => attachReuseOutcome(normalizeRepository(rest), stateData.reuseOutcomes, stateData.reuseHandOffs)));
1190
+ // Checkout roots let the assertion-record matcher verify a candidate
1191
+ // record actually lives in the row's repo (basename collisions across
1192
+ // repos must not publish one spec's proof-of-work under another's name).
1193
+ const assertionCheckouts = await stateManager
1194
+ .listRepoCheckouts()
1195
+ .catch(() => []);
1196
+ const sanitizedNewTests = await Promise.all(dedupedNewTests.map(async ({ scenarioFile: _sf, traceFile: _tf, frontendTrace: _ft, ...rest }) => {
1197
+ const row = await attachReuseOutcome(normalizeRepository(rest), stateData.reuseOutcomes, stateData.reuseHandOffs, retrofitViews);
1198
+ return attachAssertionOutcome(row, stateData.assertionOutcomes ?? {}, assertionCheckouts);
1199
+ }));
1137
1200
  const report = {
1138
1201
  businessCaseAnalysis: params.businessCaseAnalysis,
1139
1202
  newTestsCreated: sanitizedNewTests,
@@ -1142,10 +1205,25 @@ export function registerSubmitReportTool(server) {
1142
1205
  // an internal-only field, needed for matching but never meant to reach the report.
1143
1206
  // TODO(multi-repo maintenance): map(normalizeRepository) once testMaintenanceSchema
1144
1207
  // has a repository field (see TODO above).
1145
- testMaintenance: testMaintenance?.map(({ testFilePath, ...row }) => ({
1146
- ...row,
1147
- fileName: path.basename(testFilePath),
1148
- })),
1208
+ testMaintenance: testMaintenance
1209
+ ? await Promise.all(testMaintenance.map(async ({ testFilePath, ...row }) => {
1210
+ // Maintenance rows still carry the absolute path here, so the
1211
+ // assertion summary uses an EXACT canonical-path lookup — no
1212
+ // basename ambiguity. This is what carries the maintenance
1213
+ // honesty labels (nothing-to-verify vs verified) into the
1214
+ // report instead of leaving them as tool text the agent can
1215
+ // paraphrase.
1216
+ const record = stateData.assertionOutcomes?.[canonicalTestPath(testFilePath)];
1217
+ const assertions = record
1218
+ ? await rederiveAssertionOutcome(record)
1219
+ : undefined;
1220
+ return {
1221
+ ...row,
1222
+ fileName: path.basename(testFilePath),
1223
+ ...(assertions ? { assertions } : {}),
1224
+ };
1225
+ }))
1226
+ : undefined,
1149
1227
  // videoPath is filled from the run's execution records; testFilePath is the
1150
1228
  // match-only key and is stripped from the wire format, the same line drawn for
1151
1229
  // testMaintenance's own testFilePath above (downstream scoring scripts traverse
@@ -6,7 +6,7 @@ import { ChangedRoute } from "../../utils/changedRoutes.js";
6
6
  declare const registerCandidateSchema: z.ZodObject<{
7
7
  scenarioName: z.ZodString;
8
8
  description: z.ZodString;
9
- category: z.ZodEnum<["new_endpoint", "bug_caught", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
9
+ category: z.ZodEnum<["new_endpoint", "bug_caught", "requirement_conflict", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
10
10
  priority: z.ZodEnum<["high", "medium", "low"]>;
11
11
  testType: z.ZodEffects<z.ZodNativeEnum<typeof TestType>, TestType, TestType>;
12
12
  steps: z.ZodArray<z.ZodObject<{
@@ -124,7 +124,7 @@ declare const registerCandidateSchema: z.ZodObject<{
124
124
  priority: "high" | "medium" | "low";
125
125
  testType: TestType;
126
126
  scenarioName: string;
127
- category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
127
+ category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
128
128
  steps: {
129
129
  path: string;
130
130
  method: HttpMethod;
@@ -160,7 +160,7 @@ declare const registerCandidateSchema: z.ZodObject<{
160
160
  priority: "high" | "medium" | "low";
161
161
  testType: TestType;
162
162
  scenarioName: string;
163
- category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
163
+ category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
164
164
  steps: {
165
165
  path: string;
166
166
  method: HttpMethod;
@@ -12,7 +12,7 @@ import { CandidateSource, computeCandidateId, scenarioMergeKey, DiscriminatorKin
12
12
  import { selectPlan } from "../../recommendation/planRanker.js";
13
13
  import { resolveSubjectEndpoints } from "../../utils/subjectEndpoints.js";
14
14
  import { collectChangedRouteLines } from "../../utils/changedRoutes.js";
15
- import { reservedUISlots } from "../../recommendation/budgeters/shared.js";
15
+ import { generateFloors } from "../../recommendation/budgeters/shared.js";
16
16
  import { inferScenarioType } from "../../recommendation/diversity.js";
17
17
  import { validateDiscriminator } from "../../recommendation/discriminators.js";
18
18
  import { isAttackSurfaceSecurityBoundary } from "../../prompts/test-recommendation/recommendationShared.js";
@@ -76,7 +76,7 @@ const discriminatorClaimSchema = z.object({
76
76
  const registerCandidateSchema = z.object({
77
77
  scenarioName: z.string().min(1).describe("Name for this test/scenario. Reused as the match key against the generation tool's scenarioName — keep it identical when you later call the generation tool."),
78
78
  description: z.string().min(1),
79
- category: z.enum(SCENARIO_CATEGORIES).describe("Test category — drives ranking (never your own priority tag)."),
79
+ category: z.enum(SCENARIO_CATEGORIES).describe("Test category — drives ranking (never your own priority tag). `bug_caught` targets a `<bug_found>` flaw from code review. `requirement_conflict` asserts a requirement the PR title/description (or a requirements file it references) states and the implemented behavior contradicts — it fails on the current code by design. Both rank in the top tier but are carved out independently, so a requirement conflict never loses its slot to a code-review bug."),
80
80
  priority: z.enum(["high", "medium", "low"]).describe("Your own assessment — recorded, but ranking is derived from category, not this field."),
81
81
  testType: z.nativeEnum(TestType).refine((t) => PLAN_TEST_TYPES.has(t), {
82
82
  message: `testType must be one of ${[...PLAN_TEST_TYPES].join(", ")}`,
@@ -340,7 +340,26 @@ function renderGenerationDirective(plan) {
340
340
  (nonUICount > 0 ? ` Generate the ${tests(nonUICount)} of other types as well.` : ""),
341
341
  ];
342
342
  }
343
- function renderPlanText(plan, dropped) {
343
+ /** SKYR-4292: the server cannot count the diff's changed behaviors, so an
344
+ * under-budget plan is flagged (not rejected) with what a missing candidate looks
345
+ * like. Wipro PR #24 registered 3 candidates against a 20-slot UI-only budget and
346
+ * the tool answered "AUTHORITATIVE and FINAL" with no pushback. */
347
+ function renderBudgetCheck(plan, maxTotal) {
348
+ const filled = plan.generate.length + plan.additional.length;
349
+ if (filled >= maxTotal)
350
+ return [];
351
+ return [
352
+ "",
353
+ `### Budget check: ${filled} of ${maxTotal} slots filled`,
354
+ "Every observable behavior this diff changes — each option value added, removed, renamed or reordered; each label, " +
355
+ "element, route, state, validation rule, computed value or style value (spacing, size, color, radius, font weight, " +
356
+ "class name) that renders differently; each removed element, asserted absent — needs a candidate above, however " +
357
+ "small the change. A change is covered only when an existing test asserts the changed value, not when a test merely " +
358
+ "reaches the component. If any changed behavior has no candidate, call skyramp_register_test_plan again with your " +
359
+ "full updated list. If the diff truly changes fewer behaviors than the budget, this plan stands as-is.",
360
+ ];
361
+ }
362
+ function renderPlanText(plan, dropped, maxTotal) {
344
363
  const lines = [];
345
364
  lines.push(`## Approved Test Plan (${plan.planId})`);
346
365
  lines.push("");
@@ -402,6 +421,7 @@ function renderPlanText(plan, dropped) {
402
421
  }
403
422
  lines.push("");
404
423
  lines.push(...renderGenerationDirective(plan));
424
+ lines.push(...renderBudgetCheck(plan, maxTotal));
405
425
  return lines.join("\n");
406
426
  }
407
427
  // ── Tool registration ───────────────────────────────────────────────────────
@@ -544,10 +564,11 @@ export function registerRegisterTestPlanTool(server) {
544
564
  // changes would silently plan zero UI tests (runs 32419025266,
545
565
  // 32431042133, 32431106864) — reject so the agent self-corrects in one
546
566
  // step, exactly like the empty-candidates rejection above.
547
- if (reservedUISlots(budgetContext) > 0 &&
548
- !allCandidates.some((c) => inferScenarioType(c.scenario) === TestType.UI)) {
549
- errorResult = toolError("This run has frontend changes, so the plan reserves a UI slotbut no submitted or recovered " +
550
- "candidate has testType 'ui'. Resubmit your full candidate list including at least one UI candidate " +
567
+ const uiFloor = generateFloors(budgetContext).ui;
568
+ if (uiFloor > 0 && !allCandidates.some((c) => inferScenarioType(c.scenario) === TestType.UI)) {
569
+ errorResult = toolError(`This run has frontend changes, so the plan MUST include UI testsat least ${uiFloor} of the ` +
570
+ "GENERATE slots are held for them, and more are open to UI candidates on rank. No submitted or " +
571
+ "recovered candidate has testType 'ui'. Resubmit your full candidate list including UI candidates " +
551
572
  "for the changed frontend (see uiContext.changedFrontendFiles).");
552
573
  return errorResult;
553
574
  }
@@ -576,7 +597,7 @@ export function registerRegisterTestPlanTool(server) {
576
597
  return errorResult;
577
598
  }
578
599
  return {
579
- content: [{ type: "text", text: renderPlanText(approvedPlan, result.dropped) }],
600
+ content: [{ type: "text", text: renderPlanText(approvedPlan, result.dropped, budgetContext.maxTotal) }],
580
601
  };
581
602
  }
582
603
  catch (error) {
@@ -0,0 +1,68 @@
1
+ import type { TestType } from "./TestTypes.js";
2
+ import type { EnhanceType } from "../prompts/enhance-assertions/sharedAssertionRules.js";
3
+ /**
4
+ * Server-derived assertion-enhancement state. Lives in types/ (not beside
5
+ * assertion-state.ts) because AnalysisStateManager embeds AssertionRecord in
6
+ * the run state while assertion-state.ts imports AnalysisStateManager — same
7
+ * layering as ReuseOutcome.
8
+ */
9
+ /** A verdict a verify pass can produce and store. */
10
+ export type AssertionVerdict = "passed" | "failed";
11
+ /** What the report can say about verification: the stored verdicts plus
12
+ * "never-verified" — the report-time reading of a record that has no verdict
13
+ * yet (instructions were handed out or the spec was executed, but no verify
14
+ * pass ever ran). Only real verdicts are ever STORED (AssertionRecord);
15
+ * "never-verified" exists only on the wire type below. */
16
+ export type AssertionVerification = AssertionVerdict | "never-verified";
17
+ /** One spec's enhancement record in the run state, keyed by the spec's
18
+ * CANONICAL ABSOLUTE PATH (see assertion-state.ts `canonicalTestPath`).
19
+ *
20
+ * Two creation paths, which is why the baseline group is optional while the
21
+ * identity fields are not:
22
+ * - instruction hand-out (recordAssertionBaseline) writes the full record;
23
+ * - execution counting (recordAssertionExecution) creates a minimal record
24
+ * for a spec that executed without ever being enhanced — identity fields
25
+ * plus executionCount, no baseline group.
26
+ */
27
+ export interface AssertionRecord {
28
+ /** Canonical path the record is for — kept in the record as a
29
+ * defense-in-depth ownership check on reads and writes. */
30
+ testFilePath: string;
31
+ testType: TestType;
32
+ /** Times skyramp_execute_test ran this spec (recorded server-side after the
33
+ * gates pass). Proof-of-work substrate: generated-but-never-executed and
34
+ * executed-but-never-enhanced both become visible. */
35
+ executionCount: number;
36
+ /** Absent on execution-only records (no hand-out happened). */
37
+ enhanceType?: EnhanceType;
38
+ /** File hash + metrics at instruction hand-out time — the verify baseline.
39
+ * Present as a group iff a hand-out happened. */
40
+ fileSha256?: string;
41
+ /** Copy of the generated file at hand-out time, saved under the run artifact
42
+ * dir (never the repo, so it cannot leak into the customer commit). Lets the
43
+ * agent diff/restore and gives evals a ground-truth pre-enhancement artifact. */
44
+ baselineFilePath?: string;
45
+ baselineCount?: number;
46
+ baselineStrength?: number;
47
+ baselineFingerprints?: string[];
48
+ issuedAt?: string;
49
+ /** Verdict of the last verify pass; absent until one runs. */
50
+ verification?: AssertionVerdict;
51
+ /** Reason from an accepted `assertions complete` decline marker. */
52
+ declinedReason?: string;
53
+ }
54
+ /** Report-facing proof-of-work summary attached to `newTestsCreated` rows —
55
+ * the compact wire projection of AssertionRecord (which stays internal: it
56
+ * carries fingerprints, hashes, and baseline paths the report must not).
57
+ * Server-derived, re-derived from the delivered file at report time, never
58
+ * supplied by the LLM. A missing `assertions` field on a row means no record
59
+ * exists at all — the enhance tool never ran and the spec never executed. */
60
+ export interface AssertionOutcome {
61
+ verification: AssertionVerification;
62
+ assertionsBaseline?: number;
63
+ assertionsFinal?: number;
64
+ strengthBaseline?: number;
65
+ strengthFinal?: number;
66
+ declinedReason?: string;
67
+ executionCount: number;
68
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1119,7 +1119,7 @@ export declare const scenarioStepSchema: z.ZodObject<{
1119
1119
  export declare const draftedScenarioSchema: z.ZodObject<{
1120
1120
  scenarioName: z.ZodString;
1121
1121
  description: z.ZodString;
1122
- category: z.ZodEnum<["new_endpoint", "bug_caught", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
1122
+ category: z.ZodEnum<["new_endpoint", "bug_caught", "requirement_conflict", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
1123
1123
  priority: z.ZodEnum<["high", "medium", "low"]>;
1124
1124
  steps: z.ZodArray<z.ZodObject<{
1125
1125
  order: z.ZodNumber;
@@ -1231,7 +1231,7 @@ export declare const draftedScenarioSchema: z.ZodObject<{
1231
1231
  description: string;
1232
1232
  priority: "high" | "medium" | "low";
1233
1233
  scenarioName: string;
1234
- category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1234
+ category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1235
1235
  steps: {
1236
1236
  path: string;
1237
1237
  method: string;
@@ -1268,7 +1268,7 @@ export declare const draftedScenarioSchema: z.ZodObject<{
1268
1268
  description: string;
1269
1269
  priority: "high" | "medium" | "low";
1270
1270
  scenarioName: string;
1271
- category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1271
+ category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1272
1272
  steps: {
1273
1273
  path: string;
1274
1274
  method: string;
@@ -1607,7 +1607,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
1607
1607
  draftedScenarios: z.ZodArray<z.ZodObject<{
1608
1608
  scenarioName: z.ZodString;
1609
1609
  description: z.ZodString;
1610
- category: z.ZodEnum<["new_endpoint", "bug_caught", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
1610
+ category: z.ZodEnum<["new_endpoint", "bug_caught", "requirement_conflict", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
1611
1611
  priority: z.ZodEnum<["high", "medium", "low"]>;
1612
1612
  steps: z.ZodArray<z.ZodObject<{
1613
1613
  order: z.ZodNumber;
@@ -1719,7 +1719,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
1719
1719
  description: string;
1720
1720
  priority: "high" | "medium" | "low";
1721
1721
  scenarioName: string;
1722
- category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1722
+ category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1723
1723
  steps: {
1724
1724
  path: string;
1725
1725
  method: string;
@@ -1756,7 +1756,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
1756
1756
  description: string;
1757
1757
  priority: "high" | "medium" | "low";
1758
1758
  scenarioName: string;
1759
- category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1759
+ category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1760
1760
  steps: {
1761
1761
  path: string;
1762
1762
  method: string;
@@ -1799,7 +1799,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
1799
1799
  description: string;
1800
1800
  priority: "high" | "medium" | "low";
1801
1801
  scenarioName: string;
1802
- category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1802
+ category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1803
1803
  steps: {
1804
1804
  path: string;
1805
1805
  method: string;
@@ -1842,7 +1842,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
1842
1842
  description: string;
1843
1843
  priority: "high" | "medium" | "low";
1844
1844
  scenarioName: string;
1845
- category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1845
+ category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
1846
1846
  steps: {
1847
1847
  path: string;
1848
1848
  method: string;
@@ -2743,7 +2743,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
2743
2743
  description: string;
2744
2744
  priority: "high" | "medium" | "low";
2745
2745
  scenarioName: string;
2746
- category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
2746
+ category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
2747
2747
  steps: {
2748
2748
  path: string;
2749
2749
  method: string;
@@ -2956,7 +2956,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
2956
2956
  description: string;
2957
2957
  priority: "high" | "medium" | "low";
2958
2958
  scenarioName: string;
2959
- category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
2959
+ category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
2960
2960
  steps: {
2961
2961
  path: string;
2962
2962
  method: string;
@@ -1,3 +1,4 @@
1
+ import type { TestExecutionStatus } from "./TestExecution.js";
1
2
  /**
2
3
  * The code-reuse observability shape carried in a TestbotReport — the wire contract
3
4
  * between this server and the consumers that render it (test-bot.git). It covers
@@ -85,6 +86,14 @@ export declare enum HelperVerificationOutcome {
85
86
  /** The test neither wrote nor imports a shared utils file. */
86
87
  NoUtilsFile = "no-utils-file"
87
88
  }
89
+ /** A pre-existing Skyramp-generated test the reuse pass edited to import from the
90
+ * shared utils file. `execution` is the recorded post-edit run; a retrofit without
91
+ * one cannot be reported (skyramp_submit_report refuses). */
92
+ export interface RetrofitOutcome {
93
+ /** BASENAME of the edited test. */
94
+ file: string;
95
+ execution?: TestExecutionStatus;
96
+ }
88
97
  /** Shared-helper reuse for an API test, re-derived from the delivered files at report
89
98
  * time — nothing here is read back from what the agent or the verify pass recorded.
90
99
  * Absent altogether when no utils file exists for the test (the same omission rule
@@ -105,6 +114,13 @@ export interface HelperReuseOutcome {
105
114
  * per method+path, status-code-only assertions, method+resource names — with
106
115
  * documented declines (`reuse-verify: allow …`) counted as holding. */
107
116
  verification?: HelperVerificationOutcome;
117
+ /** Pre-existing generated tests this spec's reuse pass rewired onto the utils
118
+ * file, each with its recorded execution. Omitted when none. */
119
+ retrofits?: RetrofitOutcome[];
120
+ /** Whether the delivered test plus the helpers it imports still hold every
121
+ * assertion (and page-error guard) present when modularization was handed out.
122
+ * Present only when that baseline exists. */
123
+ assertionsPreserved?: boolean;
108
124
  }
109
125
  /**
110
126
  * What lands in a report's `reuse` field for a generated test. Every member is
@@ -12,7 +12,7 @@ export declare enum Novelty {
12
12
  EXISTING = "existing"
13
13
  }
14
14
  /** All categories including internal ones. */
15
- export declare const SCENARIO_CATEGORIES: readonly ["new_endpoint", "bug_caught", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"];
15
+ export declare const SCENARIO_CATEGORIES: readonly ["new_endpoint", "bug_caught", "requirement_conflict", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"];
16
16
  export type ScenarioCategory = typeof SCENARIO_CATEGORIES[number];
17
17
  /** Categories valid for tool submissions (excludes internal-only categories). */
18
18
  export declare const TEST_CATEGORIES: readonly ["business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"];
@@ -21,3 +21,13 @@ export type TestCategory = typeof TEST_CATEGORIES[number];
21
21
  export declare const CATEGORY_PRIORITY: Record<ScenarioCategory, PriorityTier>;
22
22
  /** Map internal-only categories to their external equivalent for tool submission. */
23
23
  export declare function externalCategory(cat: ScenarioCategory): TestCategory;
24
+ /**
25
+ * Categories whose scenarios target a specific identified defect — a code flaw
26
+ * (`bug_caught`) or a stated requirement the code contradicts
27
+ * (`requirement_conflict`). They survive external-test dedup: an existing test on
28
+ * the same endpoint exercises the surface, not the flaw, so removing them would
29
+ * drop the only test that fails on the defect.
30
+ */
31
+ export declare const FLAW_TARGETING_CATEGORIES: readonly ["bug_caught", "requirement_conflict"];
32
+ /** Whether `category` targets a specific identified defect (see {@link FLAW_TARGETING_CATEGORIES}). */
33
+ export declare function isFlawTargetingCategory(category: ScenarioCategory | undefined): boolean;
@@ -17,6 +17,14 @@ export var Novelty;
17
17
  const INTERNAL_CATEGORIES = [
18
18
  "new_endpoint", // MEDIUM - diff-direct scenario; where a test came from, not a guarantee of a slot
19
19
  "bug_caught", // CRITICAL - tests targeting a specific <bug_found> flaw identified during enrichment
20
+ // CRITICAL - tests asserting a requirement the PR title/description (or a
21
+ // requirements file it references) states, which the implemented behavior
22
+ // contradicts. Separate from bug_caught deliberately (SKYR-4291): labelled
23
+ // bug_caught, a requirement-vs-code mismatch competed with the code-review
24
+ // flaws for the same promotion and lost it on severity. Its own category means
25
+ // its own carve-out here, first place in the promotion order SKYR-4275's bound
26
+ // hands out, and its own coverage gate.
27
+ "requirement_conflict",
20
28
  ];
21
29
  /** External categories valid for tool submissions, ordered by priority. */
22
30
  const CATEGORIES = [
@@ -59,6 +67,12 @@ export const CATEGORY_PRIORITY = {
59
67
  // fill GENERATE.
60
68
  new_endpoint: PriorityTier.MEDIUM,
61
69
  bug_caught: PriorityTier.CRITICAL, // tests targeting a <bug_found> flaw — always in GENERATE
70
+ // A stated requirement the implementation contradicts is the point of the PR,
71
+ // so it sits in the same top tier as bug_caught. Being its own category, it is
72
+ // carved out separately in planRanker and takes the FIRST slot of the promotion
73
+ // bound SKYR-4275 sets — the code-review flaws promote into what is left, so a
74
+ // requirement conflict is never the finding that loses on severity.
75
+ requirement_conflict: PriorityTier.CRITICAL,
62
76
  business_rule: PriorityTier.HIGH, // formula/business-logic bugs are high priority
63
77
  security_boundary: PriorityTier.HIGH,
64
78
  data_integrity: PriorityTier.HIGH,
@@ -75,5 +89,25 @@ export function externalCategory(cat) {
75
89
  return "crud";
76
90
  if (cat === "bug_caught")
77
91
  return "business_rule";
92
+ // The stated requirement IS the business rule the test asserts — same landing
93
+ // spot as bug_caught, so a requirement-conflict test reads as a rule check in
94
+ // the customer-facing report rather than an unrecognised label.
95
+ if (cat === "requirement_conflict")
96
+ return "business_rule";
78
97
  return cat;
79
98
  }
99
+ /**
100
+ * Categories whose scenarios target a specific identified defect — a code flaw
101
+ * (`bug_caught`) or a stated requirement the code contradicts
102
+ * (`requirement_conflict`). They survive external-test dedup: an existing test on
103
+ * the same endpoint exercises the surface, not the flaw, so removing them would
104
+ * drop the only test that fails on the defect.
105
+ */
106
+ export const FLAW_TARGETING_CATEGORIES = [
107
+ "bug_caught",
108
+ "requirement_conflict",
109
+ ];
110
+ /** Whether `category` targets a specific identified defect (see {@link FLAW_TARGETING_CATEGORIES}). */
111
+ export function isFlawTargetingCategory(category) {
112
+ return category !== undefined && FLAW_TARGETING_CATEGORIES.includes(category);
113
+ }
@@ -246,3 +246,7 @@ export declare const codeRefactoringSchema: z.ZodObject<{
246
246
  codeReuse?: boolean | undefined;
247
247
  modularizeCode?: boolean | undefined;
248
248
  }>;
249
+ /** Test types skyramp_enhance_assertions supports — single-sourced so the
250
+ * tool's schema enum and generation-time baseline seeding cannot drift
251
+ * (contract additionally requires provider mode at the seeding site). */
252
+ export declare const ASSERTION_ENHANCEABLE_TEST_TYPES: readonly [TestType.INTEGRATION, TestType.CONTRACT, TestType.UI];
@@ -274,3 +274,11 @@ export const codeRefactoringSchema = z.object({
274
274
  .default(false)
275
275
  .describe("Whether to modularize the code"),
276
276
  });
277
+ /** Test types skyramp_enhance_assertions supports — single-sourced so the
278
+ * tool's schema enum and generation-time baseline seeding cannot drift
279
+ * (contract additionally requires provider mode at the seeding site). */
280
+ export const ASSERTION_ENHANCEABLE_TEST_TYPES = [
281
+ TestType.INTEGRATION,
282
+ TestType.CONTRACT,
283
+ TestType.UI,
284
+ ];
@@ -2,6 +2,7 @@ import type { DriftAction } from "./TestAnalysis.js";
2
2
  import type { TestExecutionStatus } from "./TestExecution.js";
3
3
  import type { HttpMethod, TestType } from "./TestTypes.js";
4
4
  import type { ReuseOutcome } from "./ReuseOutcome.js";
5
+ import type { AssertionOutcome } from "./AssertionOutcome.js";
5
6
  /** Classification of an issuesFound entry. Bug is a product/code defect; the
6
7
  * other values are tooling noise that the Testbot report renders in its
7
8
  * Configuration Errors section instead of Issues Found. */
@@ -39,6 +40,14 @@ export interface TestbotReport {
39
40
  * rest of this file mirrors Zod schemas by hand, which is why only this field
40
41
  * is imported). Absent in reports from MCP versions predating it. */
41
42
  reuse?: ReuseOutcome;
43
+ /** Assertion-enhancement proof-of-work summary, present when
44
+ * skyramp_enhance_assertions or skyramp_execute_test recorded state for
45
+ * this test. Server-derived like `reuse` (imported for the same reason);
46
+ * cross-checks the LLM-authored narrative against server-observed facts:
47
+ * verification status, before/after assertion metrics, decline reason,
48
+ * and how many times the test actually executed. Absent in reports from
49
+ * MCP versions predating it. */
50
+ assertions?: AssertionOutcome;
42
51
  }[];
43
52
  /** Omitted when no existing tests were found (agent omits the field). */
44
53
  testMaintenance?: {
@@ -51,6 +60,10 @@ export interface TestbotReport {
51
60
  beforeDetails: string;
52
61
  afterStatus: TestExecutionStatus;
53
62
  afterDetails: string;
63
+ /** See newTestsCreated[].assertions — maintenance rows are matched by
64
+ * exact canonical path, so this carries the maintenance honesty labels
65
+ * (nothing-to-verify vs verified) into the report. */
66
+ assertions?: AssertionOutcome;
54
67
  }[];
55
68
  testResults: {
56
69
  testType: TestType;
@@ -4,5 +4,5 @@ export { TestType, HttpMethod } from "./TestTypes.js";
4
4
  export type { TestbotReport } from "./TestbotReport.js";
5
5
  export { IssueFoundCategory } from "./TestbotReport.js";
6
6
  export { ReuseDeclinedBy, ReuseVerificationOutcome, HelperVerificationOutcome, } from "./ReuseOutcome.js";
7
- export type { ReuseOutcome, ReuseSkippedEntry, ReuseMissedEntry, HelperReuseOutcome, } from "./ReuseOutcome.js";
7
+ export type { ReuseOutcome, ReuseSkippedEntry, ReuseMissedEntry, HelperReuseOutcome, RetrofitOutcome, } from "./ReuseOutcome.js";
8
8
  export type { RelatedRepository, TestbotPromptOptions, } from "./TestbotPromptOptions.js";