@skyramp/mcp 0.3.8 → 0.4.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/build/commands/commandLibrary.d.ts +1 -1
  2. package/build/commands/commandLibrary.js +3 -3
  3. package/build/commands/recommendTestsAndExecuteCommand.d.ts +1 -1
  4. package/build/commands/recommendTestsAndExecuteCommand.js +35 -20
  5. package/build/commands/testThisEndpointCommand.js +35 -19
  6. package/build/index.js +9 -3
  7. package/build/playwright/blueprintDigest.d.ts +15 -0
  8. package/build/playwright/blueprintDigest.js +152 -0
  9. package/build/playwright/blueprintDigestStore.d.ts +31 -0
  10. package/build/playwright/blueprintDigestStore.js +117 -0
  11. package/build/playwright/registerPlaywrightTools.js +60 -12
  12. package/build/playwright/traceRecordingPrompt.js +8 -7
  13. package/build/prompts/enhance-assertions/sharedAssertionRules.js +9 -8
  14. package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +24 -2
  15. package/build/prompts/promptAssets.d.ts +20 -0
  16. package/build/prompts/promptAssets.js +55 -0
  17. package/build/prompts/sut-setup/modes/dockerComposePrompt.js +19 -5
  18. package/build/prompts/test-maintenance/actionsInstructions.d.ts +4 -0
  19. package/build/prompts/test-maintenance/actionsInstructions.js +14 -2
  20. package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +0 -10
  21. package/build/prompts/test-maintenance/drift-analysis-prompt.js +2 -11
  22. package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +8 -4
  23. package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +5 -22
  24. package/build/prompts/test-recommendation/diffExecutionPlan.js +37 -465
  25. package/build/prompts/test-recommendation/recommendationSections.d.ts +7 -17
  26. package/build/prompts/test-recommendation/recommendationSections.js +67 -309
  27. package/build/prompts/test-recommendation/recommendationShared.d.ts +19 -47
  28. package/build/prompts/test-recommendation/recommendationShared.js +49 -155
  29. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.d.ts +0 -5
  30. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +10 -153
  31. package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +2 -29
  32. package/build/prompts/test-recommendation/test-recommendation-prompt.js +32 -457
  33. package/build/prompts/testbot/planDeclarations.d.ts +6 -0
  34. package/build/prompts/testbot/planDeclarations.js +9 -0
  35. package/build/prompts/testbot/testbot-prompts.d.ts +8 -0
  36. package/build/prompts/testbot/testbot-prompts.js +256 -381
  37. package/build/recommendation/answers.d.ts +35 -0
  38. package/build/recommendation/answers.js +96 -0
  39. package/build/recommendation/registerPlan.d.ts +49 -0
  40. package/build/recommendation/registerPlan.js +117 -0
  41. package/build/recommendation/runVerifiers.d.ts +10 -0
  42. package/build/recommendation/runVerifiers.js +49 -0
  43. package/build/recommendation/subjectStep.d.ts +42 -0
  44. package/build/recommendation/subjectStep.js +86 -0
  45. package/build/recommendation/types.d.ts +163 -0
  46. package/build/recommendation/types.js +20 -0
  47. package/build/recommendation/verifierContracts.d.ts +382 -0
  48. package/build/recommendation/verifierContracts.js +263 -0
  49. package/build/recommendation/verifiers/changedFile.d.ts +2 -0
  50. package/build/recommendation/verifiers/changedFile.js +82 -0
  51. package/build/recommendation/verifiers/citedPath.d.ts +12 -0
  52. package/build/recommendation/verifiers/citedPath.js +35 -0
  53. package/build/recommendation/verifiers/coverage.d.ts +7 -0
  54. package/build/recommendation/verifiers/coverage.js +617 -0
  55. package/build/recommendation/verifiers/deliveredMatchesPlan.d.ts +11 -0
  56. package/build/recommendation/verifiers/deliveredMatchesPlan.js +33 -0
  57. package/build/recommendation/verifiers/endpointGrounded.d.ts +17 -0
  58. package/build/recommendation/verifiers/endpointGrounded.js +128 -0
  59. package/build/recommendation/verifiers/existingCoverage.d.ts +6 -0
  60. package/build/recommendation/verifiers/existingCoverage.js +51 -0
  61. package/build/recommendation/verifiers/expectedOutcome.d.ts +31 -0
  62. package/build/recommendation/verifiers/expectedOutcome.js +105 -0
  63. package/build/recommendation/verifiers/removedElementGuarded.d.ts +2 -0
  64. package/build/recommendation/verifiers/removedElementGuarded.js +57 -0
  65. package/build/recommendation/verifiers/reportedCategory.d.ts +26 -0
  66. package/build/recommendation/verifiers/reportedCategory.js +84 -0
  67. package/build/recommendation/verifiers/screenRoute.d.ts +10 -0
  68. package/build/recommendation/verifiers/screenRoute.js +118 -0
  69. package/build/recommendation/verifiers/statedDifference.d.ts +6 -0
  70. package/build/recommendation/verifiers/statedDifference.js +140 -0
  71. package/build/recommendation/verifiers/uiElementGrounded.d.ts +7 -0
  72. package/build/recommendation/verifiers/uiElementGrounded.js +318 -0
  73. package/build/resources/analysisResources.js +1 -114
  74. package/build/resources/testbotResource.js +23 -13
  75. package/build/services/ModularizationService.js +2 -1
  76. package/build/services/TestDiscoveryService.d.ts +3 -72
  77. package/build/services/TestDiscoveryService.js +10 -303
  78. package/build/services/containerEnv.d.ts +1 -1
  79. package/build/services/containerEnv.js +12 -0
  80. package/build/skills/fixTestImportErrorsSkill.d.ts +13 -0
  81. package/build/skills/fixTestImportErrorsSkill.js +20 -0
  82. package/build/toolNames.d.ts +1 -0
  83. package/build/toolNames.js +1 -0
  84. package/build/tools/code-refactor/enhanceAssertionsTool.js +3 -3
  85. package/build/tools/code-refactor/modularizationTool.js +2 -1
  86. package/build/tools/executeSkyrampTestTool.d.ts +80 -0
  87. package/build/tools/executeSkyrampTestTool.js +246 -19
  88. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +6 -0
  89. package/build/tools/generate-tests/generateContractRestTool.js +3 -3
  90. package/build/tools/generate-tests/planGuard.d.ts +2 -2
  91. package/build/tools/generate-tests/planGuard.js +78 -18
  92. package/build/tools/one-click/oneClickTool.d.ts +0 -1
  93. package/build/tools/one-click/oneClickTool.js +0 -5
  94. package/build/tools/submitReportTool.d.ts +48 -42
  95. package/build/tools/submitReportTool.js +576 -193
  96. package/build/tools/test-management/actionsTool.js +72 -4
  97. package/build/tools/test-management/analyzeChangesTool.d.ts +144 -48
  98. package/build/tools/test-management/analyzeChangesTool.js +212 -1219
  99. package/build/tools/test-management/analyzeTestHealthTool.js +13 -24
  100. package/build/tools/test-management/index.d.ts +1 -0
  101. package/build/tools/test-management/index.js +1 -0
  102. package/build/tools/test-management/registerTestPlanTool.d.ts +795 -172
  103. package/build/tools/test-management/registerTestPlanTool.js +609 -542
  104. package/build/tools/test-management/resolveScreenTool.d.ts +75 -0
  105. package/build/tools/test-management/resolveScreenTool.js +289 -0
  106. package/build/types/BlueprintDigest.d.ts +34 -0
  107. package/build/types/BlueprintDigest.js +1 -0
  108. package/build/types/RepositoryAnalysis.d.ts +20 -1559
  109. package/build/types/RepositoryAnalysis.js +2 -58
  110. package/build/types/StepMethod.d.ts +40 -0
  111. package/build/types/StepMethod.js +77 -0
  112. package/build/types/TestAnalysis.d.ts +12 -0
  113. package/build/types/TestExecution.d.ts +4 -0
  114. package/build/types/TestRecommendation.d.ts +24 -24
  115. package/build/types/TestRecommendation.js +91 -89
  116. package/build/types/TestbotPromptOptions.d.ts +0 -4
  117. package/build/types/TestbotReport.d.ts +64 -2
  118. package/build/utils/AnalysisStateManager.d.ts +79 -113
  119. package/build/utils/AnalysisStateManager.js +147 -57
  120. package/build/utils/assertion-verify/api-shared-lints.js +1 -1
  121. package/build/utils/assertion-verify/metrics.js +85 -36
  122. package/build/utils/assertion-verify/ui-lints.d.ts +0 -5
  123. package/build/utils/assertion-verify/ui-lints.js +32 -0
  124. package/build/utils/branchDiff.d.ts +63 -31
  125. package/build/utils/branchDiff.js +242 -94
  126. package/build/utils/containedPath.d.ts +18 -0
  127. package/build/utils/containedPath.js +73 -0
  128. package/build/utils/dartRouteExtractor.d.ts +18 -34
  129. package/build/utils/dartRouteExtractor.js +101 -173
  130. package/build/utils/featureFlags.d.ts +12 -0
  131. package/build/utils/featureFlags.js +14 -0
  132. package/build/utils/frontendSelectors.d.ts +48 -27
  133. package/build/utils/frontendSelectors.js +241 -80
  134. package/build/utils/pathMatching.d.ts +2 -4
  135. package/build/utils/pathMatching.js +2 -4
  136. package/build/utils/planMatchKeys.d.ts +38 -47
  137. package/build/utils/planMatchKeys.js +143 -81
  138. package/build/utils/rebaselineSnapshots.d.ts +24 -0
  139. package/build/utils/rebaselineSnapshots.js +65 -0
  140. package/build/utils/removedUiElements.d.ts +22 -0
  141. package/build/utils/removedUiElements.js +106 -0
  142. package/build/utils/reportVerification.d.ts +2 -6
  143. package/build/utils/reportVerification.js +61 -2
  144. package/build/utils/screenRoutes.d.ts +66 -0
  145. package/build/utils/screenRoutes.js +727 -0
  146. package/build/utils/sourceRouteExtractor.js +320 -112
  147. package/build/utils/testFileClassification.d.ts +11 -2
  148. package/build/utils/testFileClassification.js +44 -2
  149. package/build/utils/testFixtures.d.ts +5 -0
  150. package/build/utils/testFixtures.js +13 -0
  151. package/build/utils/utils.d.ts +0 -1
  152. package/build/utils/utils.js +0 -11
  153. package/build/utils/versions.d.ts +3 -3
  154. package/build/utils/versions.js +1 -1
  155. package/build/workspace/workspace.d.ts +12 -12
  156. package/node_modules/playwright/lib/mcp/skyramp/assertHiddenTool.js +56 -0
  157. package/node_modules/playwright/lib/mcp/skyramp/assertTool.js +2 -1
  158. package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +10 -0
  159. package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +4 -1
  160. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +160 -1
  161. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +4 -2
  162. package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +1 -0
  163. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderSignalProcessor.js +2 -0
  164. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder.js +5 -1
  165. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.-Id052Lr.js → index.B7KbSQcC.js} +1 -1
  166. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +1 -1
  167. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  168. package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +1 -1
  169. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderSignalProcessor.ts +7 -0
  170. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder.ts +6 -1
  171. package/node_modules/playwright/package.json +1 -1
  172. package/package.json +4 -3
  173. package/plugin/.claude-plugin/plugin.json +8 -0
  174. package/plugin/plugin.json +6 -0
  175. package/plugin/prompts/declaring-a-plan.md +20 -0
  176. package/plugin/prompts/generate-tests/context-fetching.md +4 -0
  177. package/plugin/prompts/generate-tests/execution-plan.md +63 -0
  178. package/plugin/prompts/generate-tests/generation.md +108 -0
  179. package/plugin/prompts/generate-tests/path-parameters.md +1 -0
  180. package/plugin/prompts/generate-tests/reasoning-protocol.md +17 -0
  181. package/plugin/prompts/generate-tests/tool-workflow-variants.md +61 -0
  182. package/plugin/prompts/generate-tests/tool-workflows.md +65 -0
  183. package/plugin/prompts/plan-tests.md +42 -0
  184. package/plugin/prompts/testbot-task1.md +82 -0
  185. package/plugin/skills/fix-test-import-errors/SKILL.md +98 -0
  186. package/build/prompts/test-recommendation/analysisOutputPrompt.d.ts +0 -84
  187. package/build/prompts/test-recommendation/analysisOutputPrompt.js +0 -369
  188. package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +0 -7
  189. package/build/prompts/test-recommendation/fullRepoCatalog.js +0 -283
  190. package/build/prompts/test-recommendation/scopeAssessment.d.ts +0 -81
  191. package/build/prompts/test-recommendation/scopeAssessment.js +0 -359
  192. package/build/recommendation/budgeters/diversityBalancedBudgeter.d.ts +0 -7
  193. package/build/recommendation/budgeters/diversityBalancedBudgeter.js +0 -105
  194. package/build/recommendation/budgeters/fixedNBudgeter.d.ts +0 -7
  195. package/build/recommendation/budgeters/fixedNBudgeter.js +0 -11
  196. package/build/recommendation/budgeters/shared.d.ts +0 -32
  197. package/build/recommendation/budgeters/shared.js +0 -246
  198. package/build/recommendation/discriminators.d.ts +0 -37
  199. package/build/recommendation/discriminators.js +0 -379
  200. package/build/recommendation/diversity.d.ts +0 -47
  201. package/build/recommendation/diversity.js +0 -101
  202. package/build/recommendation/planRanker.d.ts +0 -65
  203. package/build/recommendation/planRanker.js +0 -83
  204. package/build/recommendation/testFixtures.d.ts +0 -25
  205. package/build/recommendation/testFixtures.js +0 -45
  206. package/build/types/FrontendIntegration.d.ts +0 -28
  207. package/build/types/FrontendIntegration.js +0 -22
  208. package/build/types/Recommendation.d.ts +0 -146
  209. package/build/types/Recommendation.js +0 -74
  210. package/build/utils/changedRoutes.d.ts +0 -29
  211. package/build/utils/changedRoutes.js +0 -87
  212. package/build/utils/frontendIntegration.d.ts +0 -9
  213. package/build/utils/frontendIntegration.js +0 -243
  214. package/build/utils/importerHop.d.ts +0 -135
  215. package/build/utils/importerHop.js +0 -489
  216. package/build/utils/pathAffinityClassification.d.ts +0 -49
  217. package/build/utils/pathAffinityClassification.js +0 -180
  218. package/build/utils/pythonMountPrefixes.d.ts +0 -25
  219. package/build/utils/pythonMountPrefixes.js +0 -347
  220. package/build/utils/repoScanner.d.ts +0 -34
  221. package/build/utils/repoScanner.js +0 -300
  222. package/build/utils/routeParsers.d.ts +0 -95
  223. package/build/utils/routeParsers.js +0 -951
  224. package/build/utils/scenarioDrafting.d.ts +0 -92
  225. package/build/utils/scenarioDrafting.js +0 -951
  226. package/build/utils/subjectEndpoints.d.ts +0 -19
  227. package/build/utils/subjectEndpoints.js +0 -98
  228. package/build/utils/uiPageEnumerator.d.ts +0 -172
  229. package/build/utils/uiPageEnumerator.js +0 -474
