@skyramp/mcp 0.1.0-rc.5 → 0.1.0

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 (59) hide show
  1. package/build/index.js +4 -1
  2. package/build/playwright/registerPlaywrightTools.js +2 -0
  3. package/build/playwright/traceRecordingPrompt.js +0 -5
  4. package/build/prompts/code-reuse.js +7 -1
  5. package/build/prompts/enhance-assertions/contractProviderAssertionsPrompt.js +110 -0
  6. package/build/prompts/enhance-assertions/integrationAssertionsPrompt.js +128 -0
  7. package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +90 -0
  8. package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +53 -14
  9. package/build/prompts/personas.js +9 -5
  10. package/build/prompts/pom-aware-code-reuse.js +440 -0
  11. package/build/prompts/test-maintenance/driftAnalysisSections.js +5 -3
  12. package/build/prompts/test-recommendation/analysisOutputPrompt.js +12 -2
  13. package/build/prompts/test-recommendation/recommendationSections.js +41 -20
  14. package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +2 -2
  15. package/build/prompts/test-recommendation/scopeAssessment.js +21 -8
  16. package/build/prompts/test-recommendation/scopeAssessment.test.js +28 -0
  17. package/build/prompts/test-recommendation/test-recommendation-prompt.js +65 -28
  18. package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +71 -12
  19. package/build/prompts/testbot/testbot-prompts.js +91 -73
  20. package/build/prompts/testbot/testbot-prompts.test.js +60 -0
  21. package/build/resources/testbotResource.js +5 -1
  22. package/build/services/ScenarioGenerationService.js +3 -2
  23. package/build/services/TestDiscoveryService.js +5 -5
  24. package/build/services/TestExecutionService.js +23 -0
  25. package/build/services/TestExecutionService.test.js +47 -6
  26. package/build/services/TestGenerationService.js +39 -28
  27. package/build/services/containerEnv.js +18 -2
  28. package/build/tool-phases.js +1 -0
  29. package/build/tools/code-refactor/codeReuseTool.js +11 -8
  30. package/build/tools/code-refactor/enhanceAssertionsTool.js +67 -0
  31. package/build/tools/code-refactor/modularizationTool.js +2 -0
  32. package/build/tools/executeSkyrampTestTool.js +11 -4
  33. package/build/tools/fixErrorTool.js +2 -0
  34. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +17 -6
  35. package/build/tools/generate-tests/generateContractRestTool.js +127 -54
  36. package/build/tools/generate-tests/generateE2ERestTool.js +1 -1
  37. package/build/tools/generate-tests/generateIntegrationRestTool.js +2 -11
  38. package/build/tools/generate-tests/generateLoadRestTool.js +1 -1
  39. package/build/tools/generate-tests/generateUIRestTool.js +3 -36
  40. package/build/tools/test-management/analyzeChangesTool.js +37 -26
  41. package/build/tools/workspace/initializeWorkspaceTool.js +55 -3
  42. package/build/utils/docker.test.js +1 -1
  43. package/build/utils/featureFlags.js +34 -0
  44. package/build/utils/normalizeSkyrampImports.js +75 -0
  45. package/build/utils/routeParsers.js +7 -2
  46. package/build/utils/routeParsers.test.js +8 -0
  47. package/build/utils/trace-parser.js +11 -2
  48. package/build/utils/versions.js +1 -1
  49. package/build/utils/workspaceAuth.js +212 -28
  50. package/node_modules/playwright/lib/common/expectBundleImpl.js +221 -221
  51. package/node_modules/playwright/lib/mcp/browser/tools/extensionFrames.js +180 -0
  52. package/node_modules/playwright/lib/mcp/browser/tools/keyboard.js +2 -2
  53. package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +11 -0
  54. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +20 -2
  55. package/node_modules/playwright/lib/mcp/terminal/help.json +32 -0
  56. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +63 -3
  57. package/node_modules/playwright/lib/utilsBundleImpl.js +49 -49
  58. package/package.json +6 -3
  59. package/build/prompts/test-maintenance/enhanceAssertionSection.js +0 -99
