@skyramp/mcp 0.3.1 → 0.3.2-rc.pom-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 (177) hide show
  1. package/build/index.js +2 -1
  2. package/build/prompts/code-reuse.d.ts +7 -1
  3. package/build/prompts/code-reuse.js +8 -5
  4. package/build/prompts/code-reuse.test.d.ts +1 -0
  5. package/build/prompts/code-reuse.test.js +62 -0
  6. package/build/prompts/pom-aware-code-reuse.d.ts +6 -1
  7. package/build/prompts/pom-aware-code-reuse.js +100 -53
  8. package/build/prompts/pom-aware-code-reuse.test.d.ts +1 -0
  9. package/build/prompts/pom-aware-code-reuse.test.js +11 -0
  10. package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +4 -2
  11. package/build/prompts/test-recommendation/diffExecutionPlan.js +11 -65
  12. package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +2 -2
  13. package/build/prompts/test-recommendation/recommendationSections.js +5 -2
  14. package/build/prompts/test-recommendation/scopeAssessment.js +1 -1
  15. package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +26 -1
  16. package/build/prompts/test-recommendation/test-recommendation-prompt.js +68 -56
  17. package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +48 -11
  18. package/build/prompts/testbot/testbot-prompts.d.ts +1 -1
  19. package/build/prompts/testbot/testbot-prompts.js +67 -21
  20. package/build/prompts/testbot/testbot-prompts.test.js +44 -0
  21. package/build/recommendation/budgeters/diversityBalancedBudgeter.d.ts +7 -0
  22. package/build/recommendation/budgeters/diversityBalancedBudgeter.js +71 -0
  23. package/build/recommendation/budgeters/diversityBalancedBudgeter.test.d.ts +1 -0
  24. package/build/recommendation/budgeters/diversityBalancedBudgeter.test.js +75 -0
  25. package/build/recommendation/budgeters/fixedNBudgeter.d.ts +7 -0
  26. package/build/recommendation/budgeters/fixedNBudgeter.js +11 -0
  27. package/build/recommendation/budgeters/fixedNBudgeter.test.d.ts +1 -0
  28. package/build/recommendation/budgeters/fixedNBudgeter.test.js +66 -0
  29. package/build/recommendation/budgeters/shared.d.ts +19 -0
  30. package/build/recommendation/budgeters/shared.js +66 -0
  31. package/build/recommendation/discriminators.d.ts +31 -0
  32. package/build/recommendation/discriminators.js +355 -0
  33. package/build/recommendation/discriminators.test.d.ts +1 -0
  34. package/build/recommendation/discriminators.test.js +324 -0
  35. package/build/recommendation/diversity.d.ts +47 -0
  36. package/build/recommendation/diversity.js +101 -0
  37. package/build/recommendation/diversity.test.d.ts +1 -0
  38. package/build/recommendation/diversity.test.js +77 -0
  39. package/build/recommendation/planRanker.d.ts +50 -0
  40. package/build/recommendation/planRanker.js +67 -0
  41. package/build/recommendation/planRanker.test.d.ts +1 -0
  42. package/build/recommendation/planRanker.test.js +110 -0
  43. package/build/recommendation/testFixtures.d.ts +25 -0
  44. package/build/recommendation/testFixtures.js +45 -0
  45. package/build/resources/testbotResource.js +4 -1
  46. package/build/services/ScenarioGenerationService.d.ts +5 -0
  47. package/build/services/ScenarioGenerationService.js +16 -1
  48. package/build/services/ScenarioGenerationService.test.js +44 -0
  49. package/build/services/TestExecutionService.d.ts +15 -1
  50. package/build/services/TestExecutionService.js +210 -55
  51. package/build/services/TestExecutionService.test.js +397 -0
  52. package/build/services/TestGenerationService.js +19 -1
  53. package/build/services/TestGenerationService.test.js +58 -0
  54. package/build/tool-phases.js +1 -0
  55. package/build/toolNames.d.ts +19 -0
  56. package/build/toolNames.js +19 -0
  57. package/build/tools/code-refactor/codeReuseTool.d.ts +7 -0
  58. package/build/tools/code-refactor/codeReuseTool.js +130 -4
  59. package/build/tools/code-refactor/codeReuseTool.test.d.ts +1 -0
  60. package/build/tools/code-refactor/codeReuseTool.test.js +290 -0
  61. package/build/tools/executeSkyrampTestTool.js +8 -2
  62. package/build/tools/generate-tests/generateBatchScenarioRestTool.d.ts +6 -1
  63. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +110 -17
  64. package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +147 -0
  65. package/build/tools/generate-tests/generateContractRestTool.js +11 -1
  66. package/build/tools/generate-tests/generateIntegrationRestTool.js +24 -1
  67. package/build/tools/generate-tests/generateIntegrationRestTool.test.d.ts +1 -0
  68. package/build/tools/generate-tests/generateIntegrationRestTool.test.js +159 -0
  69. package/build/tools/generate-tests/planGuard.d.ts +13 -0
  70. package/build/tools/generate-tests/planGuard.js +78 -0
  71. package/build/tools/generate-tests/planGuard.test.d.ts +1 -0
  72. package/build/tools/generate-tests/planGuard.test.js +185 -0
  73. package/build/tools/generate-tests/scenarioFileIdentity.d.ts +10 -0
  74. package/build/tools/generate-tests/scenarioFileIdentity.js +46 -0
  75. package/build/tools/generate-tests/scenarioLint.d.ts +30 -0
  76. package/build/tools/generate-tests/scenarioLint.js +150 -0
  77. package/build/tools/generate-tests/scenarioLint.test.d.ts +1 -0
  78. package/build/tools/generate-tests/scenarioLint.test.js +100 -0
  79. package/build/tools/submitReportTool.js +78 -0
  80. package/build/tools/submitReportTool.test.js +255 -0
  81. package/build/tools/test-management/analyzeChangesTool.js +55 -2
  82. package/build/tools/test-management/analyzeChangesTool.test.js +12 -0
  83. package/build/tools/test-management/index.d.ts +1 -0
  84. package/build/tools/test-management/index.js +1 -0
  85. package/build/tools/test-management/registerTestPlanTool.d.ts +2 -0
  86. package/build/tools/test-management/registerTestPlanTool.js +329 -0
  87. package/build/tools/test-management/registerTestPlanTool.test.d.ts +1 -0
  88. package/build/tools/test-management/registerTestPlanTool.test.js +296 -0
  89. package/build/types/Recommendation.d.ts +97 -0
  90. package/build/types/Recommendation.js +48 -0
  91. package/build/types/RepositoryAnalysis.d.ts +14 -14
  92. package/build/types/TestExecution.d.ts +2 -0
  93. package/build/types/TestRecommendation.d.ts +12 -1
  94. package/build/types/TestRecommendation.js +26 -11
  95. package/build/types/TestTypes.js +1 -1
  96. package/build/utils/AnalysisStateManager.d.ts +47 -0
  97. package/build/utils/docker.test.js +1 -1
  98. package/build/utils/planMatchKeys.d.ts +61 -0
  99. package/build/utils/planMatchKeys.js +125 -0
  100. package/build/utils/pom-scope/import-expansion.d.ts +5 -0
  101. package/build/utils/pom-scope/import-expansion.js +32 -0
  102. package/build/utils/pom-scope/index.d.ts +39 -0
  103. package/build/utils/pom-scope/index.js +120 -0
  104. package/build/utils/pom-scope/index.test.d.ts +1 -0
  105. package/build/utils/pom-scope/index.test.js +239 -0
  106. package/build/utils/pom-scope/pom-files.d.ts +3 -0
  107. package/build/utils/pom-scope/pom-files.js +48 -0
  108. package/build/utils/pom-scope/pom-files.test.d.ts +1 -0
  109. package/build/utils/pom-scope/pom-files.test.js +29 -0
  110. package/build/utils/pom-scope/scoring.d.ts +20 -0
  111. package/build/utils/pom-scope/scoring.js +45 -0
  112. package/build/utils/pom-scope/scoring.test.d.ts +1 -0
  113. package/build/utils/pom-scope/scoring.test.js +39 -0
  114. package/build/utils/pom-scope/selector-extractor.d.ts +7 -0
  115. package/build/utils/pom-scope/selector-extractor.js +57 -0
  116. package/build/utils/pom-scope/selector-extractor.test.d.ts +1 -0
  117. package/build/utils/pom-scope/selector-extractor.test.js +67 -0
  118. package/build/utils/pom-verify/__fixtures__/af-style/asset-list.page.d.ts +5 -0
  119. package/build/utils/pom-verify/__fixtures__/af-style/asset-list.page.js +5 -0
  120. package/build/utils/pom-verify/__fixtures__/af-style/pageobjects/asset-list-page.d.ts +5 -0
  121. package/build/utils/pom-verify/__fixtures__/af-style/pageobjects/asset-list-page.js +9 -0
  122. package/build/utils/pom-verify/__fixtures__/af-style/report.iframe.page.d.ts +4 -0
  123. package/build/utils/pom-verify/__fixtures__/af-style/report.iframe.page.js +4 -0
  124. package/build/utils/pom-verify/__fixtures__/af-style/workflow-footer.page.d.ts +4 -0
  125. package/build/utils/pom-verify/__fixtures__/af-style/workflow-footer.page.js +4 -0
  126. package/build/utils/pom-verify/bindings.d.ts +19 -0
  127. package/build/utils/pom-verify/bindings.js +161 -0
  128. package/build/utils/pom-verify/bindings.test.d.ts +1 -0
  129. package/build/utils/pom-verify/bindings.test.js +164 -0
  130. package/build/utils/pom-verify/calls.d.ts +16 -0
  131. package/build/utils/pom-verify/calls.js +42 -0
  132. package/build/utils/pom-verify/calls.test.d.ts +1 -0
  133. package/build/utils/pom-verify/calls.test.js +61 -0
  134. package/build/utils/pom-verify/index.d.ts +4 -0
  135. package/build/utils/pom-verify/index.js +4 -0
  136. package/build/utils/pom-verify/resolve.d.ts +7 -0
  137. package/build/utils/pom-verify/resolve.js +27 -0
  138. package/build/utils/pom-verify/resolve.test.d.ts +1 -0
  139. package/build/utils/pom-verify/resolve.test.js +68 -0
  140. package/build/utils/pom-verify/strip.d.ts +9 -0
  141. package/build/utils/pom-verify/strip.js +89 -0
  142. package/build/utils/pom-verify/verify.d.ts +14 -0
  143. package/build/utils/pom-verify/verify.js +158 -0
  144. package/build/utils/pom-verify/verify.test.d.ts +1 -0
  145. package/build/utils/pom-verify/verify.test.js +325 -0
  146. package/build/utils/reportVerification.d.ts +61 -0
  147. package/build/utils/reportVerification.js +104 -0
  148. package/build/utils/reportVerification.test.d.ts +1 -0
  149. package/build/utils/reportVerification.test.js +185 -0
  150. package/build/utils/scenarioDrafting.js +5 -5
  151. package/build/utils/versions.d.ts +3 -3
  152. package/build/utils/versions.js +1 -1
  153. package/build/utils/workspaceAuth.d.ts +9 -1
  154. package/build/utils/workspaceAuth.js +25 -5
  155. package/build/utils/workspaceAuth.test.js +48 -0
  156. package/build/workspace/workspace.d.ts +20 -0
  157. package/build/workspace/workspace.js +4 -0
  158. package/build/workspace/workspace.test.js +10 -0
  159. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +77 -8
  160. package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
  161. package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
  162. package/node_modules/playwright/node_modules/playwright-core/lib/utils/isomorphic/volatileDate.js +101 -0
  163. package/node_modules/playwright/node_modules/playwright-core/lib/utils.js +2 -0
  164. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-aszq5EdG.js → codeMirrorModule-Bzd72-bG.js} +1 -1
  165. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-BxS7Jm4s.js → defaultSettingsView-DzxTioTK.js} +101 -101
  166. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.D4JTTy4R.js → index.BGc30U3S.js} +1 -1
  167. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
  168. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.DaRMQKOI.js → uiMode.IaDrb29A.js} +1 -1
  169. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
  170. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  171. package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
  172. package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
  173. package/node_modules/playwright/node_modules/playwright-core/src/utils/isomorphic/volatileDate.ts +131 -0
  174. package/node_modules/playwright/node_modules/playwright-core/src/utils.ts +1 -0
  175. package/node_modules/playwright/package.json +1 -1
  176. package/package.json +3 -3
  177. package/node_modules/playwright/node_modules/playwright-core/.DS_Store +0 -0
