@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
@@ -1 +0,0 @@
1
- export {};
@@ -1,436 +0,0 @@
1
- // @ts-nocheck - Jest ESM type inference issues
2
- import { jest } from "@jest/globals";
3
- import * as fs from "fs/promises";
4
- import * as path from "path";
5
- import * as os from "os";
6
- import { z } from "zod";
7
- jest.unstable_mockModule("../../utils/logger.js", () => ({
8
- logger: { info: jest.fn(), error: jest.fn(), warning: jest.fn(), debug: jest.fn() },
9
- }));
10
- jest.unstable_mockModule("../../services/AnalyticsService.js", () => ({
11
- AnalyticsService: { pushMCPToolEvent: jest.fn().mockResolvedValue(undefined) },
12
- }));
13
- const { registerActionsTool } = await import("./actionsTool.js");
14
- const { DriftAction, TestSource } = await import("../../types/TestAnalysis.js");
15
- const { TestType } = await import("../../types/TestTypes.js");
16
- function captureToolHandler() {
17
- let handler;
18
- let inputSchema;
19
- const fakeServer = {
20
- registerTool: (_name, opts, fn) => {
21
- handler = fn;
22
- inputSchema = opts.inputSchema;
23
- },
24
- };
25
- registerActionsTool(fakeServer);
26
- // Mirrors the real MCP SDK's registerTool: validates args against inputSchema via Zod
27
- // before invoking the handler, so tests exercise the real tool contract (refinements
28
- // included) instead of bypassing validation entirely.
29
- return async (params) => {
30
- const parsed = z.object(inputSchema).safeParse(params);
31
- if (!parsed.success) {
32
- return { isError: true, content: [{ type: "text", text: parsed.error.message }] };
33
- }
34
- return handler(parsed.data);
35
- };
36
- }
37
- async function writeStateFile(dir, existingTests, repositoryAnalysis) {
38
- const stateFile = path.join(dir, "analyze-changes-state.json");
39
- await fs.writeFile(stateFile, JSON.stringify({
40
- existingTests,
41
- repositoryAnalysis,
42
- analysisScope: {},
43
- newEndpoints: [],
44
- metadata: {
45
- sessionId: "test-session",
46
- stateType: "unified",
47
- repositoryPath: dir,
48
- createdAt: "2026-01-01T00:00:00.000Z",
49
- updatedAt: "2026-01-01T00:00:00.000Z",
50
- },
51
- }), "utf-8");
52
- return stateFile;
53
- }
54
- describe("registerActionsTool — verdict reconciliation & persistence (SKYR-3906, SKYR-3938)", () => {
55
- let handler;
56
- let tmpDirs = [];
57
- beforeAll(() => {
58
- handler = captureToolHandler();
59
- });
60
- afterEach(async () => {
61
- for (const dir of tmpDirs) {
62
- await fs.rm(dir, { recursive: true, force: true });
63
- }
64
- tmpDirs = [];
65
- });
66
- it("succeeds when every existing test is covered by recommendations", async () => {
67
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
68
- tmpDirs.push(tmpDir);
69
- const testFile = path.join(tmpDir, "orders_contract_test.py");
70
- await fs.writeFile(testFile, "# test");
71
- const stateFile = await writeStateFile(tmpDir, [{ testFile, source: TestSource.Skyramp }]);
72
- const result = await handler({
73
- stateFile,
74
- recommendations: [
75
- {
76
- testFilePath: testFile,
77
- action: DriftAction.Update,
78
- rationale: "New field added",
79
- testType: TestType.CONTRACT,
80
- endpoint: "GET /api/v1/orders",
81
- updateInstructions: "Assert new field",
82
- },
83
- ],
84
- });
85
- expect(result.content).toBeDefined();
86
- expect(result.isError).toBeUndefined();
87
- });
88
- it("derives the ignored set as the catalog complement of recommendations — no LLM enumeration (SKYR-3938)", async () => {
89
- // The caller supplies only actionable recommendations; every other discovered test is
90
- // recorded as ignored automatically. The LLM payload is O(recommendations) — independent of
91
- // catalog size — which is the fix for the large-repo timeout where the LLM had to echo back
92
- // every path. (Server-side derivation is still O(catalog): filter the catalog by a
93
- // recommendations Set.)
94
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
95
- tmpDirs.push(tmpDir);
96
- const testFile = path.join(tmpDir, "unrelated_test.py");
97
- await fs.writeFile(testFile, "# test");
98
- const stateFile = await writeStateFile(tmpDir, [{ testFile, source: TestSource.Skyramp }]);
99
- // No recommendations at all — the sole discovered test is recorded as ignored.
100
- const result = await handler({ stateFile, recommendations: [] });
101
- expect(result.isError).toBeUndefined();
102
- const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
103
- expect(written.ignoredTestFiles).toEqual([testFile]);
104
- expect(written.maintenanceVerdicts).toEqual([]);
105
- });
106
- it("rejects a recommendation with action: IGNORE at the schema level — must simply be omitted", async () => {
107
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
108
- tmpDirs.push(tmpDir);
109
- const testFile = path.join(tmpDir, "unrelated_test.py");
110
- await fs.writeFile(testFile, "# test");
111
- const stateFile = await writeStateFile(tmpDir, [{ testFile, source: TestSource.Skyramp }]);
112
- const result = await handler({
113
- stateFile,
114
- recommendations: [{ testFilePath: testFile, action: DriftAction.Ignore, rationale: "No change needed", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" }],
115
- // IGNORE must not silently persist via recommendations — it must simply be omitted.
116
- });
117
- expect(result.isError).toBe(true);
118
- expect(result.content[0].text).toContain("Omit IGNORE entries entirely");
119
- });
120
- it("treats an existing test omitted from recommendations as needing no action — no completeness gate (SKYR-3938)", async () => {
121
- // The prior hard-reject forced the LLM to echo back every discovered test, which is
122
- // infeasible on large repos (1,000+ tests) and caused the run to spin until CI timeout.
123
- // An omitted test is now silently no-action: the call succeeds, only the covered file
124
- // lands in the verdicts, and the dropped file is recorded in the derived ignored set.
125
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
126
- tmpDirs.push(tmpDir);
127
- const coveredFile = path.join(tmpDir, "covered_test.py");
128
- const droppedFile = path.join(tmpDir, "secret_test.py");
129
- await fs.writeFile(coveredFile, "# test");
130
- await fs.writeFile(droppedFile, "# test");
131
- const stateFile = await writeStateFile(tmpDir, [
132
- { testFile: coveredFile, source: TestSource.Skyramp },
133
- { testFile: droppedFile, source: TestSource.Skyramp },
134
- ]);
135
- const result = await handler({
136
- stateFile,
137
- recommendations: [{ testFilePath: coveredFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" }],
138
- // droppedFile intentionally absent from recommendations
139
- });
140
- expect(result.isError).toBeUndefined();
141
- const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
142
- expect(written.maintenanceVerdicts).toEqual([
143
- { testFilePath: coveredFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" },
144
- ]);
145
- expect(written.ignoredTestFiles).toEqual([droppedFile]);
146
- });
147
- it("persists maintenanceVerdicts + a derived ignoredTestFiles record to the state file", async () => {
148
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
149
- tmpDirs.push(tmpDir);
150
- const updatedFile = path.join(tmpDir, "orders_contract_test.py");
151
- const ignoredFile = path.join(tmpDir, "unrelated_test.py");
152
- await fs.writeFile(updatedFile, "# test");
153
- await fs.writeFile(ignoredFile, "# test");
154
- const stateFile = await writeStateFile(tmpDir, [
155
- { testFile: updatedFile, source: TestSource.Skyramp },
156
- { testFile: ignoredFile, source: TestSource.Skyramp },
157
- ]);
158
- // Only the actionable test is supplied; ignoredFile is derived as the catalog complement.
159
- await handler({
160
- stateFile,
161
- recommendations: [{ testFilePath: updatedFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" }],
162
- });
163
- const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
164
- expect(written.ignoredTestFiles).toEqual([ignoredFile]);
165
- expect(written.maintenanceVerdicts).toEqual([
166
- { testFilePath: updatedFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" },
167
- ]);
168
- // Original existingTests must survive the write-back untouched.
169
- expect(written.existingTests).toHaveLength(2);
170
- });
171
- it("accepts a real test file discovery missed (uncataloged) instead of dropping it (B1)", async () => {
172
- // Discovery is relevance-scoped, so a real e2e spec/POM the agent correctly identifies
173
- // can be absent from the catalog. Before B1 its verdict was dropped as "hallucinated",
174
- // leaving report.testMaintenance empty even though the edit was applied.
175
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
176
- tmpDirs.push(tmpDir);
177
- const cataloged = path.join(tmpDir, "orders_contract_test.py");
178
- await fs.writeFile(cataloged, "# test");
179
- const e2eDir = path.join(tmpDir, "e2e", "specs");
180
- await fs.mkdir(e2eDir, { recursive: true });
181
- const uncataloged = path.join(e2eDir, "drafts.spec.ts"); // real on disk, NOT in existingTests
182
- await fs.writeFile(uncataloged, "import { test } from '@playwright/test';\ntest('x', async () => {});");
183
- const stateFile = await writeStateFile(tmpDir, [{ testFile: cataloged, source: TestSource.Skyramp }]);
184
- const result = await handler({
185
- stateFile,
186
- recommendations: [
187
- { testFilePath: uncataloged, action: DriftAction.Update, rationale: "testid moved, re-scope locator", testType: TestType.E2E, endpoint: "/drafts", updateInstructions: "re-scope the badge locator" },
188
- ],
189
- });
190
- expect(result.isError).toBeUndefined();
191
- const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
192
- expect(written.maintenanceVerdicts.map((v) => v.testFilePath)).toContain(uncataloged);
193
- });
194
- it("still rejects a recommendation for a non-existent (hallucinated) test path (B1 boundary)", async () => {
195
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
196
- tmpDirs.push(tmpDir);
197
- const cataloged = path.join(tmpDir, "orders_contract_test.py");
198
- await fs.writeFile(cataloged, "# test");
199
- const stateFile = await writeStateFile(tmpDir, [{ testFile: cataloged, source: TestSource.Skyramp }]);
200
- const ghost = path.join(tmpDir, "e2e", "does_not_exist.spec.ts"); // matches test pattern but NOT on disk
201
- const result = await handler({
202
- stateFile,
203
- recommendations: [
204
- { testFilePath: ghost, action: DriftAction.Update, rationale: "hallucinated", testType: TestType.E2E, endpoint: "/x", updateInstructions: "y" },
205
- ],
206
- });
207
- expect(result.isError).toBeUndefined();
208
- const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
209
- expect(written.maintenanceVerdicts.map((v) => v.testFilePath)).not.toContain(ghost);
210
- });
211
- it("records external DELETE as a report-only verdict (real action, file untouched) — no VERIFY flattening (B2)", async () => {
212
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
213
- tmpDirs.push(tmpDir);
214
- const extTest = path.join(tmpDir, "whats_new.spec.ts");
215
- await fs.writeFile(extTest, "import { test } from '@playwright/test';\ntest('whats new', async () => {});");
216
- const stateFile = await writeStateFile(tmpDir, [{ testFile: extTest, source: TestSource.External }]);
217
- const result = await handler({
218
- stateFile,
219
- recommendations: [
220
- { testFilePath: extTest, action: DriftAction.Delete, rationale: "What's New feature removed in this PR", testType: TestType.E2E, endpoint: "/whats-new" },
221
- ],
222
- });
223
- expect(result.isError).toBeUndefined();
224
- const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
225
- const verdict = written.maintenanceVerdicts.find((v) => v.testFilePath === extTest);
226
- expect(verdict).toBeDefined();
227
- // The verdict keeps the real DELETE action — NOT flattened to VERIFY (the D2-judge bug).
228
- expect(verdict.action).toBe(DriftAction.Delete);
229
- // Report-only: the external test file must still exist on disk.
230
- await expect(fs.access(extTest)).resolves.toBeUndefined();
231
- });
232
- it("fails loud when persisting maintenanceVerdicts to state fails, instead of silently applying edits anyway", async () => {
233
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
234
- tmpDirs.push(tmpDir);
235
- const updatedFile = path.join(tmpDir, "orders_contract_test.py");
236
- await fs.writeFile(updatedFile, "# test");
237
- const stateFile = await writeStateFile(tmpDir, [{ testFile: updatedFile, source: TestSource.Skyramp }]);
238
- // Make the state file unwritable so writeRepoData's overwrite fails with EACCES.
239
- await fs.chmod(stateFile, 0o444);
240
- try {
241
- const result = await handler({
242
- stateFile,
243
- recommendations: [{ testFilePath: updatedFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" }],
244
- });
245
- expect(result.isError).toBe(true);
246
- expect(result.content[0].text).toContain("Failed to persist maintenance reconciliation");
247
- }
248
- finally {
249
- await fs.chmod(stateFile, 0o644);
250
- }
251
- });
252
- it("does not require accounting for a test when there are no existing tests at all", async () => {
253
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
254
- tmpDirs.push(tmpDir);
255
- const stateFile = await writeStateFile(tmpDir, []);
256
- const result = await handler({ stateFile });
257
- expect(result.isError).toBeUndefined();
258
- });
259
- // ── DELETE delivery routing (SKYR-3927) ─────────────────────────────────────
260
- // A DELETE on a Skyramp-managed test must produce a structured files_to_delete
261
- // bucket + its own report section — not fall into "Other Findings" prose with an
262
- // empty payload (which left delivery dependent on the agent noticing and rm-ing).
263
- it("routes a DELETE on a Skyramp-managed test into files_to_delete, not Other Findings (SKYR-3927)", async () => {
264
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
265
- tmpDirs.push(tmpDir);
266
- const deletedFile = path.join(tmpDir, "reviews_contract_test.py");
267
- await fs.writeFile(deletedFile, "# test");
268
- const stateFile = await writeStateFile(tmpDir, [{ testFile: deletedFile, source: TestSource.Skyramp }]);
269
- const result = await handler({
270
- stateFile,
271
- recommendations: [
272
- {
273
- testFilePath: deletedFile,
274
- action: DriftAction.Delete,
275
- rationale: "Both covered endpoints removed; now return 404",
276
- testType: TestType.CONTRACT,
277
- endpoint: "GET/POST /api/v1/products/{product_id}/reviews",
278
- },
279
- ],
280
- });
281
- expect(result.isError).toBeUndefined();
282
- // Assistant instruction block carries a populated delete bucket + strategy.
283
- const llmInstructions = JSON.parse(result.content[1].text);
284
- expect(llmInstructions.files_to_delete).toEqual([deletedFile]);
285
- expect(llmInstructions.delete_count).toBe(1);
286
- expect(llmInstructions.delete_strategy).toContain("git rm");
287
- // Surfaced in its own section — never as "Other Findings" prose.
288
- const report = result.content[0].text;
289
- expect(report).toContain("Tests To Delete (1)");
290
- expect(report).not.toContain("Other Findings");
291
- });
292
- it("DELETE wins over UPDATE for the same file — deleted, not edited (SKYR-3927)", async () => {
293
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
294
- tmpDirs.push(tmpDir);
295
- const file = path.join(tmpDir, "widgets_contract_test.py");
296
- await fs.writeFile(file, "# test");
297
- const stateFile = await writeStateFile(tmpDir, [{ testFile: file, source: TestSource.Skyramp }]);
298
- const result = await handler({
299
- stateFile,
300
- recommendations: [
301
- { testFilePath: file, action: DriftAction.Update, rationale: "field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/widgets" },
302
- { testFilePath: file, action: DriftAction.Delete, rationale: "endpoint removed", testType: TestType.CONTRACT, endpoint: "GET /api/v1/widgets" },
303
- ],
304
- });
305
- expect(result.isError).toBeUndefined();
306
- const llmInstructions = JSON.parse(result.content[1].text);
307
- expect(llmInstructions.files_to_delete).toEqual([file]);
308
- expect(llmInstructions.delete_count).toBe(1);
309
- expect(llmInstructions.files_to_update).toEqual([]);
310
- expect(llmInstructions.update_count).toBe(0);
311
- });
312
- it("DELETE wins over REGENERATE for the same file — deleted, not regenerated (SKYR-3927)", async () => {
313
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
314
- tmpDirs.push(tmpDir);
315
- const file = path.join(tmpDir, "gadgets_contract_test.py");
316
- await fs.writeFile(file, "# test");
317
- const stateFile = await writeStateFile(tmpDir, [{ testFile: file, source: TestSource.Skyramp }]);
318
- const result = await handler({
319
- stateFile,
320
- recommendations: [
321
- { testFilePath: file, action: DriftAction.Regenerate, rationale: "response wrapper changed", testType: TestType.CONTRACT, endpoint: "GET /api/v1/gadgets" },
322
- { testFilePath: file, action: DriftAction.Delete, rationale: "endpoint removed", testType: TestType.CONTRACT, endpoint: "GET /api/v1/gadgets" },
323
- ],
324
- });
325
- expect(result.isError).toBeUndefined();
326
- const llmInstructions = JSON.parse(result.content[1].text);
327
- expect(llmInstructions.files_to_delete).toEqual([file]);
328
- expect(llmInstructions.delete_count).toBe(1);
329
- expect(llmInstructions.files_to_regenerate).toEqual([]);
330
- expect(llmInstructions.regenerate_count).toBe(0);
331
- });
332
- // ── Multi-repo (`repository` param) ─────────────────────────────────────────
333
- // A run-scoped state file holds the primary repo at the root and each related
334
- // repo under `relatedRepos[<owner/repo>]`. `args.repository` must scope the
335
- // maintenanceVerdicts/ignoredTestFiles write-back to the matching section only.
336
- async function writeMultiRepoStateFile(stateFile, primary, related) {
337
- const now = "2026-01-01T00:00:00.000Z";
338
- await fs.writeFile(stateFile, JSON.stringify({
339
- existingTests: primary.existingTests,
340
- analysisScope: {},
341
- newEndpoints: [],
342
- metadata: {
343
- sessionId: "test-session",
344
- stateType: "unified",
345
- repositoryPath: primary.repositoryPath,
346
- repository: "org/primary",
347
- createdAt: now,
348
- updatedAt: now,
349
- },
350
- relatedRepos: {
351
- [related.repo]: {
352
- repositoryPath: related.repositoryPath,
353
- data: { existingTests: related.existingTests, analysisScope: {}, newEndpoints: [] },
354
- },
355
- },
356
- }), "utf-8");
357
- }
358
- it("scopes the write-back to a related (non-primary) repo section, leaving the primary section untouched", async () => {
359
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
360
- tmpDirs.push(tmpDir);
361
- const primaryDir = path.join(tmpDir, "primary");
362
- const secondaryDir = path.join(tmpDir, "secondary");
363
- await fs.mkdir(primaryDir, { recursive: true });
364
- await fs.mkdir(secondaryDir, { recursive: true });
365
- const secondaryTestFile = path.join(secondaryDir, "orders_contract_test.py");
366
- await fs.writeFile(secondaryTestFile, "# test");
367
- const stateFile = path.join(tmpDir, "analyze-changes-state.json");
368
- await writeMultiRepoStateFile(stateFile, { repositoryPath: primaryDir, existingTests: [] }, { repo: "org/secondary", repositoryPath: secondaryDir, existingTests: [{ testFile: secondaryTestFile, source: TestSource.Skyramp }] });
369
- const result = await handler({
370
- stateFile,
371
- repository: "org/secondary",
372
- recommendations: [{ testFilePath: secondaryTestFile, action: DriftAction.Update, rationale: "Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" }],
373
- });
374
- expect(result.isError).toBeUndefined();
375
- const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
376
- // UPDATE converts to VERIFY — the agent has no write access to a related repo's
377
- // checkout, so no edit is ever attempted there.
378
- expect(written.relatedRepos["org/secondary"].data.maintenanceVerdicts).toEqual([
379
- { testFilePath: secondaryTestFile, action: DriftAction.Verify, rationale: "[related repo — no write access, needs manual review] Field added", testType: TestType.CONTRACT, endpoint: "GET /api/v1/orders" },
380
- ]);
381
- // Primary section must be untouched by a write scoped to the related repo.
382
- expect(written.maintenanceVerdicts).toBeUndefined();
383
- expect(written.existingTests).toEqual([]);
384
- });
385
- it("also converts DELETE to VERIFY for a related repo — no action type gets write access there", async () => {
386
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
387
- tmpDirs.push(tmpDir);
388
- const primaryDir = path.join(tmpDir, "primary");
389
- const secondaryDir = path.join(tmpDir, "secondary");
390
- await fs.mkdir(primaryDir, { recursive: true });
391
- await fs.mkdir(secondaryDir, { recursive: true });
392
- const secondaryTestFile = path.join(secondaryDir, "reset_contract_test.py");
393
- await fs.writeFile(secondaryTestFile, "# test");
394
- const stateFile = path.join(tmpDir, "analyze-changes-state.json");
395
- await writeMultiRepoStateFile(stateFile, { repositoryPath: primaryDir, existingTests: [] }, { repo: "org/secondary", repositoryPath: secondaryDir, existingTests: [{ testFile: secondaryTestFile, source: TestSource.Skyramp }] });
396
- const result = await handler({
397
- stateFile,
398
- repository: "org/secondary",
399
- recommendations: [{ testFilePath: secondaryTestFile, action: DriftAction.Delete, rationale: "Endpoint removed", testType: TestType.CONTRACT, endpoint: "DELETE /api/v1/reset" }],
400
- });
401
- expect(result.isError).toBeUndefined();
402
- const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
403
- expect(written.relatedRepos["org/secondary"].data.maintenanceVerdicts).toEqual([
404
- { testFilePath: secondaryTestFile, action: DriftAction.Verify, rationale: "[related repo — no write access, needs manual review] Endpoint removed", testType: TestType.CONTRACT, endpoint: "DELETE /api/v1/reset" },
405
- ]);
406
- });
407
- it("scopes the write-back to the related repo section, writing an empty verdict record when nothing needs action (SKYR-3938)", async () => {
408
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "actions-test-"));
409
- tmpDirs.push(tmpDir);
410
- const primaryDir = path.join(tmpDir, "primary");
411
- const secondaryDir = path.join(tmpDir, "secondary");
412
- await fs.mkdir(primaryDir, { recursive: true });
413
- await fs.mkdir(secondaryDir, { recursive: true });
414
- const primaryTestFile = path.join(primaryDir, "covered_test.py");
415
- const secondaryTestFile = path.join(secondaryDir, "secret_test.py");
416
- await fs.writeFile(primaryTestFile, "# test");
417
- await fs.writeFile(secondaryTestFile, "# test");
418
- const stateFile = path.join(tmpDir, "analyze-changes-state.json");
419
- await writeMultiRepoStateFile(stateFile, { repositoryPath: primaryDir, existingTests: [{ testFile: primaryTestFile, source: TestSource.Skyramp }] }, { repo: "org/secondary", repositoryPath: secondaryDir, existingTests: [{ testFile: secondaryTestFile, source: TestSource.Skyramp }] });
420
- // With no completeness gate, a related-repo test the LLM left unlisted is simply
421
- // no-action: the call succeeds and writes an empty maintenanceVerdicts to the related
422
- // section only, leaving the primary section untouched.
423
- const result = await handler({
424
- stateFile,
425
- repository: "org/secondary",
426
- recommendations: [],
427
- });
428
- expect(result.isError).toBeUndefined();
429
- const written = JSON.parse(await fs.readFile(stateFile, "utf-8"));
430
- expect(written.relatedRepos["org/secondary"].data.maintenanceVerdicts).toEqual([]);
431
- // The related section's lone test is recorded as ignored (derived complement), scoped
432
- // to that section — the primary section stays untouched.
433
- expect(written.relatedRepos["org/secondary"].data.ignoredTestFiles).toEqual([secondaryTestFile]);
434
- expect(written.maintenanceVerdicts).toBeUndefined();
435
- });
436
- });