@@ -0,0 +1,440 @@
1
+ import { generateSkyrampHeader, SKYRAMP_UTILS_HEADER } from "../utils/utils.js";
2
+ const TS_UTILS_FILE = "skyrampUtils.ts";
3
+ export function getPomAwareCodeReusePrompt(testFile) {
4
+ return `# POM-AWARE CODE REUSE — TYPESCRIPT/PLAYWRIGHT
5
+
6
+ ## COMPLIANCE RULES — READ THIS FIRST, APPLY TO EVERY STEP
7
+
8
+ You are about to execute a multi-step process. These rules govern your behavior for the entire session:
9
+
10
+ 1. **Read the ENTIRE guidance before taking any action**
11
+ 2. **Execute steps in order** — never skip, combine, or reorder steps
12
+ 3. **Output all intermediate artifacts** (POM catalog, mapping table) to chat before proceeding to the next step
13
+ 4. **BLOCKING and MANDATORY mean hard stops** — do not proceed until that step is complete; confirm completion in a single output line then continue automatically — do NOT ask the user for confirmation
14
+ 5. **Never shortcut** — even if the pattern seems obvious from existing tests, follow every step. "Being efficient" by skipping steps is non-compliance, not optimization.
15
+ 6. **Never pause for user input** between steps — execute the full sequence autonomously from start to finish.
16
+
17
+ If you catch yourself about to skip a step, stop and execute it.
18
+
19
+ ---
20
+
21
+ ## DEFINITIONS
22
+
23
+ **POM-Aware Code Reuse** means refactoring a generated test to use the project's existing Page Object Model (POM) classes and methods, replacing raw inline locators and action sequences with the proper abstractions the customer already has.
24
+
25
+ This is NOT the same as SkyrampUtils consolidation. Do NOT create a SkyrampUtils file on the POM path.
26
+
27
+ ---
28
+
29
+ ## STEP 1: DETECT POM CLASSES IN THE PROJECT
30
+
31
+ Use the Glob tool to search for POM files starting from the directory containing \`${testFile}\`:
32
+ - Pattern 1: \`**/pageobjects/**/*.ts\`
33
+ - Pattern 2: \`**/page-objects/**/*.ts\`
34
+ - Pattern 3: \`**/pages/**/*.ts\`
35
+ - Pattern 4: \`**/*.page.ts\`
36
+ - Pattern 5: \`**/*Page.ts\`
37
+ - Pattern 6: \`**/pom/**/*.ts\`
38
+
39
+ Exclude any file with \`test\`, \`spec\`, \`.test.\`, or \`.spec.\` in its name.
40
+ Also exclude \`node_modules\`.
41
+
42
+ **Decision:**
43
+ - Zero matches found → **SKIP TO STEP 6 (FALLBACK)**
44
+ - One or more matches found → continue to STEP 2
45
+
46
+ ---
47
+
48
+ ## STEP 2: READ AND CATALOG ALL REUSABLE MODULES
49
+
50
+ **Step 2a: Check for existing catalog (MANDATORY FIRST STEP)**
51
+
52
+ Check if \`skyramp-pom-catalog.md\` exists in the same directory as \`tsconfig.json\`:
53
+ - **If it exists:** Read it and verify it starts with \`<!-- skyramp-pom-catalog: auto-generated -->\`
54
+ - Valid → Output: *"Using existing POM catalog from /path/to/skyramp-pom-catalog.md"* and **skip to STEP 2b**
55
+ - Invalid → Continue to full catalog generation below
56
+ - **If it doesn't exist:** Continue to full catalog generation below
57
+
58
+ ---
59
+
60
+ **Step 2 (full catalog generation — only if catalog doesn't exist or is invalid):**
61
+
62
+ Before writing a single line of refactored code, do the following:
63
+
64
+ 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.
65
+
66
+ 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.
67
+
68
+ 3. **Read every POM file found in STEP 1** — For each file, document:
69
+ - Class name, import path (using tsconfig aliases), and what page/component it represents
70
+ - Iframe selector used in the constructor (e.g. \`page.frameLocator('iframe#asset-list')\`), or "top-level page" if no iframe
71
+ - Every property (locator): name and its exact selector string from the constructor assignment
72
+ - Every method: name, parameters, what action it performs
73
+ - **For each method, explicitly check and record:**
74
+ - Any \`waitForTimeout\` or \`sleep\` calls inside the method body and their durations
75
+ - Any \`waitForResponse\` calls inside the method body and their URL patterns
76
+ - 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**
77
+ - Flag methods as **timing-aware**, **network-aware**, or **wrapping** as appropriate — all three are high-value substitution targets
78
+
79
+ 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.
80
+
81
+ **BLOCKING: Do not proceed to STEP 2b until you have written this file and confirmed the path in a single output line:**
82
+ *"POM catalog written to /path/to/skyramp-pom-catalog.md"*
83
+
84
+ **Verification gate:** Before continuing to STEP 2b, confirm you either (a) found and used an existing catalog in Step 2a, OR (b) just wrote a new catalog in step 4 above. If neither is true, STOP and complete step 4 now.
85
+
86
+ Use this exact format for the file:
87
+
88
+ \`\`\`markdown
89
+ <!-- skyramp-pom-catalog: auto-generated by skyramp_reuse_code -->
90
+
91
+ ## ClassName
92
+ - **Import:** \`@pageobjects/path/to/file\`
93
+ - **Iframe:** \`iframe#selector\` | top-level page
94
+ - **Properties:**
95
+ - \`propertyName\` — selector: \`exact-selector-string\`
96
+ - **Methods:**
97
+ - \`methodName(param1, param2)\` — description; ⏱ timing-aware: waitForTimeout(Xms) + sleep(Ys) | 🌐 network-aware: waitForResponse('**/pattern**') | (omit tags if neither)
98
+ \`\`\`
99
+
100
+ ---
101
+
102
+ ## STEP 2b: BUILD AND OUTPUT A MAPPING TABLE
103
+
104
+ **This step is MANDATORY. Do not write any code until it is complete.**
105
+
106
+ 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.
107
+
108
+ ### Matching criteria
109
+
110
+ Use **selector-string evidence** as the primary signal — not naming similarity or context guessing:
111
+
112
+ **PREFERRED** (highest priority — always substitute, even if raw test has explicit waits or correlations for the same sequence):
113
+ - The POM method is **timing-aware** (has internal \`waitForTimeout\`/\`sleep\`) AND its action sequence covers the raw test's steps — the method's internal timing replaces the raw test's explicit waits for that sequence
114
+ - The POM method is **network-aware** (has internal \`waitForResponse\`) AND its URL pattern matches or is equivalent to the raw test's correlation — the method's internal correlation replaces the raw test's explicit \`responsePromise\` block
115
+ - 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
116
+
117
+ **HIGH confidence** (substitute in refactored test):
118
+ - 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("資産登録")')\`
119
+ - A POM method's internal steps clearly and completely cover the entire raw action sequence with no gaps
120
+ - **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.
121
+
122
+ **LOW / UNCERTAIN confidence** (leave inline):
123
+ - You can only infer the match from naming or context alone — no selector string overlap AND no convention evidence from existing refactored tests
124
+ - The POM property's selector is different, no existing test uses this method for this action, and you are not sure the elements match
125
+
126
+ **UNMAPPED** (leave inline):
127
+ - No POM property or method found that covers this locator or action
128
+
129
+ ### Iframe context gate — apply BEFORE assigning any confidence
130
+
131
+ Before considering any POM match, verify the iframe context matches:
132
+ - Identify the iframe the raw locator uses (e.g., \`frameLocator('iframe#asset-list')\`)
133
+ - Read the POM class constructor to find which iframe it scopes to
134
+ - If the iframe IDs differ → the match is UNMAPPED regardless of selector similarity
135
+ - **Never match a method from a different-iframe class just because it has a similar name.** Example: \`AssetListPage.searchAssetsByCondition()\` uses \`iframe#asset-list\` — it CANNOT be used for actions in \`#application-new-applied\`. For asset search inside a workflow, use \`ApplicationNewAppliedAssetSelectPage.searchAssetsSection.searchByAssetName()\` which uses \`iframe#application-new-applied\`.
136
+ - When filtering POM candidates for a raw action, first narrow to classes whose iframe matches, then look for methods within those classes only.
137
+
138
+ ### Mapping table format
139
+
140
+ Output the table in this format:
141
+
142
+ \`\`\`
143
+ | Raw locator / action | POM class | Property / Method | Selector evidence | Confidence |
144
+ |---|---|---|---|---|
145
+ | goto + fill email/password + click login | LoginPage | login(email, password) | method is timing-aware (waitForTimeout+sleep) covering login sequence | PREFERRED |
146
+ | frameLocator('iframe#asset-list').locator('button:has-text("資産登録")') | AssetListPage | assetEntryBtn | iframe.locator('button:has-text("資産登録")') | HIGH |
147
+ | page.locator('input[name="email"]').fill(...) | LoginPage | emailInput | getByPlaceholder('メールアドレス(半角)') | LOW |
148
+ | frameLocator('iframe#wf-input').locator('input[name="changeQty"]') | — | — | No match | UNMAPPED |
149
+ \`\`\`
150
+
151
+ ### Rules after completing the table
152
+
153
+ - **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
154
+ - **HIGH entries MUST be substituted** — use the POM method or property; do NOT write an inline locator
155
+ - **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.
156
+ - **Do NOT upgrade LOW or UNCERTAIN** because the substitution seems reasonable
157
+ - **Conservative default**: an incorrect substitution that breaks the test is worse than leaving something inline — when in doubt, leave it inline
158
+ - If the table has ZERO PREFERRED or HIGH entries → **SKIP TO STEP 6 (FALLBACK)**
159
+ - If AT LEAST ONE PREFERRED or HIGH entry exists → continue to STEP 3
160
+
161
+ ---
162
+
163
+ ## STEP 3: ANALYZE AND APPLY TRANSFORMATION RULES
164
+
165
+ Read through \`${testFile}\` and apply the following rules:
166
+
167
+ ### 3a. Skyramp Infrastructure — PRESERVE EXACTLY, NO EXCEPTIONS
168
+
169
+ The following must appear in the refactored test exactly as they are in the original. Never remove, alter, or move them:
170
+
171
+ \`\`\`typescript
172
+ // Generated by Skyramp ...
173
+ // Command: skyramp generate ui rest \\
174
+ // ...
175
+ \`\`\`
176
+
177
+ \`\`\`typescript
178
+ import { test } from '@playwright/test';
179
+ import { newSkyrampPlaywrightPage, expect } from '@skyramp/skyramp';
180
+
181
+ test.use({
182
+ viewport: { width: 1450, height: 900 },
183
+ });
184
+
185
+ // Inside the test body:
186
+ test.setTimeout(1000000);
187
+ page.setDefaultTimeout(pageTimeout);
188
+ const skyrampPage = newSkyrampPlaywrightPage(page, testInfo);
189
+ \`\`\`
190
+
191
+ > **Note on the page variable:** Assign \`newSkyrampPlaywrightPage\` to \`skyrampPage\`. Pass \`skyrampPage\` to all module constructors and use it for all \`waitForTimeout\`, \`waitForResponse\`, \`locator\`, and \`expect\` calls. If the original test reassigns \`page\` directly, update it to use a separate \`skyrampPage\` variable instead.
192
+
193
+ ### 3b. Test Data — Extract to Named Constants
194
+
195
+ Extract all hardcoded strings (credentials, tenant names, workflow names, asset names, URLs, etc.) to named \`const\` declarations at the top of the file, before \`test.use\`. Group them under a \`// Test data\` comment.
196
+
197
+ **Uniqueness:** Any string used as the name of an entity being created, registered, or saved during the test must be made unique per run. First check whether existing refactored tests in the repository already use a convention for this. If a convention exists, follow it exactly. If not, use:
198
+ \`\`\`typescript
199
+ const uniqueSuffix = Date.now();
200
+ const workflowName = "MyWorkflow_" + uniqueSuffix;
201
+ \`\`\`
202
+
203
+ After making a name dynamic, scan the entire test for any raw locator that hardcodes that same string value and replace only the hardcoded value with the variable.
204
+
205
+ ### 3c. Module Instantiation — Declare All Together at the Top of the Test Body
206
+
207
+ All module instances must be declared together in a clearly labeled block immediately after \`skyrampPage\` is initialized, before any test actions begin. Match whatever label and comment style the repository's existing tests use. Pass \`skyrampPage\` to every constructor unless the class explicitly requires a different argument (such as a frame handle).
208
+
209
+ The only exception: a module that genuinely cannot be instantiated until a dynamic value is known at that point in the flow. When this happens, instantiate it inline and add a comment explaining why.
210
+
211
+ ### 3d. Import Organization — Match the Repository's Conventions
212
+
213
+ Match the import grouping and labeling style of the existing test files exactly:
214
+ - Keep Skyramp imports (\`@playwright/test\` and \`@skyramp/skyramp\`) together and unchanged
215
+ - Group POM imports separately, consistent with the repo's pattern
216
+ - Use whatever path alias convention the repository defines — never introduce a new path style
217
+
218
+ ### 3e. \`waitForTimeout\` Calls
219
+
220
+ **Always prefer the POM method over preserving a raw wait:**
221
+ If an action sequence maps to a POM method at HIGH or PREFERRED confidence, **always call the method** — even if the raw test has a \`waitForTimeout\` in the middle of that sequence. Drop the raw wait that falls inside the method's covered scope. The customer's POM is the right abstraction; duplicating their code inline just to preserve a trace-recorded wait defeats the purpose of code reuse.
222
+
223
+ - Call the POM method
224
+ - Drop any \`waitForTimeout\` that falls *inside* the method's covered sequence
225
+ - Add a comment: \`// raw waitForTimeout(Xms) dropped — POM method used instead\`
226
+
227
+ **How to determine inside vs outside a method's scope:**
228
+ - **Inside scope** = the wait appears between two actions that the POM method covers. Example: a raw test has \`fill(password)\` → \`waitForTimeout(1500)\` → \`click(loginBtn)\`. Since \`loginPage.login()\` covers both the fill and the click, the \`waitForTimeout(1500)\` is inside scope → drop it.
229
+ - **Outside scope** = the wait appears AFTER all actions the POM method covers, as a standalone pause before the next operation. Example: \`await loginPage.login(...)\` → \`waitForTimeout(27000)\`. The login is complete; the 27000ms is waiting for the app to settle after login → this is outside scope → preserve it.
230
+
231
+ **Preserve waits that fall between POM calls:**
232
+ Any \`waitForTimeout\` that falls *outside* all POM method scopes — i.e. as a standalone wait between two POM calls — must be preserved in exact position, updated to \`await skyrampPage.waitForTimeout(...)\`.
233
+
234
+ ### 3f. \`waitForResponse\` Network Intercepts
235
+
236
+ **If the action maps to a PREFERRED method (network-aware):**
237
+ - Call the POM method — its internal \`waitForResponse\` handles the correlation
238
+ - Drop the raw test's entire \`const responsePromiseN / await action / const responseN\` block
239
+ - Add a comment: \`// method handles network correlation internally — raw responsePromise dropped\`
240
+
241
+ **If no POM method is network-aware for this action:**
242
+ - Leave the \`waitForResponse\` block inline as-is. Do NOT create a new POM method.
243
+
244
+ **Side-effect endpoints — always remove regardless:**
245
+ Remove the \`waitForResponse\` wrapper entirely (keep only the action click) for endpoints that do not correlate to the primary action. These cause flaky timeouts. Patterns to remove:
246
+ - \`**/live/**\` — live chat / messaging (e.g. \`getAFTalkRelations\`)
247
+ - \`**/notification/**\`, \`**/notifications/**\`
248
+ - \`**/analytics/**\`, \`**/tracking/**\`, \`**/telemetry/**\`
249
+
250
+ \`\`\`typescript
251
+ // Before — remove this
252
+ const responsePromise1 = skyrampPage.waitForResponse("**/live/getAFTalkRelations**");
253
+ await someAction.click();
254
+ const response1 = await responsePromise1;
255
+
256
+ // After — just the action
257
+ await someAction.click();
258
+ \`\`\`
259
+
260
+ ### 3g. Iframe and Frame Context — Match Constructor Expectations
261
+
262
+ When a module operates inside an iframe, determine the correct frame handle by reading its constructor. Pass the matching \`contentFrame()\` handle — never pass \`skyrampPage\` when the class expects a frame.
263
+
264
+ **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.
265
+
266
+ ### 3h. Dynamic Locators — Prefer Positional Module Methods
267
+
268
+ 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.
269
+
270
+ **Never hardcode dynamic values** (IDs, generated names, timestamps) in locators. Example: \`getByRole("link", { name: "28457681" })\` hardcodes a workflow ID that changes every run — use \`appliedListPage.wfidLinkByIndex(0)\` instead.
271
+
272
+ ### 3i. Checkbox Interactions — Follow Repo Pattern
273
+
274
+ If the existing POM files have an established pattern for checkbox interactions, follow it. If no such pattern exists, keep the checkbox interaction inline exactly as it appears — do not invent a new abstraction.
275
+
276
+ ### 3j. Screenshot Assertions — Preserve Exact Position
277
+
278
+ 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\`
279
+
280
+ ---
281
+
282
+ ## STEP 4: WRITE THE REFACTORED TEST
283
+
284
+ Using your catalog from STEP 2 and analysis from STEP 3, produce the refactored test.
285
+
286
+ **CRITICAL — Consult the mapping table for every single action before writing it:**
287
+ Before writing each line of refactored code, look up that action in your STEP 2b mapping table:
288
+ - If it is marked **PREFERRED** → use the POM method and drop any raw waits/correlations inside its scope. This is the highest-value substitution.
289
+ - If it is marked **HIGH** → you MUST use the POM method or property. Do NOT write an inline locator.
290
+ - If it is marked **LOW / UNCERTAIN / UNMAPPED** → write it inline exactly as in the original test.
291
+
292
+ Do not skip this lookup for any action. The mapping table is your source of truth — not the raw test's locators.
293
+
294
+ ## STEP 4b: VERIFY AGAINST MAPPING TABLE
295
+
296
+ 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.
297
+
298
+ **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 -----*/\`.
299
+
300
+ **Modularization Priority:**
301
+ 1. **Existing module method** — always preferred; replaces entire action sequences
302
+ 2. **Existing module property** — use directly in \`expect(...)\` or chained calls when no method wraps it
303
+ 3. **Inline raw locator** — only when no modular equivalent exists; keep as-is
304
+
305
+ **Assertions:**
306
+ - Module locator assertions: \`expect(module.someLocator).toBeVisible()\`
307
+ - Screenshot assertions (\`toHaveScreenshot\`): always keep inline; if mid-method sequence, inline that method's steps around it
308
+ - Page readiness checks: use module methods such as \`pageToBeVisible()\` if they exist
309
+
310
+ **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.
311
+
312
+ ---
313
+
314
+ ## STEP 5: WRITE THE OUTPUT
315
+
316
+ 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.
317
+
318
+ Do not output a section breakdown, modularization summary, or any analysis to the chat. Perform all analysis internally.
319
+
320
+ **Do NOT run the test. Do NOT call any other tools. Your work is complete once the file is written.**
321
+
322
+ ---
323
+
324
+ ## STEP 6: FALLBACK — STANDARD SKYRAMPUTILS PATH
325
+
326
+ **You should only reach this step if:**
327
+ - No POM files were detected in STEP 1, OR
328
+ - Zero locators/actions in the test mapped to any existing module in STEP 2
329
+
330
+ Inform the user: *"No POM layer found in this project — falling back to standard SkyrampUtils code reuse."*
331
+
332
+ Then follow the standard SkyrampUtils process below exactly:
333
+
334
+ ---
335
+
336
+ # CODE REUSE - 6 CLEAR STEPS
337
+ **CRITICAL WARNING: VIOLATION OF THESE RULES WILL RESULT IN ERROR**
338
+
339
+ ## DEFINITIONS (READ FIRST):
340
+ **What is a "Helper Function"?**
341
+ - A helper function is a STANDALONE, DISTINCT function that is ALREADY DEFINED in the code
342
+ - It has a clear function signature (e.g., \`async function createProduct(page, data) { ... }\`)
343
+ - It is NOT just repetitive inline test code or patterns
344
+ - Example of helper function: \`async function loginUser(page, email, password) { ... }\`
345
+ - Example of NOT a helper function: Repetitive \`await page.click()\` statements scattered in test
346
+
347
+ **What is "Repetitive Code Pattern"?**
348
+ - Sequential test steps that look similar but are NOT extracted into standalone functions
349
+ - Example: Multiple sequences of \`await page.fill()\`, \`await page.click()\` directly in test body
350
+ - These patterns should NOT be extracted during code reuse - that's what modularization is for
351
+
352
+ ** CODE REUSE ≠ REFACTORING**
353
+ - Code reuse = Finding EXISTING helper functions and reusing them
354
+ - Refactoring = Creating NEW helper functions from patterns (handled by modularization tool)
355
+
356
+ ## STEP 1: READ TEST FILE
357
+ Read ${testFile} and look for ALREADY DEFINED helper functions (not just repetitive patterns).
358
+
359
+ ## STEP 2: FIND EXISTING UTILS
360
+ Use the Grep tool to search for files containing "${SKYRAMP_UTILS_HEADER}":
361
+ - Pattern: "${SKYRAMP_UTILS_HEADER}"
362
+ - Type: "ts"
363
+ - Output mode: "files_with_matches"
364
+ **CRITICAL: Only look for util files with header ${SKYRAMP_UTILS_HEADER}** - exclude test files (files with "test", "spec", ".test.", ".spec." in the name).
365
+ Read the matching non-test files and check if any helpers can be reused in ${testFile}.
366
+
367
+ ## STEP 3: USE EXISTING HELPERS FROM UTILS SOURCE FILES FOUND IN STEP 2
368
+ If helpers exist in util file that can be reused in ${testFile} without modifying them:
369
+ - Import them into ${testFile}
370
+ - Remove any duplicate code in ${testFile}
371
+ - Test that ${testFile} still works without any errors and logical is same as original test file.
372
+
373
+ ## STEP 4: FIND LOCAL HELPERS IN OTHER TEST SOURCE FILES THAT HAS HEADER ${SKYRAMP_UTILS_HEADER}
374
+ Use the Grep tool to search for other test files containing "${SKYRAMP_UTILS_HEADER}":
375
+ - Pattern: "${SKYRAMP_UTILS_HEADER}"
376
+ - Type: "ts"
377
+ - Output mode: "files_with_matches"
378
+ **CRITICAL: Exclude ${testFile} from the results** - only look at OTHER test files, not the current file.
379
+
380
+ **STOP HERE IF NO OTHER TEST FILES FOUND**
381
+ **IF NO OTHER TEST FILES ARE FOUND, SKIP TO STEP 6 - DO NOT CREATE ANY UTILS FILES.**
382
+
383
+ If other test files are found, read those files and look for ALREADY DEFINED helper functions with clear function signatures.
384
+
385
+ **How to identify helper functions in other test files:**
386
+ HELPER FUNCTION (move to utils):
387
+ - Has function signature: \`async function doSomething(params) { ... }\`
388
+ - Can be called multiple times with different parameters
389
+ - Example: \`async function fillProductForm(page, product) { ... }\`
390
+
391
+ NOT A HELPER FUNCTION (do not extract):
392
+ - Just repetitive inline test code in test body
393
+ - No function definition/signature
394
+ - Example: Multiple \`await page.getByTestId("xyz").click()\` directly in test
395
+
396
+ **IF OTHER TEST FILES ONLY CONTAIN REPETITIVE PATTERNS (NO ACTUAL HELPER FUNCTIONS), SKIP TO STEP 6**
397
+ **IF OTHER TEST FILES ARE ESSENTIALLY IDENTICAL TO CURRENT FILE, SKIP TO STEP 6**
398
+
399
+ ## STEP 5: IF LOCAL HELPERS ARE FOUND IN STEP 4 THAT CAN BE REUSED in ${testFile}, MOVE THOSE LOCAL HELPERS TO UTILS SOURCE FILES AND USE THEM
400
+
401
+ **ONLY PROCEED WITH STEP 5 IF ALL CONDITIONS ARE MET:**
402
+ - You found OTHER test files in STEP 4 (not just ${testFile})
403
+ - Those test files contain ACTUAL HELPER FUNCTIONS with function signatures (not just repetitive patterns)
404
+ - The helper functions are ALREADY IMPLEMENTED and working in those OTHER test files
405
+ - The helper functions are DIFFERENT from the current file (not just identical patterns)
406
+
407
+ **IF ANY CONDITION IS NOT MET, SKIP TO STEP 6 - DO NOT CREATE ANY UTILS FILES.**
408
+
409
+ **CRITICAL:** For helpers found in STEP 4:
410
+ 1. **CREATE** \`${TS_UTILS_FILE}\` file if it doesn't exist with the following header:
411
+ \`\`\`ts
412
+ ${generateSkyrampHeader("typescript")}
413
+ \`\`\`
414
+ **CRITICAL: IF \`${TS_UTILS_FILE}\` is already big, create a new file with a different name and add the header to the new file.**
415
+ 2. **COPY** those local helper functions from original test source files to \`${TS_UTILS_FILE}\` without modifying them
416
+ 3. **DELETE** those local helper functions from test source files (REMOVE THEM COMPLETELY) WITHOUT ANY OTHER CHANGES.
417
+ 4. **IMPORT** those local helper functions from \`${TS_UTILS_FILE}\` back into test source files WITHOUT MAKING ANY OTHER CHANGE.
418
+ 5. **IMPORT** those local helper functions from \`${TS_UTILS_FILE}\` into ${testFile}
419
+ 6. **REPLACE** duplicate code in ${testFile} with calls to imported helper functions WITHOUT MAKING ANY OTHER CHANGE.
420
+
421
+ **CRITICAL: DO NOT CREATE UNNECESSARY HELPER FUNCTIONS**
422
+ **DO NOT CREATE HELPERS BASED ON PATTERNS IN THE CURRENT FILE**
423
+
424
+ ## STEP 6: VERIFY AND VALIDATE
425
+ 1. **BEFORE** making any changes, read the original test file and identify ALL helper functions
426
+ 2. **AFTER** copying helpers to utils, read the original file again to confirm helpers are GONE
427
+ 3. **VERIFY** that helper functions are NO LONGER in original test files
428
+ 4. **VERIFY** that the original test files only have import statements and no duplicate code
429
+ 5. **VERIFY** that both original and new test files import from utils and use the helper functions
430
+ 6. **VERIFY** that no unnecessary helper functions were created
431
+ 7. **VERIFY** that all helper functions in utils are actually imported and used in the test files
432
+ 8. **REMOVE** any helper functions that are not being used after refactoring
433
+ 9. **NEVER** refactor, reorganize, or restructure existing source test files beyond moving helpers
434
+ 10. **Do NOT run the test** — your work is complete once the file is written
435
+
436
+ **Do NOT call skyramp_modularization.** Code reuse is complete. The test file has been refactored in place — no further tools should be called.
437
+
438
+ SUMMARIZE THE CODE REUSE PROCESS AND THE RESULTS.
439
+ `;
440
+ }
@@ -2,7 +2,6 @@
2
2
  * Modular section builders for the Drift Analysis prompt,