@@ -0,0 +1,97 @@
1
+ import { DraftedScenario } from "../types/RepositoryAnalysis.js";
2
+ import { Novelty, PriorityTier } from "../types/TestRecommendation.js";
3
+ /**
4
+ * The structural bug-shape a candidate claims to probe. Verified against the
5
+ * candidate's `steps[]` by `validateDiscriminator` (discriminators.ts); a claim
6
+ * that does not survive verification is demoted, never used to reject.
7
+ */
8
+ export declare enum DiscriminatorKind {
9
+ BOUNDARY_EQUALITY = "boundary_equality",
10
+ MULTI_RECORD_AGGREGATE = "multi_record_aggregate",
11
+ STATE_TRANSITION = "state_transition",
12
+ NEGATIVE_MATCH = "negative_match"
13
+ }
14
+ /** Who drafted a candidate: the LLM loop, or the MCP server's own pre-ranking. */
15
+ export declare enum CandidateSource {
16
+ AGENT = "agent",
17
+ SERVER = "server"
18
+ }
19
+ /**
20
+ * A ranked test candidate — the existing scored scenario plus the fields the
21
+ * budgeting stage reads. Matches the shape produced today by the scoring block
22
+ * in test-recommendation-prompt.ts (`{ scenario, priority, novelty }`), extended
23
+ * for the SKYR-3879 Path B register-plan checkpoint with provenance, a stable id
24
+ * and the verified-discriminator marker the ranker reads.
25
+ */
26
+ export interface Candidate {
27
+ scenario: DraftedScenario;
28
+ priority: PriorityTier;
29
+ novelty: Novelty;
30
+ /** Who drafted this candidate: the LLM loop or the MCP server. */
31
+ source: CandidateSource;
32
+ /**
33
+ * Stable, deterministic id: slugified `scenarioName` + short content hash.
34
+ * Reproducible across calls (same scenario → same id) so the generation-tool
35
+ * plan guard can match a generated test back to its approved candidate.
36
+ */
37
+ candidateId: string;
38
+ /**
39
+ * Set by the caller AFTER `validateDiscriminator` succeeds for a declared
40
+ * discriminator. Present only when the structural claim was verified against
41
+ * `steps[]` and the diff anchor; the ranker floats these ahead of unverified
42
+ * peers. Absent = no verified discriminator (unclaimed or demoted).
43
+ */
44
+ verifiedDiscriminator?: DiscriminatorKind;
45
+ }
46
+ /** Environment-derived inputs a Budgeter needs to split GENERATE vs ADDITIONAL. */
47
+ export interface BudgetContext {
48
+ /** Upper bound on GENERATE items (a strategy parameter, formerly the hard cap). */
49
+ maxGenerate: number;
50
+ /** Total recommendations (GENERATE + ADDITIONAL); today's `topN`. */
51
+ maxTotal: number;
52
+ isUIOnlyPR: boolean;
53
+ hasFrontendChanges: boolean;
54
+ /** Method-aware coverage keys of external tests; matching candidates are dropped. */
55
+ externalCoverage: Set<string>;
56
+ }
57
+ /** A demoted claim: which candidate, and the human-readable reason returned to
58
+ * the agent so it can fix the claim (never a silent drop). */
59
+ export interface Demotion {
60
+ candidateId: string;
61
+ reason: string;
62
+ }
63
+ /** Result of budgeting: the GENERATE set, the ADDITIONAL set, how many UI
64
+ * placeholder slots the render layer should add (mixed/UI-only PRs), and the
65
+ * demoted-claim channel (the missing rejection surface for register-plan). */
66
+ export interface SelectionResult {
67
+ generate: Candidate[];
68
+ additional: Candidate[];
69
+ reservedUISlots: number;
70
+ demotions: Demotion[];
71
+ }
72
+ /** A pluggable budgeting algorithm. Deterministic and pure. */
73
+ export interface Budgeter {
74
+ readonly name: string;
75
+ select(ranked: Candidate[], ctx: BudgetContext): SelectionResult;
76
+ }
77
+ /**
78
+ * Deterministic candidate id: a slug of the scenario name plus an 8-char content
79
+ * hash. Pure and reproducible — the same scenario always yields the same id, so
80
+ * a candidate registered in the plan can be matched back when its test is later
81
+ * generated. The hash covers the salient, stable structural fields (name,
82
+ * category, testType, and each step's method/path/interactionType/status) so
83
+ * cosmetic re-serialization does not change the id while a real edit does.
84
+ */
85
+ export declare function computeCandidateId(scenario: DraftedScenario): string;
86
+ /** One approved plan item as persisted in `UnifiedAnalysisState.approvedPlan`.
87
+ * Deliberately slim (no steps/requestBody/etc.) — `matchKeys` already carries
88
+ * everything a downstream matcher needs. */
89
+ export interface ApprovedPlanItem {
90
+ candidateId: string;
91
+ scenarioName: string;
92
+ testType: string;
93
+ category: string;
94
+ source: CandidateSource;
95
+ verifiedDiscriminator?: string;
96
+ matchKeys: string[];
97
+ }
@@ -0,0 +1,48 @@
1
+ import * as crypto from "crypto";
2
+ /**
3
+ * The structural bug-shape a candidate claims to probe. Verified against the
4
+ * candidate's `steps[]` by `validateDiscriminator` (discriminators.ts); a claim
5
+ * that does not survive verification is demoted, never used to reject.
6
+ */
7
+ export var DiscriminatorKind;
8
+ (function (DiscriminatorKind) {
9
+ DiscriminatorKind["BOUNDARY_EQUALITY"] = "boundary_equality";
10
+ DiscriminatorKind["MULTI_RECORD_AGGREGATE"] = "multi_record_aggregate";
11
+ DiscriminatorKind["STATE_TRANSITION"] = "state_transition";
12
+ DiscriminatorKind["NEGATIVE_MATCH"] = "negative_match";
13
+ })(DiscriminatorKind || (DiscriminatorKind = {}));
14
+ /** Who drafted a candidate: the LLM loop, or the MCP server's own pre-ranking. */
15
+ export var CandidateSource;
16
+ (function (CandidateSource) {
17
+ CandidateSource["AGENT"] = "agent";
18
+ CandidateSource["SERVER"] = "server";
19
+ })(CandidateSource || (CandidateSource = {}));
20
+ /**
21
+ * Deterministic candidate id: a slug of the scenario name plus an 8-char content
22
+ * hash. Pure and reproducible — the same scenario always yields the same id, so
23
+ * a candidate registered in the plan can be matched back when its test is later
24
+ * generated. The hash covers the salient, stable structural fields (name,
25
+ * category, testType, and each step's method/path/interactionType/status) so
26
+ * cosmetic re-serialization does not change the id while a real edit does.
27
+ */
28
+ export function computeCandidateId(scenario) {
29
+ const slugSource = scenario.scenarioName ?? "";
30
+ const slug = slugSource
31
+ .toLowerCase()
32
+ .replace(/[^a-z0-9]+/g, "-")
33
+ .replace(/^-+|-+$/g, "")
34
+ .slice(0, 48) || "scenario";
35
+ const canonical = {
36
+ scenarioName: scenario.scenarioName ?? "",
37
+ category: scenario.category ?? "",
38
+ testType: scenario.testType ?? "",
39
+ steps: (scenario.steps ?? []).map((s) => ({
40
+ method: s?.method ?? "",
41
+ path: s?.path ?? "",
42
+ interactionType: s?.interactionType ?? "",
43
+ expectedStatusCode: s?.expectedStatusCode ?? 0,
44
+ })),
45
+ };
46
+ const hash = crypto.createHash("sha256").update(JSON.stringify(canonical)).digest("hex").slice(0, 8);
47
+ return `${slug}-${hash}`;
48
+ }
@@ -1309,25 +1309,25 @@ export declare const branchDiffContextSchema: z.ZodObject<{
1309
1309
  }, "strip", z.ZodTypeAny, {
1310
1310
  method: string;
1311
1311
  sourceFile: string;
1312
- changeType: "added" | "modified" | "removed";
1312
+ changeType: "modified" | "added" | "removed";
1313
1313
  }, {
1314
1314
  method: string;
1315
1315
  sourceFile: string;
1316
- changeType: "added" | "modified" | "removed";
1316
+ changeType: "modified" | "added" | "removed";
1317
1317
  }>, "many">;
