@skyramp/mcp 0.3.2-rc.pom-3 → 0.3.2

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 (289) hide show
  1. package/build/commands/commandLibrary.d.ts +1 -0
  2. package/build/commands/commandLibrary.js +19 -13
  3. package/build/commands/localDevTestChangesCommand.d.ts +15 -0
  4. package/build/commands/localDevTestChangesCommand.js +201 -0
  5. package/build/index.js +80 -6
  6. package/build/prompts/code-reuse.js +3 -0
  7. package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +1 -0
  8. package/build/prompts/enhance-assertions/sharedAssertionRules.js +17 -0
  9. package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +11 -9
  10. package/build/prompts/local-dev/local-dev-plan.d.ts +35 -0
  11. package/build/prompts/local-dev/local-dev-plan.js +429 -0
  12. package/build/prompts/local-dev/local-dev-prompts.d.ts +4 -0
  13. package/build/prompts/local-dev/local-dev-prompts.js +190 -0
  14. package/build/prompts/pom-aware-code-reuse.js +12 -0
  15. package/build/prompts/prompt-utils.d.ts +8 -0
  16. package/build/prompts/prompt-utils.js +33 -0
  17. package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +37 -14
  18. package/build/prompts/sut-setup/shared.js +16 -12
  19. package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
  20. package/build/prompts/test-recommendation/scopeAssessment.d.ts +24 -7
  21. package/build/prompts/test-recommendation/scopeAssessment.js +111 -12
  22. package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
  23. package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
  24. package/build/prompts/testbot/testbot-prompts.js +32 -52
  25. package/build/recommendation/planRanker.d.ts +15 -2
  26. package/build/recommendation/planRanker.js +76 -5
  27. package/build/resources/testbotResource.js +2 -1
  28. package/build/services/AnalyticsService.d.ts +1 -1
  29. package/build/services/TestExecutionService.d.ts +2 -1
  30. package/build/services/TestExecutionService.js +8 -3
  31. package/build/services/TestGenerationService.d.ts +2 -2
  32. package/build/services/TestGenerationService.js +39 -21
  33. package/build/services/containerEnv.js +3 -1
  34. package/build/tool-phases.js +6 -0
  35. package/build/tools/code-refactor/codeReuseTool.js +43 -4
  36. package/build/tools/code-refactor/enhanceAssertionsTool.js +68 -18
  37. package/build/tools/code-refactor/reuse-outcome.d.ts +109 -0
  38. package/build/tools/code-refactor/reuse-outcome.js +158 -0
  39. package/build/tools/code-refactor/reuse-state.d.ts +45 -0
  40. package/build/tools/code-refactor/reuse-state.js +140 -0
  41. package/build/tools/enrichTestWithMocksTool.d.ts +28 -0
  42. package/build/tools/enrichTestWithMocksTool.js +726 -0
  43. package/build/tools/executeSkyrampTestTool.d.ts +11 -0
  44. package/build/tools/executeSkyrampTestTool.js +62 -21
  45. package/build/tools/generate-tests/batchMockGenerationTool.d.ts +106 -0
  46. package/build/tools/generate-tests/batchMockGenerationTool.js +545 -0
  47. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +25 -23
  48. package/build/tools/generate-tests/generateContractRestTool.js +2 -2
  49. package/build/tools/generate-tests/generateE2ERestTool.d.ts +1 -1
  50. package/build/tools/generate-tests/generateE2ERestTool.js +1 -1
  51. package/build/tools/generate-tests/generateLoadRestTool.d.ts +1 -1
  52. package/build/tools/generate-tests/generateLoadRestTool.js +1 -1
  53. package/build/tools/generate-tests/generateMockRestTool.d.ts +179 -6
  54. package/build/tools/generate-tests/generateMockRestTool.js +391 -22
  55. package/build/tools/generate-tests/loadTestSchema.d.ts +1 -1
  56. package/build/tools/generate-tests/planGuard.js +2 -22
  57. package/build/tools/generateEnrichedIntegrationTestTool.d.ts +25 -0
  58. package/build/tools/generateEnrichedIntegrationTestTool.js +235 -0
  59. package/build/tools/localDevWorkerComposeTool.d.ts +24 -0
  60. package/build/tools/localDevWorkerComposeTool.js +264 -0
  61. package/build/tools/one-click/oneClickTool.d.ts +13 -0
  62. package/build/tools/one-click/oneClickTool.js +195 -24
  63. package/build/tools/preflightMockCheckTool.d.ts +2 -0
  64. package/build/tools/preflightMockCheckTool.js +96 -0
  65. package/build/tools/queryProxyMocksTool.d.ts +70 -0
  66. package/build/tools/queryProxyMocksTool.js +522 -0
  67. package/build/tools/runExistingTestsTool.d.ts +31 -0
  68. package/build/tools/runExistingTestsTool.js +214 -13
  69. package/build/tools/submitReportTool.js +38 -3
  70. package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
  71. package/build/tools/test-management/analyzeChangesTool.js +63 -40
  72. package/build/tools/test-management/registerTestPlanTool.js +55 -7
  73. package/build/tools/trace/startTraceCollectionTool.js +3 -3
  74. package/build/types/FrontendIntegration.d.ts +3 -0
  75. package/build/types/FrontendIntegration.js +3 -0
  76. package/build/types/OneClickCommands.d.ts +1 -1
  77. package/build/types/Recommendation.d.ts +20 -0
  78. package/build/types/Recommendation.js +32 -6
  79. package/build/types/RepositoryAnalysis.d.ts +131 -14
  80. package/build/types/RepositoryAnalysis.js +16 -2
  81. package/build/types/ReuseOutcome.d.ts +63 -0
  82. package/build/types/ReuseOutcome.js +33 -0
  83. package/build/types/TestExecution.d.ts +1 -0
  84. package/build/types/TestTypes.d.ts +25 -7
  85. package/build/types/TestTypes.js +22 -6
  86. package/build/types/TestbotReport.d.ts +7 -0
  87. package/build/types/index.d.ts +2 -0
  88. package/build/types/index.js +1 -0
  89. package/build/utils/AnalysisStateManager.d.ts +26 -0
  90. package/build/utils/AnalysisStateManager.js +31 -1
  91. package/build/utils/analyze-openapi.js +18 -1
  92. package/build/utils/branchDiff.d.ts +17 -1
  93. package/build/utils/branchDiff.js +99 -14
  94. package/build/utils/featureFlags.d.ts +31 -0
  95. package/build/utils/featureFlags.js +37 -0
  96. package/build/utils/frontendIntegration.js +8 -1
  97. package/build/utils/grpcMockValidation.d.ts +1 -0
  98. package/build/utils/grpcMockValidation.js +49 -0
  99. package/build/utils/httpMethodValidation.d.ts +4 -0
  100. package/build/utils/httpMethodValidation.js +15 -0
  101. package/build/utils/logger.js +1 -1
  102. package/build/utils/mockCompatibility.d.ts +49 -0
  103. package/build/utils/mockCompatibility.js +82 -0
  104. package/build/utils/pom-scope/pom-files.js +7 -0
  105. package/build/utils/pom-verify/bindings.js +11 -1
  106. package/build/utils/pom-verify/re-exports.d.ts +10 -0
  107. package/build/utils/pom-verify/re-exports.js +61 -0
  108. package/build/utils/pom-verify/resolve.d.ts +4 -1
  109. package/build/utils/pom-verify/resolve.js +7 -4
  110. package/build/utils/pom-verify/verify.d.ts +5 -0
  111. package/build/utils/pom-verify/verify.js +25 -3
  112. package/build/utils/progress.js +10 -5
  113. package/build/utils/proxy-terminal.js +3 -3
  114. package/build/utils/routeParsers.d.ts +3 -9
  115. package/build/utils/routeParsers.js +79 -4
  116. package/build/utils/utils.js +2 -2
  117. package/build/utils/versions.d.ts +4 -3
  118. package/build/utils/versions.js +3 -1
  119. package/build/utils/workspaceAuth.d.ts +46 -0
  120. package/build/utils/workspaceAuth.js +156 -1
  121. package/build/workspace/testSuites.d.ts +2 -1
  122. package/build/workspace/testSuites.js +1 -1
  123. package/build/workspace/workspace.d.ts +108 -32
  124. package/build/workspace/workspace.js +32 -4
  125. package/package.json +5 -2
  126. package/build/adapters/jestAdapter.test.d.ts +0 -1
  127. package/build/adapters/jestAdapter.test.js +0 -93
  128. package/build/adapters/mochaAdapter.test.d.ts +0 -1
  129. package/build/adapters/mochaAdapter.test.js +0 -63
  130. package/build/adapters/playwrightAdapter.test.d.ts +0 -1
  131. package/build/adapters/playwrightAdapter.test.js +0 -169
  132. package/build/adapters/pytestAdapter.test.d.ts +0 -1
  133. package/build/adapters/pytestAdapter.test.js +0 -90
  134. package/build/prompts/code-reuse.test.d.ts +0 -1
  135. package/build/prompts/code-reuse.test.js +0 -62
  136. package/build/prompts/pom-aware-code-reuse.test.d.ts +0 -1
  137. package/build/prompts/pom-aware-code-reuse.test.js +0 -11
  138. package/build/prompts/test-maintenance/drift-analysis-prompt.test.d.ts +0 -1
  139. package/build/prompts/test-maintenance/drift-analysis-prompt.test.js +0 -51
  140. package/build/prompts/test-recommendation/analysisOutputPrompt.test.d.ts +0 -1
  141. package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +0 -264
  142. package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.d.ts +0 -1
  143. package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.js +0 -126
  144. package/build/prompts/test-recommendation/promptPlan.test.d.ts +0 -1
  145. package/build/prompts/test-recommendation/promptPlan.test.js +0 -336
  146. package/build/prompts/test-recommendation/scopeAssessment.test.d.ts +0 -1
  147. package/build/prompts/test-recommendation/scopeAssessment.test.js +0 -371
  148. package/build/prompts/test-recommendation/test-recommendation-prompt.test.d.ts +0 -1
  149. package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +0 -1925
  150. package/build/prompts/testbot/testbot-prompts.test.d.ts +0 -1
  151. package/build/prompts/testbot/testbot-prompts.test.js +0 -451
  152. package/build/recommendation/budgeters/diversityBalancedBudgeter.test.d.ts +0 -1
  153. package/build/recommendation/budgeters/diversityBalancedBudgeter.test.js +0 -138
  154. package/build/recommendation/budgeters/fixedNBudgeter.test.d.ts +0 -1
  155. package/build/recommendation/budgeters/fixedNBudgeter.test.js +0 -66
  156. package/build/recommendation/discriminators.test.d.ts +0 -1
  157. package/build/recommendation/discriminators.test.js +0 -324
  158. package/build/recommendation/diversity.test.d.ts +0 -1
  159. package/build/recommendation/diversity.test.js +0 -77
  160. package/build/recommendation/planRanker.test.d.ts +0 -1
  161. package/build/recommendation/planRanker.test.js +0 -110
  162. package/build/resources/testbotResource.test.d.ts +0 -1
  163. package/build/resources/testbotResource.test.js +0 -44
  164. package/build/services/AnalyticsService.test.d.ts +0 -1
  165. package/build/services/AnalyticsService.test.js +0 -86
  166. package/build/services/ScenarioGenerationService.integration.test.d.ts +0 -1
  167. package/build/services/ScenarioGenerationService.integration.test.js +0 -162
  168. package/build/services/ScenarioGenerationService.test.d.ts +0 -1
  169. package/build/services/ScenarioGenerationService.test.js +0 -414
  170. package/build/services/TestDiscoveryService.test.d.ts +0 -1
  171. package/build/services/TestDiscoveryService.test.js +0 -983
  172. package/build/services/TestExecutionService.test.d.ts +0 -1
  173. package/build/services/TestExecutionService.test.js +0 -1032
  174. package/build/services/TestGenerationService.test.d.ts +0 -1
  175. package/build/services/TestGenerationService.test.js +0 -578
  176. package/build/tool-phase-coverage.test.d.ts +0 -1
  177. package/build/tool-phase-coverage.test.js +0 -49
  178. package/build/tools/code-refactor/codeReuseTool.test.d.ts +0 -1
  179. package/build/tools/code-refactor/codeReuseTool.test.js +0 -572
  180. package/build/tools/generate-tests/generateBatchScenarioRestTool.test.d.ts +0 -1
  181. package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +0 -433
  182. package/build/tools/generate-tests/generateContractRestTool.test.d.ts +0 -1
  183. package/build/tools/generate-tests/generateContractRestTool.test.js +0 -142
  184. package/build/tools/generate-tests/generateIntegrationRestTool.test.d.ts +0 -1
  185. package/build/tools/generate-tests/generateIntegrationRestTool.test.js +0 -159
  186. package/build/tools/generate-tests/generateLoadRestTool.test.d.ts +0 -1
  187. package/build/tools/generate-tests/generateLoadRestTool.test.js +0 -169
  188. package/build/tools/generate-tests/generateUIRestTool.test.d.ts +0 -1
  189. package/build/tools/generate-tests/generateUIRestTool.test.js +0 -32
  190. package/build/tools/generate-tests/planGuard.test.d.ts +0 -1
  191. package/build/tools/generate-tests/planGuard.test.js +0 -185
  192. package/build/tools/generate-tests/scenarioLint.test.d.ts +0 -1
  193. package/build/tools/generate-tests/scenarioLint.test.js +0 -100
  194. package/build/tools/runExistingTestsTool.test.d.ts +0 -1
  195. package/build/tools/runExistingTestsTool.test.js +0 -379
  196. package/build/tools/submitReportTool.test.d.ts +0 -1
  197. package/build/tools/submitReportTool.test.js +0 -1428
  198. package/build/tools/test-management/actionsTool.test.d.ts +0 -1
  199. package/build/tools/test-management/actionsTool.test.js +0 -436
  200. package/build/tools/test-management/analyzeChangesTool.test.d.ts +0 -1
  201. package/build/tools/test-management/analyzeChangesTool.test.js +0 -522
  202. package/build/tools/test-management/analyzeTestHealthTool.test.d.ts +0 -1
  203. package/build/tools/test-management/analyzeTestHealthTool.test.js +0 -385
  204. package/build/tools/test-management/registerTestPlanTool.test.d.ts +0 -1
  205. package/build/tools/test-management/registerTestPlanTool.test.js +0 -296
  206. package/build/tools/trace/resolveSaveStoragePath.test.d.ts +0 -1
  207. package/build/tools/trace/resolveSaveStoragePath.test.js +0 -17
  208. package/build/tools/trace/resolveSessionPaths.test.d.ts +0 -1
  209. package/build/tools/trace/resolveSessionPaths.test.js +0 -104
  210. package/build/tools/trace/sessionState.test.d.ts +0 -1
  211. package/build/tools/trace/sessionState.test.js +0 -17
  212. package/build/tools/workspace/initializeWorkspaceTool.test.d.ts +0 -1
  213. package/build/tools/workspace/initializeWorkspaceTool.test.js +0 -139
  214. package/build/tools/workspace/serviceUpsert.test.d.ts +0 -1
  215. package/build/tools/workspace/serviceUpsert.test.js +0 -50
  216. package/build/utils/AnalysisStateManager.test.d.ts +0 -1
  217. package/build/utils/AnalysisStateManager.test.js +0 -134
  218. package/build/utils/dartRouteExtractor.test.d.ts +0 -1
  219. package/build/utils/dartRouteExtractor.test.js +0 -307
  220. package/build/utils/docker.test.d.ts +0 -1
  221. package/build/utils/docker.test.js +0 -114
  222. package/build/utils/featureFlags.test.d.ts +0 -1
  223. package/build/utils/featureFlags.test.js +0 -81
  224. package/build/utils/fileWalk.test.d.ts +0 -1
  225. package/build/utils/fileWalk.test.js +0 -252
  226. package/build/utils/frontendIntegration.test.d.ts +0 -1
  227. package/build/utils/frontendIntegration.test.js +0 -229
  228. package/build/utils/frontendSelectors.test.d.ts +0 -1
  229. package/build/utils/frontendSelectors.test.js +0 -118
  230. package/build/utils/gitStaging.test.d.ts +0 -1
  231. package/build/utils/gitStaging.test.js +0 -111
  232. package/build/utils/httpDefaults.test.d.ts +0 -1
  233. package/build/utils/httpDefaults.test.js +0 -21
  234. package/build/utils/importerHop.test.d.ts +0 -1
  235. package/build/utils/importerHop.test.js +0 -469
  236. package/build/utils/pathAffinityClassification.test.d.ts +0 -1
  237. package/build/utils/pathAffinityClassification.test.js +0 -208
  238. package/build/utils/planMatchKeys.test.d.ts +0 -1
  239. package/build/utils/planMatchKeys.test.js +0 -123
  240. package/build/utils/pom-scope/index.test.d.ts +0 -1
  241. package/build/utils/pom-scope/index.test.js +0 -278
  242. package/build/utils/pom-scope/pom-files.test.d.ts +0 -1
  243. package/build/utils/pom-scope/pom-files.test.js +0 -29
  244. package/build/utils/pom-scope/scoring.test.d.ts +0 -1
  245. package/build/utils/pom-scope/scoring.test.js +0 -39
  246. package/build/utils/pom-scope/selector-extractor.test.d.ts +0 -1
  247. package/build/utils/pom-scope/selector-extractor.test.js +0 -67
  248. package/build/utils/pom-verify/bindings.test.d.ts +0 -1
  249. package/build/utils/pom-verify/bindings.test.js +0 -164
  250. package/build/utils/pom-verify/calls.test.d.ts +0 -1
  251. package/build/utils/pom-verify/calls.test.js +0 -61
  252. package/build/utils/pom-verify/resolve.test.d.ts +0 -1
  253. package/build/utils/pom-verify/resolve.test.js +0 -114
  254. package/build/utils/pom-verify/verify.test.d.ts +0 -1
  255. package/build/utils/pom-verify/verify.test.js +0 -374
  256. package/build/utils/pr-comment-parser.test.d.ts +0 -1
  257. package/build/utils/pr-comment-parser.test.js +0 -428
  258. package/build/utils/progress.test.d.ts +0 -1
  259. package/build/utils/progress.test.js +0 -37
  260. package/build/utils/projectMetadata.test.d.ts +0 -1
  261. package/build/utils/projectMetadata.test.js +0 -172
  262. package/build/utils/pythonMountPrefixes.test.d.ts +0 -1
  263. package/build/utils/pythonMountPrefixes.test.js +0 -113
  264. package/build/utils/repoScanner.test.d.ts +0 -1
  265. package/build/utils/repoScanner.test.js +0 -190
  266. package/build/utils/reportVerification.test.d.ts +0 -1
  267. package/build/utils/reportVerification.test.js +0 -185
  268. package/build/utils/routeParsers.test.d.ts +0 -1
  269. package/build/utils/routeParsers.test.js +0 -1011
  270. package/build/utils/scenarioDrafting.test.d.ts +0 -1
  271. package/build/utils/scenarioDrafting.test.js +0 -876
  272. package/build/utils/sourceRouteExtractor.test.d.ts +0 -1
  273. package/build/utils/sourceRouteExtractor.test.js +0 -738
  274. package/build/utils/telemetry.test.d.ts +0 -1
  275. package/build/utils/telemetry.test.js +0 -70
  276. package/build/utils/trace-parser.test.d.ts +0 -6
  277. package/build/utils/trace-parser.test.js +0 -140
  278. package/build/utils/uiPageEnumerator.test.d.ts +0 -1
  279. package/build/utils/uiPageEnumerator.test.js +0 -824
  280. package/build/utils/utils.test.d.ts +0 -1
  281. package/build/utils/utils.test.js +0 -103
  282. package/build/utils/walkerCharacterization.test.d.ts +0 -1
  283. package/build/utils/walkerCharacterization.test.js +0 -233
  284. package/build/utils/workspaceAuth.test.d.ts +0 -1
  285. package/build/utils/workspaceAuth.test.js +0 -260
  286. package/build/workspace/testSuites.test.d.ts +0 -1
  287. package/build/workspace/testSuites.test.js +0 -23
  288. package/build/workspace/workspace.test.d.ts +0 -1
  289. package/build/workspace/workspace.test.js +0 -264