3
3
  * mirroring the recommendationSections.ts pattern.
4
4
  */
5
- import { ENHANCE_ASSERTIONS_FOR_INTEGRATION_AND_CONTRACTPROVIDER } from "./enhanceAssertionSection.js";
6
5
  export function buildDriftScoringGuide() {
7
6
  return `## Drift Score Guide (0–100)
8
7
 
@@ -159,9 +158,12 @@ Remove the test file when ALL endpoints it covers were removed from the codebase
159
158
  Never use hardcoded resource IDs (e.g. \`order_id=1\`) in any test step, including GET or DELETE steps. Always create required resources via prior POST steps and chain IDs dynamically. Use timestamp-based unique names for created resources (e.g. \`"Product-\${int(time.time())}"\`) to prevent collisions across test runs.
160
159
 
161
160
  ### Enhance assertions after UPDATE (MANDATORY)
162
- Apply to **new test functions you are adding** and **existing functions that cover endpoints changed in the diff** only. Do NOT touch existing functions for endpoints unrelated to the diff.
161
+ Call \`skyramp_enhance_assertions\` with \`testFile\` set to the absolute path of the test file you just updated, \`enhanceType: "maintenance"\`, and the matching \`testType\` based on the file you are editing:
162
+ - **Integration test file** (multi-step chained requests): call with \`testType: "integration"\`
163
+ - **Contract-provider test file** (single endpoint with \`beforeAll\`/\`afterAll\` setup, provider mode): call with \`testType: "contract"\`. Skip for consumer-mode contract tests.
164
+ - **UI test file** (imports \`@playwright/test\`, uses \`page.\` calls): call with \`testType: "ui"\`
163
165
 
164
- ${ENHANCE_ASSERTIONS_FOR_INTEGRATION_AND_CONTRACTPROVIDER}`;
166
+ Then apply every instruction returned by the tool to the test file.`;
165
167
  }
166
168
  export function buildDriftOutputChecklist(existingTestCount, newEndpointCount, inlineMode = false, stateFile) {
167
169
  const finalStep = inlineMode
@@ -93,8 +93,18 @@ ${nextStep}`;
93
93
  ? `\n<diff>\n${p.diffContent}\n</diff>`
94
94
  : "";
95
95
  const step2 = isUIOnly
96
- ? `### Step 2: Identify consumed API endpoints
97
- UI-only PR — read changed components to find API calls (fetch, axios, hooks).`
96
+ ? `### Step 2: Identify consumed API endpoints and integration status
97
+ UI-only PR — perform two checks:
98
+ 1. Read changed frontend files to find API calls (fetch, axios, hooks).
99
+ 2. For each changed component file (skip CSS/HTML/style-only files — they have no exported component name to search for): check whether any production source file imports, re-exports, or renders it.
100
+ - Search for both the component's exported name AND its module path/filename to catch aliased and default imports (e.g. \`import Foo from './CartLine'\`).
101
+ - Derive the exported name from the file itself: use the default export name, a named exported PascalCase component, or the PascalCase file basename when no clearer name exists.
102
+ - Exclude test/story files from the search: ignore matches in \`*.test.*\`, \`*.spec.*\`, \`*.stories.*\`, and \`__tests__/\` directories — only production code imports count as integration.
103
+
104
+ If no production file imports, re-exports, or renders a changed component, mark it as **unintegrated** in the Execution Plan output.
105
+ Exception: if the same PR also adds a route/page file (e.g. under Next.js \`pages/\` or \`app/\`) that imports the component, the route IS the integration point — do NOT mark it as unintegrated.
106
+ Do NOT apply the unintegrated heuristic to route/entrypoint files themselves — those are always reachable by convention.
107
+ An unintegrated non-route component has no DOM node in the running app and cannot be browser-tested — it qualifies as a dead-code / unintegrated-component no-surface PR regardless of how complex the component logic is.`
98
108
  : p.diffContent
99
109
  ? `### Step 2: Extract new and modified API endpoints from the diff
100
110
  Read the \`<diff>\` above and identify every new or modified API endpoint — route registrations, handler methods, controller annotations. Then use the **Router Mounting / Nesting** section above to reconstruct the full URL path for each endpoint by chaining all parent router prefixes down to the handler (e.g. a handler in a file with \`prefix="/reviews"\` that is mounted at \`/{product_id}\` under a router mounted at \`/api/v1/products\` → full path \`/api/v1/products/{product_id}/reviews\`).
@@ -1,3 +1,7 @@
1
+ import { isContractConsumerModeEnabled } from "../../utils/featureFlags.js";
2
+ import { WorkspaceAuthType, getAuthScheme, isAuthorizationHeaderName, AUTH_MIDDLEWARE_PATTERNS_STR } from "../../utils/workspaceAuth.js";
3
+ // Cached at module-load — the flag is process-wide and cannot change per call.
4
+ const CONSUMER_MODE_ENABLED = isContractConsumerModeEnabled();
1
5
  export const MAX_TESTS_TO_GENERATE = 3;
2
6
  export const MAX_RECOMMENDATIONS = 20;
3
7
  export const MAX_CRITICAL_TESTS = 3;
@@ -65,19 +69,30 @@ function serializeAuthCallParams(params) {
65
69
  }
66
70
  return parts.join(", ");
67
71
  }
68
- export function getAuthSnippets(authHeaderValue, authType) {
72
+ /**
73
+ * Returns the authScheme snippet for a given authHeader + authType combination.
74
+ *
75
+ * Delegates scheme resolution to getAuthScheme() in workspaceAuth.ts — the single
76
+ * source of truth — so this file no longer maintains its own slug→scheme table.
77
+ */
78
+ export function getAuthSnippets(authHeaderValue, authType, explicitScheme) {
69
79
  if (!authHeaderValue) {
70
80
  return { authSchemeSnippet: "", authTokenSnippet: "" };
71
81
  }
72
- if (/^authorization$/i.test(authHeaderValue)) {
73
- if (authType && authType !== "none") {
74
- // Known auth type from workspace config — embed directly so the LLM doesn't need to guess.
75
- const scheme = authType.charAt(0).toUpperCase() + authType.slice(1);
82
+ // Only Authorization headers carry an authScheme prefix.
83
+ if (!isAuthorizationHeaderName(authHeaderValue)) {
84
+ return { authSchemeSnippet: "", authTokenSnippet: "" };
85
+ }
86
+ if (authType && authType !== WorkspaceAuthType.None) {
87
+ // Resolve via the canonical workspaceAuth mapping, passing explicit scheme if set.
88
+ const wsType = authType;
89
+ const scheme = getAuthScheme(wsType, explicitScheme);
90
+ // Use !== undefined so intentionally empty scheme ("" = raw token, no prefix) is preserved.
91
+ if (scheme !== undefined) {
76
92
  return { authSchemeSnippet: `, authScheme: "${scheme}"`, authTokenSnippet: "" };
77
93
  }
78
- return { authSchemeSnippet: ', authScheme: "<scheme e.g. Bearer, Basic, Token or empty>"', authTokenSnippet: "" };
79
94
  }
80
- return { authSchemeSnippet: "", authTokenSnippet: "" };
95
+ return { authSchemeSnippet: ', authScheme: "<scheme e.g. Bearer, Basic, Token or empty>"', authTokenSnippet: "" };
81
96
  }
82
97
  export const PATH_PARAM_UUID_GUIDANCE = `**Path parameters:** keep the placeholder in \`endpointURL\` (e.g. \`/coupons/{coupon_id}\`). ` +
83
98
  `Pass the value via \`pathParams\` (e.g. \`coupon_id=<random-uuid-v4>\`). ` +
@@ -137,10 +152,11 @@ export function buildGenerationRules(isUIOnlyPR) {
137
152
  **Available test types:** integration, contract, E2E, UI. **No smoke or fuzz tests.**
138
153
  Choose based on what adds the most value for this PR's changes.
139
154
 
140
- **Contract test mode — signal-based selection:**
155
+ ${CONSUMER_MODE_ENABLED ? `**Contract test mode — signal-based selection:**
141
156
  - **Consumer contract** (\`consumerMode: true\`): Look for outbound HTTP client code (fetch, axios, httpx, requests, http.Client), service client classes, or calls to external base URLs. If an endpoint's implementation makes downstream calls, that downstream boundary is a consumer contract test candidate.
142
157
  - **Provider contract** (\`providerMode: true\`): Look for new or modified endpoint handlers, route changes, or response shape modifications. If the diff adds/changes an endpoint this service owns, that is a provider contract test candidate.
143
- - **Both modes** (\`providerMode: true, consumerMode: true\`) — produces the same output as omitting both flags (generates provider and consumer contract tests). Use when the diff contains BOTH provider signals (new/modified endpoint handlers) AND consumer signals (outbound HTTP client calls to another service).
158
+ - **Both modes** (\`providerMode: true, consumerMode: true\`) — produces the same output as omitting both flags (generates provider and consumer contract tests). Use when the diff contains BOTH provider signals (new/modified endpoint handlers) AND consumer signals (outbound HTTP client calls to another service).` : `**Contract tests — provider-only:**
159
+ Only provider-side contract tests are supported. Recommend a contract test (\`providerMode: true\`) when the diff adds or modifies an endpoint handler, route, or response shape that this service owns.`}
144
160
 
145
161
  **Scenario fidelity:** Every workflow scenario should reflect the actual resource
146
162
  relationships in the code. If the pre-drafted scenarios don't match the real data model,
@@ -152,8 +168,7 @@ Use Playwright browser tools to auto-record traces and generate UI tests.
152
168
  When no Playwright trace exists, use the Playwright browser tools (\`browser_navigate\`,
153
169
  \`browser_click\`, etc.) to record a trace, then export via \`skyramp_export_zip\` for UI tests.
154
170
  `}
155
- **No duplicate coverage.** If an existing test already covers an endpoint + test type,
156
- recommend a different test that adds new coverage.`;
171
+ `;
157
172
  }
158
173
  export function buildVerificationChecklist(topN, maxGen) {
159
174
  const minTotal = Math.min(maxGen + 1, topN);
@@ -168,6 +183,7 @@ Before finalizing your output, verify:
168
183
  7. **scenarioFile**: \`skyramp_integration_test_generation\` uses the exact \`filePath\` returned by \`skyramp_batch_scenario_test_generation\` — not a guessed or hardcoded filename.
169
184
  8. **bugCatchingTarget**: Every GENERATE integration test that targets a business rule, formula, or constraint has a non-empty \`bugCatchingTarget\`.
170
185
  9. **FK chaining**: In multi-step integration tests, path params sourced from a prior step's response (e.g. \`order_id\` from step 1) use \`chainsFrom\` — not hardcoded IDs.
186
+ 10. **Concrete scenario names**: No GENERATE item uses a placeholder name ending in a numeric suffix (e.g. \`ui-test-for-changed-component-1\`, \`ui-test-from-trace-2\`). Derive the name from the actual changed component or flow: if the diff touches \`LinkCard.tsx\`, the scenario name should be \`link-card-pin-toggle\` or \`link-card-edit-description\`, not \`ui-test-for-changed-component-1\`. The changed file list is available above — use it.
171
187
  </verification>`;
172
188
  }
173
189
  export function buildFewShotExamples() {
@@ -222,29 +238,33 @@ Reasoning: Catches a missing 404 guard on DELETE — verifies the handler return
222
238
  </example>
223
239
  </examples>`;
224
240
  }
225
- export function buildToolWorkflows(authHeaderValue, authTypeValue = "") {
226
- const isAuthorizationHeader = /^authorization$/i.test(authHeaderValue);
241
+ export function buildToolWorkflows(authHeaderValue, authTypeValue = "", explicitScheme) {
242
+ const isAuthorizationHeader = isAuthorizationHeaderName(authHeaderValue);
227
243
  const noAuth = !authHeaderValue;
228
244
  let authGuidance;
229
245
  let authParams;
230
246
  if (noAuth) {
231
247
  authGuidance = `**Auth Verification Required:** The workspace config indicates no authentication, but you MUST verify this independently before omitting auth:
232
248
  1. **OpenAPI spec** \u2192 check \`securitySchemes\` / \`securityDefinitions\` for \`type: http\`, \`type: apiKey\`, or \`type: oauth2\`
233
- 2. **Source code** \u2192 look for auth middleware (\`passport\`, \`jwt.verify\`, \`authMiddleware\`, \`@requires_auth\`, \`Depends(get_current_user)\`, \`@UseGuards\`), route guards, or token extraction logic
249
+ 2. **Source code** \u2192 look for known auth signals (${AUTH_MIDDLEWARE_PATTERNS_STR}).
234
250
  3. **Route definitions** \u2192 check if routes have auth decorators or middleware applied
235
- If you find auth requirements, pass the appropriate \`authHeader\` (e.g., "Authorization") and \`authScheme\` (e.g., "Bearer") to EVERY tool call. Only pass \`authHeader: ""\` if you confirm the API is truly unauthenticated.`;
251
+ 4. **Still unknown** \u2192 proceed with \`authHeader: ""\` and note "auth pattern unrecognized" in your recommendation description.
252
+ If you find auth requirements, pass the appropriate \`authHeader\` and \`authScheme\` to EVERY tool call. Only pass \`authHeader: ""\` if you confirm the API is truly unauthenticated.`;
236
253
  authParams = { authHeader: "" };
237
254
  }
238
255
  else if (isAuthorizationHeader) {
256
+ // Use getAuthScheme (single source of truth) instead of capitalizing the raw slug.
257
+ // Pass explicitScheme so custom schemes from workspace.yml (e.g. "Ghost", "Hawk") are used.
258
+ const wsType = authTypeValue;
239
259
  const resolvedScheme = (authTypeValue && authTypeValue !== "none")
240
- ? authTypeValue.charAt(0).toUpperCase() + authTypeValue.slice(1)
260
+ ? (getAuthScheme(wsType, explicitScheme) ?? "<scheme e.g. Bearer, Token or empty>")
241
261
  : "<scheme e.g. Bearer, Token or empty>";
242
262
  authParams = { authHeader: "Authorization", authScheme: resolvedScheme };
243
263
  if (authTypeValue) {
244
264
  authGuidance = `**Auth Scheme:** The workspace \`api.authType\` is \`"${authTypeValue}"\`.
245
265
  **Where to find the scheme** (check in order):
246
266
  1. **OpenAPI spec** \u2192 look at \`securitySchemes\` / \`securityDefinitions\` for \`type: http, scheme: bearer\` or \`type: apiKey\`
247
- 2. **Source code** \u2192 auth middleware (\`passport.use\`, \`jwt.verify\`, \`authMiddleware\`), route guards, or header extraction logic (e.g., \`req.headers.authorization.split(" ")[1]\` means Bearer)
267
+ 2. **Source code** \u2192 auth middleware signals: ${AUTH_MIDDLEWARE_PATTERNS_STR}
248
268
  3. **Workspace config** \u2192 use \`api.authType\` value as the scheme if source is inconclusive
249
269
  Pass the prefix as \`authScheme\` (e.g., \`"Bearer"\`, \`"Token"\`, \`"Basic"\`). If the API uses raw tokens with no prefix, pass \`authScheme: ""\`.
250
270
  **Do NOT guess the scheme.**
@@ -254,7 +274,7 @@ To skip auth entirely, pass \`authHeader: ""\`.`;
254
274
  authGuidance = `**Auth Scheme:** No \`api.authType\` in workspace config.
255
275
  **Where to find the scheme** (check in order):
256
276
  1. **OpenAPI spec** \u2192 look at \`securitySchemes\` / \`securityDefinitions\` for \`type: http, scheme: bearer\` or \`type: apiKey\`
257
- 2. **Source code** \u2192 auth middleware (\`passport.use\`, \`jwt.verify\`, \`authMiddleware\`), route guards, or header extraction logic (e.g., \`req.headers.authorization.split(" ")[1]\` means Bearer)
277
+ 2. **Source code** \u2192 auth middleware signals: ${AUTH_MIDDLEWARE_PATTERNS_STR}
258
278
  3. **Fallback** \u2192 use \`"Bearer"\` only if the project clearly uses JWT or OAuth; otherwise pass \`authScheme: ""\`
259
279
  Pass the prefix as \`authScheme\` (e.g., \`"Bearer"\`, \`"Token"\`, \`"Basic"\`). If the API uses raw tokens with no prefix, pass \`authScheme: ""\`.
260
280
  **Do NOT guess the scheme.**
@@ -321,13 +341,14 @@ If an OpenAPI spec exists, ALSO pass \`apiSchema\` — it enables schema-aware v
321
341
  Without a spec, \`endpointURL\` alone is sufficient.
322
342
  ${PATH_PARAM_UUID_GUIDANCE}
323
343
 
324
- **Contract test mode selection — set based on this service's role at the boundary:**
344
+ ${CONSUMER_MODE_ENABLED ? `**Contract test mode selection — set based on this service's role at the boundary:**
325
345
  - \`providerMode: true\` — this service IS the API; validates the implementation matches the spec.
326
346
  Use for new or modified endpoints this codebase owns.
327
347
  - \`consumerMode: true\` — this service CALLS another API; validates outbound requests conform to the downstream contract.
328
348
  Use when the endpoint's implementation makes HTTP calls to external services (look for fetch/axios/httpx/http.Client/service clients).
329
349
  A request-aware mock stands in for the real downstream service — no live dependency needed.
330
- - **Both modes** (\`providerMode: true, consumerMode: true\`) — same output as omitting both flags. Generates both consumer and provider contract tests. Use when the diff contains BOTH provider signals (new/modified endpoint handlers) AND consumer signals (outbound HTTP client calls to another service).
350
+ - **Both modes** (\`providerMode: true, consumerMode: true\`) — same output as omitting both flags. Generates both consumer and provider contract tests. Use when the diff contains BOTH provider signals (new/modified endpoint handlers) AND consumer signals (outbound HTTP client calls to another service).` : `**Contract tests — provider-only:**
351
+ Only provider-side contract tests are supported. Pass \`providerMode: true\` for new or modified endpoints this codebase owns.`}
331
352
 
332
353
  **For UI tests:**
333
354
  1. \`browser_navigate\` to the target URL (from workspace \`api.baseUrl\`)