1318
1318
  }, "strip", z.ZodTypeAny, {
1319
1319
  path: string;
1320
1320
  methods: {
1321
1321
  method: string;
1322
1322
  sourceFile: string;
1323
- changeType: "added" | "modified" | "removed";
1323
+ changeType: "modified" | "added" | "removed";
1324
1324
  }[];
1325
1325
  }, {
1326
1326
  path: string;
1327
1327
  methods: {
1328
1328
  method: string;
1329
1329
  sourceFile: string;
1330
- changeType: "added" | "modified" | "removed";
1330
+ changeType: "modified" | "added" | "removed";
1331
1331
  }[];
1332
1332
  }>, "many">;
1333
1333
  removedEndpoints: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -1379,7 +1379,7 @@ export declare const branchDiffContextSchema: z.ZodObject<{
1379
1379
  methods: {
1380
1380
  method: string;
1381
1381
  sourceFile: string;
1382
- changeType: "added" | "modified" | "removed";
1382
+ changeType: "modified" | "added" | "removed";
1383
1383
  }[];
1384
1384
  }[];
1385
1385
  affectedServices: string[];
@@ -1409,7 +1409,7 @@ export declare const branchDiffContextSchema: z.ZodObject<{
1409
1409
  methods: {
1410
1410
  method: string;
1411
1411
  sourceFile: string;
1412
- changeType: "added" | "modified" | "removed";
1412
+ changeType: "modified" | "added" | "removed";
1413
1413
  }[];
1414
1414
  }[];