@@ -288,11 +288,11 @@ The ranked test recommendation catalog is pre-built and shown below (after the s
288
288
  **If** Steps 1–2 revealed additional scenarios the catalog does not cover (e.g. a computed formula or foreign-key relationship that was missed), you may optionally call \`skyramp_recommend_tests\` with \`stateFile: "${p.stateFile ?? p.sessionId}"\` and \`enrichedScenarios\` to regenerate a more complete catalog — but only after presenting the current one.`;
289
289
  const hasJavaFiles = p.candidateRouteFiles?.some((f) => /\.(java|kt)$/.test(f)) ?? false;
290
290
  const routeFilesSection = p.candidateRouteFiles && p.candidateRouteFiles.length > 0
291
- ? `\nRoute/controller files found by static scan (read these to discover endpoints — the regex-based catalog below may be incomplete for your framework):\n${p.candidateRouteFiles.map((f) => `- ${f}`).join("\n")}\n`
291
+ ? `\nCandidate route/controller files for LLM inspection (read these to discover endpoints; static parser output is only a hint):\n${p.candidateRouteFiles.map((f) => `- ${f}`).join("\n")}\n`
292
292
  : "";
293
293
  const resolvePathsNote = p.routerMountContext.length
294
- ? `**Resolve nested paths** using your Step ${ANALYSIS_STEP_RESOLVE_PATHS} table — a router in the table with prefix \`/api/v1/products/{product_id}/reviews\` means every endpoint in that file lives under that full path.`
295
- : `**Resolve full paths** using the prefixes you identified in Step ${ANALYSIS_STEP_READ_FILES} (e.g. Java Spring class-level \`@RequestMapping\` prefix + method-level path).`;
294
+ ? `**Resolve nested paths** using your Step ${ANALYSIS_STEP_RESOLVE_PATHS} table. The table you build from source/router context is authoritative over static parser hints.`
295
+ : `**Resolve full paths** using the prefixes, mounts, annotations, decorators, or routing DSL you identify in Step ${ANALYSIS_STEP_READ_FILES}.`;
296
296
  return `## Your Task — Fill in and Present the Catalog (full repo)
297
297
 
298
298
  ### Step ${ANALYSIS_STEP_READ_FILES}: Read key files
@@ -335,36 +335,28 @@ ${p.routerFileContents?.length
335
335
  p.routerMountContext.map((f) => `- \`${f}\``).join("\n")}`
336
336
  : "";
337
337
  const enrichment = buildEnrichmentInstructions(p);
338
- // LLM fallback: when heuristic scanning may have missed backend route files.
339
- const scannerFallbackHasCatalog = p.scannedEndpoints.length > 0;
340
- const scannerFallbackReason = scannerFallbackHasCatalog
341
- ? "The heuristic endpoint scanner may have missed route/controller files in this diff, even though endpoint data is present from another source"
342
- : "The heuristic endpoint scanner found **0 endpoints**";
343
- const scannerFallbackInstruction = scannerFallbackHasCatalog
344
- ? "Treat this as a supplemental gap check: merge only HTTP endpoints that are visibly missing from the existing catalog/spec data, and keep the existing catalog as the primary endpoint source."
345
- : "Build a table of discovered endpoints (method, full path, source file) and use it as the authoritative endpoint list for all subsequent steps.";
346
- const llmFallbackSection = (isDiffScope || p.scannedEndpoints.length === 0) &&
347
- p.candidateRouteFiles &&
348
- p.candidateRouteFiles.length > 0
338
+ const staticHintCount = p.scannedEndpoints.length;
339
+ const routeDiscoverySection = isDiffScope ||
340
+ staticHintCount > 0 ||
341
+ (p.candidateRouteFiles?.length ?? 0) > 0
349
342
  ? `