@@ -1,359 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { MAX_RECOMMENDATIONS, MAX_TESTS_TO_GENERATE } from "./recommendationSections.js";
4
- // .vue and .svelte cannot be route handlers in any framework — always frontend.
5
- // Stylesheets cannot be either (SKYR-4292): `packages/excalidraw/css/styles.scss`
6
- // classified as backend because `css/` is not in FRONTEND_DIR_PATTERN, so a
7
- // stylesheet-only PR took the zero-classified backend branch and never saw the
8
- // changed-behavior rule.
9
- const ALWAYS_FRONTEND_EXT = /\.(vue|svelte|css|scss|less)$/i;
10
- // .dart is always frontend in Flutter projects (no server-side Dart web framework
11
- // in scope). Gated on hasFlutterPubspec so non-Flutter Dart files (rare, but
12
- // possible — e.g. Dart-on-server backends) don't accidentally classify as UI.
13
- const FLUTTER_DART_EXT = /\.dart$/i;
14
- // .tsx/.jsx are usually UI components but can be Next.js API handlers (pages/api/*.tsx).
15
- const LIKELY_FRONTEND_EXT = /\.(tsx|jsx)$/i;
16
- // Ambiguous extensions need directory context to distinguish frontend from backend.
17
- const AMBIGUOUS_FRONTEND_PATTERN = /\.(tsx?|jsx?|html?|erb|jsp|asp|jinja2?|twig)$/i;
18
- // /api/ always signals backend — even .tsx files (e.g. Next.js pages/api/*.tsx handlers).
19
- const STRICT_API_DIR_PATTERN = /\/(api)\//i;
20
- // Framework route dirs that contain React page components in modern full-stack apps
21
- // (Medusa Admin, Remix, React Router v6). These only block non-.tsx/.jsx files —
22
- // a .tsx file in routes/ is a page component, not a route handler.
23
- const FRAMEWORK_ROUTE_DIR_PATTERN = /\/(routes?|controllers?|routers?|handlers?|endpoints?|server)\//i;
24
- const FRONTEND_DIR_PATTERN = /(^|\/)(components?|pages?|views?|layouts?|app|src\/app|frontend|client|public|styles?|templates?)\//i;
25
- // A .json (or .yaml/.yml) file nested under an i18n/locale directory holds only
26
- // user-facing copy — frontend regardless of nesting depth (subject to the
27
- // /api/ check above), unlike generic JSON (package.json, tsconfig.json, data
28
- // fixtures) which needs no such carve-out since it's never under one of these
29
- // directory names.
30
- const I18N_LOCALE_FILE_PATTERN = /(^|\/)(i18n|locales?|lang|translations?)\/.*\.(json|ya?ml)$/i;
31
- /**
32
- * Returns true if the file path looks like i18n/locale content (a JSON/YAML
33
- * file under an i18n/, locale(s)/, lang/, or translation(s)/ directory).
34
- * Exported so callers outside the frontend/backend classification itself
35
- * (e.g. the production-importer integration check, which greps for static
36
- * quoted imports) can special-case these files: real i18n loaders commonly
37
- * fetch locale content by a templated runtime path rather than a static
38
- * import, so the grep-based check would otherwise misreport them as
39
- * unintegrated dead code (SKYR-3977).
40
- */
41
- export function isI18nLocaleFile(filePath) {
42
- return I18N_LOCALE_FILE_PATTERN.test(filePath);
43
- }
44
- /**
45
- * Returns true if the repository root's `pubspec.yaml` declares a Flutter
46
- * SDK dependency (i.e., a `flutter:` key with `sdk: flutter` under
47
- * `dependencies:`). This is the load-bearing signal that the project
48
- * is a Flutter UI app or package, NOT a pure Dart server / CLI / library
49
- * (those have a `pubspec.yaml` too but no `sdk: flutter` dep).
50
- *
51
- * Used as the gate for treating `.dart` files as frontend in
52
- * `isFrontendFile`. Pure-Dart-backend repos correctly fall through to
53
- * the existing classifier behaviour (no `.dart` recognition).
54
- *
55
- * Synchronous + best-effort: returns false on any read/parse error.
56
- * Cheap because pubspec.yaml is small and we only call this once per
57
- * tool invocation (computed at the budget-driving boundary, then passed
58
- * through as an option).
59
- */
60
- /**
61
- * Common subdir locations for `pubspec.yaml` in real Flutter repos:
62
- * - root (single-app — birdle, customer SPA)
63
- * - app/, mobile/, frontend/, web/ (mixed-stack repos)
64
- * - apps/<name>/, packages/<name>/ (monorepos — depth-2 walked)
65
- *
66
- * We stop at the first pubspec we find that declares `sdk: flutter`. Pure-Dart
67
- * subprojects (e.g. a CLI tool in `tools/`) are correctly skipped because their
68
- * pubspec doesn't have the Flutter SDK line.
69
- *
70
- * Less-common shapes that are NOT auto-discovered: `services/mobile/`,
71
- * `clients/<name>/`, etc. If a customer's repo uses one of those, add the
72
- * parent dir here or tell them to symlink `pubspec.yaml` at the root.
73
- */
74
- const PUBSPEC_SEARCH_DIRS = [
75
- ".",
76
- "app",
77
- "mobile",
78
- "frontend",
79
- "web",
80
- "client",
81
- ];
82
- const PUBSPEC_MONOREPO_PARENTS = ["apps", "packages"];
83
- export function hasFlutterSdkDep(repositoryPath) {
84
- for (const subdir of PUBSPEC_SEARCH_DIRS) {
85
- if (checkPubspec(path.join(repositoryPath, subdir, "pubspec.yaml"))) {
86
- return true;
87
- }
88
- }
89
- // Monorepo shape: apps/<name>/pubspec.yaml, packages/<name>/pubspec.yaml.
90
- // One level of fan-out — enough for typical Flutter monorepos, cheap to walk.
91
- for (const parent of PUBSPEC_MONOREPO_PARENTS) {
92
- const parentPath = path.join(repositoryPath, parent);
93
- let entries;
94
- try {
95
- entries = fs.readdirSync(parentPath, { withFileTypes: true });
96
- }
97
- catch {
98
- continue;
99
- }
100
- for (const entry of entries) {
101
- if (!entry.isDirectory())
102
- continue;
103
- if (checkPubspec(path.join(parentPath, entry.name, "pubspec.yaml"))) {
104
- return true;
105
- }
106
- }
107
- }
108
- return false;
109
- }
110
- function checkPubspec(pubspecPath) {
111
- try {
112
- if (!fs.existsSync(pubspecPath))
113
- return false;
114
- const content = fs.readFileSync(pubspecPath, "utf8");
115
- // Detect the `flutter:` SDK dep — proper YAML parsing would be overkill.
116
- // The canonical pattern is:
117
- // dependencies:
118
- // flutter:
119
- // sdk: flutter
120
- // The `sdk: flutter` line under a `flutter:` key is unique to Flutter
121
- // projects; pure-Dart projects use `sdk: dart` or no sdk: line at all.
122
- return /^\s+sdk:\s*flutter\s*$/m.test(content);
123
- }
124
- catch {
125
- return false;
126
- }
127
- }
128
- /**
129
- * Returns true if the file path is a frontend file.
130
- *
131
- * Five tiers:
132
- * 1. .vue / .svelte — always frontend; these file types cannot be route handlers.
133
- * 2. .dart — always frontend IFF `hasFlutterSdkDep` is true (Flutter project).
134
- * Sits above `STRICT_API_DIR_PATTERN` because there's no server-side Dart
135
- * web framework in scope when the Flutter SDK dep is present.
136
- * 3. i18n/locale JSON/YAML (e.g. locales/en/common.json) — frontend UNLESS in
137
- * an API/backend directory (e.g. an i18n content API under api/), since
138
- * unlike tiers 1-2 a locale file CAN be served by a backend route (SKYR-3977).
139
- * 4. .tsx / .jsx — frontend unless in an API/backend directory (e.g. pages/api/).
140
- * 5. Ambiguous extensions (.ts, .js, .css, .html, …) — require a recognised
141
- * frontend directory AND must not be in an API/backend directory.
142
- *
143
- * The `hasFlutterSdkDep` opt is passed in by the budget-driving caller
144
- * (analyzeChangesTool) which has repository context.
145
- * Pure-path callers (test-recommendation-prompt, analysisOutputPrompt,
146
- * scopeAssessment's own buildScopeAssessmentSection) default to false —
147
- * they don't drive the budget.
148
- */
149
- export function isFrontendFile(filePath, { hasFlutterSdkDep = false } = {}) {
150
- if (ALWAYS_FRONTEND_EXT.test(filePath))
151
- return true;
152
- if (hasFlutterSdkDep && FLUTTER_DART_EXT.test(filePath))
153
- return true;
154
- // /api/ always means backend regardless of extension (Next.js API routes use .tsx).
155
- // Must run before I18N_LOCALE_FILE_PATTERN — unlike .vue/.svelte/.dart, a locale
156
- // JSON/YAML file CAN be served by a backend route (e.g. an i18n content API), so
157
- // it doesn't get the same always-frontend exemption as those extensions.
158
- if (STRICT_API_DIR_PATTERN.test(filePath))
159
- return false;
160
- if (isI18nLocaleFile(filePath))
161
- return true;
162
- // routes/, controllers/ etc. block .ts/.js but not .tsx/.jsx — Medusa Admin and
163
- // React Router place page components under routes/, not API handlers.
164
- if (!LIKELY_FRONTEND_EXT.test(filePath) && FRAMEWORK_ROUTE_DIR_PATTERN.test(filePath))
165
- return false;
166
- if (LIKELY_FRONTEND_EXT.test(filePath))
167
- return true;
168
- return AMBIGUOUS_FRONTEND_PATTERN.test(filePath) && FRONTEND_DIR_PATTERN.test(filePath);
169
- }
170
- /**
171
- * Returns true if the file path looks like a test file rather than UI source.
172
- *
173
- * Catches:
174
- * - Skyramp-generated tests (`*_test.ts`, `*_smoke.ts`, `*_contract.ts`,
175
- * `*_fuzz.ts`, `*_integration.ts`, `*_load.ts`, `*_e2e.ts`, `*_ui.ts`)
176
- * - Skyramp scenario files (`scenario_*.json`)
177
- * - Conventional Vitest/Jest/Playwright spec naming
178
- * (`*.spec.{ts,tsx,js,jsx}`, `*.test.{ts,tsx,js,jsx}`)
179
- * - Files inside `__tests__/` directories (Jest convention)
180
- *
181
- * Used by callers that want to filter test files OUT of frontend-source
182
- * processing — `isFrontendFile` returns true for `.spec.ts` under a
183
- * frontend directory because the rule is "tier-3 ambiguous + frontend dir,"
184
- * but those tests aren't UI source we'd want to ground recommendations in.
185
- */
186
- export function isTestFile(filePath) {
187
- return (/(?:_test|_smoke|_contract|_fuzz|_integration|_load|_e2e|_ui)\.[^/]+$/.test(filePath) ||
188
- /scenario_[^/]+\.json$/.test(filePath) ||
189
- // Include the `.e2e.` dot form so Playwright/Cypress specs discovery treats as UI tests
190
- // (UI_TEST_EXT matches `.(test|spec|e2e).`) are recognized here too — e.g. `login.e2e.ts`.
191
- /\.(spec|test|e2e)\.(tsx?|jsx?)$/.test(filePath) ||
192
- /(?:^|\/)__tests__\//.test(filePath));
193
- }
194
- // ── LLM scope assessment ──────────────────────────────────────────────────────
195
- /**
196
- * The changed-behavior budget rule, shared by every branch that can see a frontend
197
- * diff (SKYR-4292).
198
- *
199
- * This replaces the zero-new-surface abstention override (SKYR-4099). That override
200
- * named three abstain categories — cosmetic, deletion-only, and modification of an
201
- * already-covered surface — and its only sanctioned action was a Budget Plan of 0. On
202
- * a 20-change UI-only diff under a 20 budget (Wipro PR #24) the agent generalized it
203
- * into a graded cut to 3, classified option additions no test asserted (Siebel,
204
- * Windows, .NET, GO, MQ) as "modification of an already-covered surface" because the
205
- * existing specs reached the wizard page, and shipped zero recommendations for the
206
- * rest. The taxonomy is gone: every observable behavior the diff changes gets a
207
- * candidate, coverage means an existing test ASSERTS the changed value (not that it
208
- * reaches the component), and the total tracks the count of changed behaviors — the
209
- * ranker in `skyramp_register_test_plan` trims to the ceiling and reports the drops.
210
- *
211
- * Backend-only diffs do not render this: their GENERATE list is server pre-ranked
212
- * from classified endpoints (and SKYR-3820 handles a zero-classified diff), whereas a
213
- * frontend GENERATE list is placeholders the agent must fill from the diff itself.
214
- */
215
- function changedBehaviorSection(effectiveGenerate, budgetPlanTiming = "") {
216
- return `**Fill the budget from changed behavior:** The ceiling above is the most this run may plan, not a quota to reach or a default to cut. Enumerate every observable behavior this diff changes — each option value added, removed, renamed or reordered; each label, element, route, state, validation rule or computed value that renders or behaves differently — and give each one its own candidate. Treat no change as too trivial to test: a changed option value that no test asserts is a test. A changed style value counts too — a spacing, size, color, radius, font weight, icon size or class name that now renders differently is a changed behavior; its candidate asserts the new computed style or class on that element, or records a \`browser_visual_snapshot\` of it. A removed option, element or feature is a changed behavior too: its candidate asserts the removed thing is absent from the page where it used to render — the removal is what this PR ships, so the removal is what gets tested.
217
-
218
- A changed behavior is something a user can observe differently after this diff. Code the diff deletes, dedupes or simplifies without changing what renders is not a changed behavior — its coverage is maintenance of the tests that reach it — and a pre-existing defect you notice nearby is an \`issuesFound\` entry, not a slot in this budget. Only a comment, whitespace or equivalent-value edit (\`0px\` → \`0\`) changes nothing observable.
219
-
220
- A changed behavior is covered only when an existing test asserts the changed value (including after a maintenance UPDATE you recorded). A test that merely reaches the component does not cover it — that change still needs its own candidate.
221
-
222
- - Fewer changed behaviors than the ceiling → your Budget Plan total is that count, split as generate first (up to ${effectiveGenerate}) and the remainder additional. Do not pad with tests of behavior this diff did not change.
223
- - More changed behaviors than the ceiling → keep every one in the candidate list you register; \`skyramp_register_test_plan\` trims to the ceiling and reports what it dropped.
224
-
225
- **A requirement the diff did NOT implement counts too.** The enumeration above is of behaviors the diff changed; a \`requirement_conflict\` is the opposite — the PR title/description (or a requirements file it references) states a requirement and the diff failed to implement it. Give it its own candidate anyway, category \`requirement_conflict\`, asserting what the description requires so that it fails on the current code. When the description says a feature was removed and it still renders, the absence assertion IS that test — it fails today, which is the finding. It counts toward your total like any changed behavior, and it is the one candidate a total of zero must never swallow: abstaining past a stated requirement reports the code back to the author as if it were the requirement.
226
-
227
- Never reduce the total below the number of changed behaviors you found. State your Budget Plan in the canonical format${budgetPlanTiming} — \`Budget Plan: <total> total (<generate> generate + <additional> additional), <ui_pct>% UI/E2E\` — and use those numbers throughout the rest of the prompt.`;
228
- }
229
- /**
230
- * Builds the PR scope assessment section.
231
- *
232
- * When `precomputedUIPct` is provided (0 = backend-only, 100 = UI-only) the server has
233
- * already determined the split unambiguously, so Steps A–C are skipped. Backend-only
234
- * (0) renders a single Budget Plan line; UI-only (100) renders that line plus the
235
- * changed-behavior budget rule (SKYR-4292), because a frontend GENERATE list is
236
- * placeholders the agent fills from the diff and the total must track what changed.
237
- *
238
- * For mixed PRs (`precomputedUIPct` is undefined, `hasFrontendChanges` is true) skip
239
- * Steps A–C but keep Step D so the LLM can apply judgment to determine the UI%.
240
- *
241
- * Falls back to the full four-step assessment when no precomputed data is available.
242
- */
243
- export function buildScopeAssessmentSection(maxTotal = MAX_RECOMMENDATIONS, maxGenerate = MAX_TESTS_TO_GENERATE, isUIOnly = false,
244
- /** Server-determined UI/E2E percentage. `undefined` = mixed PR or unknown. */
245
- precomputedUIPct,
246
- /** Whether the diff contains frontend files (true ↔ mixed PR when precomputedUIPct is undefined). */
247
- hasFrontendChanges,
248
- /**
249
- * PR-mode: whether the diff classified at least one new/modified/removed endpoint.
250
- * When false on the backend branch (precomputedUIPct === 0), the Budget Plan
251
- * defaults to 0 total with a conditional ceiling instead of a fixed mandate —
252
- * SKYR-3820: a config-only PR must not be pushed to fill the budget with
253
- * baseline tests for unchanged endpoints. Defaults to true so existing callers
254
- * keep the unconditional fixed-budget rendering.
255
- */
256
- hasClassifiedApiChanges = true) {
257
- const effectiveGenerate = Math.min(maxGenerate, maxTotal);
258
- const additional = Math.max(0, maxTotal - effectiveGenerate);
259
- // Backend-only diff with zero classified endpoint changes (SKYR-3820): the
260
- // budget defaults to 0 so the tool output agrees with the testbot prompt's
261
- // zero-test report path, instead of mandating a fixed total the diff cannot
262
- // justify. The ceiling stays claimable because a zero-classified diff can be
263
- // a classification miss on a real behavior change (e.g. a DTO/serializer
264
- // change the endpoint scanner cannot attribute — SKYR-3855's immich case).
265
- if (precomputedUIPct === 0 && !hasClassifiedApiChanges) {
266
- return `### PR Scope Assessment
267
- Budget Plan: 0 total — no new, modified, or removed endpoints were classified from this diff, and no frontend files changed.
268
-
269
- With a 0-total Budget Plan: generate zero tests, recommend zero tests, and follow the zero-test report path. Do NOT draft baseline or generic tests for unchanged endpoints to fill a budget — an empty diff surface is a valid, expected outcome.
270
-
271
- **Exception — claim the ceiling only with evidence:** if your code review of the changed files shows an observable API behavior change the classifier missed (e.g. a DTO/serializer/service change that alters a response shape, a shared library/default-value or business-rule constant change that alters the behavior of an existing, unchanged endpoint (e.g. a default schedule, threshold, or config constant imported by a route handler elsewhere in the codebase), a deployment/config change that newly exposes or removes endpoints, or a schema-defined API contract change — a CRD type/kubebuilder validation marker, GraphQL schema, or gRPC proto edit that adds, removes, or re-validates what the server accepts or returns), raise your Budget Plan to cover exactly those affected endpoints, up to ${maxTotal} total (${effectiveGenerate} generate + ${additional} additional), 0% UI/E2E. Note: repositories whose entire API surface is schema-defined (e.g. a Kubernetes operator serving CRDs through the kube-apiserver) ALWAYS classify zero endpoints — for these, a schema change in the diff IS the endpoint change; evaluate this exception against the schema files instead of concluding there is nothing to test. Similarly, a changed file with zero classified endpoints is not by itself evidence of "no testable surface" — trace what imports the changed export (grep for its name) to check whether it feeds an existing endpoint's behavior before concluding the diff has no test value. **A requirement conflict is also grounds to raise the plan:** if the PR title/description (or a requirements file it references) states a requirement the diff did not implement, claim at least one slot for a \`requirement_conflict\` test asserting that requirement — it fails on the current code, which is the finding. Every test must name the changed file that justifies it. State your raised plan now in the canonical format — \`Budget Plan: <total> total (<generate> generate + <additional> additional), 0% UI/E2E\` — and use those exact numbers throughout the rest of the prompt; the raised generate count is your committed generate count.`;
272
- }
273
- // Unambiguous backend-only or UI-only: no LLM counting needed. Backend-only emits just
274
- // the Budget Plan line; UI-only appends the changed-behavior budget rule (see above).
275
- if (precomputedUIPct !== undefined) {
276
- const uiSuffix = precomputedUIPct > 0 ? `, ${precomputedUIPct}% UI/E2E` : "";
277
- // Backend-only (precomputedUIPct === 0): the server pre-ranked the GENERATE list
278
- // from classified endpoints, so the numbers are fixed. UI-only: the list is
279
- // placeholders the agent fills from the diff, so the line is a ceiling and the
280
- // changed-behavior rule sets the Budget Plan — a fixed "Budget Plan" plus "use
281
- // these exact numbers" here would contradict that rule (Copilot on mcp#835).
282
- if (precomputedUIPct === 0) {
283
- return `### PR Scope Assessment
284
- Budget Plan: ${maxTotal} total (${effectiveGenerate} generate + ${additional} additional)
285
-
286
- Use these exact numbers throughout the rest of the prompt.`;
287
- }
288
- return `### PR Scope Assessment
289
- Budget ceiling: ${maxTotal} total (${effectiveGenerate} generate + ${additional} additional)${uiSuffix}
290
-
291
- ${changedBehaviorSection(effectiveGenerate)}`;
292
- }
293
- // Mixed PR: server can pre-compute the total but not the UI/E2E split — keep Step D.
294
- if (hasFrontendChanges) {
295
- return `### PR Scope Assessment — determine UI% before planning recommendations
296
-
297
- Budget ceiling: **${maxTotal} total (${effectiveGenerate} generate + ${additional} additional)**
298
-
299
- ${changedBehaviorSection(effectiveGenerate, ", after Step D below sets your UI%")}
300
-
301
- **Step D — Determine UI vs backend split for your Budget Plan:**
302
- - Non-UI slots are backend tests; start from file-count ratio for UI%, then apply judgment:
303
- - Cosmetic CSS/style changes alongside real changes inflate the frontend file count without adding test value → reduce UI%
304
- - Frontend logic bugs (state management, calculation errors, form validation) in the diff → increase UI% even if few frontend files
305
- - Frontend component calls a changed backend API → an E2E test covers both sides → count toward UI%
306
- - Frontend files only in \`__tests__/\` or \`.stories.\` → exclude from the ratio
307
-
308
- **Append your UI% now** and state your Budget Plan — total and split per the changed-behavior rule above, within the ceiling:
309
- \`Budget Plan: <total> total (<generate> generate + <additional> additional), <ui_pct>% UI/E2E\`
310
-
311
- Use those numbers throughout the rest of the prompt.`;
312
- }
313
- // Fallback (no diff context — full_repo else-branch or test): full four-step assessment.
314
- const minTotal = Math.min(effectiveGenerate + 1, maxTotal);
315
- const minAdditional = Math.max(0, minTotal - effectiveGenerate);
316
- const baselineFormula = `${effectiveGenerate} (generate) + ${minAdditional} (min additional) = ${minTotal}`;
317
- const stepD = isUIOnly
318
- ? `**Step D — UI/E2E confirmation (frontend-only PR):**
319
- This is a frontend-only PR — set **100% UI/E2E** in your Budget Plan.
320
- Budget generate slots toward directly recording and generating UI tests; budget additional slots toward more UI/E2E flows derived from the changed components.`
321
- : `**Step D — Determine UI vs backend split:**
322
- - Backend-only PR (0 frontend files changed or only CSS/copy): **0% UI/E2E** — focus on integration and contract tests
323
- - Frontend-only PR: **100% UI/E2E** — all tests should be UI/E2E
324
- - Mixed PR — non-UI slots are backend tests; start from file-count ratio for UI%, then apply judgment:
325
- - Pure CSS/style changes inflate the frontend file count without adding test value → reduce UI%
326
- - Frontend logic bugs (state management, calculation errors, form validation) in the diff → increase UI% even if few frontend files
327
- - Frontend component calls a changed backend API → an E2E test covers both sides → count toward UI%
328
- - Frontend files only in \`__tests__/\` or \`.stories.\` → exclude from the ratio`;
329
- return `### PR Scope Assessment — complete this first, before planning any recommendations
330
-
331
- Read the Changed Files list and endpoint changes above, then work through the four steps below. This determines your **Budget Plan** (total count + backend/frontend split) for the rest of the prompt.
332
-
333
- **Step A — Classify changed files:**
334
- Count each type from the diff context (ignore generated test files, lock files, and build artifacts):
335
- - **Frontend files**: .vue / .svelte / .css / .scss / .less anywhere (always UI). .dart anywhere (always UI in a Flutter project — repo has a \`pubspec.yaml\` with \`sdk: flutter\`). .tsx / .jsx anywhere except in api/, routes/, routers/, controllers/, handlers/, endpoints/, or server/ directories. .ts / .js / .html / .erb / .jsp / .asp / .jinja2 / .twig only when in a frontend directory (components/, pages/, views/, layouts/, app/, frontend/, client/, styles/, templates/).
336
- - **Backend files**: route handlers, controllers, services, models, API modules, middleware, config with business logic
337
- - **Non-application** (exclude from test value): README and docs, lock files, CI config with no logic
338
-
339
- **Step B — Assess semantic complexity (quality over quantity):**
340
- Weigh changes by their test value, not file count:
341
- - New API endpoint → HIGH test value: needs happy path + at least one error path (contributes ~2 to budget)
342
- - Modified endpoint with formula / business logic change → HIGH: edge cases matter (contributes ~1–2)
343
- - Auth middleware change → CRITICAL: flag for extra security tests regardless of file count
344
- - Frontend state / validation / calculation logic → HIGH for UI tests even if zero backend endpoints changed
345
- - Changed style values, labels or copy → each rendered difference is a changed behavior: assert the new value (computed style, class or text) — no change is too trivial to test
346
-
347
- **Step C — Determine total recommendation count (${minTotal}–${maxTotal}):**
348
- Start from the baseline formula: *${baselineFormula}*, then adjust:
349
- - **Scale up** for: critical auth/data-integrity changes (+2), complex multi-step business workflows (+1 each), new endpoints with non-trivial validation (+1 each beyond the formula)
350
- - **Scale down** for: already well-tested paths confirmed by existing test list, trivial CRUD with no validation
351
- - **Hard cap**: ${maxTotal}
352
-
353
- ${stepD}
354
-
355
- **State your Budget Plan now** (one line, before any recommendation):
356
- \`Budget Plan: <total> total (<generate> generate + <additional> additional), <ui_pct>% UI/E2E\`
357
-
358
- Use these exact numbers throughout the rest of the prompt.`;
359
- }
@@ -1,7 +0,0 @@
1
- import { Budgeter } from "../../types/Recommendation.js";
2
- /**
3
- * Budgeter that guarantees cross-test-type coverage in the GENERATE set.
4
- * `maxGenerate` acts as an upper cap (not a hard N). Fixes the single-type skew
5
- * that starves contract/error-path tests. Opt-in via strategy selection.
6
- */
7
- export declare const diversityBalancedBudgeter: Budgeter;
@@ -1,105 +0,0 @@
1
- import { runBudget } from "./shared.js";
2
- import { bucketByType, inferScenarioType, isProtectedCandidate, roundRobinFill, } from "../diversity.js";
3
- const typeOf = (c) => inferScenarioType(c.scenario);
4
- /**
5
- * Pick `count` items guaranteeing each present test type at least one slot.
6
- *
7
- * Unlike roundRobinByType (which fills ALL protected items first and can
8
- * starve other types at small N — the SKYR-3879 "0 contract executed" skew),
9
- * this caps protected occupancy to `count - (numTypes - 1)` so at least one slot
10
- * per remaining type is always reachable, then:
11
- * Phase 1 — one item for each still-uncovered present type (by rank), then
12
- * Phase 2 — round-robin the remainder across types until `count` is reached.
13
- * Rank order is preserved within each type; degenerate cases match a rank slice.
14
- */
15
- function floorBalancedPick(items, count) {
16
- if (count <= 0)
17
- return [];
18
- if (count >= items.length)
19
- return items.slice(0, count);
20
- // Present types in first-appearance (rank) order.
21
- const typesOrder = [];
22
- const seen = new Set();
23
- for (const it of items) {
24
- const t = typeOf(it);
25
- if (!seen.has(t)) {
26
- seen.add(t);
27
- typesOrder.push(t);
28
- }
29
- }
30
- // Two passes, because "protected" is broader than "is a caught bug".
31
- //
32
- // A `bug_caught` candidate may take a type-floor slot: a caught bug is the
33
- // strongest thing a run produces, and it outranks type spread. Every OTHER
34
- // protected candidate — anything else CRITICAL, and attack-surface
35
- // security_boundary items — still yields to the floor, which is what stops
36
- // the SKYR-3879 skew where three CRITICAL integration candidates took all
37
- // three slots and no contract test was generated.
38
- //
39
- // The second pass replaces `maxProtected = count - (typesOrder.length - 1)`,
40
- // a static cap computed from the whole pool. It reserved a slot for every
41
- // other type whether or not the picks already covered it, so at the default
42
- // budget of 3 with three types present it evaluated to 1. Counting the types
43
- // still uncovered keeps the same floor and drops the arithmetic that made a
44
- // second finding unreachable.
45
- // The same bound the prompt states (`maxGen - 1`, and the whole budget only
46
- // at a budget of 1). Without it the first pass took every caught bug up to
47
- // `count`, which put the SKYR-3879 skew back: at the default budget of 3, two
48
- // same-type bugs left no slot for the type floor, and a measured 9-20% of
49
- // backend-only pools lost a test type. It also evicted attack-surface
50
- // security_boundary items the prompt in this same branch says to preserve
51
- // while any non-bug slot remains.
52
- const bugSlots = count <= 1 ? count : count - 1;
53
- const selected = [];
54
- const deferred = [];
55
- const pool = [];
56
- let bugsTaken = 0;
57
- for (const it of items) {
58
- if (it.scenario?.category === "bug_caught" && bugsTaken < bugSlots && selected.length < count) {
59
- selected.push(it);
60
- bugsTaken++;
61
- }
62
- else {
63
- deferred.push(it);
64
- }
65
- }
66
- const takenTypes = new Set(selected.map(typeOf));
67
- for (const it of deferred) {
68
- const stillUncovered = typesOrder.filter((t) => !takenTypes.has(t) && t !== typeOf(it)).length;
69
- if (isProtectedCandidate(it.priority, it.scenario) &&
70
- selected.length < count &&
71
- count - (selected.length + 1) >= stillUncovered) {
72
- selected.push(it);
73
- takenTypes.add(typeOf(it));
74
- }
75
- else {
76
- pool.push(it);
77
- }
78
- }
79
- const { buckets } = bucketByType(pool, typeOf);
80
- const covered = new Set(selected.map(typeOf));
81
- // Phase 1 — floor: one item per still-uncovered present type.
82
- for (const t of typesOrder) {
83
- if (selected.length >= count)
84
- break;
85
- if (covered.has(t))
86
- continue;
87
- const bucket = buckets.get(t);
88
- if (bucket && bucket.length > 0) {
89
- selected.push(bucket.shift());
90
- covered.add(t);
91
- }
92
- }
93
- // Phase 2 — round-robin the remainder across types until full.
94
- roundRobinFill(selected, typesOrder, buckets, count);
95
- return selected;
96
- }
97
- /**
98
- * Budgeter that guarantees cross-test-type coverage in the GENERATE set.
99
- * `maxGenerate` acts as an upper cap (not a hard N). Fixes the single-type skew
100
- * that starves contract/error-path tests. Opt-in via strategy selection.
101
- */
102
- export const diversityBalancedBudgeter = {
103
- name: "diversity-balanced",
104
- select: (ranked, ctx) => runBudget(ranked, ctx, floorBalancedPick),
105
- };
@@ -1,7 +0,0 @@
1
- import { Budgeter } from "../../types/Recommendation.js";
2
- /**
3
- * Default budgeter — reproduces the pre-refactor behavior exactly: fill up to
4
- * `maxGenerate` GENERATE slots by round-robin across the present test types
5
- * (protected items first), the rest become ADDITIONAL.
6
- */
7
- export declare const fixedNBudgeter: Budgeter;
@@ -1,11 +0,0 @@
1
- import { roundRobinByType } from "../diversity.js";
2
- import { runBudget } from "./shared.js";
3
- /**
4
- * Default budgeter — reproduces the pre-refactor behavior exactly: fill up to
5
- * `maxGenerate` GENERATE slots by round-robin across the present test types
6
- * (protected items first), the rest become ADDITIONAL.
7
- */
8
- export const fixedNBudgeter = {
9
- name: "fixed-n",
10
- select: (ranked, ctx) => runBudget(ranked, ctx, roundRobinByType),
11
- };
@@ -1,32 +0,0 @@
1
- import { Candidate, BudgetContext, SelectionResult } from "../../types/Recommendation.js";
2
- /**
3
- * What each PR class GUARANTEES in GENERATE (SKYR-4275). These are floors, not
4
- * quotas: the slots above them are open to either kind, decided by `pick`.
5
- * An open slot goes to whichever candidate `pick` chooses on rank, so a backend
6
- * candidate takes one when it outranks the UI candidates, not merely because it
7
- * exists. Measured against main on random mixed pools at `maxGenerate` 5 with no
8
- * caught bugs: 35% get fewer backend tests than main did, average 3.53 to 3.24.
9
- *
10
- * Backend-only floors nothing on purpose: of 86 backend-only diffs in the eval
11
- * corpus, ZERO generated a UI test, so a floor there would guarantee what
12
- * already happens every time.
13
- *
14
- * At one slot there is no room for a floor plus an open slot, so each class
15
- * keeps that slot for its own kind.
16
- *
17
- * The floors always sum to at most `maxGenerate`, which is what lets
18
- * `applyFloors` satisfy both without one starving the other.
19
- */
20
- export declare function generateFloors(ctx: BudgetContext): {
21
- ui: number;
22
- backend: number;
23
- };
24
- /**
25
- * Shared budgeting pipeline. All Budgeters run the same external-dedup,
26
- * attack-surface prioritization, and ADDITIONAL set-difference; they differ
27
- * ONLY in how they pick the GENERATE items from the slot-ordered list (`pick`).
28
- *
29
- * With `pick = roundRobinByType` this reproduces the pre-refactor selection in
30
- * diffExecutionPlan.ts exactly.
31
- */
32
- export declare function runBudget(ranked: Candidate[], rawCtx: BudgetContext, pick: (items: Candidate[], count: number) => Candidate[]): SelectionResult;