1415
1415
  affectedServices: string[];
@@ -2483,25 +2483,25 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
2483
2483
  }, "strip", z.ZodTypeAny, {
2484
2484
  method: string;
2485
2485
  sourceFile: string;
2486
- changeType: "added" | "modified" | "removed";
2486
+ changeType: "modified" | "added" | "removed";
2487
2487
  }, {
2488
2488
  method: string;
2489
2489
  sourceFile: string;
2490
- changeType: "added" | "modified" | "removed";
2490
+ changeType: "modified" | "added" | "removed";
2491
2491
  }>, "many">;
2492
2492
  }, "strip", z.ZodTypeAny, {
2493
2493
  path: string;
2494
2494
  methods: {
2495
2495
  method: string;
2496
2496
  sourceFile: string;
2497
- changeType: "added" | "modified" | "removed";
2497
+ changeType: "modified" | "added" | "removed";
2498
2498
  }[];
2499
2499
  }, {
2500
2500
  path: string;
2501
2501
  methods: {
2502
2502
  method: string;
2503
2503
  sourceFile: string;
2504
- changeType: "added" | "modified" | "removed";
2504
+ changeType: "modified" | "added" | "removed";
2505
2505
  }[];
2506
2506
  }>, "many">;
2507
2507
  removedEndpoints: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -2553,7 +2553,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