350
- ## Scanner Fallback — Manual Endpoint Discovery Required
343
+ ## LLM Route Discovery Inputs
351
344
 
352
- ${scannerFallbackReason}, and this repository contains ${p.candidateRouteFiles.length} file(s) that appear to define HTTP routes. The scanner likely failed due to multi-line definitions, framework-specific patterns, indirection the regex does not cover, or endpoint data being supplied by an OpenAPI spec instead of source scanning.
345
+ Static endpoint scan results are **best-effort hints only**. Do not assume the scanner supports this repository's language, framework, routing DSL, or helper abstractions.
353
346
 
354
- **Read the following controller/router files and identify all HTTP route registrations (method + path).** Include these discovered endpoints when executing the steps above.
347
+ ${staticHintCount > 0 ? `Static hints available: ${staticHintCount}. Verify every hinted method/path against source before using it.` : "Static hints available: 0. Build the endpoint list from source, router context, spec, and diff."}
355
348
 
356
- ${p.candidateRouteFiles.slice(0, 15).map((f) => `- \`${f}\``).join("\n")}
357
- ${p.candidateRouteFiles.length > 15 ? `\n_(${p.candidateRouteFiles.length - 15} more files not shown)_` : ""}
349
+ ${p.candidateRouteFiles && p.candidateRouteFiles.length > 0
350
+ ? `Candidate files to inspect:\n${p.candidateRouteFiles
351
+ .slice(0, 15)
352
+ .map((f) => `- \`${f}\``)
353
+ .join("\n")}${p.candidateRouteFiles.length > 15 ? `\n_(${p.candidateRouteFiles.length - 15} more files not shown)_` : ""}`
354
+ : staticHintCount > 0
355
+ ? "Candidate files to inspect: none identified by static scanning. Verify the static hints against their source files, changed files, router context, and specs above."
356
+ : "Candidate files to inspect: use the changed files and routing entry-point files above."}
358
357
 
359
- For each file, look for:
360
- - Express/Fastify/Koa/Hapi: \`router.<method>('/path', ...)\` or \`app.<method>('/path', ...)\`
361
- - FastAPI/Flask: \`@router.<method>('/path')\` or \`@app.route('/path', ...)\`
362
- - Spring/NestJS: \`@GetMapping\`, \`@PostMapping\`, \`@Controller\`, etc.
363
- - Go (Gin/Echo/Chi): \`r.GET("/path", ...)\`, \`r.Group("/prefix")\`
364
- - GraphQL: schema/resolver artifacts are unsupported for REST test generation; do not invent REST endpoints from them
365
- - Any other framework-specific route registration pattern
366
-
367
- ${scannerFallbackInstruction}`
358
+ For each candidate file, infer route registrations from the source's actual framework or DSL. Record method, full path, and source file. Resolve mount prefixes through the routing entry-point files. If a file is GraphQL-only, do not invent REST endpoints from it.
359
+ `
368
360
  : "";
369
361
  return `# Repository Analysis
