@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
package/build/index.js CHANGED
@@ -24,7 +24,7 @@ import { registerCodeReuseTool } from "./tools/code-refactor/codeReuseTool.js";
24
24
  import { registerEnhanceAssertionsTool } from "./tools/code-refactor/enhanceAssertionsTool.js";
25
25
  import { registerBatchScenarioTestTool } from "./tools/generate-tests/generateBatchScenarioRestTool.js";
26
26
  import { registerMockTool } from "./tools/generate-tests/generateMockRestTool.js";
27
- import { registerAnalyzeChangesTool, registerAnalyzeTestHealthTool, registerActionsTool, } from "./tools/test-management/index.js";
27
+ import { registerAnalyzeChangesTool, registerAnalyzeTestHealthTool, registerActionsTool, registerRegisterTestPlanTool, } from "./tools/test-management/index.js";
28
28
  import { registerTestbotPrompt } from "./prompts/testbot/testbot-prompts.js";
29
29
  import { registerSutSetupResource } from "./resources/sutSetupResource.js";
30
30
  import { registerTestbotResource } from "./resources/testbotResource.js";
@@ -151,6 +151,7 @@ registerProgressResource(server);
151
151
  registerAnalyzeChangesTool(server);
152
152
  registerAnalyzeTestHealthTool(server);
153
153
  registerActionsTool(server);
154
+ registerRegisterTestPlanTool(server);
154
155
  // Register workspace management tools
155
156
  registerInitScanWorkspaceTool(server);
156
157
  registerInitializeWorkspaceTool(server);
@@ -1 +1,7 @@
1
- export declare function getCodeReusePrompt(testFile: string, language: string, framework?: string): string;
1
+ import type { ScopedPom } from "../utils/pom-scope/index.js";
2
+ export declare function isPomAwareTarget(language: string, framework?: string): boolean;
3
+ export declare function getCodeReusePrompt(testFile: string, language: string, framework?: string, scopedPoms?: {
4
+ tier1: ScopedPom[];
5
+ tier2: ScopedPom[];
6
+ scannedNoOverlap?: boolean;
7
+ }): string;
@@ -18,11 +18,14 @@ const LANGUAGE_MAP = {
18
18
  fileName: "skyrampUtils.ts",
19
19
  },
20
20
  };