2553
2553
  methods: {
2554
2554
  method: string;
2555
2555
  sourceFile: string;
2556
- changeType: "added" | "modified" | "removed";
2556
+ changeType: "modified" | "added" | "removed";
2557
2557
  }[];
2558
2558
  }[];
2559
2559
  affectedServices: string[];
@@ -2583,7 +2583,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
2583
2583
  methods: {
2584
2584
  method: string;
2585
2585
  sourceFile: string;
2586
- changeType: "added" | "modified" | "removed";
2586
+ changeType: "modified" | "added" | "removed";
2587
2587
  }[];
2588
2588
  }[];
2589
2589
  affectedServices: string[];
@@ -2785,7 +2785,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
2785
2785
  methods: {
2786
2786
  method: string;
2787
2787
  sourceFile: string;
2788
- changeType: "added" | "modified" | "removed";
2788
+ changeType: "modified" | "added" | "removed";
2789
2789
  }[];
2790
2790
  }[];
2791
2791
  affectedServices: string[];
@@ -2987,7 +2987,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
2987
2987
  methods: {
2988
2988
  method: string;
2989
2989
  sourceFile: string;
2990
- changeType: "added" | "modified" | "removed";
2990
+ changeType: "modified" | "added" | "removed";
2991
2991
  }[];
2992
2992
  }[];
2993
2993
  affectedServices: string[];