370
362
 
@@ -373,7 +365,7 @@ ${scannerFallbackInstruction}`
373
365
  **Analysis Scope**: \`${p.analysisScope}\`
374
366
  ${isDiffScope ? `**Diff endpoints**: ${(p.parsedDiff?.newEndpoints.length ?? 0) + (p.parsedDiff?.modifiedEndpoints.length ?? 0) + (p.parsedDiff?.removedEndpoints?.length ?? 0)}` : `**Pre-scanned endpoints**: ${p.scannedEndpoints.length}`}
375
367
  ${routerSection}
376
- ${llmFallbackSection}
368
+ ${routeDiscoverySection}
377
369
  ${enrichment}
378
370
 
379
371
  **CRITICAL**: No .json/.md file creation. Prioritize cross-resource workflows.`;
@@ -1,14 +1,28 @@
1
+ /**
2
+ * Returns true if the file path looks like i18n/locale content (a JSON/YAML
3
+ * file under an i18n/, locale(s)/, lang/, or translation(s)/ directory).
4
+ * Exported so callers outside the frontend/backend classification itself
5
+ * (e.g. the production-importer integration check, which greps for static
6
+ * quoted imports) can special-case these files: real i18n loaders commonly
7
+ * fetch locale content by a templated runtime path rather than a static
8
+ * import, so the grep-based check would otherwise misreport them as
9
+ * unintegrated dead code (SKYR-3977).
10
+ */
11
+ export declare function isI18nLocaleFile(filePath: string): boolean;
1
12
  export declare function hasFlutterSdkDep(repositoryPath: string): boolean;
