@skyramp/mcp 0.4.2-rc.1 → 0.4.2-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/commands/localDevTestChangesCommand.js +1 -1
- package/build/commands/recommendTestsAndExecuteCommand.js +10 -1
- package/build/commands/testThisEndpointCommand.js +19 -2
- package/build/execution/wrapperConfig.d.ts +56 -0
- package/build/execution/wrapperConfig.js +155 -0
- package/build/index.js +6 -6
- package/build/playwright/registerPlaywrightTools.js +14 -0
- package/build/playwright/traceExportStore.d.ts +22 -0
- package/build/playwright/traceExportStore.js +81 -0
- package/build/playwright/traceRecordingPrompt.js +2 -1
- package/build/prompts/code-reuse.js +24 -21
- package/build/prompts/local-dev/local-dev-plan.js +6 -23
- package/build/prompts/local-dev/local-dev-prompts.js +1 -1
- package/build/prompts/shared-helper-policy.d.ts +36 -0
- package/build/prompts/shared-helper-policy.js +33 -1
- package/build/prompts/startTraceCollectionPrompts.js +1 -1
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +6 -7
- package/build/prompts/sut-setup/shared.d.ts +1 -1
- package/build/prompts/sut-setup/shared.js +5 -3
- package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +16 -8
- package/build/prompts/test-maintenance/drift-analysis-prompt.js +90 -36
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +1 -1
- package/build/prompts/test-recommendation/recommendationShared.d.ts +1 -1
- package/build/prompts/test-recommendation/recommendationShared.js +0 -1
- package/build/prompts/testbot/testbot-prompts.js +11 -9
- package/build/services/TestDiscoveryService.js +32 -4
- package/build/skills/runTestSkill.d.ts +6 -0
- package/build/skills/runTestSkill.js +17 -0
- package/build/tool-phases.js +0 -1
- package/build/tools/budgetExcuse.d.ts +15 -0
- package/build/tools/budgetExcuse.js +113 -0
- package/build/tools/code-refactor/utils-verify-gates.js +17 -3
- package/build/tools/executeSkyrampTestTool.d.ts +97 -48
- package/build/tools/executeSkyrampTestTool.js +775 -449
- package/build/tools/submitReportTool.js +128 -0
- package/build/tools/test-management/actionsTool.js +31 -0
- package/build/tools/test-management/analyzeChangesTool.d.ts +4 -4
- package/build/tools/test-management/analyzeTestHealthTool.d.ts +0 -11
- package/build/tools/test-management/analyzeTestHealthTool.js +7 -63
- package/build/tools/test-management/testsOwedBeforeRun.d.ts +28 -0
- package/build/tools/test-management/testsOwedBeforeRun.js +53 -0
- package/build/tools/trace/stopTraceCollectionTool.js +1 -1
- package/build/types/RepositoryAnalysis.d.ts +32 -32
- package/build/types/ReuseOutcome.d.ts +4 -3
- package/build/types/TestExecution.d.ts +2 -2
- package/build/types/TestTypes.d.ts +3 -0
- package/build/types/TestTypes.js +6 -0
- package/build/utils/AnalysisStateManager.d.ts +0 -7
- package/build/utils/AnalysisStateManager.js +1 -1
- package/build/utils/connectionErrors.d.ts +10 -0
- package/build/utils/connectionErrors.js +10 -0
- package/build/utils/language-helper.js +24 -3
- package/build/utils/progress.d.ts +1 -1
- package/build/utils/progress.js +1 -1
- package/build/utils/rebaselineSnapshots.d.ts +1 -1
- package/build/utils/rebaselineSnapshots.js +6 -16
- package/build/utils/reuseRouting.d.ts +10 -0
- package/build/utils/reuseRouting.js +15 -0
- package/build/utils/runContextGauge.d.ts +27 -0
- package/build/utils/runContextGauge.js +181 -0
- package/build/utils/skyrampMdContent.d.ts +1 -1
- package/build/utils/skyrampMdContent.js +1 -1
- package/build/utils/skyrampSdkVersion.d.ts +9 -0
- package/build/utils/skyrampSdkVersion.js +16 -0
- package/build/utils/testDependencyPolicy.js +21 -0
- package/build/utils/testExecutionRecord.d.ts +5 -1
- package/build/utils/testExecutionRecord.js +3 -1
- package/build/utils/testFileClassification.d.ts +8 -0
- package/build/utils/testFileClassification.js +36 -3
- package/build/utils/utils-verify/action-key.d.ts +42 -0
- package/build/utils/utils-verify/action-key.js +118 -36
- package/build/utils/utils-verify/action-sites.d.ts +32 -0
- package/build/utils/utils-verify/action-sites.js +202 -0
- package/build/utils/utils-verify/body-reach.js +2 -4
- package/build/utils/utils-verify/call-sites.d.ts +25 -6
- package/build/utils/utils-verify/call-sites.js +8 -5
- package/build/utils/utils-verify/index.d.ts +1 -0
- package/build/utils/utils-verify/index.js +1 -0
- package/build/utils/utils-verify/language-spec.d.ts +25 -0
- package/build/utils/utils-verify/language-spec.js +16 -2
- package/build/utils/utils-verify/parse.d.ts +10 -1
- package/build/utils/utils-verify/parse.js +19 -2
- package/build/utils/utils-verify/verify.d.ts +3 -2
- package/build/utils/utils-verify/verify.js +16 -18
- package/build/workspace/workspace.d.ts +72 -52
- package/build/workspace/workspace.js +12 -8
- package/package.json +1 -1
- package/plugin/prompts/testbot-task1.md +0 -2
- package/plugin/skills/fix-test-import-errors/SKILL.md +2 -1
- package/plugin/skills/run-test/SKILL.md +16 -0
- package/build/adapters/jestAdapter.d.ts +0 -14
- package/build/adapters/jestAdapter.js +0 -131
- package/build/adapters/mochaAdapter.d.ts +0 -13
- package/build/adapters/mochaAdapter.js +0 -93
- package/build/adapters/playwrightAdapter.d.ts +0 -17
- package/build/adapters/playwrightAdapter.js +0 -184
- package/build/adapters/pytestAdapter.d.ts +0 -15
- package/build/adapters/pytestAdapter.js +0 -119
- package/build/tools/runExistingTestsTool.d.ts +0 -138
- package/build/tools/runExistingTestsTool.js +0 -666
- package/build/types/ExternalTestExecution.d.ts +0 -67
- package/build/types/ExternalTestExecution.js +0 -8
- package/build/workspace/testSuites.d.ts +0 -20
- package/build/workspace/testSuites.js +0 -17
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { SKYRAMP_UTILS_HEADER } from "../utils.js";
|
|
4
|
-
import { blankCommentsAndStrings, isSingleLiteral, parseUtilsFile, } from "./parse.js";
|
|
4
|
+
import { blankCommentsAndStrings, isSingleLiteral, localHelperAt, localHelpersOf, parseUtilsFile, } from "./parse.js";
|
|
5
5
|
import { normalizePath } from "./language-spec.js";
|
|
6
6
|
import { realpath } from "./locate.js";
|
|
7
7
|
import { escapeRegExp } from "../regex.js";
|
|
@@ -66,7 +66,9 @@ export const REQUEST_CALL_RE = /\b(?:send_request|sendRequest)\s*\(/g;
|
|
|
66
66
|
* normalised path, nothing else — literals are what the call's arguments are for.
|
|
67
67
|
*
|
|
68
68
|
* Advisory by design: it names a change to a PRE-EXISTING test, which the maintenance
|
|
69
|
-
* flow owns, so it informs rather than gates. Never throws.
|
|
69
|
+
* flow owns, so it informs rather than gates. Never throws. The API family's scan;
|
|
70
|
+
* findSiblingInlineActionSites is the browser family's, and inlineCallSiteFamily
|
|
71
|
+
* decides which runs.
|
|
70
72
|
*/
|
|
71
73
|
export async function findSiblingInlineCallSites(testFile, utilsFiles, _helpers, spec, preReadSiblings) {
|
|
72
74
|
const byRoute = await helperRoutes(utilsFiles, spec);
|
|
@@ -91,6 +93,7 @@ export async function findSiblingInlineCallSites(testFile, utilsFiles, _helpers,
|
|
|
91
93
|
}))
|
|
92
94
|
continue;
|
|
93
95
|
out.push({
|
|
96
|
+
kind: "request",
|
|
94
97
|
file,
|
|
95
98
|
line: c.line,
|
|
96
99
|
method: c.method,
|
|
@@ -178,7 +181,7 @@ async function helperRoutes(utilsFiles, spec) {
|
|
|
178
181
|
/** The generated siblings less the utils files themselves (a utils file carries no
|
|
179
182
|
* codegen marker, so this is belt and braces). Pre-read siblings are used as given —
|
|
180
183
|
* the caller's walk decides whether the spec itself is among them. */
|
|
181
|
-
async function nonUtilsSiblings(testFile, utilsFiles, spec, preReadSiblings, includeSelf) {
|
|
184
|
+
export async function nonUtilsSiblings(testFile, utilsFiles, spec, preReadSiblings, includeSelf) {
|
|
182
185
|
const utilsSet = new Set(await Promise.all(utilsFiles.map((f) => realpath(f))));
|
|
183
186
|
return (preReadSiblings ??
|
|
184
187
|
(await generatedSiblings(testFile, spec, { includeSelf }))).filter((sib) => !utilsSet.has(path.resolve(sib.file)));
|
|
@@ -189,7 +192,7 @@ async function nonUtilsSiblings(testFile, utilsFiles, spec, preReadSiblings, inc
|
|
|
189
192
|
* commented-out or quoted request is never a site and a `)` inside a string never
|
|
190
193
|
* truncates the argument text, which is read from the original. */
|
|
191
194
|
function requestCallsIn(content, spec) {
|
|
192
|
-
const localHelpers =
|
|
195
|
+
const localHelpers = localHelpersOf(content, spec);
|
|
193
196
|
const scan = blankCommentsAndStrings(content, spec);
|
|
194
197
|
const out = [];
|
|
195
198
|
for (const m of scan.matchAll(REQUEST_CALL_RE)) {
|
|
@@ -205,7 +208,7 @@ function requestCallsIn(content, spec) {
|
|
|
205
208
|
if (!method || !pm || !isSingleLiteral(call, pm))
|
|
206
209
|
continue;
|
|
207
210
|
const p = pm[1];
|
|
208
|
-
const owner = localHelpers
|
|
211
|
+
const owner = localHelperAt(localHelpers, line);
|
|
209
212
|
out.push({
|
|
210
213
|
line,
|
|
211
214
|
start,
|
|
@@ -78,7 +78,32 @@ export interface HelperFamilySpec {
|
|
|
78
78
|
/** One clause for the fix-instruction sentence — composed by the gate, never
|
|
79
79
|
* restated there (the browser clause reads as a continuation of the api one). */
|
|
80
80
|
fixRule: string;
|
|
81
|
+
/** The sibling inline call-site advisory's family-specific half: what a site is
|
|
82
|
+
* called, what the helper already does with it, and how to replace it. The gate
|
|
83
|
+
* appends the tail both families share. */
|
|
84
|
+
inlineSite: {
|
|
85
|
+
/** The prompt's noun for a site — `HelperPolicy.inlineSite.noun`. */
|
|
86
|
+
noun: string;
|
|
87
|
+
/** `… that a shared helper already <verb>:` */
|
|
88
|
+
already: string;
|
|
89
|
+
/** What makes a site a candidate, and what the replacement keeps. */
|
|
90
|
+
instruction: string;
|
|
91
|
+
/** The copy as the shared tail names it inside a local helper: `that request`
|
|
92
|
+
* / `the sequence`. */
|
|
93
|
+
localCopy: string;
|
|
94
|
+
};
|
|
81
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* The RECEIVED value inside `expect(...)` — its first argument, up to the matcher.
|
|
98
|
+
*
|
|
99
|
+
* Only the first, because Playwright takes a message as the second: `expect(locator,
|
|
100
|
+
* "why this matters")`. Reading every argument let that message decide, so a
|
|
101
|
+
* structural wait whose message merely NAMED a text-selecting form was refused — and
|
|
102
|
+
* a refusal blocks a delivery that is right, which is worse than missing one that is
|
|
103
|
+
* wrong. The split walks bracket depth on a copy whose strings are blanked, so a comma
|
|
104
|
+
* inside a selector or a message does not end the argument.
|
|
105
|
+
*/
|
|
106
|
+
export declare function expectArgs(expr: string): string;
|
|
82
107
|
export declare const HELPER_FAMILIES: Record<HelperFamily, HelperFamilySpec>;
|
|
83
108
|
/** The helper family a test type's shared helpers follow. Unlike `isBrowserTestType`,
|
|
84
109
|
* an ABSENT test type resolves to `api`: this runs against a utils file that already
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* SKYR-4196 Part B runs 5 and 11 leaked a scenario-literal body assertion into a shared
|
|
11
11
|
* helper despite both rules being in the prompt.
|
|
12
12
|
*/
|
|
13
|
-
import { HELPER_ASSERTION_POLICIES, CONTENT_ROLES, } from "../../prompts/shared-helper-policy.js";
|
|
13
|
+
import { API_HELPERS, BROWSER_HELPERS, HELPER_ASSERTION_POLICIES, CONTENT_ROLES, } from "../../prompts/shared-helper-policy.js";
|
|
14
14
|
import { escapeRegExp } from "../regex.js";
|
|
15
15
|
/**
|
|
16
16
|
* A Playwright name as the language SPELLS it: camelCase unchanged, Python
|
|
@@ -228,7 +228,7 @@ const ROLE_WITH_NAME_RE = /\b(?:getByRole|get_by_role)\s*\(\s*["'`]([A-Za-z]+)["
|
|
|
228
228
|
* wrong. The split walks bracket depth on a copy whose strings are blanked, so a comma
|
|
229
229
|
* inside a selector or a message does not end the argument.
|
|
230
230
|
*/
|
|
231
|
-
function expectArgs(expr) {
|
|
231
|
+
export function expectArgs(expr) {
|
|
232
232
|
const open = expr.indexOf("(", expr.indexOf("expect"));
|
|
233
233
|
// `.not.to…` in TypeScript, `.not_to_…` in Python. Reading only the first left a
|
|
234
234
|
// negated Python assertion with no end, so the whole source line — a trailing
|
|
@@ -325,6 +325,14 @@ export const HELPER_FAMILIES = {
|
|
|
325
325
|
isAllowed: (spec, expr) => spec.allowedAssertion.test(expr),
|
|
326
326
|
assertionRule: HELPER_ASSERTION_POLICIES.api.boundary,
|
|
327
327
|
fixRule: "a helper that wraps an SDK request asserts only the status code — one helper per method+path, extended with defaulted parameters (the expected status excepted: it has no default and every call passes it), named method + resource",
|
|
328
|
+
inlineSite: {
|
|
329
|
+
// The prompt's word for the same thing, read from the policy so the advisory
|
|
330
|
+
// and the rule the agent followed name the site alike.
|
|
331
|
+
noun: API_HELPERS.inlineSite.noun,
|
|
332
|
+
already: "wraps",
|
|
333
|
+
instruction: "Each is a candidate: the request's method, path and call shape match the helper's. Replace the block with a call to the helper only if the remaining differences are literals (they become the arguments; keep the block's response variable; import the helper into that file; change nothing else there).",
|
|
334
|
+
localCopy: "that request",
|
|
335
|
+
},
|
|
328
336
|
},
|
|
329
337
|
browser: {
|
|
330
338
|
family: "browser",
|
|
@@ -336,6 +344,12 @@ export const HELPER_FAMILIES = {
|
|
|
336
344
|
!selectsByRenderedText(expectArgs(expr)),
|
|
337
345
|
assertionRule: `${HELPER_ASSERTION_POLICIES.browser.boundary}. Move this value assertion to the test body immediately after the helper call, selector verbatim`,
|
|
338
346
|
fixRule: "every other helper holds actions and structural waits only, named for the user intent",
|
|
347
|
+
inlineSite: {
|
|
348
|
+
noun: BROWSER_HELPERS.inlineSite.noun,
|
|
349
|
+
already: "performs",
|
|
350
|
+
instruction: "Each is a candidate: the sequence's actions and locators equal the helper's, in order. Replace the sequence with a call to the helper only if the remaining differences are literals — fill values, option labels, literal arguments — which become the arguments (import the helper into that file; every assertion and the page-error guard stay in the test body where they are; change nothing else there).",
|
|
351
|
+
localCopy: "the sequence",
|
|
352
|
+
},
|
|
339
353
|
},
|
|
340
354
|
};
|
|
341
355
|
/** The helper family a test type's shared helpers follow. Unlike `isBrowserTestType`,
|
|
@@ -44,8 +44,10 @@ export interface UtilsHelper {
|
|
|
44
44
|
/** The helper's Playwright action sequence — the duplicate key of a browser
|
|
45
45
|
* helper, where an API helper has method+path. See actionKeyOf. Read for
|
|
46
46
|
* TypeScript, JavaScript and Python (through the JavaScript spelling); absent
|
|
47
|
-
* for a helper with no readable action.
|
|
47
|
+
* for a helper with no readable action. `actionSteps` is the same sequence one
|
|
48
|
+
* step per entry, for the sibling scan that matches step by step. */
|
|
48
49
|
actionKey?: string;
|
|
50
|
+
actionSteps?: string[];
|
|
49
51
|
}
|
|
50
52
|
/** The declared parameters of a signature, raw text trimmed, empty entries dropped
|
|
51
53
|
* (a trailing comma). Reads the signature from the definition line to its body
|
|
@@ -91,6 +93,13 @@ export declare function unusableParamTypesOf(sigLines: string[]): string[];
|
|
|
91
93
|
* next non-blank column-0 line; TS/JS bodies end when the brace depth opened by
|
|
92
94
|
* the definition returns to zero.
|
|
93
95
|
*/
|
|
96
|
+
/** A generated TEST's own module-level helpers: what parseUtilsFile reads from it,
|
|
97
|
+
* less the test functions themselves (`test_…` / `testUi`), which are not helpers a
|
|
98
|
+
* call can sit "inside" in the sense the sibling scans and the body-reach check
|
|
99
|
+
* report. */
|
|
100
|
+
export declare function localHelpersOf(content: string, spec: UtilsLanguageSpec): UtilsHelper[];
|
|
101
|
+
/** The helper whose body holds `line` (1-based), if any. */
|
|
102
|
+
export declare function localHelperAt(helpers: UtilsHelper[], line: number): UtilsHelper | undefined;
|
|
94
103
|
export declare function parseUtilsFile(content: string, spec: UtilsLanguageSpec): UtilsHelper[];
|
|
95
104
|
/** True when the `path` value is a single string literal ending the argument — a
|
|
96
105
|
* concatenation (`"/api/orders/" + str(id) + "/items"`) has no route this can name. */
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { normalizePath } from "./language-spec.js";
|
|
2
|
-
import {
|
|
2
|
+
import { actionStepKeys, actionStepsOf, } from "./action-key.js";
|
|
3
|
+
/** A helper's action steps and their joined key, read once from its body. */
|
|
4
|
+
function actionSequence(body, language) {
|
|
5
|
+
const steps = actionStepsOf(body, language);
|
|
6
|
+
const keys = actionStepKeys(steps);
|
|
7
|
+
return keys ? { actionKey: keys.join("; "), actionSteps: keys } : {};
|
|
8
|
+
}
|
|
3
9
|
import { fixedSleepsIn, pythonSleepBindings, } from "./fixed-sleep.js";
|
|
4
10
|
import { stripComments, stripCommentsAndStrings, stripStrings, } from "../pom-scope/strip.js";
|
|
5
11
|
/** Whether the `<` at `i` opens a generic (`Record<string, …>`, `Array<{…}>`) rather
|
|
@@ -204,6 +210,17 @@ export function unusableParamTypesOf(sigLines) {
|
|
|
204
210
|
* next non-blank column-0 line; TS/JS bodies end when the brace depth opened by
|
|
205
211
|
* the definition returns to zero.
|
|
206
212
|
*/
|
|
213
|
+
/** A generated TEST's own module-level helpers: what parseUtilsFile reads from it,
|
|
214
|
+
* less the test functions themselves (`test_…` / `testUi`), which are not helpers a
|
|
215
|
+
* call can sit "inside" in the sense the sibling scans and the body-reach check
|
|
216
|
+
* report. */
|
|
217
|
+
export function localHelpersOf(content, spec) {
|
|
218
|
+
return parseUtilsFile(content, spec).filter((h) => !/^test/i.test(h.name));
|
|
219
|
+
}
|
|
220
|
+
/** The helper whose body holds `line` (1-based), if any. */
|
|
221
|
+
export function localHelperAt(helpers, line) {
|
|
222
|
+
return helpers.find((h) => line >= h.line && line <= h.endLine);
|
|
223
|
+
}
|
|
207
224
|
export function parseUtilsFile(content, spec) {
|
|
208
225
|
const lines = content.split("\n");
|
|
209
226
|
// Structure — where a body ends — is read on a copy with comments and string
|
|
@@ -263,7 +280,7 @@ export function parseUtilsFile(content, spec) {
|
|
|
263
280
|
fixedSleeps: fixedSleepsIn(body, scan, spec.language, params ?? [], start + 1, sleepBindings),
|
|
264
281
|
// Comments blanked, strings kept: a commented-out click is not a step, and
|
|
265
282
|
// the selector literals are the key.
|
|
266
|
-
|
|
283
|
+
...actionSequence(blankComments(body.join("\n"), spec), spec.language),
|
|
267
284
|
};
|
|
268
285
|
let requests = 0;
|
|
269
286
|
for (let k = 0; k < body.length; k++) {
|
|
@@ -50,8 +50,9 @@ export interface UtilsVerifyResult {
|
|
|
50
50
|
* a helper that issues an SDK request is judged by the api rules regardless. See
|
|
51
51
|
* helperFamilyOf. */
|
|
52
52
|
fallbackFamily: HelperFamilySpec;
|
|
53
|
-
/** Advisory (does not affect `ok`): inline request calls
|
|
54
|
-
*
|
|
53
|
+
/** Advisory (does not affect `ok`): inline request calls (integration) or inline
|
|
54
|
+
* action sequences (seeded UI) in sibling generated tests that a utils helper
|
|
55
|
+
* already wraps — see findSiblingInlineCallSites. */
|
|
55
56
|
inlineCallSites: InlineCallSite[];
|
|
56
57
|
/** Advisory (does not affect `ok`): operations written in two or more sibling
|
|
57
58
|
* generated tests (this one included) that NO utils helper wraps — the extraction
|
|
@@ -7,10 +7,12 @@ import { typecheckDelivered } from "./typecheck.js";
|
|
|
7
7
|
import { blankComments, blankCommentsAndStrings, countAssertions, hasPageErrorGuard, parseUtilsFile, } from "./parse.js";
|
|
8
8
|
import { escapeRegExp } from "../regex.js";
|
|
9
9
|
import { detectGitRoot } from "../gitStaging.js";
|
|
10
|
-
import { isModularizeFirstTarget } from "../reuseRouting.js";
|
|
10
|
+
import { inlineCallSiteFamily, isModularizeFirstTarget, } from "../reuseRouting.js";
|
|
11
|
+
import { describeActionKey } from "./action-key.js";
|
|
11
12
|
import { findDuplicateBodyReach, findUnreachableBodies } from "./body-reach.js";
|
|
12
13
|
import { misnamedNewModules } from "./module-name.js";
|
|
13
14
|
import { additiveChanges, locateInHouseModules, resolveInHouseCalls, } from "./in-house.js";
|
|
15
|
+
import { findSiblingInlineActionSites } from "./action-sites.js";
|
|
14
16
|
import { findSiblingInlineCallSites, findUnextractedDuplicates, generatedSiblings, } from "./call-sites.js";
|
|
15
17
|
import { parseUtilsAllows, } from "./allow.js";
|
|
16
18
|
import { describeFixedSleeps, fixedSleepReplacement } from "./fixed-sleep.js";
|
|
@@ -633,17 +635,24 @@ export async function verifyUtils(params) {
|
|
|
633
635
|
});
|
|
634
636
|
result.ok =
|
|
635
637
|
result.violations.length === 0 && result.malformedAllows.length === 0;
|
|
636
|
-
//
|
|
637
|
-
//
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
if ((params.testType ?? "").toLowerCase() === "integration") {
|
|
638
|
+
// The gate the prompt renders the rule on: one predicate, so the advisory can
|
|
639
|
+
// never describe a rewiring the prompt did not ask for.
|
|
640
|
+
const scanFamily = inlineCallSiteFamily(params.testType, spec.language);
|
|
641
|
+
if (scanFamily) {
|
|
641
642
|
try {
|
|
642
|
-
result.inlineCallSites =
|
|
643
|
+
result.inlineCallSites =
|
|
644
|
+
scanFamily === "api"
|
|
645
|
+
? await findSiblingInlineCallSites(params.testFile, utilsFiles, allHelpers, spec, siblings)
|
|
646
|
+
: await findSiblingInlineActionSites(params.testFile, helpersByFile, spec, siblings);
|
|
643
647
|
}
|
|
644
648
|
catch {
|
|
645
649
|
/* advisory only — never fails a verify */
|
|
646
650
|
}
|
|
651
|
+
}
|
|
652
|
+
// Integration only — `contract`, `load` and an absent type also resolve to the API
|
|
653
|
+
// family, but the reuse flow never targets them.
|
|
654
|
+
const integration = (params.testType ?? "").toLowerCase() === "integration";
|
|
655
|
+
if (integration) {
|
|
647
656
|
try {
|
|
648
657
|
result.unextractedDuplicates = await findUnextractedDuplicates(params.testFile, utilsFiles, spec, siblings);
|
|
649
658
|
}
|
|
@@ -953,14 +962,3 @@ incumbents = new Set()) {
|
|
|
953
962
|
}
|
|
954
963
|
return out;
|
|
955
964
|
}
|
|
956
|
-
/** `click empty-cart-btn; fill qty` → `click \`empty-cart-btn\`; fill \`qty\`` — the
|
|
957
|
-
* key as the agent reads it, selectors quoted so a `#id` never reads as prose. */
|
|
958
|
-
function describeActionKey(key) {
|
|
959
|
-
return key
|
|
960
|
-
.split("; ")
|
|
961
|
-
.map((step) => {
|
|
962
|
-
const at = step.indexOf(" ");
|
|
963
|
-
return `${step.slice(0, at)} \`${step.slice(at + 1)}\``;
|
|
964
|
-
})
|
|
965
|
-
.join("; ");
|
|
966
|
-
}
|