@@ -37,6 +37,8 @@ export interface BatchExecutionResult {
37
37
  export interface TestExecutionOptions {
38
38
  testFile: string;
39
39
  workspacePath: string;
40
+ /** Cross-repo test repository root (testsRepoDir) when testFile was delivered outside workspacePath (SKYR-3819). */
41
+ testRepoPath?: string;
40
42
  language: string;
41
43
  testType: string;
42
44
  token?: string;
@@ -1,5 +1,16 @@
1
1
  /** Internal scenario-scoring tier — UPPERCASE, includes CRITICAL. */
2
- export type PriorityTier = "CRITICAL" | "HIGH" | "MEDIUM" | "LOW";
2
+ export declare enum PriorityTier {
3
+ CRITICAL = "CRITICAL",
4
+ HIGH = "HIGH",
5
+ MEDIUM = "MEDIUM",
6
+ LOW = "LOW"
7
+ }
8
+ /** How a scenario relates to the diff — tie-breaker within a priority tier. */
9
+ export declare enum Novelty {
10
+ NEW = "new",
11
+ MODIFIED = "modified",
12
+ EXISTING = "existing"
13
+ }
3
14
  /** All categories including internal ones. */
4
15
  export declare const SCENARIO_CATEGORIES: readonly ["new_endpoint", "bug_caught", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"];
5
16
  export type ScenarioCategory = typeof SCENARIO_CATEGORIES[number];
@@ -1,3 +1,18 @@
1
+ /** Internal scenario-scoring tier — UPPERCASE, includes CRITICAL. */
2
+ export var PriorityTier;
3
+ (function (PriorityTier) {
4
+ PriorityTier["CRITICAL"] = "CRITICAL";
5
+ PriorityTier["HIGH"] = "HIGH";
6
+ PriorityTier["MEDIUM"] = "MEDIUM";
7
+ PriorityTier["LOW"] = "LOW";
8
+ })(PriorityTier || (PriorityTier = {}));
9
+ /** How a scenario relates to the diff — tie-breaker within a priority tier. */
10
+ export var Novelty;
11
+ (function (Novelty) {
12
+ Novelty["NEW"] = "new";
13
+ Novelty["MODIFIED"] = "modified";
14
+ Novelty["EXISTING"] = "existing";
15
+ })(Novelty || (Novelty = {}));
1
16
  /** Internal-only categories (not submitted to tools). */
2
17
  const INTERNAL_CATEGORIES = [
3
18
  "new_endpoint", // CRITICAL - diff-direct scenarios always fill GENERATE slots first
@@ -24,17 +39,17 @@ export const SCENARIO_CATEGORIES = [...INTERNAL_CATEGORIES, ...CATEGORIES];
24
39
  export const TEST_CATEGORIES = CATEGORIES;
25
40
  /** Priority assignment for each category. */
26
41
  export const CATEGORY_PRIORITY = {
27
- new_endpoint: "CRITICAL",
28
- bug_caught: "CRITICAL", // tests targeting a <bug_found> flaw — always in GENERATE
29
- business_rule: "HIGH", // formula/business-logic bugs are high priority but CRITICAL is reserved for new-endpoint diff-direct scenarios
30
- security_boundary: "HIGH",
31
- data_integrity: "HIGH",
32
- breaking_change: "HIGH",
33
- auth: "HIGH",
34
- error_handling: "HIGH",
35
- workflow: "MEDIUM",
36
- data_validation: "MEDIUM",
37
- crud: "LOW",
42
+ new_endpoint: PriorityTier.CRITICAL,
43
+ bug_caught: PriorityTier.CRITICAL, // tests targeting a <bug_found> flaw — always in GENERATE
44
+ business_rule: PriorityTier.HIGH, // formula/business-logic bugs are high priority but CRITICAL is reserved for new-endpoint diff-direct scenarios
45
+ security_boundary: PriorityTier.HIGH,
46
+ data_integrity: PriorityTier.HIGH,
47
+ breaking_change: PriorityTier.HIGH,
48
+ auth: PriorityTier.HIGH,
49
+ error_handling: PriorityTier.HIGH,
50
+ workflow: PriorityTier.MEDIUM,
51
+ data_validation: PriorityTier.MEDIUM,
52
+ crud: PriorityTier.LOW,
38
53
  };
39
54
  /** Map internal-only categories to their external equivalent for tool submission. */
40
55
  export function externalCategory(cat) {
@@ -243,7 +243,7 @@ export const codeRefactoringSchema = z.object({
243
243
  codeReuse: z
244
244
  .boolean()
245
245
  .default(false)
246
- .describe("Whether to reuse existing code"),
246
+ .describe("Reuse existing code in the generated test — for TS/JS Playwright tests this refactors the test to use the repo's existing Page Object Model"),
247
247
  modularizeCode: z
248
248
  .boolean()
249
249
  .default(false)
@@ -4,6 +4,7 @@ import { PRTestContext } from "./pr-comment-parser.js";
4
4
  import { ParsedDiffEndpoint } from "./routeParsers.js";
5
5
  import type { CandidateUiPage } from "./uiPageEnumerator.js";
6
6
  import type { FrontendFileIntegration } from "../types/FrontendIntegration.js";
7
+ import type { ApprovedPlanItem } from "../types/Recommendation.js";
7
8
  export type { CandidateUiPage } from "./uiPageEnumerator.js";
8
9
  export declare function setTestsRepoDir(dir: string | undefined): void;
9
10
  export declare function getTestsRepoDir(): string | undefined;
@@ -65,6 +66,41 @@ export interface UiAnalysisContext {
65
66
  */
66
67
  frontendFileIntegration?: FrontendFileIntegration[];
67
68
  }
69
+ /**
70
+ * SKYR-3879 Path B — the server-side selection checkpoint's persisted result.
71
+ * Written by skyramp_register_test_plan (and pre-seeded by skyramp_analyze_changes
72
+ * when it has its own pre-ranked GENERATE items). `generate` is the mandated,
73
+ * final list generation tools gate against (see planGuard.ts); `additional` is
74
+ * report-only; `demotions` carries discriminator claims that failed structural
75
+ * verification (the candidate itself is never dropped, only the claim).
76
+ */
77
+ export interface ApprovedPlan {
78
+ planId: string;
79
+ createdAt: string;
80
+ generate: ApprovedPlanItem[];
81
+ additional: ApprovedPlanItem[];
82
+ demotions: Array<{
83
+ candidateId: string;
84
+ reason: string;
85
+ }>;
86
+ }
87
+ /**
88
+ * Budget inputs computed once by skyramp_analyze_changes (from the same
89
+ * classification the Execution Plan prompt is built from) and reused by
90
+ * skyramp_register_test_plan, so re-ranking the agent's candidates never
91
+ * drifts from the budget the agent was shown. Persisted even when no
92
+ * `approvedPlan` is pre-seeded (e.g. no pre-drafted scenarios) so a
93
+ * register_test_plan call with only agent-drafted candidates still budgets
94
+ * against the correct maxGenerate/maxTotal/UI split.
95
+ */
96
+ export interface PlanBudgetContext {
97
+ maxGenerate: number;
98
+ maxTotal: number;
99
+ isUIOnlyPR: boolean;
100
+ hasFrontendChanges: boolean;
101
+ /** Serialized form of the BudgetContext.externalCoverage Set. */
102
+ externalCoverageKeys: string[];
103
+ }
68
104
  /**
69
105
  * Unified state data combining test discovery + endpoint scanning
70
106
  * Used by the unified test-management workflow
@@ -86,6 +122,17 @@ export interface UnifiedAnalysisState {
86
122
  /** Maintenance actions applied via skyramp_actions — skyramp_submit_report derives
87
123
  * testMaintenance from this. */
88
124
  maintenanceVerdicts?: MaintenanceActionCore[];
125
+ /**
126
+ * Raw branch-diff text, size-capped (~200KB), persisted so
127
+ * skyramp_register_test_plan can verify a discriminator's `changedCodeAnchor`
128
+ * occurs verbatim in the actual diff. Absent when the analysis wasn't
129
+ * diff-scoped or no diff content was produced.
130
+ */
131
+ diffText?: string;
132
+ /** SKYR-3879 Path B — see {@link PlanBudgetContext}. */
133
+ planBudgetContext?: PlanBudgetContext;
134
+ /** SKYR-3879 Path B — see {@link ApprovedPlan}. */
135
+ approvedPlan?: ApprovedPlan;
89
136
  }
90
137
  /**
91
138
  * State file metadata
@@ -55,7 +55,7 @@ describe("dockerImageExistsLocally", () => {
55
55
  });
56
56
  });
57
57
  describe("pullDockerImage", () => {
58
- const IMAGE = "skyramp/executor:v1.3.34";
58
+ const IMAGE = "skyramp/executor:v1.3.35";
59
59
  beforeEach(() => jest.clearAllMocks());
60
60
  describe("on amd64 host", () => {
61
61
  const originalArch = process.arch;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * SKYR-3879 Path B — shared match-key computation for the register-plan
3
+ * checkpoint. `computeMatchKeys` runs ONCE, at approval time (inside
4
+ * registerTestPlanTool / the analyze_changes pre-seed), so the two
5
+ * consumers — planGuard (generation-tool hard gate) and submitReportTool
6
+ * (report cross-check) — only ever do a Set/array membership check, never
7
+ * re-derive normalization logic against a live candidate.
8
+ *
9
+ * Two kinds of key:
10
+ * - `name:<slug>` — normalized scenarioName, for multi-step
11
+ * candidates (batch-scenario / integration) where the agent's own name is
12
+ * the natural join key.
13
+ * - `ep:<testType>:<METHOD>:<path>` — one per step, for single-endpoint
14
+ * matching (contract tests, or a fallback when names drift between
15
+ * register and generate).
16
+ */
17
+ import { DraftedScenario } from "../types/RepositoryAnalysis.js";
18
+ import { ApprovedPlanItem, Candidate } from "../types/Recommendation.js";
19
+ /** Query shape built from a generation-tool call or a submitted report entry. */
20
+ export interface PlanMatchQuery {
21
+ scenarioName?: string;
22
+ testType?: string;
23
+ method?: string;
24
+ path?: string;
25
+ }
26
+ /** Lowercase, collapse non-alphanumeric runs to '-', trim leading/trailing '-'. */
27
+ export declare function slugifyName(name: string | undefined): string;
28
+ /**
29
+ * Normalize a path so param segments collapse: numeric ids, `{brace}` params,
30
+ * `:colon` params, and long hex/uuid segments all become `:p`. Mirrors
31
+ * discriminators.ts's normalizePath (duplicated here rather than imported —
32
+ * that function verifies discriminator claims against a diff, a different
33
+ * concern from plan matching, and keeping them independent avoids coupling
34
+ * this module's stability to phase 1's internal helper).
35
+ */
36
+ export declare function normalizeMatchPath(rawPath: string | undefined): string;
37
+ /** Name key (if a name exists) plus one endpoint key per step. */
38
+ export declare function computeMatchKeys(scenario: DraftedScenario): string[];
39
+ /** Build the persisted plan item from a ranked Candidate. */
40
+ export declare function buildApprovedPlanItem(candidate: Candidate): ApprovedPlanItem;
41
+ /**
42
+ * True when `query` matches at least one of `items`' matchKeys:
43
+ * - by scenarioName slug (`name:<slug>`), or
44
+ * - by testType + path (`ep:<testType>:*:<path>`), method-exact when
45
+ * `query.method` is given, method-agnostic (any method) otherwise — the
46
+ * contract tool's `method: ""` ("test all methods") case.
47
+ */
48
+ export declare function matchesApprovedItem(items: ApprovedPlanItem[], query: PlanMatchQuery): boolean;
49
+ /**
50
+ * True when `query` is admissible under the approved plan: it matches a
51
+ * GENERATE item, or it matches an ADDITIONAL item while the plan has at least
52
+ * one GENERATE item. ADDITIONAL is strictly a backfill pool for failed
53
+ * GENERATE items — on a zero-budget plan (`generate: []`, e.g. maxGenerate=0
54
+ * for a config-only PR) there is nothing to backfill, so ADDITIONAL must not
55
+ * admit generation. Shared by planGuard and submitReportTool so the gate and
56
+ * the report cross-check can never disagree on admissibility.
57
+ */
58
+ export declare function matchesApprovedPlan(plan: {
59
+ generate: ApprovedPlanItem[];
60
+ additional: ApprovedPlanItem[];
61
+ }, query: PlanMatchQuery): boolean;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * SKYR-3879 Path B — shared match-key computation for the register-plan
3
+ * checkpoint. `computeMatchKeys` runs ONCE, at approval time (inside
4
+ * registerTestPlanTool / the analyze_changes pre-seed), so the two
5
+ * consumers — planGuard (generation-tool hard gate) and submitReportTool
6
+ * (report cross-check) — only ever do a Set/array membership check, never
7
+ * re-derive normalization logic against a live candidate.
8
+ *
9
+ * Two kinds of key:
10
+ * - `name:<slug>` — normalized scenarioName, for multi-step
11
+ * candidates (batch-scenario / integration) where the agent's own name is
12
+ * the natural join key.
13
+ * - `ep:<testType>:<METHOD>:<path>` — one per step, for single-endpoint
14
+ * matching (contract tests, or a fallback when names drift between
15
+ * register and generate).
16
+ */
17
+ /** Lowercase, collapse non-alphanumeric runs to '-', trim leading/trailing '-'. */
18
+ export function slugifyName(name) {
19
+ return (name ?? "")
20
+ .toLowerCase()
21
+ .replace(/[^a-z0-9]+/g, "-")
22
+ .replace(/^-+|-+$/g, "");
23
+ }
24
+ /**
25
+ * Normalize a path so param segments collapse: numeric ids, `{brace}` params,
26
+ * `:colon` params, and long hex/uuid segments all become `:p`. Mirrors
27
+ * discriminators.ts's normalizePath (duplicated here rather than imported —
28
+ * that function verifies discriminator claims against a diff, a different
29
+ * concern from plan matching, and keeping them independent avoids coupling
30
+ * this module's stability to phase 1's internal helper).
31
+ */
32
+ export function normalizeMatchPath(rawPath) {
33
+ const pathPart = (rawPath ?? "").split("?")[0];
34
+ return pathPart
35
+ .split("/")
36
+ .map((seg) => {
37
+ if (seg === "")
38
+ return seg;
39
+ if (/^\{.*\}$/.test(seg))
40
+ return ":p";
41
+ if (/^:.+/.test(seg))
42
+ return ":p";
43
+ if (/^\d+$/.test(seg))
44
+ return ":p";
45
+ if (/^[0-9a-fA-F-]{16,}$/.test(seg))
46
+ return ":p";
47
+ return seg.toLowerCase();
48
+ })
49
+ .join("/");
50
+ }
51
+ function inferTestType(scenario) {
52
+ return (scenario.testType ?? (scenario.steps.length === 1 ? "contract" : "integration")).toLowerCase();
53
+ }
54
+ /** Name key (if a name exists) plus one endpoint key per step. */
55
+ export function computeMatchKeys(scenario) {
56
+ const keys = [];
57
+ const slug = slugifyName(scenario.scenarioName);
58
+ if (slug)
59
+ keys.push(`name:${slug}`);
60
+ const testType = inferTestType(scenario);
61
+ for (const step of scenario.steps ?? []) {
62
+ if (!step?.method || !step?.path)
63
+ continue;
64
+ keys.push(`ep:${testType}:${step.method.toUpperCase()}:${normalizeMatchPath(step.path)}`);
65
+ }
66
+ return [...new Set(keys)];
67
+ }
68
+ /** Build the persisted plan item from a ranked Candidate. */
69
+ export function buildApprovedPlanItem(candidate) {
70
+ return {
71
+ candidateId: candidate.candidateId,
72
+ scenarioName: candidate.scenario.scenarioName,
73
+ testType: inferTestType(candidate.scenario),
74
+ category: candidate.scenario.category,
75
+ source: candidate.source,
76
+ ...(candidate.verifiedDiscriminator ? { verifiedDiscriminator: candidate.verifiedDiscriminator } : {}),
77
+ matchKeys: computeMatchKeys(candidate.scenario),
78
+ };
79
+ }
80
+ /**
81
+ * True when `query` matches at least one of `items`' matchKeys:
82
+ * - by scenarioName slug (`name:<slug>`), or
83
+ * - by testType + path (`ep:<testType>:*:<path>`), method-exact when
84
+ * `query.method` is given, method-agnostic (any method) otherwise — the
85
+ * contract tool's `method: ""` ("test all methods") case.
86
+ */
87
+ export function matchesApprovedItem(items, query) {
88
+ const nameSlug = query.scenarioName ? slugifyName(query.scenarioName) : "";
89
+ if (nameSlug) {
90
+ const nameKey = `name:${nameSlug}`;
91
+ if (items.some((item) => item.matchKeys.includes(nameKey)))
92
+ return true;
93
+ }
94
+ if (query.testType && query.path) {
95
+ const testType = query.testType.toLowerCase();
96
+ const normPath = normalizeMatchPath(query.path);
97
+ if (query.method) {
98
+ const epKey = `ep:${testType}:${query.method.toUpperCase()}:${normPath}`;
99
+ if (items.some((item) => item.matchKeys.includes(epKey)))
100
+ return true;
101
+ }
102
+ else {
103
+ const prefix = `ep:${testType}:`;
104
+ const suffix = `:${normPath}`;
105
+ if (items.some((item) => item.matchKeys.some((k) => k.startsWith(prefix) && k.endsWith(suffix)))) {
106
+ return true;
107
+ }
108
+ }
109
+ }
110
+ return false;
111
+ }
112
+ /**
113
+ * True when `query` is admissible under the approved plan: it matches a
114
+ * GENERATE item, or it matches an ADDITIONAL item while the plan has at least
115
+ * one GENERATE item. ADDITIONAL is strictly a backfill pool for failed
116
+ * GENERATE items — on a zero-budget plan (`generate: []`, e.g. maxGenerate=0
117
+ * for a config-only PR) there is nothing to backfill, so ADDITIONAL must not
118
+ * admit generation. Shared by planGuard and submitReportTool so the gate and
119
+ * the report cross-check can never disagree on admissibility.
120
+ */
121
+ export function matchesApprovedPlan(plan, query) {
122
+ if (matchesApprovedItem(plan.generate, query))
123
+ return true;
124
+ return plan.generate.length > 0 && matchesApprovedItem(plan.additional, query);
125
+ }
@@ -0,0 +1,5 @@
1
+ /** One hop: for each selected file, resolve its relative import/require refs to files in the POM set. */
2
+ export declare function expandImports(selected: string[], allPoms: string[]): Promise<Array<{
3
+ file: string;
4
+ referrer?: string;
5
+ }>>;
@@ -0,0 +1,32 @@
1
+ import { readFile } from "fs/promises";
2
+ import * as path from "path";
3
+ const REF_RE = /(?:from\s+["']([^"']+)["']|require\(\s*["']([^"']+)["']\s*\))/g;
4
+ /** One hop: for each selected file, resolve its relative import/require refs to files in the POM set. */
5
+ export async function expandImports(selected, allPoms) {
6
+ const pomSet = new Set(allPoms);
7
+ const out = selected.map((file) => ({ file }));
8
+ const seen = new Set(selected);
9
+ for (const file of selected) {
10
+ let content;
11
+ try {
12
+ content = await readFile(file, "utf8");
13
+ }
14
+ catch {
15
+ continue;
16
+ }
17
+ for (const m of content.matchAll(REF_RE)) {
18
+ const ref = m[1] ?? m[2];
19
+ if (!ref || !ref.startsWith("."))
20
+ continue;
21
+ const base = path.resolve(path.dirname(file), ref);
22
+ for (const cand of [base, `${base}.js`, `${base}.ts`, base.replace(/\.js$/, ".ts")]) {
23
+ if (pomSet.has(cand) && !seen.has(cand)) {
24
+ seen.add(cand);
25
+ out.push({ file: cand, referrer: file });
26
+ break;
27
+ }
28
+ }
29
+ }
30
+ }
31
+ return out;
32
+ }