@skyramp/mcp 0.2.8 → 0.2.10-rc.1
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.
- package/build/index.js +1 -8
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +28 -1
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +1 -0
- package/build/prompts/personas.d.ts +2 -2
- package/build/prompts/personas.js +2 -2
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +3 -1
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +6 -1
- package/build/prompts/sut-setup/shared.d.ts +20 -0
- package/build/prompts/sut-setup/shared.js +67 -7
- package/build/prompts/test-maintenance/actionsInstructions.js +5 -1
- package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +15 -2
- package/build/prompts/test-maintenance/drift-analysis-prompt.js +63 -3
- package/build/prompts/test-maintenance/driftAnalysisSections.js +7 -25
- package/build/prompts/test-maintenance/driftAnalysisShared.d.ts +20 -0
- package/build/prompts/test-maintenance/driftAnalysisShared.js +80 -0
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.d.ts +32 -0
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +212 -0
- package/build/prompts/test-recommendation/recommendationShared.js +1 -1
- package/build/prompts/test-recommendation/scopeAssessment.js +12 -2
- package/build/prompts/testbot/testbot-prompts.js +10 -9
- package/build/resources/testbotResource.js +1 -1
- package/build/services/TestDiscoveryService.d.ts +12 -10
- package/build/services/TestDiscoveryService.js +137 -83
- package/build/services/TestDiscoveryService.test.js +370 -15
- package/build/services/TestExecutionService.d.ts +1 -1
- package/build/services/TestExecutionService.js +7 -7
- package/build/services/TestExecutionService.test.js +4 -1
- package/build/tools/executeSkyrampTestTool.js +40 -71
- package/build/tools/submitReportTool.d.ts +25 -25
- package/build/tools/submitReportTool.js +123 -34
- package/build/tools/submitReportTool.test.js +413 -94
- package/build/tools/test-management/actionsTool.js +177 -77
- package/build/tools/test-management/actionsTool.test.d.ts +1 -0
- package/build/tools/test-management/actionsTool.test.js +297 -0
- package/build/tools/test-management/analyzeChangesTool.d.ts +9 -0
- package/build/tools/test-management/analyzeChangesTool.js +74 -169
- package/build/tools/test-management/analyzeChangesTool.test.js +34 -3
- package/build/tools/test-management/analyzeTestHealthTool.js +63 -22
- package/build/tools/test-management/analyzeTestHealthTool.test.d.ts +1 -0
- package/build/tools/test-management/analyzeTestHealthTool.test.js +268 -0
- package/build/tools/workspace/initializeWorkspaceTool.js +37 -24
- package/build/tools/workspace/initializeWorkspaceTool.test.js +9 -4
- package/build/types/FrontendIntegration.d.ts +25 -0
- package/build/types/FrontendIntegration.js +19 -0
- package/build/types/RepositoryAnalysis.d.ts +8 -8
- package/build/types/TestAnalysis.d.ts +18 -49
- package/build/types/TestAnalysis.js +0 -28
- package/build/types/TestTypes.d.ts +3 -1
- package/build/types/TestTypes.js +3 -1
- package/build/types/TestbotReport.d.ts +1 -1
- package/build/utils/AnalysisStateManager.d.ts +16 -22
- package/build/utils/docker.test.js +1 -1
- package/build/utils/featureFlags.d.ts +1 -1
- package/build/utils/featureFlags.js +1 -1
- package/build/utils/frontendIntegration.d.ts +9 -0
- package/build/utils/frontendIntegration.js +237 -0
- package/build/utils/frontendIntegration.test.d.ts +1 -0
- package/build/utils/frontendIntegration.test.js +229 -0
- package/build/utils/pr-comment-parser.d.ts +3 -3
- package/build/utils/pr-comment-parser.js +6 -6
- package/build/utils/pr-comment-parser.test.js +3 -3
- package/build/utils/repoScanner.d.ts +7 -0
- package/build/utils/repoScanner.js +14 -6
- package/build/utils/repoScanner.test.js +9 -9
- package/build/utils/routeParsers.d.ts +32 -0
- package/build/utils/routeParsers.js +205 -2
- package/build/utils/routeParsers.test.js +279 -29
- package/build/utils/sourceRouteExtractor.js +174 -5
- package/build/utils/sourceRouteExtractor.test.js +173 -0
- package/build/utils/utils.d.ts +11 -0
- package/build/utils/utils.js +19 -0
- package/build/utils/utils.test.js +23 -1
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +21 -21
- package/build/workspace/workspace.js +7 -4
- package/build/workspace/workspace.test.js +65 -6
- package/node_modules/playwright/lib/mcp/browser/tools/files.js +8 -3
- package/node_modules/playwright/lib/mcp/browser/tools/wait.js +1 -1
- package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +10 -1
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +33 -9
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +7 -1
- package/node_modules/playwright/lib/mcp/test/skyRampExport.test.js +44 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.B_7ywgmr.js → index.d4gkVSou.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +1 -1
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Shared drift prompt sections (assign-action, external test policy, symbol discovery) for API and UI prompts. */
|
|
2
|
+
/**
|
|
3
|
+
* Build the symbol-discovery grep instruction — unified for API and UI tests.
|
|
4
|
+
* Two-pass: hunk-context symbols (what changed) + full file exports (what exists),
|
|
5
|
+
* covering aliases, indirect renders, and shared hooks the diff doesn't mention.
|
|
6
|
+
*/
|
|
7
|
+
export function buildSymbolDiscoveryStep(hasTests, repoPaths) {
|
|
8
|
+
if (!hasTests)
|
|
9
|
+
return "";
|
|
10
|
+
const repoList = repoPaths && repoPaths.length > 1
|
|
11
|
+
? repoPaths.map(p => `\`${p}\``).join(", ")
|
|
12
|
+
: repoPaths?.[0] ? `\`${repoPaths[0]}\`` : "the repository";
|
|
13
|
+
return `**Symbol discovery (before assessing):** Find additional test files that reference changed code:
|
|
14
|
+
|
|
15
|
+
1. **Hunk context:** Extract function and class names from \`@@\` context lines and changed endpoint URL path segments. Grep ${repoList} for each.
|
|
16
|
+
2. **Full file exports:** For each changed source file in the diff, read it and extract all exported symbols (component names, function names, hooks, class names). Grep ${repoList} for each (case-insensitive). This catches tests using aliased imports, indirect renders, or shared hooks not visible in the diff hunk.
|
|
17
|
+
|
|
18
|
+
Add any matched test files not already in your assessment, and include them in \`recommendations[]\`.`;
|
|
19
|
+
}
|
|
20
|
+
/** One source of truth for the recommendations[] contract passed to skyramp_actions. */
|
|
21
|
+
export const RECOMMENDATIONS_INSTRUCTION = `Pass VERIFY, UPDATE, REGENERATE, and DELETE entries to \`recommendations[]\`. Omit IGNORE.`;
|
|
22
|
+
export function buildSharedExternalTestPolicy() {
|
|
23
|
+
return `**For user-written (external) tests** marked \`[external]\` in the test list:
|
|
24
|
+
- UPDATE is permitted — pass the exact file that needs editing as \`testFile\`. This can be the test file itself, a snapshot file, a page object, or a fixture. \`skyramp_actions\` will edit that file using \`updateInstructions\`; application source files are automatically rejected.
|
|
25
|
+
- REGENERATE and DELETE: assign these actions when the evidence clearly warrants them (page gone → DELETE, component structure fundamentally changed → REGENERATE). \`skyramp_actions\` handles execution restrictions at apply time — assign the action that the evidence supports. Leave user-authored test files unchanged; edits go only through \`skyramp_actions\` with explicit \`updateInstructions\`. VERIFY means "uncertain, need more context" — assign DELETE or REGENERATE when you have confirmed the failure, and VERIFY only when you genuinely cannot determine impact without more information.`;
|
|
26
|
+
}
|
|
27
|
+
/** Shared boilerplate: VERIFY rule, pre-commit structure, and external policy. */
|
|
28
|
+
function buildAssignActionCore(opts) {
|
|
29
|
+
return `Based on the above checks, choose the action (IGNORE / VERIFY / UPDATE / REGENERATE / DELETE) and provide a 1-2 sentence rationale.
|
|
30
|
+
|
|
31
|
+
**Every action requires a specific rationale — including IGNORE:**
|
|
32
|
+
- UPDATE / REGENERATE / DELETE: ${opts.triggerEvidence}.
|
|
33
|
+
- VERIFY: name the uncertain element (e.g. ${opts.verifyExample}).
|
|
34
|
+
- IGNORE: ${opts.ignoreGuidance}.
|
|
35
|
+
|
|
36
|
+
${opts.domainNotes}
|
|
37
|
+
|
|
38
|
+
**When uncertain, use VERIFY not IGNORE:** If the diff touches code this test could exercise but you cannot confirm impact without reading more context, assign VERIFY — do not default to IGNORE.
|
|
39
|
+
|
|
40
|
+
**Pre-commit verification — confirm before finalizing UPDATE/REGENERATE/DELETE:**
|
|
41
|
+
1. You can quote a specific diff line${opts.blueprintSuffix} this test observes that triggered the action.
|
|
42
|
+
2. ${opts.reachabilityCheck}
|
|
43
|
+
3. ${opts.regenerateCheck}
|
|
44
|
+
4. ${opts.deleteCheck}
|
|
45
|
+
If any check fails, downgrade to VERIFY or IGNORE.
|
|
46
|
+
|
|
47
|
+
${buildSharedExternalTestPolicy()}`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Assign-action section for the API drift prompt.
|
|
51
|
+
* @param domainNotes API-specific bullets (additive-fields guidance, scope-gate note).
|
|
52
|
+
*/
|
|
53
|
+
export function buildApiAssignAction(domainNotes) {
|
|
54
|
+
return buildAssignActionCore({
|
|
55
|
+
triggerEvidence: "quote the specific diff line that triggered it",
|
|
56
|
+
ignoreGuidance: `name the changed file(s) from the diff AND explain why they cannot reach this test's endpoint (e.g. "diff only touches \`auth/session-service.js\` — this test targets \`/api/v1/orders\` which has no session dependency"). You must reference at least one specific changed file. Generic "unrelated endpoint" or "service boundary" without naming the changed file is not sufficient`,
|
|
57
|
+
verifyExample: `"model-only change, cannot confirm field is exposed without checking the output layer"`,
|
|
58
|
+
blueprintSuffix: "",
|
|
59
|
+
reachabilityCheck: "The changed code is reachable through this test's service and base URL.",
|
|
60
|
+
regenerateCheck: "For REGENERATE: every assertion in the file is invalid, not just some — if you can patch N paths, it is UPDATE.",
|
|
61
|
+
deleteCheck: "For DELETE: all covered endpoints no longer exist.",
|
|
62
|
+
domainNotes,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Assign-action section for the UI drift prompt.
|
|
67
|
+
* @param domainNotes UI-specific bullets (component/widget matching notes).
|
|
68
|
+
*/
|
|
69
|
+
export function buildUiAssignAction(domainNotes) {
|
|
70
|
+
return buildAssignActionCore({
|
|
71
|
+
triggerEvidence: "quote the specific diff line and/or blueprint element that triggered it",
|
|
72
|
+
ignoreGuidance: `name the specific route or component and why the diff cannot reach it (e.g. "diff only touches \`/api/v1/users\` — this test targets \`/dashboard\`")`,
|
|
73
|
+
verifyExample: `"component is imported but not directly rendered — cannot confirm impact without reading the parent"`,
|
|
74
|
+
blueprintSuffix: " or blueprint element",
|
|
75
|
+
reachabilityCheck: "The changed code is reachable through this test's routes (browser tests) or a render/mount call in the test that references a changed component (component/widget tests).",
|
|
76
|
+
regenerateCheck: "For REGENERATE: the interaction model changed fundamentally — the test's action sequence has no migration path. If selectors changed but the flow (navigate → interact → assert) can still be expressed with targeted edits, it is UPDATE regardless of how many selectors changed.",
|
|
77
|
+
deleteCheck: "For DELETE: the route is completely gone (browser tests) or the rendered component was deleted (component/widget tests).",
|
|
78
|
+
domainNotes,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the UI Action Decision Tree section.
|
|
3
|
+
* This is the main entry point for UI drift analysis.
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildUiActionDecisionTree(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Check if routes targeted by the UI test still exist.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildUiCheckRouteExistence(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Check if selectors used by the UI test still exist in the blueprints.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildUiCheckSelectors(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Check if page objects/fixtures referenced by the test still exist.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildUiCheckPageObjects(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Check for component behavior changes that affect assertions.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildUiCheckBehavioralChanges(): string;
|
|
22
|
+
/**
|
|
23
|
+
* Assign the final UI drift action.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildUiCheckAssignAction(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Build the UI drift output checklist.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildUiDriftOutputChecklist(stateFile?: string, tests?: Array<{
|
|
30
|
+
testFile: string;
|
|
31
|
+
source?: string;
|
|
32
|
+
}>): string;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI Drift Analysis prompt sections for Component 2.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the driftAnalysisSections.ts pattern but for UI-specific drift detection.
|
|
5
|
+
* These sections are included whenever `uiContext.changedFrontendFiles` is non-empty —
|
|
6
|
+
* both browser tests (Playwright/Cypress/Selenium) and component/widget tests
|
|
7
|
+
* (RTL/Flutter widget/vue-test-utils/angular-testbed) go through the same gate.
|
|
8
|
+
* Component tests always use source-only analysis regardless of blueprintCaptured.
|
|
9
|
+
*/
|
|
10
|
+
import { buildUiAssignAction } from "./driftAnalysisShared.js";
|
|
11
|
+
/**
|
|
12
|
+
* Build the UI Action Decision Tree section.
|
|
13
|
+
* This is the main entry point for UI drift analysis.
|
|
14
|
+
*/
|
|
15
|
+
export function buildUiActionDecisionTree() {
|
|
16
|
+
return `<ui_decision_rules>
|
|
17
|
+
For each UI test listed in the \`<ui_analysis_context>\` above, assess whether the test is still valid given the diff (and blueprint diff when blueprints were captured). The diff may have changed:
|
|
18
|
+
- Element selectors (CSS class changes, component restructuring)
|
|
19
|
+
- Page structure (new containers, removed sections)
|
|
20
|
+
- Route paths (URL changes)
|
|
21
|
+
- Component behavior (state changes, visibility conditions)
|
|
22
|
+
|
|
23
|
+
**Scope gate:** Read each test file to determine its type, then apply the correct gate:
|
|
24
|
+
- **Browser test** (has \`page.goto()\`, \`cy.visit()\`, \`driver.get()\`): check that the test navigates to a page the changed frontend file serves. Infer from the changed file's directory path (e.g. \`modules/bookings/\` → bookings page) or explicit route-path diff signals.
|
|
25
|
+
- **Component/widget test** (has \`render(<…>)\`, \`mount()\`, \`shallowMount()\`): check if it directly renders/mounts a changed component. If none of the rendered components match a changed file, the test is unaffected.
|
|
26
|
+
If neither condition applies, assign **IGNORE** and skip remaining checks.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
**Severity order (highest first): DELETE > REGENERATE > UPDATE > VERIFY > IGNORE**
|
|
30
|
+
|
|
31
|
+
**Pre-scan the diff for UI-relevant changes:**
|
|
32
|
+
- Component file changes: \`+ import { NewComponent }\`, \`- <OldComponent />\` / \`+ <NewComponent />\`
|
|
33
|
+
- CSS/Tailwind changes: \`+ className="..."\`, \`+ tw="..."\`, \`+ styles.\`
|
|
34
|
+
- Route changes: \`- path: "/old"\` / \`+ path: "/new"\`
|
|
35
|
+
- Selector changes: \`+ data-testid="..."\`, \`- data-testid="..."\`, \`+ aria-label="..."\`
|
|
36
|
+
- State/visibility changes: \`+ isOpen\`, \`+ isVisible\`, \`+ showModal\`
|
|
37
|
+
|
|
38
|
+
Build a detection list first: for each matched diff line, write one line: \`{pattern type} — "{diff line}" — affects {route/component}\`.
|
|
39
|
+
|
|
40
|
+
**Action definitions:**
|
|
41
|
+
- **DELETE**: The test targets a page/route that no longer exists, or all selectors the test uses have been removed and there's no migration path.
|
|
42
|
+
- **REGENERATE**: The interaction model changed so fundamentally that the test flow itself is broken — not just selector values, but the sequence of actions (e.g. a form replaced by a modal, buttons replaced by a dropdown, a multi-step flow collapsed into one). There is no rename mapping; re-recording from scratch is the only viable approach.
|
|
43
|
+
- **UPDATE**: Specific selectors need updating, assertions need adjustment, or new elements were added to a page the test already exercises. Always an in-place edit to the existing file — do not generate a new spec to cover the same page or component.
|
|
44
|
+
- **VERIFY**: The test may be impacted but you cannot confirm breakage without reading more context (e.g. selectors are hidden in a page object file you have not read, or the component is imported indirectly).
|
|
45
|
+
- **IGNORE**: The changed code cannot reach this test's routes or rendered components — the test covers unchanged functionality. If you cannot point to a diff line that affects this test's routes, selectors, or rendered components, the action is IGNORE or VERIFY, not UPDATE.
|
|
46
|
+
|
|
47
|
+
**Rules:**
|
|
48
|
+
- DELETE when all routes the test covers were removed or all critical selectors are gone.
|
|
49
|
+
- REGENERATE when the interaction model changed fundamentally — the test's sequence of actions is broken with no migration path (e.g. a click-to-update button replaced by an auto-updating stepper; a remove button moved into a dropdown). The number of broken selectors alone does not determine REGENERATE; what matters is whether the flow can still be expressed with targeted edits.
|
|
50
|
+
- UPDATE when specific selectors changed (renamed classes, moved data-testid) but the test flow remains valid, or when new elements were added to a page the test already covers — append the assertion to the existing file.
|
|
51
|
+
- VERIFY when a page object or indirect import hides the affected selectors — read the imported file before escalating to UPDATE.
|
|
52
|
+
- IGNORE when the diff touches unrelated frontend code (different routes, different components).
|
|
53
|
+
|
|
54
|
+
**Additional rule for component/widget tests** ([rtl], [flutter-widget], [vue-test-utils], [angular-testbed]):
|
|
55
|
+
- Rule #7: Component deleted — source file removed with no re-export → **DELETE** (report-only for external tests). These tests have no navigation; the rendered component is their only target.
|
|
56
|
+
- Skip the route-existence check (step 1.1) for component/widget tests — they have no \`page.goto()\` or equivalent navigation calls.
|
|
57
|
+
- Primary matching signal: read the test to check if it directly renders a changed component — if so, it is impacted.
|
|
58
|
+
</ui_decision_rules>`;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if routes targeted by the UI test still exist.
|
|
62
|
+
*/
|
|
63
|
+
export function buildUiCheckRouteExistence() {
|
|
64
|
+
return `Does the route/page this UI test navigates to still exist?
|
|
65
|
+
|
|
66
|
+
**Skip this check for component/widget tests** ([rtl], [flutter-widget], [vue-test-utils], [angular-testbed]) — they render components directly with no navigation calls. Proceed to selector checks.
|
|
67
|
+
|
|
68
|
+
Diff signals to look for (browser tests only):
|
|
69
|
+
- Route removed: \`- path: "/old-path"\`, \`- @app.route("/old-path")\`, \`- router.get("/old-path")\`
|
|
70
|
+
- Route renamed: paired \`-\` and \`+\` on route definitions with different paths
|
|
71
|
+
- Redirect added: \`+ redirect: "/old-path" → "/new-path"\`, \`+ res.redirect(301, "/new-path")\`
|
|
72
|
+
|
|
73
|
+
Actions:
|
|
74
|
+
- ALL routes the test uses were removed → **DELETE**
|
|
75
|
+
- SOME routes renamed with no redirect → **UPDATE** (update navigation calls to new URLs)
|
|
76
|
+
- Routes renamed with redirect kept → **UPDATE** (verify redirect behavior, test should still work)
|
|
77
|
+
- Routes unchanged → proceed to selector checks`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Check if selectors used by the UI test still exist in the blueprints.
|
|
81
|
+
*/
|
|
82
|
+
export function buildUiCheckSelectors() {
|
|
83
|
+
return `Do the selectors/locators this UI test uses still match the current UI?
|
|
84
|
+
|
|
85
|
+
Read the test (and any imported page object/fixture files) to extract the selectors/locators it uses — \`getByTestId(...)\`, \`locator(...)\`, \`.find('...')\`, \`data-testid\`, \`aria-label\`, CSS class names.
|
|
86
|
+
|
|
87
|
+
Diff signals to look for:
|
|
88
|
+
- CSS class changes: \`- className="old-class"\` / \`+ className="new-class"\`
|
|
89
|
+
- Tailwind changes: \`- tw="p-4"\` / \`+ tw="p-6"\`
|
|
90
|
+
- Test ID changes: \`- data-testid="old-id"\` / \`+ data-testid="new-id"\`
|
|
91
|
+
- ARIA changes: \`- aria-label="Old"\` / \`+ aria-label="New"\`
|
|
92
|
+
- Component restructuring: \`+ <NewWrapper>\` around existing content, \`- <OldWrapper>\`
|
|
93
|
+
|
|
94
|
+
**When blueprints are available** (\`blueprintCaptured=true\`): compare extracted selectors against \`browser_blueprint_diff\` for the affected page — elements absent from the new blueprint signal breakage.
|
|
95
|
+
|
|
96
|
+
**When blueprints are not available** (\`blueprintCaptured=false\`): use diff signals only. If a selector the test uses matches a value renamed or removed in the diff, assign UPDATE. If you cannot confirm breakage from the diff alone (e.g. selectors live in a page object you have not read), assign VERIFY rather than guessing.
|
|
97
|
+
|
|
98
|
+
Actions:
|
|
99
|
+
- Selectors confirmed unchanged (diff or blueprint) → **IGNORE** (no drift)
|
|
100
|
+
- Selectors renamed (class, testid, aria-label changed) → **UPDATE** (patch selector values)
|
|
101
|
+
- Selectors removed with no confirmed replacement → **UPDATE** (find replacement; use blueprint if available, diff otherwise)
|
|
102
|
+
- Major restructuring (most selectors gone) → **REGENERATE** (page structure fundamentally changed)
|
|
103
|
+
- All selectors gone, no replacement elements → **DELETE** (page no longer testable)
|
|
104
|
+
- Cannot confirm without reading page object or additional files → **VERIFY**
|
|
105
|
+
- Hardcoded visible string wrapped in a translation key (e.g. a literal moved to \`$t('...')\`) and the default-locale rendering is unchanged → **UPDATE**: the assertion now silently depends on the active locale. Add explicit locale context to the test fixture or assert via the translation key so the test tracks the translation rather than a hardcoded copy.`;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Check if page objects/fixtures referenced by the test still exist.
|
|
109
|
+
*/
|
|
110
|
+
export function buildUiCheckPageObjects() {
|
|
111
|
+
return `Do the page objects or fixture files imported by this UI test still exist, and are their underlying selectors still valid?
|
|
112
|
+
|
|
113
|
+
**Always follow imports to find page objects — regardless of whether blueprints were captured.**
|
|
114
|
+
Page objects can live anywhere in the repo (not just \`page-objects/\` or \`fixtures/\`). Do not assume a fixed directory. The only reliable way to find them is to follow the spec file's \`import\` statements.
|
|
115
|
+
|
|
116
|
+
**Protocol for every browser test (Playwright/Cypress/Selenium):**
|
|
117
|
+
1. Read the spec file and collect all local \`import\` statements (skip \`@playwright/test\`, \`vitest\`, etc. — only follow relative or repo-local paths).
|
|
118
|
+
2. For each imported file, read it and extract the selectors it encapsulates — \`locator()\`, \`getByTestId()\`, \`getByRole()\`, \`getByLabel()\`, \`data-test-*\`, \`aria-label\`, CSS class strings.
|
|
119
|
+
3. Compare those selectors against the diff: if a selector value the page object uses was renamed or removed in a \`-\` diff line → the page object needs **UPDATE**.
|
|
120
|
+
4. **Update the page object file, not the spec file** — the page object is the selector source of truth. In the recommendation: set \`testFile\` to the **spec file** (so it shows correctly in the maintenance report) and set \`pomFile\` to the **page object path** (so \`skyramp_actions\` edits the right file). If the edit is rejected at action time, the agent or developer can address it separately.
|
|
121
|
+
5. If the imported file is missing or unreadable → **VERIFY** (cannot confirm without the file).
|
|
122
|
+
|
|
123
|
+
A clean spec with no raw selector calls is not a reason to assign VERIFY — it is expected when page objects are used. Assign the action based on the POM's selectors.
|
|
124
|
+
|
|
125
|
+
Diff signals to look for in the spec's imports:
|
|
126
|
+
- Import deleted: \`- import { OldPage } from './pages/OldPage'\` → page object removed
|
|
127
|
+
- Import path changed: paired \`-\`/\`+\` on the same import → page object moved or renamed
|
|
128
|
+
- New import added: \`+ import { NewFixture } from '../fixtures/new'\` → new dependency
|
|
129
|
+
|
|
130
|
+
Actions:
|
|
131
|
+
- Page object's selectors match a renamed/removed diff value → **UPDATE** (patch the page object)
|
|
132
|
+
- Page object import path changed in the diff → **UPDATE** (fix the import in the spec)
|
|
133
|
+
- Page object file deleted, no replacement found → **UPDATE** (inline selectors or find alternative)
|
|
134
|
+
- Selectors in page object unchanged and diff does not affect them → **IGNORE**
|
|
135
|
+
- **All POM selectors are gone with no migration path in the diff** (the component's interaction model changed fundamentally — no ` + ` lines showing replacement selectors) → **REGENERATE**. Do not stay at VERIFY after reading the POM and confirming this — escalate once you have confirmed the failure.`;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Check for component behavior changes that affect assertions.
|
|
139
|
+
*/
|
|
140
|
+
export function buildUiCheckBehavioralChanges() {
|
|
141
|
+
return `Have component behaviors changed in ways that would break test assertions?
|
|
142
|
+
|
|
143
|
+
Diff signals to look for:
|
|
144
|
+
- State changes: \`+ isLoading\`, \`+ isOpen\`, \`+ showModal\`, \`+ disabled\`
|
|
145
|
+
- Visibility conditions: \`+ if (condition) return null\`, \`+ {condition && <Component />}\`
|
|
146
|
+
- Interaction changes: \`+ onClick={() => setOpen(true)}\`, \`+ require('auth')\`
|
|
147
|
+
- Async behavior: \`+ setTimeout\`, \`+ debounce\`, \`+ async/await\` in handlers
|
|
148
|
+
|
|
149
|
+
Actions:
|
|
150
|
+
- New loading states → **UPDATE** (add waitFor or assertion for loading state)
|
|
151
|
+
- New conditional rendering → **UPDATE** (update assertions to match new conditions)
|
|
152
|
+
- Auth behavior changed → **UPDATE** (adjust login flow in test)
|
|
153
|
+
- Async delays added → **UPDATE** (add appropriate waits/timeouts)
|
|
154
|
+
- Shared component changed (used across multiple pages, dialogs, or layouts): if the test covers only one consumer and you have not read all other consumers, assign **VERIFY** — the blast radius across other consumers is indeterminate. Only assign UPDATE if you have confirmed the change is isolated to this test's consumer.`;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Assign the final UI drift action.
|
|
158
|
+
*/
|
|
159
|
+
export function buildUiCheckAssignAction() {
|
|
160
|
+
return buildUiAssignAction(`**Component/widget test matching (source-only):**
|
|
161
|
+
- Read the test to check if it directly renders a changed component — if so, it is impacted.
|
|
162
|
+
- If the test only imports but does not directly render the component, assign VERIFY (component-import signal — cannot confirm impact without reading the parent).
|
|
163
|
+
- If neither signal fires, assign **IGNORE**.`);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Build the UI drift output checklist.
|
|
167
|
+
*/
|
|
168
|
+
export function buildUiDriftOutputChecklist(stateFile, tests) {
|
|
169
|
+
const finalStep = `Call \`skyramp_actions\` with \`stateFile: "${stateFile ?? "<stateFile>"}"\`.`;
|
|
170
|
+
const hasTests = (tests?.length ?? 0) > 0;
|
|
171
|
+
const testList = hasTests
|
|
172
|
+
? tests.map(t => `- ${t.testFile}${t.source === "external" ? " [external]" : ""}`).join("\n")
|
|
173
|
+
: "- (none)";
|
|
174
|
+
const noTestsNote = !hasTests
|
|
175
|
+
? `\nNo existing tests were found. Call \`skyramp_actions\` with \`recommendations: []\`.\n`
|
|
176
|
+
: "";
|
|
177
|
+
const existingTestSection = `**Tests (${tests?.length ?? 0} total) — assess ALL of the following:**
|
|
178
|
+
${testList}
|
|
179
|
+
${noTestsNote}
|
|
180
|
+
|
|
181
|
+
**Execution policy:**
|
|
182
|
+
- Skyramp-generated tests: UPDATE/REGENERATE/DELETE applied automatically by \`skyramp_actions\`. Run via \`skyramp_execute_test\`.
|
|
183
|
+
- External tests (Playwright, Cypress, RTL, etc.): \`skyramp_actions\` applies UPDATE edits to \`testFile\` — which can be the test itself, a snapshot file, a page object, or a fixture. Application source files are automatically rejected. After a successful UPDATE, call \`skyramp_execute_test\` to confirm the patched test passes (the test environment is already running). Record the result as the after-state in the report. For DELETE and REGENERATE on external tests, see the execution policy in the decision rules above.
|
|
184
|
+
|
|
185
|
+
For each UI test above, output one structured entry in \`recommendations[]\`:
|
|
186
|
+
- **IGNORE**: \`{testFile} — IGNORE: {specific reason the diff cannot reach this test — name the route or component that differs}\` — do not pass to \`skyramp_actions\`
|
|
187
|
+
- **VERIFY**: \`{testFile} — VERIFY: {uncertain element, e.g. "selectors hidden in page object not yet read"}\`
|
|
188
|
+
\`\`\`
|
|
189
|
+
Test: {testFile}
|
|
190
|
+
Action: UPDATE
|
|
191
|
+
Rationale: UPDATE because {quoted diff line}; {selector/route/behavior change}
|
|
192
|
+
UpdateInstructions: {what must change — specific selectors to update, routes to patch, assertions to add}
|
|
193
|
+
\`\`\`
|
|
194
|
+
\`\`\`
|
|
195
|
+
Test: {testFile}
|
|
196
|
+
Action: REGENERATE
|
|
197
|
+
Rationale: REGENERATE because {quoted diff line}; page structure fundamentally changed
|
|
198
|
+
\`\`\`
|
|
199
|
+
\`\`\`
|
|
200
|
+
Test: {testFile}
|
|
201
|
+
Action: DELETE
|
|
202
|
+
Rationale: DELETE because {quoted diff line}; all routes removed or no valid selectors remain
|
|
203
|
+
\`\`\`
|
|
204
|
+
|
|
205
|
+
Be concise — one line per VERIFY/IGNORE entry.`;
|
|
206
|
+
const sections = [existingTestSection, finalStep];
|
|
207
|
+
return `<ui_output_format>
|
|
208
|
+
Complete ALL of the following:
|
|
209
|
+
|
|
210
|
+
${sections.join("\n\n")}
|
|
211
|
+
</ui_output_format>`;
|
|
212
|
+
}
|
|
@@ -76,7 +76,7 @@ export function buildExternalCoverageSet(testLocations) {
|
|
|
76
76
|
}
|
|
77
77
|
return coverage;
|
|
78
78
|
}
|
|
79
|
-
// Shared
|
|
79
|
+
// Shared Testbot task and step labels used by prompt modules that cannot import
|
|
80
80
|
// testbot-prompts.ts directly without creating a circular dependency.
|
|
81
81
|
export const TASK_UI_PRESCAN = "0";
|
|
82
82
|
export const TASK_ANALYZE_MAINTAIN = "1";
|
|
@@ -11,7 +11,12 @@ const FLUTTER_DART_EXT = /\.dart$/i;
|
|
|
11
11
|
const LIKELY_FRONTEND_EXT = /\.(tsx|jsx)$/i;
|
|
12
12
|
// Ambiguous extensions need directory context to distinguish frontend from backend.
|
|
13
13
|
const AMBIGUOUS_FRONTEND_PATTERN = /\.(tsx?|jsx?|css|scss|less|html?|erb|jsp|asp|jinja2?|twig)$/i;
|
|
14
|
-
|
|
14
|
+
// /api/ always signals backend — even .tsx files (e.g. Next.js pages/api/*.tsx handlers).
|
|
15
|
+
const STRICT_API_DIR_PATTERN = /\/(api)\//i;
|
|
16
|
+
// Framework route dirs that contain React page components in modern full-stack apps
|
|
17
|
+
// (Medusa Admin, Remix, React Router v6). These only block non-.tsx/.jsx files —
|
|
18
|
+
// a .tsx file in routes/ is a page component, not a route handler.
|
|
19
|
+
const FRAMEWORK_ROUTE_DIR_PATTERN = /\/(routes?|controllers?|routers?|handlers?|endpoints?|server)\//i;
|
|
15
20
|
const FRONTEND_DIR_PATTERN = /(^|\/)(components?|pages?|views?|layouts?|app|src\/app|frontend|client|public|styles?|templates?)\//i;
|
|
16
21
|
/**
|
|
17
22
|
* Returns true if the repository root's `pubspec.yaml` declares a Flutter
|
|
@@ -120,7 +125,12 @@ export function isFrontendFile(filePath, { hasFlutterSdkDep = false } = {}) {
|
|
|
120
125
|
return true;
|
|
121
126
|
if (hasFlutterSdkDep && FLUTTER_DART_EXT.test(filePath))
|
|
122
127
|
return true;
|
|
123
|
-
|
|
128
|
+
// /api/ always means backend regardless of extension (Next.js API routes use .tsx).
|
|
129
|
+
if (STRICT_API_DIR_PATTERN.test(filePath))
|
|
130
|
+
return false;
|
|
131
|
+
// routes/, controllers/ etc. block .ts/.js but not .tsx/.jsx — Medusa Admin and
|
|
132
|
+
// React Router place page components under routes/, not API handlers.
|
|
133
|
+
if (!LIKELY_FRONTEND_EXT.test(filePath) && FRAMEWORK_ROUTE_DIR_PATTERN.test(filePath))
|
|
124
134
|
return false;
|
|
125
135
|
if (LIKELY_FRONTEND_EXT.test(filePath))
|
|
126
136
|
return true;
|
|
@@ -86,7 +86,7 @@ Verify the prompt inside <USER_PROMPT> is related to adding or removing tests fr
|
|
|
86
86
|
|
|
87
87
|
### Task 1: Retrieve Previous Recommendations
|
|
88
88
|
Call \`skyramp_analyze_changes\` with \`repositoryPath\`: "${repositoryPath}", \`scope\`: "branch_diff"${baseBranch ? `, \`baseBranch\`: "${baseBranch}"` : ""}${prNumber ? `, \`prNumber\`: ${prNumber}` : ""}${testsRepoDir ? `, \`testsRepoDir\`: "${testsRepoDir}"` : ""}.
|
|
89
|
-
This will fetch the previous
|
|
89
|
+
This will fetch the previous Testbot report from the PR comments and return deduplicated recommendations.
|
|
90
90
|
Use those recommendations as your baseline. Only add or remove tests that the user requested AND that appear in the Additional Recommendations. Then proceed straight to Task 2: Generate New Tests.
|
|
91
91
|
`
|
|
92
92
|
: `
|
|
@@ -102,7 +102,7 @@ Use those recommendations as your baseline. Only add or remove tests that the us
|
|
|
102
102
|
**Feature context check:** After capturing a blueprint per the UI Blueprint Capture instructions, cross-check whether it actually shows the changed feature. Look at the diff — what component, field, or UI element was changed? If that component only renders in a specific context (a particular collection type, a non-empty list, a specific user role, a form that only appears after a prior action), and the blueprint you captured doesn't contain it, navigate to the right context before using this blueprint for recommendations. A blueprint that doesn't show the changed feature produces recommendations that test the wrong thing — for example, recommending tests against a system collection when the PR changed behavior on custom collections. If you can't determine the right context from the diff and workspace config, note it in \`issuesFound\` and fall back to source-grounded recommendations for that URL.
|
|
103
103
|
|
|
104
104
|
**If \`skyramp_analyze_changes\` returns an error:** retry once only if the error is transient (timeout, network blip, temporary unavailability) — do NOT retry for permanent errors (invalid repository path, missing required parameter, authentication failure). If it fails again, call \`skyramp_submit_report\` with a minimal valid payload: leave all test arrays empty and add the error to \`issuesFound\`. Refer to the \`skyramp_submit_report\` schema for required fields. Do NOT attempt Task 2 without a valid stateFile.
|
|
105
|
-
**If all changed files are non-application** (CI/CD, docs, lock files, config) → skip to Task 3 (Submit Report) with empty arrays
|
|
105
|
+
**If all changed files are non-application** (CI/CD, docs, lock files, config) → skip to Task 3 (Submit Report) with empty arrays. Put the one-paragraph summary in \`businessCaseAnalysis\` (always populated; that's where end-state narration belongs); leave \`issuesFound\` empty — a non-application diff is not an issue. Example narration for a Testbot onboarding PR (\`.github/workflows/skyramp-testbot.yml\` and/or files under \`.skyramp/\`): "This PR adds Skyramp Testbot GitHub Actions workflow configuration to enable automated test generation on every pull request. It also adds System Under Test (SUT) setup files under \`.skyramp/sut/\` required for the testbot workflow, to bring up services for testing. It contains no application code changes and has no testable behavioral surface."
|
|
106
106
|
${hasRelatedRepos ? `
|
|
107
107
|
**MULTI-REPO CONTEXT (MANDATORY).** This run includes ${relatedRepositories.length} related ${relatedRepositories.length === 1 ? "repository" : "repositories"} listed in the \`<related_repositories>\` block below, each with an explicit \`repository\` (\`owner/repo\`), \`path\`, and \`base_branch\`. Use the \`repository\` value verbatim — do NOT infer it from git remotes or paths. You MUST analyze EACH related repository — exactly one \`skyramp_analyze_changes\` call per listed repo (${relatedRepositories.length} ${relatedRepositories.length === 1 ? "call" : "calls"}), in addition to the primary call in step 2.
|
|
108
108
|
|
|
@@ -126,11 +126,11 @@ ${hasRelatedRepos ? `
|
|
|
126
126
|
|
|
127
127
|
2. **Maintain existing tests:**
|
|
128
128
|
|
|
129
|
-
a. Call \`skyramp_analyze_test_health\` with \`stateFile\` (from \`skyramp_analyze_changes\` output). **Do NOT read application source files** (routes, models, controllers) — all change information you need is in the \`skyramp_analyze_changes\` output and the diff.
|
|
129
|
+
a. Call \`skyramp_analyze_test_health\` with \`stateFile\` (from \`skyramp_analyze_changes\` output). Pass \`blueprintCaptured: true\` when \`browser_blueprint\` was called successfully earlier in this session — see the parameter description for when this applies. **Do NOT read application source files** (routes, models, controllers) — all change information you need is in the \`skyramp_analyze_changes\` output and the diff. Exception: the UI drift pre-scan (\`UI_SYMBOL_PRESCAN\`) may instruct you to read changed frontend files to extract exported symbols — follow those instructions when present.
|
|
130
130
|
|
|
131
131
|
b. Write \`updateInstructions\` for each UPDATE or REGENERATE test before calling \`skyramp_actions\` — articulating the change first prevents file content from overriding diff-based reasoning.
|
|
132
132
|
|
|
133
|
-
c. Call \`skyramp_execute_test\` with \`phase: "before"\` and \`stateFile\` for every test whose action is UPDATE or
|
|
133
|
+
c. Call \`skyramp_execute_test\` with \`phase: "before"\` and \`stateFile\` for every test whose action is UPDATE, REGENERATE, or DELETE. Run them sequentially, not in parallel. This captures the pre-edit baseline — do not skip even if you expect the test to fail.
|
|
134
134
|
|
|
135
135
|
d. Call \`skyramp_actions\` with \`stateFile\` (from \`skyramp_analyze_changes\` output) and apply the edits it returns.
|
|
136
136
|
|
|
@@ -370,7 +370,7 @@ ${userPrompt ? "Generate only the tests that the user requested from the Additio
|
|
|
370
370
|
**Flutter web apps:** Skyramp's Playwright tools automatically enable Flutter's accessibility semantics tree on every \`browser_navigate\` call — you do NOT need to manually click \`flt-semantics-placeholder\` or add any activation step to the trace. Do NOT log an \`issuesFound\` entry about Flutter canvas rendering or accessibility activation — this is handled transparently. **Do NOT skip test generation or abstain from recording based on what you see in the Flutter source code** (e.g. \`SemanticsBinding.ensureSemantics()\` commented out, \`IS_TESTING\` flag absent, or similar) — Skyramp enables accessibility from the browser side regardless of the app's Dart code. Proceed with \`browser_navigate\` and test recording as normal. **Start at the app's root URL** (e.g. \`{baseUrl}/\`) — do NOT \`browser_navigate\` straight to a deep sub-route (e.g. \`/authors\`, \`/orders/13\`). Flutter \`go_router\` SPAs route from the root: deep-linking on a cold page load often fails to render the expected screen (the route's widgets never mount, so the trace captures the wrong page). Load the root, let the app's own routing/auth-redirect render, then reach target screens by interaction. **After the initial login, navigate using in-app controls only** (tab buttons, links, back buttons) — do NOT call \`browser_navigate\` to a different URL after login. Flutter web apps are SPAs: a \`browser_navigate\` to a new URL after login triggers a full page reload which clears the auth session, causing redundant re-login cycles in the generated test. Use button clicks to reach target screens instead.
|
|
371
371
|
**Skip only if one of these conditions is met:**
|
|
372
372
|
- **(a) App is unreachable** — \`browser_navigate\` fails or connection is refused.
|
|
373
|
-
- **(b) Unintegrated non-route component** — the changed file is a leaf component (not a framework route/entrypoint) that has no integration point in the running app.
|
|
373
|
+
- **(b) Unintegrated non-route component** — the changed file is a leaf component (not a framework route/entrypoint) that has no integration point in the running app. **The server already computes this** — check \`uiContext.frontendFileIntegration\` in the \`skyramp_analyze_changes\` output: if it marks the changed file \`integrated: false\`, treat the component as unintegrated WITHOUT re-running the grep below (the tool output's accompanying instruction block already tells you what to do — do not substitute another page or trace). Only fall back to the manual grep procedure when \`frontendFileIntegration\` is absent (older MCP versions) or doesn't cover the changed file:
|
|
374
374
|
1. Grep for the component's exported name AND its module path/filename across all production source files (excluding \`*.test.*\`, \`*.spec.*\`, \`*.stories.*\`, \`__tests__/\` directories — only production code imports count).
|
|
375
375
|
2. If no production file imports, re-exports, or renders it, the component has no DOM node in the running app → unintegrated.
|
|
376
376
|
3. **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 — test through it.
|
|
@@ -547,7 +547,7 @@ Do NOT use \`page.waitForTimeout()\` with fixed delays. Do NOT retry more than o
|
|
|
547
547
|
4. **Wait**: Do NOT proceed to test execution until steps 1–3 are complete and the verification checklist in the \`skyramp_enhance_assertions\` tool result has been validated for EVERY generated test file.
|
|
548
548
|
Do not make any changes other than the assertion enhancements described above. For example: do not modify auth headers, cookies, tokens, env vars, or imports that the generation tool already set correctly — those are correct by construction and changing them breaks auth or execution.
|
|
549
549
|
|
|
550
|
-
**Final execution (mandatory):** Do NOT call \`skyramp_execute_test\` until ALL maintenance edits AND ALL new test generation/enhancement are complete.
|
|
550
|
+
**Final execution (mandatory):** Do NOT call \`skyramp_execute_test\` until ALL maintenance edits AND ALL new test generation/enhancement are complete. Run these calls sequentially, not in parallel.
|
|
551
551
|
- Only report test results for files you actually ran.
|
|
552
552
|
**Auth**: If \`skyramp_analyze_changes\` reports an auth token or \`SKYRAMP_TEST_TOKEN\` is set, pass it in **every** \`skyramp_execute_test\` call from the first attempt — do NOT wait for a 401/403 to discover auth is needed.
|
|
553
553
|
|
|
@@ -572,13 +572,14 @@ In these cases:
|
|
|
572
572
|
Otherwise: in \`newTestsCreated\`, you must have exactly ${maxGenerate} budget-counting new tests for the planned GENERATE items. Only new files (ADD) created for those planned GENERATE items count toward this ${maxGenerate} target — GENERATE items converted to UPDATE do not. You may also include at most one additional discovered-scenario file in \`newTestsCreated\` (the bug-catching test generated after all planned items); that extra test does **not** count against the ${maxGenerate} budget. If you have fewer than ${maxGenerate} budget-counting new tests, backfill from the remaining ADDITIONAL candidates before proceeding. Only proceed with fewer than ${maxGenerate} budget-counting new tests if all candidates failed after retry AND the fallback single-contract test also failed.
|
|
573
573
|
|
|
574
574
|
Call \`skyramp_submit_report\` with \`summaryOutputFile\`: "${summaryOutputFile}" and \`stateFile\` (from \`skyramp_analyze_changes\` output) — the stateFile is required for execution outcome tracking. Field names, types, and formats are defined in the tool's parameter schema — follow them exactly.
|
|
575
|
+
|
|
575
576
|
${hasRelatedRepos ? `
|
|
576
577
|
- **MULTI-REPO attribution**: Set the \`repository\` field (\`owner/repo\`) on EVERY \`newTestsCreated\`, \`testResults\`, \`issuesFound\`, and \`additionalRecommendations\` item — including items about the PRIMARY repo — so each finding is unambiguously attributed. The primary repo's \`repository\` is \`${primaryRepo || "<the primary repo's owner/repo>"}\`; items derived from a related repo's diff (from the \`<related_repositories>\` analysis) carry that repo's \`repository\` value. In \`businessCaseAnalysis\`, include a short per-repo subsection and call out any cross-repo correlations you found.
|
|
577
578
|
` : ""}
|
|
578
579
|
- **additionalRecommendations**: AT MOST ${maxRecommendations - maxGenerate} items.
|
|
579
580
|
- For \`testType: "contract"\` entries: **\`primaryEndpoint\` is required** (e.g. \`"GET /api/v1/users/{user_id}"\`). The tool will reject the submission without it — do not omit it or you will be forced to resubmit.
|
|
580
581
|
- For \`testType: "integration"\` or \`"e2e"\` entries: omit \`primaryEndpoint\` — use \`description\` to list the endpoints involved instead.
|
|
581
|
-
-
|
|
582
|
+
- **\`testMaintenanceDetails\`**: include one entry per test that received UPDATE, REGENERATE, DELETE, or VERIFY. Do NOT include IGNORE — a test assessed and confirmed clean is not a maintenance action and adds noise to the report.
|
|
582
583
|
- **testResults**: For UI/E2E test entries, include the \`videoPath\` field if \`skyramp_execute_test\` returned one.
|
|
583
584
|
|
|
584
585
|
---
|
|
@@ -630,7 +631,7 @@ export function buildWorkspaceRecoveryPrefix(repositoryPath) {
|
|
|
630
631
|
export function registerTestbotPrompt(server) {
|
|
631
632
|
logger.info("Registering testbot prompt");
|
|
632
633
|
server.registerPrompt("skyramp_testbot", {
|
|
633
|
-
description: "Run Skyramp
|
|
634
|
+
description: "Run Skyramp Testbot to generate test recommendations and perform test maintenance for a pull request.",
|
|
634
635
|
argsSchema: {
|
|
635
636
|
prTitle: z.string().describe("Pull request title"),
|
|
636
637
|
prDescription: z.string().describe("Pull request description/body"),
|
|
@@ -660,7 +661,7 @@ export function registerTestbotPrompt(server) {
|
|
|
660
661
|
prNumber: z
|
|
661
662
|
.number()
|
|
662
663
|
.optional()
|
|
663
|
-
.describe("GitHub PR number. Passed to skyramp_analyze_changes to fetch previous
|
|
664
|
+
.describe("GitHub PR number. Passed to skyramp_analyze_changes to fetch previous Testbot comments for recommendation consistency across commits."),
|
|
664
665
|
userPrompt: z
|
|
665
666
|
.string()
|
|
666
667
|
.optional()
|
|
@@ -14,7 +14,7 @@ export function registerTestbotResource(server) {
|
|
|
14
14
|
list: undefined,
|
|
15
15
|
});
|
|
16
16
|
server.registerResource("skyramp_testbot", template, {
|
|
17
|
-
title: "Skyramp
|
|
17
|
+
title: "Skyramp Testbot Prompt",
|
|
18
18
|
description: "Returns task instructions for PR test analysis, generation, and maintenance.",
|
|
19
19
|
mimeType: "text/plain",
|
|
20
20
|
}, async (uri) => {
|
|
@@ -9,14 +9,16 @@ export interface TestDiscoveryOptions {
|
|
|
9
9
|
* Resource names derived from endpoints in changed files (e.g. ["orders", "products"]).
|
|
10
10
|
* - Non-empty array: external files partitioned by relevance; only relevant files
|
|
11
11
|
* get full endpoint extraction. May be the sentinel `["unknown"]` when endpoints
|
|
12
|
-
* exist but resource names are unresolvable —
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* tests are skipped entirely to avoid flooding context.
|
|
12
|
+
* exist but resource names are unresolvable — most files score 0 (low-relevance)
|
|
13
|
+
* and are excluded; only the few that happen to match the sentinel are kept.
|
|
14
|
+
* - Empty array `[]`: PR mode with no endpoints from diff or scanner — API/integration
|
|
15
|
+
* external tests are excluded entirely; UI tests may still be promoted via hasFrontendChanges.
|
|
17
16
|
* - `undefined`: full-repo mode — external tests capped at MAX_EXTERNAL_FULL_REPO.
|
|
18
17
|
*/
|
|
19
18
|
changedResources?: string[];
|
|
19
|
+
/** True when the diff contains frontend files — promotes UI test files for framework
|
|
20
|
+
* detection regardless of changedResources. Orthogonal to API relevance scoring. */
|
|
21
|
+
hasFrontendChanges?: boolean;
|
|
20
22
|
}
|
|
21
23
|
export declare class TestDiscoveryService {
|
|
22
24
|
private readonly EXCLUDED_DIRS;
|
|
@@ -45,9 +47,13 @@ export declare class TestDiscoveryService {
|
|
|
45
47
|
* Example: "test_orders_api.py" vs ["orders"] → score 1.
|
|
46
48
|
*/
|
|
47
49
|
private scoreRelevance;
|
|
50
|
+
private readonly MAX_CONTENT_PROMOTED;
|
|
48
51
|
/**
|
|
49
52
|
* Partition external test files into relevant (score > 0) and low-relevance (score = 0).
|
|
50
|
-
*
|
|
53
|
+
* Two-pass: filename token overlap first (primary); content-based endpoint path scan
|
|
54
|
+
* as a capped fallback for tests misnamed relative to the endpoint they exercise
|
|
55
|
+
* (e.g. test_checkout_flow.py testing /api/orders scores 0 by name but matches in content).
|
|
56
|
+
* Content matches are capped at MAX_CONTENT_PROMOTED to bound token impact.
|
|
51
57
|
*/
|
|
52
58
|
private partitionByRelevance;
|
|
53
59
|
/**
|
|
@@ -101,9 +107,6 @@ export declare class TestDiscoveryService {
|
|
|
101
107
|
* Falls back to directory/filename heuristics since external tests lack Skyramp command lines.
|
|
102
108
|
*/
|
|
103
109
|
private detectExternalTestType;
|
|
104
|
-
/**
|
|
105
|
-
* Detect test framework from external test file content by checking imports.
|
|
106
|
-
*/
|
|
107
110
|
private detectExternalFramework;
|
|
108
111
|
/**
|
|
109
112
|
* Detect programming language from file extension
|
|
@@ -118,7 +121,6 @@ export declare class TestDiscoveryService {
|
|
|
118
121
|
* Extract API schema path from test content
|
|
119
122
|
* Looks for Skyramp command line first, then fallback to other patterns
|
|
120
123
|
*/
|
|
121
|
-
private extractApiSchema;
|
|
122
124
|
/**
|
|
123
125
|
* Extract Framework from test content
|
|
124
126
|
* Looks for Skyramp command line first, then fallback to other patterns
|