21
- export function getCodeReusePrompt(testFile, language, framework) {
22
- const isTypescriptPlaywright = language.toLowerCase() === "typescript" &&
23
- framework?.toLowerCase() === "playwright";
24
- if (isTypescriptPlaywright) {
25
- return getPomAwareCodeReusePrompt(testFile);
21
+ export function isPomAwareTarget(language, framework) {
22
+ const lang = language.toLowerCase();
23
+ return (lang === "typescript" || lang === "javascript") && framework?.toLowerCase() === "playwright";
24
+ }
25
+ export function getCodeReusePrompt(testFile, language, framework, scopedPoms) {
26
+ const lang = language.toLowerCase();
27
+ if (isPomAwareTarget(lang, framework)) {
28
+ return getPomAwareCodeReusePrompt(testFile, lang, scopedPoms);
26
29
  }
27
30
  const ext = LANGUAGE_MAP[language].extension || "py";
28
31
  const fileName = LANGUAGE_MAP[language].fileName || "SkyrampUtils.py";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,62 @@
1
+ import { getCodeReusePrompt } from "./code-reuse.js";
2
+ const POM = "POM-AWARE CODE REUSE"; // unique to the POM branch
3
+ const pom = (file, score = 2, rareHits = 2, matchedTokens = ["a-b-c"]) => ({ file, score, rareHits, matchedTokens });
4
+ describe("getCodeReusePrompt routing", () => {
5
+ it("routes javascript + playwright to the POM-aware prompt", () => {
6
+ expect(getCodeReusePrompt("t.spec.js", "javascript", "playwright")).toContain(POM);
7
+ });
8
+ it("still routes typescript + playwright to the POM-aware prompt", () => {
9
+ expect(getCodeReusePrompt("t.spec.ts", "typescript", "playwright")).toContain(POM);
10
+ });
11
+ it("routes javascript + non-playwright to SkyrampUtils", () => {
12
+ expect(getCodeReusePrompt("t.spec.js", "javascript", "jest")).not.toContain(POM);
13
+ });
14
+ it("routes python to SkyrampUtils (no regression)", () => {
15
+ expect(getCodeReusePrompt("t.py", "python", "pytest")).not.toContain(POM);
16
+ });
17
+ it("does not trigger POM path when framework is undefined", () => {
18
+ expect(getCodeReusePrompt("t.spec.ts", "typescript")).not.toContain(POM);
19
+ });
20
+ });
21
+ describe("getCodeReusePrompt scoped POM list", () => {
22
+ it("emits the pre-detected two-tier STEP 1 when scoped files are supplied", () => {
23
+ const p = getCodeReusePrompt("t.spec.js", "javascript", "playwright", { tier1: [pom("/repo/pages/loginPage.js")], tier2: [pom("/repo/pages/homePage.js", 0.3, 1, ["menu-link-/alerts-item"])] });
24
+ expect(p).toContain("ALREADY DETECTED");
25
+ expect(p).toContain("/repo/pages/loginPage.js");
26
+ expect(p).toContain("catalog fully");
27
+ expect(p).toContain("/repo/pages/homePage.js");
28
+ expect(p).toContain("signature level");
29
+ expect(p).toContain("menu-link-/alerts-item"); // matched tokens surface for tier-2 mapping evidence
30
+ expect(p).not.toContain("DETECT POM CLASSES");
31
+ });
32
+ it("emits the glob STEP 1 when no scoped files are supplied", () => {
33
+ expect(getCodeReusePrompt("t.spec.js", "javascript", "playwright")).toContain("DETECT POM CLASSES");
34
+ });
35
+ it("emits the glob STEP 1 when both tiers are empty (fallback)", () => {
36
+ expect(getCodeReusePrompt("t.spec.js", "javascript", "playwright", { tier1: [], tier2: [] }))
37
+ .toContain("DETECT POM CLASSES");
38
+ });
39
+ it("returns the short skip response on a clean scan with zero overlap", () => {
40
+ const p = getCodeReusePrompt("t.spec.js", "javascript", "playwright", {
41
+ tier1: [], tier2: [], scannedNoOverlap: true,
42
+ });
43
+ expect(p).toContain("No reusable POM layer detected");
44
+ expect(p).toContain("verify: true"); // escape hatch mandates re-verify
45
+ expect(p).not.toContain("COMPLIANCE RULES"); // NOT the full workflow prompt
46
+ expect(p.length).toBeLessThan(2000);
47
+ });
48
+ it("keeps the full agent-driven fallback when scoping was inconclusive", () => {
49
+ // no scopedPoms argument = scan didn't run or didn't conclude
50
+ const p = getCodeReusePrompt("t.spec.js", "javascript", "playwright");
51
+ expect(p).toContain("COMPLIANCE RULES");
52
+ expect(p).not.toContain("No reusable POM layer detected");
53
+ });
54
+ it("leaves the SkyrampUtils path untouched for non-Playwright languages", () => {
55
+ const p = getCodeReusePrompt("t.py", "python", undefined, { tier1: [], tier2: [], scannedNoOverlap: true });
56
+ expect(p).not.toContain("No reusable POM layer detected");
57
+ expect(p).toContain("SkyrampUtils");
58
+ });
59
+ it("does not annotate when scoping simply was not attempted", () => {
60
+ expect(getCodeReusePrompt("t.spec.js", "javascript", "playwright")).not.toContain("No reusable POM layer detected");
61
+ });
62
+ });
@@ -1 +1,6 @@
1
- export declare function getPomAwareCodeReusePrompt(testFile: string): string;
1
+ import type { ScopedPom } from "../utils/pom-scope/index.js";
2
+ export declare function getPomAwareCodeReusePrompt(testFile: string, language?: string, scopedPoms?: {
3
+ tier1: ScopedPom[];
4
+ tier2: ScopedPom[];
5
+ scannedNoOverlap?: boolean;
6
+ }): string;
@@ -1,8 +1,52 @@
1
1
  import { generateSkyrampHeader, SKYRAMP_UTILS_HEADER } from "../utils/utils.js";
2
- const TS_UTILS_FILE = "skyrampUtils.ts";
3
- const TS_CODEGEN_MARKER = "// Generated by Skyramp v";
4
- export function getPomAwareCodeReusePrompt(testFile) {
5
- return `# POM-AWARE CODE REUSE TYPESCRIPT/PLAYWRIGHT
2
+ export function getPomAwareCodeReusePrompt(testFile, language = "typescript", scopedPoms) {
3
+ const isJs = language.toLowerCase() === "javascript";
4
+ const ext = isJs ? "js" : "ts";
5
+ const utilsFile = isJs ? "skyrampUtils.js" : "skyrampUtils.ts";
6
+ const codegenMarker = "// Generated by Skyramp v";
7
+ const headerLang = isJs ? "javascript" : "typescript";
8
+ const scopedActive = scopedPoms && scopedPoms.tier1.length + scopedPoms.tier2.length > 0;
9
+ if (!scopedActive && scopedPoms?.scannedNoOverlap) {
10
+ return `No reusable POM layer detected: a repo-wide scan found zero overlap between this test's selectors and any non-test source file. Code reuse is skipped — continue with the generated test as-is.
11
+
12
+ Caveat: detection matches literal selector tokens; a POM layer built on computed/dynamic selectors could be missed. If you have concrete evidence of one (e.g. a pageobjects/ directory with classes covering this flow), you may refactor ${testFile} manually following the repo's existing spec conventions — and if you do, you MUST call this tool again with \`verify: true\` and follow its report before finishing. Do NOT call skyramp_modularization.`;
13
+ }
14
+ const renderPom = (p) => `- \`${p.file}\`${p.matchedTokens.length > 0 ? ` — matched: ${p.matchedTokens.map((t) => `\`${t}\``).join(", ")}` : ""}`;
15
+ const step1Section = scopedActive
16
+ ? `## STEP 1: POM FILES — ALREADY DETECTED
17
+
18
+ POM detection has been performed programmatically from this test's selectors.
19
+
20
+ **Tier 1 — high relevance (catalog fully — every property and method):**
21
+ ${scopedPoms.tier1.map(renderPom).join("\n") || "(none)"}
22
+
23
+ **Tier 2 — supporting/possible (catalog at signature level ONLY — class name, method names, and the matched selectors shown; deepen a file only if STEP 2b maps an action to it):**
24
+ ${scopedPoms.tier2.map(renderPom).join("\n") || "(none)"}
25
+
26
+ **Caution on matched tokens:** the tokens above are raw substring evidence from the detector, not a guarantee of the file's full frame id — a matched token like \`#application-new-applied\` can hide that the file's ACTUAL frame is a prefix-sibling such as \`#application-new-applied-type\`. When you catalog each file in STEP 2, record its ACTUAL full selector/frame id from source rather than the bare matched token.
27
+
28
+ Do NOT glob or scan for other POM files. Proceed to STEP 2 and catalog exactly these files (plus any file they import or extend that you find essential while reading them).
29
+
30
+ **Catalog-completeness guard for STEP 2a:** when checking for an existing \`skyramp-pom-catalog.md\`, it is only valid for THIS run if it already contains an entry for EVERY file listed above. If any listed file is missing from the catalog, do NOT reuse it wholesale — keep its existing entries and generate + append entries for the missing listed files (then use the updated catalog).
31
+
32
+ **Decision:** continue to STEP 2.`
33
+ : `## STEP 1: DETECT POM CLASSES IN THE PROJECT
34
+
35
+ Use the Glob tool to search for POM files starting from the directory containing \`${testFile}\`:
36
+ - Pattern 1: \`**/pageobjects/**/*.{ts,js}\`
37
+ - Pattern 2: \`**/page-objects/**/*.{ts,js}\`
38
+ - Pattern 3: \`**/pages/**/*.{ts,js}\`
39
+ - Pattern 4: \`**/*.page.{ts,js}\`
40
+ - Pattern 5: \`**/*Page.{ts,js}\`
41
+ - Pattern 6: \`**/pom/**/*.{ts,js}\`
42
+
43
+ Exclude test/spec files by **basename only** — never match on the directory path. POM layers commonly live under a \`tests/\` directory (e.g. \`tests/ui-testing/pages/\`), and path-matching would wrongly drop every POM. A file is excluded only if its **basename** ends in \`.spec.ts\`, \`.spec.js\`, \`.test.ts\`, or \`.test.js\`. Do NOT exclude on the bare substrings \`test\` or \`spec\`: they falsely match legitimate POMs (e.g. \`searchJobInspectorPage.js\` contains "spec") and directory names (\`tests/\`).
44
+ Also exclude \`node_modules\`.
45
+
46
+ **Decision:**
47
+ - Zero matches found → **SKIP TO STEP 6 (FALLBACK)**
48
+ - One or more matches found → continue to STEP 2`;
49
+ return `# POM-AWARE CODE REUSE — TYPESCRIPT/JAVASCRIPT + PLAYWRIGHT
6
50
 
7
51
  ## COMPLIANCE RULES — READ THIS FIRST, APPLY TO EVERY STEP
8
52
 
@@ -27,22 +71,7 @@ This is NOT the same as SkyrampUtils consolidation. Do NOT create a SkyrampUtils
27
71
 
28
72
  ---
29
73
 
30
- ## STEP 1: DETECT POM CLASSES IN THE PROJECT
31
-
32
- Use the Glob tool to search for POM files starting from the directory containing \`${testFile}\`:
33
- - Pattern 1: \`**/pageobjects/**/*.ts\`
34
- - Pattern 2: \`**/page-objects/**/*.ts\`
35
- - Pattern 3: \`**/pages/**/*.ts\`
36
- - Pattern 4: \`**/*.page.ts\`
37
- - Pattern 5: \`**/*Page.ts\`
38
- - Pattern 6: \`**/pom/**/*.ts\`
39
-
40
- Exclude any file with \`test\`, \`spec\`, \`.test.\`, or \`.spec.\` in its name.
41
- Also exclude \`node_modules\`.
42
-
43
- **Decision:**
44
- - Zero matches found → **SKIP TO STEP 6 (FALLBACK)**
45
- - One or more matches found → continue to STEP 2
74
+ ${step1Section}
46
75
 
47
76
  ---
48
77
 
@@ -50,7 +79,7 @@ Also exclude \`node_modules\`.
50
79
 
51
80
  **Step 2a: Check for existing catalog (MANDATORY FIRST STEP)**
52
81
 
53
- Check if \`skyramp-pom-catalog.md\` exists in the same directory as \`tsconfig.json\`:
82
+ Check if \`skyramp-pom-catalog.md\` exists in the same directory as \`tsconfig.json\` (or \`jsconfig.json\`, or the test root if neither exists):
54
83
  - **If it exists:** Read it and verify it starts with \`<!-- skyramp-pom-catalog: auto-generated -->\`
55
84
  - Valid → Output: *"Using existing POM catalog from /path/to/skyramp-pom-catalog.md"* and **skip to STEP 2b**
56
85
  - Invalid → Continue to full catalog generation below
@@ -62,12 +91,17 @@ Check if \`skyramp-pom-catalog.md\` exists in the same directory as \`tsconfig.j
62
91
 
63
92
  Before writing a single line of refactored code, do the following:
64
93
 
65
- can 1. **Read existing refactored tests** — Find any already-refactored test files in the repository and note their established conventions: how imports are grouped, what comment style sections use, and how \`waitForTimeout\`, \`waitForResponse\`, and dynamic locators are handled. Where existing tests provide a pattern, follow it exactly.
94
+ 1. **Extract conventions from ONE existing test** — Open one existing test spec (prefer one that imports the POM layer). Extract ONLY: (a) the instantiation convention (central PageManager, e.g. \`const pm = new PageManager(page)\`, vs direct \`new X(page)\`), (b) import style and path aliases, (c) the section-comment style. Ignore everything else in that file — do NOT mimic its test logic, waits, or assertions.
66
95
 
67
96
  2. **Read tsconfig.json / jsconfig.json** — Capture path alias conventions (e.g., \`@pageobjects/*\`, \`@common-function/*\`). All imports in the refactored test must use these aliases exactly.
68
97
 
69
- 3. **Read every POM file found in STEP 1** — For each file, document:
98
+ 3. **Read the POM files from STEP 1** — Depth depends on how STEP 1 listed them: files marked **Tier 2 (signature level)** get a REDUCED entry — class name, method names (signatures only), and the matched selectors STEP 1 showed; do NOT read their full bodies or record per-method timing/network details unless STEP 2b later maps an action to that file (then deepen just that file). **Tier 1 files — and ALL files when STEP 1 did not use tiers (agent-detected mode)** — get the FULL treatment below. For each full-depth file, document:
70
99
  - Class name, import path (using tsconfig aliases), and what page/component it represents
100
+ **Locator styles to capture — a POM class may expose selectors in ANY of these forms; catalog all of them, recording the selector string passed to \`.locator()\` / \`getByRole\` / \`getByTestId\` etc.:**
101
+ - Constructor-assigned: \`this.loginButton = page.locator('[data-test="login-sign-in"]')\`
102
+ - Getter accessors: \`get firstRow() { return this.page.locator('[data-test^="row-"]').first(); }\`
103
+ - Object-literal maps: \`this.locators = { menuItem: '[data-test="menu-item"]', ... }\` (each key is a named selector)
104
+ - Inline in methods: a \`.locator(...)\` used directly inside an action method (still a reusable target, named by its method)
71
105
  - Iframe selector used in the constructor (e.g. \`page.frameLocator('iframe#asset-list')\`), or "top-level page" if no iframe
72
106
  - Every property (locator): name and its exact selector string from the constructor assignment
73
107
  - Every method: name, parameters, what action it performs
@@ -77,7 +111,7 @@ can 1. **Read existing refactored tests** — Find any already-refactored test f
77
111
  - Whether the method wraps a multi-step sequence using sub-properties or components (e.g. \`searchAssetsByCondition\` internally calls \`searchConditionArea.expandWhenClosed()\`, \`clearBtn.click()\`, \`selectItemName()\`, \`valueKeyword.fill()\`, \`searchBtn.click()\`) — flag these as **wrapping methods**
78
112
  - Flag methods as **timing-aware**, **network-aware**, or **wrapping** as appropriate — all three are high-value substitution targets
79
113
 
80
- 4. **Write the POM catalog — MANDATORY** — Write the complete catalog to \`skyramp-pom-catalog.md\` in the same directory as \`tsconfig.json\`. This gives you a clean structured reference for STEP 2b and leaves a record for debugging.
114
+ 4. **Write the POM catalog — MANDATORY** — Write the complete catalog to \`skyramp-pom-catalog.md\` in the same directory as \`tsconfig.json\` (or \`jsconfig.json\`, or the test root if neither exists). This gives you a clean structured reference for STEP 2b and leaves a record for debugging.
81
115
 
82
116
  **BLOCKING: Do not proceed to STEP 2b until you have written this file and confirmed the path in a single output line:**
83
117
  *"POM catalog written to /path/to/skyramp-pom-catalog.md"*
@@ -98,13 +132,15 @@ Use this exact format for the file:
98
132
  - \`methodName(param1, param2)\` — description; ⏱ timing-aware: waitForTimeout(Xms) + sleep(Ys) | 🌐 network-aware: waitForResponse('**/pattern**') | (omit tags if neither)
99
133
  \`\`\`
100
134
 
135
+ **Tier-2 entries in the catalog:** for files cataloged at signature level, the entry contains the class name, the import path, a \`- **Methods:**\` list of names/signatures only, and the matched selectors from STEP 1 under \`- **Properties:**\`. Mark such entries with \`<!-- depth: signature -->\` on the line after the class heading so a later run knows the entry is shallow.
136
+
101
137
  ---
102
138
 
103
139
  ## STEP 2b: BUILD AND OUTPUT A MAPPING TABLE
104
140
 
105
141
  **This step is MANDATORY. Do not write any code until it is complete.**
106
142
 
107
- For every raw locator and action sequence in \`${testFile}\`, find the best POM match and record it in a table. Output the complete table to chat before proceeding to STEP 3.
143
+ For every raw locator and action sequence in \`${testFile}\`, find the best POM match; record the PREFERRED and HIGH matches in a table (all others simply remain inline). Output the complete table to chat before proceeding to STEP 3.
108
144
 
109
145
  ### Matching criteria
110
146
 
@@ -116,7 +152,7 @@ Use **selector-string evidence** as the primary signal — not naming similarity
116
152
  - When a PREFERRED method is used: drop the raw test's \`waitForTimeout\` and/or \`responsePromise\` block that fall *inside* the method's covered sequence; add a comment noting the method handles it internally
117
153
 
118
154
  **HIGH confidence** (substitute in refactored test):
119
- - The selector string in the raw locator appears verbatim or near-verbatim in the POM property assignment in the constructor. Example: raw test has \`locator('button:has-text("資産登録")')\` and the POM constructor has \`this.iframe.locator('button:has-text("資産登録")')\`
155
+ - The selector string in the raw locator appears verbatim or near-verbatim in the POM property assignment in the constructor. Example: raw test has \`locator('button:has-text("資産登録")')\` and the POM constructor has \`this.iframe.locator('button:has-text("資産登録")')\`. A verbatim match against a Tier-1 file's property or constant is evidence TO SUBSTITUTE the owning method/property — never evidence the raw locator is acceptable.
120
156
  - A POM method's internal steps clearly and completely cover the entire raw action sequence with no gaps
121
157
  - **HIGH via convention:** The existing refactored tests in this repository already use this POM method for the same action pattern — even if the selector strings differ between the raw test and the POM. Convention evidence from the repo's own tests overrides selector mismatch. Example: every refactored test in the repo calls \`mainMenu.assetInformation.select()\` to navigate to the asset list, so a raw test doing the same navigation with different locators should also use it.
122
158
 
@@ -138,6 +174,8 @@ Before considering any POM match, verify the iframe context matches:
138
174
 
139
175
  ### Mapping table format
140
176
 
177
+ List ONLY rows with PREFERRED or HIGH confidence — these are the substitutions you will make. Do NOT enumerate LOW/UNCERTAIN or UNMAPPED locators in the table; every locator not in the table remains inline as-is.
178
+
141
179
  Output the table in this format:
142
180
 
143
181
  \`\`\`
@@ -145,17 +183,18 @@ Output the table in this format:
145
183
  |---|---|---|---|---|
146
184
  | goto + fill email/password + click login | LoginPage | login(email, password) | method is timing-aware (waitForTimeout+sleep) covering login sequence | PREFERRED |
147
185
  | frameLocator('iframe#asset-list').locator('button:has-text("資産登録")') | AssetListPage | assetEntryBtn | iframe.locator('button:has-text("資産登録")') | HIGH |
148
- | page.locator('input[name="email"]').fill(...) | LoginPage | emailInput | getByPlaceholder('メールアドレス(半角)') | LOW |
149
- | frameLocator('iframe#wf-input').locator('input[name="changeQty"]') | — | — | No match | UNMAPPED |
150
186
  \`\`\`
151
187
 
188
+ All other locators remain inline.
189
+
152
190
  ### Rules after completing the table
153
191
 
154
192
  - **PREFERRED entries MUST be substituted** — use the POM method and drop the raw test's explicit waits/correlations that fall inside the method's scope
155
193
  - **HIGH entries MUST be substituted** — use the POM method or property; do NOT write an inline locator
156
194
  - **Always prefer the highest level of abstraction available** — if a wrapping method exists that covers a multi-step sequence, use it instead of calling the individual sub-properties directly. Example: use \`searchAssetsByCondition("資産名", assetName)\` not \`searchConditionArea.expandWhenClosed()\` + \`getConditionRow(1)\` + \`selectItemName()\` etc. Use \`bulkDelete()\` not \`bulkDeleteBtn.click()\` + \`confirmDeleteDialog\` + \`noticeDialog\` inline.
157
- - **Do NOT upgrade LOW or UNCERTAIN** because the substitution seems reasonable
195
+ - **Do NOT upgrade LOW or UNCERTAIN to PREFERRED/HIGH** because the substitution seems reasonable — leave them inline and out of the table
158
196
  - **Conservative default**: an incorrect substitution that breaks the test is worse than leaving something inline — when in doubt, leave it inline
197
+ - **"The raw locator already matches the repo's selector convention" is NEVER a reason to keep it inline** — identical selector strings are the argument for reuse, not against it. If you catch yourself abstaining on this basis, map the action to the owning POM member instead.
159
198
  - If the table has ZERO PREFERRED or HIGH entries → **SKIP TO STEP 6 (FALLBACK)**
160
199
  - If AT LEAST ONE PREFERRED or HIGH entry exists → continue to STEP 3
161
200
 
@@ -264,6 +303,8 @@ When a module operates inside an iframe, determine the correct frame handle by r
264
303
 
265
304
  **Never create standalone \`frameLocator()\` references in the test body.** If an action is UNMAPPED and must stay inline, access the frame through the POM's own \`iframe\` property (e.g. \`assetSelectPage.iframe.locator(...)\`, \`inputPage.iframe.getByRole(...)\`). Do not create a separate \`const inputPageFrame = skyrampPage.frameLocator('#application-new-applied')\` — this duplicates the frame reference that already exists in the POM.
266
305
 
306
+ **Exception — orphan frame:** if NO cataloged POM class scopes to the frame a raw action uses, there is no POM \`iframe\` property to borrow. In that case a standalone inline \`frameLocator(...)\` for that action is CORRECT — keep it, and mark it with a brief comment (e.g. \`// no POM owns this frame — kept inline\`). Do not force-fit a method from a different-frame class (the gate above still forbids that) and do not invent a POM to cover it.
307
+
267
308
  ### 3h. Dynamic Locators — Prefer Positional Module Methods
268
309
 
269
310
  Before keeping a dynamic locator inline, check whether a module method exists that targets the same element by position rather than by value. If one exists, use it with the appropriate position (typically \`0\` for the first result) and add a comment noting it replaces the original dynamic locator. If no positional equivalent exists, keep the raw dynamic locator inline as-is.
@@ -278,6 +319,12 @@ If the existing POM files have an established pattern for checkbox interactions,
278
319
 
279
320
  Screenshot assertions (\`toHaveScreenshot\`) must stay in their **exact original position** relative to surrounding actions from the raw test. Note what actions appear immediately before and after the screenshot in the raw test, then place the screenshot at that same relative position in the refactored test. If a POM method would move the screenshot, inline that method's steps around it with a comment: \`// POM method not used — screenshot position must be preserved\`
280
321
 
322
+ ### 3k. Page Object Instantiation — Follow the Repo's Convention
323
+
324
+ **Instantiation convention — follow the repo's existing pattern (read the existing specs to detect it):**
325
+ - If the repo uses a central PageManager (e.g. \`const pm = new PageManager(page)\` then \`pm.loginPage.login()\`), the refactored test MUST use that accessor convention — do NOT instantiate page objects directly.
326
+ - Otherwise, instantiate page objects directly, matching what the existing specs do.
327
+
281
328
  ---
282
329
 
283
330
  ## STEP 4: WRITE THE REFACTORED TEST
@@ -288,13 +335,11 @@ Using your catalog from STEP 2 and analysis from STEP 3, produce the refactored
288
335
  Before writing each line of refactored code, look up that action in your STEP 2b mapping table:
289
336
  - If it is marked **PREFERRED** → use the POM method and drop any raw waits/correlations inside its scope. This is the highest-value substitution.
290
337
  - If it is marked **HIGH** → you MUST use the POM method or property. Do NOT write an inline locator.
291
- - If it is marked **LOW / UNCERTAIN / UNMAPPED** → write it inline exactly as in the original test.
338
+ - If it is NOT in the table (LOW / UNCERTAIN / UNMAPPED) → write it inline exactly as in the original test.
292
339
 
293
- Do not skip this lookup for any action. The mapping table is your source of truth not the raw test's locators.
340
+ A locator with a PREFERRED/HIGH mapping that is written inline anyway is a DEFECT the customer reads that as a missed reuse.
294
341
 
295
- ## STEP 4b: VERIFY AGAINST MAPPING TABLE
296
-
297
- Before proceeding to STEP 5, scan your refactored test output and for each HIGH entry in the mapping table confirm the corresponding POM call is present in the output. If any HIGH entry is missing its POM call and was written as inline code instead, go back and fix it before writing the file.
342
+ Do not skip this lookup for any action. The mapping table is your source of truth — not the raw test's locators.
298
343
 
299
344
  **Section Comments:** Divide the test body into clearly labeled sections corresponding to the logical phases of the user journey. Use the comment style from the repository's existing tests. If none exists, use \`/*----- Section Name -----*/\`.
300
345
 
@@ -308,17 +353,19 @@ Before proceeding to STEP 5, scan your refactored test output and for each HIGH
308
353
  - Screenshot assertions (\`toHaveScreenshot\`): always keep inline; if mid-method sequence, inline that method's steps around it
309
354
  - Page readiness checks: use module methods such as \`pageToBeVisible()\` if they exist
310
355
 
311
- **IMPORTANT:** Do NOT create a \`skyrampUtils.ts\` or any SkyrampUtils file on this path. Do NOT call \`skyramp_modularization\` after completing POM-aware code reuse — POM refactoring replaces that step.
356
+ **IMPORTANT:** Do NOT create a \`${utilsFile}\` or any SkyrampUtils file on this path. Do NOT call \`skyramp_modularization\` after completing POM-aware code reuse — POM refactoring replaces that step.
312
357
 
313
358
  ---
314
359
 
315
360
  ## STEP 5: WRITE THE OUTPUT
316
361
 
317
- Write the complete, runnable refactored test directly to \`${testFile}\`, overwriting it in place. The file must contain every line — never use placeholder comments like \`// ...continue\` or \`// ...existing code\`. If the test is long, write it in multiple passes until the file is complete. Confirm the file path written to in a single line once done.
362
+ Write the complete, runnable refactored test directly to \`${testFile}\`, overwriting it in place. Before overwriting, save the current (pre-refactor) content to \`${testFile}.raw.bak\` — verification uses it to restore demoted substitutions. The file must contain every line — never use placeholder comments like \`// ...continue\` or \`// ...existing code\`. If the test is long, write it in multiple passes until the file is complete. Confirm the file path written to in a single line once done.
318
363
 
319
364
  Do not output a section breakdown, modularization summary, or any analysis to the chat. Perform all analysis internally.
320
365
 
321
- **Do NOT run the test. Do NOT call any other tools. Your work is complete once the file is written.**
366
+ **A low substitution count can be the correct outcome.** When many actions stay inline because the POM layer simply does not model that flow (iframe-gate rejections, orphan frames, vocabulary drift from the cataloged classes), that is honest reuse, not a shortfall — do not pad the mapping table to inflate the count. In your one-line confirmation, state how many actions were substituted vs. kept inline and why in one clause (e.g. "6 substituted; 9 kept inline — POM layer does not model the apply-from-asset flow").
367
+
368
+ **Do NOT run the test.** After writing the file, you MUST call \`skyramp_reuse_code\` once more with the same \`testFile\`/\`language\`/\`framework\` and \`verify: true\`, and follow its report: re-verify after every fix pass; a violation may be remapped at most once — if flagged again, demote it to the raw inline form; finish only when it reports PASSED (unverifiable notes are acceptable). Include \`verification: passed\` (or the unresolved count) in your final confirmation line.
322
369
 
323
370
  ---
324
371
 
@@ -360,7 +407,7 @@ Read ${testFile} and look for ALREADY DEFINED helper functions (not just repetit
360
407
  ## STEP 2: FIND EXISTING UTILS
361
408
  Use the Grep tool to search for files containing "${SKYRAMP_UTILS_HEADER}":
362
409
  - Pattern: "${SKYRAMP_UTILS_HEADER}"
363
- - Type: "ts"
410
+ - Type: "${ext}"
364
411
  - Output mode: "files_with_matches"
365
412
  **CRITICAL: Only look for util files with header ${SKYRAMP_UTILS_HEADER}** - exclude test files (files with "test", "spec", ".test.", ".spec." in the name).
366
413
  Read the matching non-test files and check if any helpers can be reused in ${testFile}.
@@ -373,18 +420,18 @@ If helpers exist in util file that can be reused in ${testFile} without modifyin
373
420
 
374
421
  ## STEP 4: FIND LOCAL HELPERS IN OTHER SKYRAMP-GENERATED TEST FILES
375
422
 
376
- You are looking for sibling test files generated by Skyramp's codegen. They start with the comment line \`${TS_CODEGEN_MARKER}…\` (note: the version suffix is what distinguishes them from the SkyrampUtils header). The substring \`${SKYRAMP_UTILS_HEADER}\` is contained in that line, so:
423
+ You are looking for sibling test files generated by Skyramp's codegen. They start with the comment line \`${codegenMarker}…\` (note: the version suffix is what distinguishes them from the SkyrampUtils header). The substring \`${SKYRAMP_UTILS_HEADER}\` is contained in that line, so:
377
424
 
378
425
  Use the Grep tool to search for other test files containing "${SKYRAMP_UTILS_HEADER}":
379
426
  - Pattern: "${SKYRAMP_UTILS_HEADER}"
380
- - Type: "ts"
427
+ - Type: "${ext}"
381
428
  - Output mode: "files_with_matches"
382
429
 
383
430
  **CRITICAL: Exclude ${testFile} from the results** — only look at OTHER files, not the current file.
384
431
 
385
432
  The result will mix two kinds of files:
386
433
  - **Utils file(s)** — already handled by STEP 2/STEP 3.
387
- - **Other Skyramp-generated test files** — these are the targets of STEP 4 / 4b / 5. Identify them by the \`${TS_CODEGEN_MARKER}\` codegen marker on line 1 (NOT the bare utils header).
434
+ - **Other Skyramp-generated test files** — these are the targets of STEP 4 / 4b / 5. Identify them by the \`${codegenMarker}\` codegen marker on line 1 (NOT the bare utils header).
388
435
 
389
436
  **STOP HERE IF NO OTHER SKYRAMP-GENERATED TEST FILES FOUND**
390
437
  **IF NO OTHER SKYRAMP-GENERATED TEST FILES ARE FOUND, SKIP TO STEP 6 - DO NOT CREATE ANY UTILS FILES.**
@@ -407,7 +454,7 @@ NOT A HELPER FUNCTION (do not extract):
407
454
 
408
455
  ## STEP 4b: PARAMETERIZE NEAR-DUPLICATE HELPERS ACROSS SKYRAMP-GENERATED TEST FILES
409
456
 
410
- **This step handles the cross-test case: ${testFile} AND another Skyramp-generated test file each define their own helper that does the SAME shape of work with only literal-value differences. These should become ONE parameterized helper in \`${TS_UTILS_FILE}\`.**
457
+ **This step handles the cross-test case: ${testFile} AND another Skyramp-generated test file each define their own helper that does the SAME shape of work with only literal-value differences. These should become ONE parameterized helper in \`${utilsFile}\`.**
411
458
 
412
459
  ### Definition: "near-duplicate" helpers
413
460
 
@@ -478,9 +525,9 @@ For each helper defined locally in ${testFile}:
478
525
  - **Body**: the common primitive sequence with the differing literals replaced by parameters.
479
526
  - **Forbidden**: do NOT add if/else, ternaries, defaults, or any logic to bridge the differences. If a single body can't cover both call sites without new logic, they are NOT near-duplicates — leave them alone.
480
527
  4. **If multiple matches** → process each pair independently. Merge every pair that clearly meets all three near-duplicate criteria. Skip any pair you're uncertain about — a missed merge is recoverable, a wrong merge breaks both tests.
481
- 5. **Write** the merged helper into \`${TS_UTILS_FILE}\` (create the file with the standard header if it doesn't exist; same header as STEP 5 below).
528
+ 5. **Write** the merged helper into \`${utilsFile}\` (create the file with the standard header if it doesn't exist; same header as STEP 5 below).
482
529
  6. **Delete** both original helpers from their test files.
483
- 7. **Import** the merged helper from \`${TS_UTILS_FILE}\` into both test files.
530
+ 7. **Import** the merged helper from \`${utilsFile}\` into both test files.
484
531
  8. **Replace** the two original call sites with calls to the merged helper, passing the values that were originally hardcoded or argument-passed in each test.
485
532
 
486
533
  ### Hard rules
@@ -488,7 +535,7 @@ For each helper defined locally in ${testFile}:
488
535
  - **Conservative bias**: a wrong merge breaks both tests; a missed merge leaves a duplicate. When in doubt, do NOT merge.
489
536
  - **No "cleanup"**: the merged body must mirror the original primitive sequence — same waits, same correlation patterns, same order. This is not refactoring.
490
537
  - **Preserve typing style**: if originals used \`page: any\` (or untyped \`page\`), keep that convention.
491
- - **Don't merge with helpers already in \`${TS_UTILS_FILE}\`**: those are STEP 3's territory. STEP 4b is strictly cross-test merge between sibling Skyramp-generated test files.
538
+ - **Don't merge with helpers already in \`${utilsFile}\`**: those are STEP 3's territory. STEP 4b is strictly cross-test merge between sibling Skyramp-generated test files.
492
539
 
493
540
  ---
494
541
 
@@ -505,15 +552,15 @@ For each helper defined locally in ${testFile}:
505
552
  **IF ANY CONDITION IS NOT MET, SKIP TO STEP 6 - DO NOT CREATE ANY UTILS FILES.**
506
553
 
507
554
  **CRITICAL:** For helpers found in STEP 4:
508
- 1. **CREATE** \`${TS_UTILS_FILE}\` file if it doesn't exist with the following header:
555
+ 1. **CREATE** \`${utilsFile}\` file if it doesn't exist with the following header:
509
556
  \`\`\`ts
510
- ${generateSkyrampHeader("typescript")}
557
+ ${generateSkyrampHeader(headerLang)}
511
558
  \`\`\`
512
- **CRITICAL: IF \`${TS_UTILS_FILE}\` is already big, create a new file with a different name and add the header to the new file.**
513
- 2. **COPY** those local helper functions from original test source files to \`${TS_UTILS_FILE}\` without modifying them
559
+ **CRITICAL: IF \`${utilsFile}\` is already big, create a new file with a different name and add the header to the new file.**
560
+ 2. **COPY** those local helper functions from original test source files to \`${utilsFile}\` without modifying them
514
561
  3. **DELETE** those local helper functions from test source files (REMOVE THEM COMPLETELY) WITHOUT ANY OTHER CHANGES.
515
- 4. **IMPORT** those local helper functions from \`${TS_UTILS_FILE}\` back into test source files WITHOUT MAKING ANY OTHER CHANGE.
516
- 5. **IMPORT** those local helper functions from \`${TS_UTILS_FILE}\` into ${testFile}
562
+ 4. **IMPORT** those local helper functions from \`${utilsFile}\` back into test source files WITHOUT MAKING ANY OTHER CHANGE.
563
+ 5. **IMPORT** those local helper functions from \`${utilsFile}\` into ${testFile}
517
564
  6. **REPLACE** duplicate code in ${testFile} with calls to imported helper functions WITHOUT MAKING ANY OTHER CHANGE.
518
565
 
519
566
  **CRITICAL: DO NOT CREATE UNNECESSARY HELPER FUNCTIONS**
@@ -525,14 +572,14 @@ For each helper defined locally in ${testFile}:
525
572
  3. **VERIFY** that helper functions are NO LONGER in original test files
526
573
  4. **VERIFY** that the original test files only have import statements and no duplicate code
527
574
  5. **VERIFY** that both original and new test files import from utils and use the helper functions
528
- 5a. **VERIFY STEP 4b OUTCOMES** — for every near-duplicate pair you merged in STEP 4b: the merged helper exists in \`${TS_UTILS_FILE}\` exactly once; BOTH original helpers are deleted from their respective test files; BOTH test files import the merged helper from \`${TS_UTILS_FILE}\`; BOTH original call sites are replaced with calls to the merged helper. If any of these is false, fix it before finishing.
575
+ 5a. **VERIFY STEP 4b OUTCOMES** — for every near-duplicate pair you merged in STEP 4b: the merged helper exists in \`${utilsFile}\` exactly once; BOTH original helpers are deleted from their respective test files; BOTH test files import the merged helper from \`${utilsFile}\`; BOTH original call sites are replaced with calls to the merged helper. If any of these is false, fix it before finishing.
529
576
  6. **VERIFY** that no unnecessary helper functions were created
530
577
  7. **VERIFY** that all helper functions in utils are actually imported and used in the test files
531
578
  8. **REMOVE** any helper functions that are not being used after refactoring
532
579
  9. **NEVER** refactor, reorganize, or restructure existing source test files beyond moving helpers
533
580
  10. **Do NOT run the test** — your work is complete once the file is written
534
581
 
535
- **Do NOT call skyramp_modularization.** Code reuse is complete. The test file has been refactored in place — no further tools should be called.
582
+ **Do NOT call skyramp_modularization.** Code reuse is complete. The test file has been refactored in place — no further tools should be called on this FALLBACK path. (If you took the POM-aware path instead, STEP 5's verification requirement still applies.)
536
583
 
537
584
  SUMMARIZE THE CODE REUSE PROCESS AND THE RESULTS.
538
585
  `;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import { getPomAwareCodeReusePrompt } from "./pom-aware-code-reuse.js";
2
+ describe("getPomAwareCodeReusePrompt language derivation", () => {
3
+ it("uses .ts utils filename for typescript (default)", () => {
4
+ expect(getPomAwareCodeReusePrompt("t.spec.ts")).toContain("skyrampUtils.ts");
5
+ });
6
+ it("uses .js utils filename for javascript", () => {
7
+ const p = getPomAwareCodeReusePrompt("t.spec.js", "javascript");
8
+ expect(p).toContain("skyrampUtils.js");
9
+ expect(p).not.toContain("skyrampUtils.ts");
10
+ });
11
+ });
@@ -1,5 +1,5 @@
1
1
  import { DraftedScenario } from "../../types/RepositoryAnalysis.js";
2
- import { PriorityTier } from "../../types/TestRecommendation.js";
2
+ import { Novelty, PriorityTier } from "../../types/TestRecommendation.js";
3
3
  /** "0" — Code Review: correctness analysis */
4
4
  export declare const EXEC_STEP_CODE_REVIEW: string;
5
5
  /** "1" — External test coverage verification */
@@ -10,10 +10,12 @@ export declare const EXEC_STEP_ENRICH: string;
10
10
  export declare const EXEC_STEP_DIVERSITY: string;
11
11
  /** "4" — Execute merged plan */
12
12
  export declare const EXEC_STEP_EXECUTE: string;
13
+ /** "5" — Register test plan (SKYR-3879 Path B checkpoint) */
14
+ export declare const EXEC_STEP_REGISTER: string;
13
15
  export declare function buildExecutionPlan(scored: Array<{
14
16
  scenario: DraftedScenario;
15
17
  priority: PriorityTier;
16
- novelty: string;
18
+ novelty: Novelty;
17
19
  }>, maxGen: number, topN: number, baseUrl: string, authHeaderValue: string, authSchemeSnippet: string, authTypeValue: string, seed: string, endpointCount: number, isUIOnlyPR: boolean, hasFrontendChanges?: boolean, hasTraces?: boolean, externalCoverage?: Set<string>, relevantExternalTestPaths?: string[],
18
20
  /**
19
21
  * Whether the diff classified at least one new/modified/removed endpoint.