2
13
  /**
3
14
  * Returns true if the file path is a frontend file.
4
15
  *
5
- * Four tiers:
16
+ * Five tiers:
6
17
  * 1. .vue / .svelte — always frontend; these file types cannot be route handlers.
7
18
  * 2. .dart — always frontend IFF `hasFlutterSdkDep` is true (Flutter project).
8
- * Sits in tier 1 because there's no server-side Dart web framework in scope
9
- * when the Flutter SDK dep is present, so `API_DIR_PATTERN` doesn't apply.
10
- * 3. .tsx / .jsx — frontend unless in an API/backend directory (e.g. pages/api/).
11
- * 4. Ambiguous extensions (.ts, .js, .css, .html, …) — require a recognised
19
+ * Sits above `STRICT_API_DIR_PATTERN` because there's no server-side Dart
20
+ * web framework in scope when the Flutter SDK dep is present.
21
+ * 3. i18n/locale JSON/YAML (e.g. locales/en/common.json) — frontend UNLESS in
22
+ * an API/backend directory (e.g. an i18n content API under api/), since
23
+ * unlike tiers 1-2 a locale file CAN be served by a backend route (SKYR-3977).
24
+ * 4. .tsx / .jsx — frontend unless in an API/backend directory (e.g. pages/api/).
25
+ * 5. Ambiguous extensions (.ts, .js, .css, .html, …) — require a recognised
12
26
  * frontend directory AND must not be in an API/backend directory.
13
27
  *
14
28
  * The `hasFlutterSdkDep` opt is passed in by the budget-driving caller
@@ -40,8 +54,11 @@ export declare function isTestFile(filePath: string): boolean;
40
54
  /**
41
55
  * Builds the PR scope assessment section.
42
56
  *
43
- * When `precomputedUIPct` is provided (0 = backend-only, 100 = UI-only) the server
44
- * has already determined the split unambiguously — skip Steps A–C and emit one line.
57
+ * When `precomputedUIPct` is provided (0 = backend-only, 100 = UI-only) the server has
58
+ * already determined the split unambiguously, so Steps A–C are skipped. Backend-only
59
+ * (0) renders a single Budget Plan line; UI-only (100) renders that line plus the
60
+ * zero-new-surface override (SKYR-4099), because the budget is a default there rather
61
+ * than a mandate and a diff that adds no new surface must be able to abstain.
45
62
  *
46
63
  * For mixed PRs (`precomputedUIPct` is undefined, `hasFrontendChanges` is true) skip
47
64
  * Steps A–C but keep Step D so the LLM can apply judgment to determine the UI%.
@@ -18,6 +18,25 @@ const STRICT_API_DIR_PATTERN = /\/(api)\//i;
18
18
  // a .tsx file in routes/ is a page component, not a route handler.
19
19
  const FRAMEWORK_ROUTE_DIR_PATTERN = /\/(routes?|controllers?|routers?|handlers?|endpoints?|server)\//i;
20
20
  const FRONTEND_DIR_PATTERN = /(^|\/)(components?|pages?|views?|layouts?|app|src\/app|frontend|client|public|styles?|templates?)\//i;
21
+ // A .json (or .yaml/.yml) file nested under an i18n/locale directory holds only
22
+ // user-facing copy — frontend regardless of nesting depth (subject to the
23
+ // /api/ check above), unlike generic JSON (package.json, tsconfig.json, data
24
+ // fixtures) which needs no such carve-out since it's never under one of these
25
+ // directory names.
26
+ const I18N_LOCALE_FILE_PATTERN = /(^|\/)(i18n|locales?|lang|translations?)\/.*\.(json|ya?ml)$/i;
27
+ /**
28
+ * Returns true if the file path looks like i18n/locale content (a JSON/YAML
29
+ * file under an i18n/, locale(s)/, lang/, or translation(s)/ directory).
30
+ * Exported so callers outside the frontend/backend classification itself
31
+ * (e.g. the production-importer integration check, which greps for static
32
+ * quoted imports) can special-case these files: real i18n loaders commonly
33
+ * fetch locale content by a templated runtime path rather than a static
34
+ * import, so the grep-based check would otherwise misreport them as
35
+ * unintegrated dead code (SKYR-3977).
36
+ */
37
+ export function isI18nLocaleFile(filePath) {
38
+ return I18N_LOCALE_FILE_PATTERN.test(filePath);
39
+ }
21
40
  /**
22
41
  * Returns true if the repository root's `pubspec.yaml` declares a Flutter
23
42
  * SDK dependency (i.e., a `flutter:` key with `sdk: flutter` under
@@ -105,13 +124,16 @@ function checkPubspec(pubspecPath) {
105
124
  /**
106
125
  * Returns true if the file path is a frontend file.
107
126
  *
108
- * Four tiers:
127
+ * Five tiers:
109
128
  * 1. .vue / .svelte — always frontend; these file types cannot be route handlers.
110
129
  * 2. .dart — always frontend IFF `hasFlutterSdkDep` is true (Flutter project).
111
- * Sits in tier 1 because there's no server-side Dart web framework in scope
112
- * when the Flutter SDK dep is present, so `API_DIR_PATTERN` doesn't apply.
113
- * 3. .tsx / .jsx — frontend unless in an API/backend directory (e.g. pages/api/).
114
- * 4. Ambiguous extensions (.ts, .js, .css, .html, …) — require a recognised
130
+ * Sits above `STRICT_API_DIR_PATTERN` because there's no server-side Dart
131
+ * web framework in scope when the Flutter SDK dep is present.
132
+ * 3. i18n/locale JSON/YAML (e.g. locales/en/common.json) — frontend UNLESS in
133
+ * an API/backend directory (e.g. an i18n content API under api/), since
134
+ * unlike tiers 1-2 a locale file CAN be served by a backend route (SKYR-3977).
135
+ * 4. .tsx / .jsx — frontend unless in an API/backend directory (e.g. pages/api/).
136
+ * 5. Ambiguous extensions (.ts, .js, .css, .html, …) — require a recognised
115
137
  * frontend directory AND must not be in an API/backend directory.
116
138
  *
117
139
  * The `hasFlutterSdkDep` opt is passed in by the budget-driving caller
@@ -126,8 +148,13 @@ export function isFrontendFile(filePath, { hasFlutterSdkDep = false } = {}) {
126
148
  if (hasFlutterSdkDep && FLUTTER_DART_EXT.test(filePath))
127
149
  return true;
128
150
  // /api/ always means backend regardless of extension (Next.js API routes use .tsx).
151
+ // Must run before I18N_LOCALE_FILE_PATTERN — unlike .vue/.svelte/.dart, a locale
152
+ // JSON/YAML file CAN be served by a backend route (e.g. an i18n content API), so
153
+ // it doesn't get the same always-frontend exemption as those extensions.
129
154
  if (STRICT_API_DIR_PATTERN.test(filePath))
130
155
  return false;
156
+ if (isI18nLocaleFile(filePath))
157
+ return true;
131
158
  // routes/, controllers/ etc. block .ts/.js but not .tsx/.jsx — Medusa Admin and
132
159
  // React Router place page components under routes/, not API handlers.
133
160
  if (!LIKELY_FRONTEND_EXT.test(filePath) && FRAMEWORK_ROUTE_DIR_PATTERN.test(filePath))
@@ -161,11 +188,77 @@ export function isTestFile(filePath) {
161
188
  /(?:^|\/)__tests__\//.test(filePath));
162
189
  }
163
190
  // ── LLM scope assessment ──────────────────────────────────────────────────────
191
+ /**
192
+ * The zero-new-surface abstention rule, shared by every branch that can see a frontend
193
+ * diff (SKYR-4099).
194
+ *
195
+ * It previously existed only on the mixed-PR branch, so a frontend-ONLY diff — which
196
+ * takes the precomputed branch — had no sanctioned path to zero tests and the agent
197
+ * generated unnecessary UI tests while stating in its own reasoning that the change was
198
+ * cosmetic. Two copies then meant two definitions, and the mixed-PR one carved out
199
+ * "changes that alter visibility, layout, or state", which classifies a spacing-token
200
+ * change as non-cosmetic and made the override inert for exactly the diffs it should
201
+ * catch. One definition, both branches.
202
+ *
203
+ * Scoped to match `testbot-prompts.ts`'s "Do not fabricate tests outside the GENERATE
204
+ * list", which names three zero-test cases — deletion-only, cosmetic, and
205
+ * modification-of-existing-with-no-new-surface — under one principle: a new spec covers
206
+ * NEW observable surface only. An earlier revision of this section implemented cosmetic
207
+ * alone, and its keep-the-budget list contradicted the other two (an element being
208
+ * removed, or a `data-testid` being renamed, both forced the budget to stand). The test
209
+ * is coverage, not the kind of edit: does an existing test already reach this surface?
210
+ *
211
+ * `skipClause` is appended to the opening paragraph: the mixed-PR branch has a UI%
212
+ * step that becomes irrelevant once the budget is 0, the precomputed branch does not.
213
+ */
214
+ /**
215
+ * The zero-new-surface abstention rule, shared by every branch that can see a frontend
216
+ * diff (SKYR-4099).
217
+ *
218
+ * It previously existed only on the mixed-PR branch, so a frontend-ONLY diff — which
219
+ * takes the precomputed branch — had no sanctioned path to zero tests and the agent
220
+ * generated unnecessary UI tests while stating in its own reasoning that the change was
221
+ * cosmetic. Two copies then meant two definitions, and the mixed-PR one carved out
222
+ * "changes that alter visibility, layout, or state", which classifies a spacing-token
223
+ * change as non-cosmetic and made the override inert for exactly the diffs it should
224
+ * catch. One definition, both branches.
225
+ *
226
+ * Scoped to match `testbot-prompts.ts`'s "Do not fabricate tests outside the GENERATE
227
+ * list", which names three zero-test cases — deletion-only, cosmetic, and
228
+ * modification-of-existing-with-no-new-surface — under one principle: a new spec covers
229
+ * NEW observable surface only. The test is coverage, not the kind of edit: does an
230
+ * existing test already reach this surface?
231
+ *
232
+ * `skipClause` is appended to the opening paragraph: the mixed-PR branch has a UI% step
233
+ * that becomes irrelevant once the budget is 0, the precomputed branch does not.
234
+ */
235
+ function zeroSurfaceSection(skipClause = "") {
236
+ return `**Zero-new-surface override:** The total above is a default, not a mandate. A new spec exists to cover **new observable surface** — a component, route, page or flow that no existing test reaches. If your code review finds the diff adds none, set your Budget Plan to **0 total** and abstain — recommend and generate zero tests${skipClause}.
237
+
238
+ Abstain — the diff adds no new surface:
239
+ - **Cosmetic.** A styling-only value change (a spacing, size, color or font token, or a utility class swap such as \`size-4\`→\`size-5\`), or a \`.css\`/\`.scss\` reformat (property reordering, comment or whitespace edits, \`0px\`→\`0\`).
240
+ - **Deletion-only.** A component, route, element or feature was removed. The work is DELETING the tests that covered it — a removed surface cannot be the subject of a new spec.
241
+ - **Modification of an already-covered surface.** A renamed or moved selector, \`data-testid\`, \`aria-*\` or role; changed copy; an added field; a reordered or conditionally hidden element — where an existing test already reaches it. The work is UPDATING that test in place.
242
+
243
+ Keep the budget only for surface no existing test covers:
244
+ - A newly added component, route, page or flow.
245
+ - A component that was previously unintegrated and now has an integration point.
246
+ - New interactive behavior, state or validation on a surface no existing test reaches.
247
+
248
+ Two things that are NOT evidence of new surface: a frontend file appearing in the diff, and a large diff. Judge by whether an existing test already reaches the changed surface.
249
+
250
+ With a 0-total Budget Plan the work this diff needs is maintenance of the tests that already cover the affected surface — update the ones whose selectors or copy moved, and delete the ones that covered something this diff removed. Do NOT add a spec asserting that a removed feature is absent: the tests that covered it are the ones to delete, and an "is not present" assertion breaks the next time an unrelated sibling element changes.
251
+
252
+ If nothing currently covers the changed surface, there is no maintenance to do — and still no new spec to write, because a test added now would assert behavior this diff did not change. The missing coverage is a pre-existing gap, not something this PR introduced. Record it in \`additionalRecommendations\` in recommendatory voice ("would verify …") so the gap is visible without claiming a test was written.`;
253
+ }
164
254
  /**
165
255
  * Builds the PR scope assessment section.
166
256
  *
167
- * When `precomputedUIPct` is provided (0 = backend-only, 100 = UI-only) the server
168
- * has already determined the split unambiguously — skip Steps A–C and emit one line.
257
+ * When `precomputedUIPct` is provided (0 = backend-only, 100 = UI-only) the server has
258
+ * already determined the split unambiguously, so Steps A–C are skipped. Backend-only
259
+ * (0) renders a single Budget Plan line; UI-only (100) renders that line plus the
260
+ * zero-new-surface override (SKYR-4099), because the budget is a default there rather
261
+ * than a mandate and a diff that adds no new surface must be able to abstain.
169
262
  *
170
263
  * For mixed PRs (`precomputedUIPct` is undefined, `hasFrontendChanges` is true) skip
171
264
  * Steps A–C but keep Step D so the LLM can apply judgment to determine the UI%.
@@ -200,14 +293,20 @@ Budget Plan: 0 total — no new, modified, or removed endpoints were classified
200
293
 
201
294
  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.
202
295
 
203
- **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 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. 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.`;
296
+ **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. 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.`;
204
297
  }
205
- // Unambiguous backend-only or UI-only: emit a single Budget Plan line — no LLM counting needed.
298
+ // Unambiguous backend-only or UI-only: no LLM counting needed. Backend-only emits just
299
+ // the Budget Plan line; UI-only appends the zero-new-surface override (see below).
206
300
  if (precomputedUIPct !== undefined) {
301
+ const uiSuffix = precomputedUIPct > 0 ? `, ${precomputedUIPct}% UI/E2E` : "";
302
+ // Ordered ahead of the "use these exact numbers" line — that line reads as final, so
303
+ // an override printed after it cannot fire. Backend-only (precomputedUIPct === 0) has
304
+ // no frontend file to call cosmetic, so it gets no override.
305
+ const cosmeticOverride = precomputedUIPct > 0 ? `${zeroSurfaceSection()}\n\n` : "";
207
306
  return `### PR Scope Assessment
208
- Budget Plan: ${maxTotal} total (${effectiveGenerate} generate + ${additional} additional), ${precomputedUIPct}% UI/E2E
307
+ Budget Plan: ${maxTotal} total (${effectiveGenerate} generate + ${additional} additional)${uiSuffix}
209
308
 
210
- Use these exact numbers throughout the rest of the prompt.`;
309
+ ${cosmeticOverride}Use these exact numbers throughout the rest of the prompt.`;
211
310
  }
212
311
  // Mixed PR: server can pre-compute the total but not the UI/E2E split — keep Step D.
213
312
  if (hasFrontendChanges) {
@@ -215,7 +314,7 @@ Use these exact numbers throughout the rest of the prompt.`;
215
314
 
216
315
  Budget Plan (total already determined): **${maxTotal} total (${effectiveGenerate} generate + ${additional} additional)**
217
316
 
218
- **Cosmetic-only override:** If, after code review, the entire diff is cosmetic with no observable rendering or interaction change (e.g. a \`.css\`/\`.scss\` reformat — property reordering, comment/whitespace edits, \`0px\`→\`0\`), the total above does NOT apply — set your Budget Plan to **0 total**, abstain (recommend and generate zero tests), and skip Step D below. A frontend file appearing in the diff is not, by itself, a behavior change. (Style changes that alter visibility, layout, or state are NOT cosmetic — keep the budget for those.)
317
+ ${zeroSurfaceSection(", and skip Step D below")}
219
318
 
220
319
  **Step D — Determine UI vs backend split for the budget above:**
221
320
  - Non-UI slots are backend tests; start from file-count ratio for UI%, then apply judgment:
@@ -178,6 +178,9 @@ Write UI recommendation \`reasoning\` fields in **natural prose** that names ele
178
178
  }
179
179
  }
180
180
  const fmtEndpoint = (m, ep) => ` ${m.method} ${ep.path}${m.authRequired ? " [auth]" : ""} (${(m.interactions ?? []).length} interactions)`;
181
+ // In diff scope, cap the reference endpoint list to prevent context overflow.
182
+ // Changed endpoints are always shown in full; only the "other" list is capped.
183
+ const DIFF_SCOPE_OTHER_ENDPOINT_CAP = 20;
181
184
  let endpointLines;
182
185
  if (isDiffScope && changedEndpointKeys.size > 0) {
183
186
  const changedLines = [];
@@ -201,7 +204,25 @@ Write UI recommendation \`reasoning\` fields in **natural prose** that names ele
201
204
  changedLines.push(` ${m.method} ${ep.path} [removed]`);
202
205
  }
203
206
  }
204
- endpointLines = `**Likely changed in this PR (from static file→endpoint mapping — verify against diff in Step ${ANALYSIS_STEP_EXTRACT}):**\n${changedLines.join("\n") || " none"}\n\n**Other endpoints (reference only):**\n${otherLines.join("\n") || " none"}`;
207
+ const cappedOther = otherLines.slice(0, DIFF_SCOPE_OTHER_ENDPOINT_CAP);
208
+ const hiddenOtherCount = otherLines.length - cappedOther.length;
209
+ const otherSuffix = hiddenOtherCount > 0
210
+ ? `\n ... and ${hiddenOtherCount} more (full endpoint list in state file)`
211
+ : "";
212
+ const otherLabel = hiddenOtherCount > 0
213
+ ? `Other endpoints (reference only, ${cappedOther.length} of ${otherLines.length} shown)`
214
+ : "Other endpoints (reference only)";
215
+ endpointLines = `**Likely changed in this PR (from static file→endpoint mapping — verify against diff in Step ${ANALYSIS_STEP_EXTRACT}):**\n${changedLines.join("\n") || " none"}\n\n**${otherLabel}:**\n${cappedOther.join("\n") || " none"}${otherSuffix}`;
216
+ }
217
+ else if (isDiffScope) {
218
+ // Diff scope but no changed endpoints detected — cap to avoid dumping the full catalog.
219
+ const allMethodLines = allEndpoints.flatMap((ep) => (ep.methods ?? []).map((m) => fmtEndpoint(m, ep)));
220
+ const cappedLines = allMethodLines.slice(0, DIFF_SCOPE_OTHER_ENDPOINT_CAP);
221
+ const hiddenCount = allMethodLines.length - cappedLines.length;
222
+ const suffix = hiddenCount > 0
223
+ ? `\n ... and ${hiddenCount} more (full endpoint list in state file — trace changed files directly from the diff to find affected endpoints)`
224
+ : "";
225
+ endpointLines = `${cappedLines.join("\n") || " none"}${suffix}`;
205
226
  }
206
227
  else {
207
228
  endpointLines = allEndpoints
@@ -230,9 +251,22 @@ Write UI recommendation \`reasoning\` fields in **natural prose** that names ele
230
251
  }
231
252
  }
232
253
  const { authSchemeSnippet } = getAuthSnippets(authHeaderValue, authTypeValue, workspaceAuthScheme);
254
+ const routeDiscovery = analysis.routeDiscovery;
255
+ const routeDiscoverySection = routeDiscovery
256
+ ? `
257
+ ## LLM Route Discovery Inputs
258
+ Static endpoint data below is a best-effort hint, not a complete parser for every framework.
259
+ Authoritative endpoint extraction must come from reading the changed source files, router/module context, OpenAPI paths when available, and the diff.
260
+ Candidate files to inspect: ${routeDiscovery.candidateFiles.length > 0 ? routeDiscovery.candidateFiles.join(", ") : "none"}
261
+ Router/module context files: ${routeDiscovery.routerMountContext.length > 0 ? routeDiscovery.routerMountContext.join(", ") : "none"}
262
+ OpenAPI paths available: ${routeDiscovery.openApiPaths.length}
263
+ Static hints available: ${routeDiscovery.staticHints.length}
264
+ ${routeDiscovery.diffFilePath ? `Diff file: ${routeDiscovery.diffFilePath}` : ""}
265
+ `.trim()
266
+ : "";
233
267
  const sourcePriority = `
234
268
  ## Source Priority
235
- When information conflicts, prefer: **Traces** (actual behavior) > **Code** (implemented behavior) > **Spec/Docs** (documented behavior).
269
+ When information conflicts, prefer: **Traces** (actual behavior) > **Source code read by the LLM** (implemented behavior) > **OpenAPI spec/docs** (documented behavior) > **Static parser hints** (best-effort, may be incomplete or framework-blind).
236
270
  `;
237
271
  // Compact fingerprint of tests already covering endpoints in this repo (Skyramp + external).
238
272
  // Re-derived fresh each run from test files on disk — no separate persistence needed.
@@ -274,8 +308,11 @@ Framework: ${analysis.projectClassification.primaryFramework} (${analysis.projec
274
308
  Project type: ${analysis.projectClassification.projectType}
275
309
  Auth: ${authMethod} (header: ${authHeaderValue}${authTypeValue ? `, type: ${authTypeValue}` : ""})
276
310
  Base URL: ${analysis.apiEndpoints.baseUrl}
277
- Candidate endpoints from static scan — unverified, confirm paths against spec or source before use (${analysis.apiEndpoints.totalCount}):
311
+ Candidate endpoint hints from static scan — unverified and non-exhaustive; confirm paths by reading source/router context before use (${analysis.apiEndpoints.totalCount}):
278
312
  ${endpointLines}${testFingerprint}
313
+ ${routeDiscoverySection ? `
314
+
315
+ ${routeDiscoverySection}` : ""}
279
316
  `.trim();
280
317
  // ── Branch diff ──
281
318
  let diffSection = "";
@@ -295,7 +332,7 @@ Affected services: ${diffContext.affectedServices.join(", ") || "N/A"}
295
332
 
296
333
  Focus on tests that validate these changes and how they interact with existing resources.
297
334
  For removed endpoints: verify they now return 404 or the appropriate deprecation status code.
298
- Allocate your test budget to endpoints listed under "Likely changed in this PR". Use other endpoints only as setup steps (e.g. creating a resource before testing its deletion).
335
+ Treat the endpoint lists above as static hints. If source/diff inspection finds a different changed endpoint set, prefer the source-grounded set and use other endpoints only as setup steps.
299
336
  `;
300
337
  }
301
338
  // ── Interactions ──
@@ -17,10 +17,5 @@ export interface RelatedRepository {
17
17
  export declare function parseRelatedRepositories(raw: string | undefined): RelatedRepository[] | undefined;
18
18
  export declare function getTestbotPrompt(prTitle: string, prDescription: string, summaryOutputFile: string, repositoryPath: string, baseBranch?: string, maxRecommendations?: number, maxGenerate?: number, _maxCritical?: number, // Reserved — accepted for API compat but not yet wired into prompt
19
19
  prNumber?: number, userPrompt?: string, services?: Service[], uiCredentials?: string, testsRepoDir?: string, relatedRepositories?: RelatedRepository[], primaryRepo?: string, planOnly?: boolean, language?: string): string;
20
- /**
21
- * Read services from .skyramp/workspace.yml. Returns empty array if
22
- * the workspace file doesn't exist or can't be parsed.
23
- */
24
- export declare function readWorkspaceServices(repositoryPath: string): Promise<Service[]>;
25
20
  export declare function buildWorkspaceRecoveryPrefix(repositoryPath: string): string;
26
21
  export declare function registerTestbotPrompt(server: McpServer): void;