@skyramp/mcp 0.3.8 → 0.4.0-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/commands/commandLibrary.d.ts +1 -1
- package/build/commands/commandLibrary.js +3 -3
- package/build/commands/recommendTestsAndExecuteCommand.d.ts +1 -1
- package/build/commands/recommendTestsAndExecuteCommand.js +35 -20
- package/build/commands/testThisEndpointCommand.js +35 -19
- package/build/index.js +9 -3
- package/build/playwright/blueprintDigest.d.ts +15 -0
- package/build/playwright/blueprintDigest.js +152 -0
- package/build/playwright/blueprintDigestStore.d.ts +31 -0
- package/build/playwright/blueprintDigestStore.js +117 -0
- package/build/playwright/registerPlaywrightTools.js +60 -12
- package/build/playwright/traceRecordingPrompt.js +8 -7
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +9 -8
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +24 -2
- package/build/prompts/promptAssets.d.ts +20 -0
- package/build/prompts/promptAssets.js +55 -0
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +19 -5
- package/build/prompts/test-maintenance/actionsInstructions.d.ts +4 -0
- package/build/prompts/test-maintenance/actionsInstructions.js +14 -2
- package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +0 -10
- package/build/prompts/test-maintenance/drift-analysis-prompt.js +2 -11
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +8 -4
- package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +5 -22
- package/build/prompts/test-recommendation/diffExecutionPlan.js +37 -465
- package/build/prompts/test-recommendation/recommendationSections.d.ts +7 -17
- package/build/prompts/test-recommendation/recommendationSections.js +67 -309
- package/build/prompts/test-recommendation/recommendationShared.d.ts +19 -47
- package/build/prompts/test-recommendation/recommendationShared.js +49 -155
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.d.ts +0 -5
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +10 -153
- package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +2 -29
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +32 -457
- package/build/prompts/testbot/planDeclarations.d.ts +6 -0
- package/build/prompts/testbot/planDeclarations.js +9 -0
- package/build/prompts/testbot/testbot-prompts.d.ts +8 -0
- package/build/prompts/testbot/testbot-prompts.js +256 -381
- package/build/recommendation/answers.d.ts +35 -0
- package/build/recommendation/answers.js +96 -0
- package/build/recommendation/registerPlan.d.ts +49 -0
- package/build/recommendation/registerPlan.js +117 -0
- package/build/recommendation/runVerifiers.d.ts +10 -0
- package/build/recommendation/runVerifiers.js +49 -0
- package/build/recommendation/subjectStep.d.ts +42 -0
- package/build/recommendation/subjectStep.js +86 -0
- package/build/recommendation/types.d.ts +163 -0
- package/build/recommendation/types.js +20 -0
- package/build/recommendation/verifierContracts.d.ts +382 -0
- package/build/recommendation/verifierContracts.js +263 -0
- package/build/recommendation/verifiers/changedFile.d.ts +2 -0
- package/build/recommendation/verifiers/changedFile.js +82 -0
- package/build/recommendation/verifiers/citedPath.d.ts +12 -0
- package/build/recommendation/verifiers/citedPath.js +35 -0
- package/build/recommendation/verifiers/coverage.d.ts +7 -0
- package/build/recommendation/verifiers/coverage.js +617 -0
- package/build/recommendation/verifiers/deliveredMatchesPlan.d.ts +11 -0
- package/build/recommendation/verifiers/deliveredMatchesPlan.js +33 -0
- package/build/recommendation/verifiers/endpointGrounded.d.ts +17 -0
- package/build/recommendation/verifiers/endpointGrounded.js +128 -0
- package/build/recommendation/verifiers/existingCoverage.d.ts +6 -0
- package/build/recommendation/verifiers/existingCoverage.js +51 -0
- package/build/recommendation/verifiers/expectedOutcome.d.ts +31 -0
- package/build/recommendation/verifiers/expectedOutcome.js +105 -0
- package/build/recommendation/verifiers/removedElementGuarded.d.ts +2 -0
- package/build/recommendation/verifiers/removedElementGuarded.js +57 -0
- package/build/recommendation/verifiers/reportedCategory.d.ts +26 -0
- package/build/recommendation/verifiers/reportedCategory.js +84 -0
- package/build/recommendation/verifiers/screenRoute.d.ts +10 -0
- package/build/recommendation/verifiers/screenRoute.js +118 -0
- package/build/recommendation/verifiers/statedDifference.d.ts +6 -0
- package/build/recommendation/verifiers/statedDifference.js +140 -0
- package/build/recommendation/verifiers/uiElementGrounded.d.ts +7 -0
- package/build/recommendation/verifiers/uiElementGrounded.js +318 -0
- package/build/resources/analysisResources.js +1 -114
- package/build/resources/testbotResource.js +23 -13
- package/build/services/ModularizationService.js +2 -1
- package/build/services/TestDiscoveryService.d.ts +3 -72
- package/build/services/TestDiscoveryService.js +10 -303
- package/build/services/containerEnv.d.ts +1 -1
- package/build/services/containerEnv.js +12 -0
- package/build/skills/fixTestImportErrorsSkill.d.ts +13 -0
- package/build/skills/fixTestImportErrorsSkill.js +20 -0
- package/build/toolNames.d.ts +1 -0
- package/build/toolNames.js +1 -0
- package/build/tools/code-refactor/enhanceAssertionsTool.js +3 -3
- package/build/tools/code-refactor/modularizationTool.js +2 -1
- package/build/tools/executeSkyrampTestTool.d.ts +80 -0
- package/build/tools/executeSkyrampTestTool.js +246 -19
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +6 -0
- package/build/tools/generate-tests/generateContractRestTool.js +3 -3
- package/build/tools/generate-tests/planGuard.d.ts +2 -2
- package/build/tools/generate-tests/planGuard.js +78 -18
- package/build/tools/one-click/oneClickTool.d.ts +0 -1
- package/build/tools/one-click/oneClickTool.js +0 -5
- package/build/tools/submitReportTool.d.ts +48 -42
- package/build/tools/submitReportTool.js +576 -193
- package/build/tools/test-management/actionsTool.js +72 -4
- package/build/tools/test-management/analyzeChangesTool.d.ts +144 -48
- package/build/tools/test-management/analyzeChangesTool.js +212 -1219
- package/build/tools/test-management/analyzeTestHealthTool.js +13 -24
- package/build/tools/test-management/index.d.ts +1 -0
- package/build/tools/test-management/index.js +1 -0
- package/build/tools/test-management/registerTestPlanTool.d.ts +795 -172
- package/build/tools/test-management/registerTestPlanTool.js +609 -542
- package/build/tools/test-management/resolveScreenTool.d.ts +75 -0
- package/build/tools/test-management/resolveScreenTool.js +289 -0
- package/build/types/BlueprintDigest.d.ts +34 -0
- package/build/types/BlueprintDigest.js +1 -0
- package/build/types/RepositoryAnalysis.d.ts +20 -1559
- package/build/types/RepositoryAnalysis.js +2 -58
- package/build/types/StepMethod.d.ts +40 -0
- package/build/types/StepMethod.js +77 -0
- package/build/types/TestAnalysis.d.ts +12 -0
- package/build/types/TestExecution.d.ts +4 -0
- package/build/types/TestRecommendation.d.ts +24 -24
- package/build/types/TestRecommendation.js +91 -89
- package/build/types/TestbotPromptOptions.d.ts +0 -4
- package/build/types/TestbotReport.d.ts +64 -2
- package/build/utils/AnalysisStateManager.d.ts +79 -113
- package/build/utils/AnalysisStateManager.js +147 -57
- package/build/utils/assertion-verify/api-shared-lints.js +1 -1
- package/build/utils/assertion-verify/metrics.js +85 -36
- package/build/utils/assertion-verify/ui-lints.d.ts +0 -5
- package/build/utils/assertion-verify/ui-lints.js +32 -0
- package/build/utils/branchDiff.d.ts +63 -31
- package/build/utils/branchDiff.js +242 -94
- package/build/utils/containedPath.d.ts +18 -0
- package/build/utils/containedPath.js +73 -0
- package/build/utils/dartRouteExtractor.d.ts +18 -34
- package/build/utils/dartRouteExtractor.js +101 -173
- package/build/utils/featureFlags.d.ts +12 -0
- package/build/utils/featureFlags.js +14 -0
- package/build/utils/frontendSelectors.d.ts +48 -27
- package/build/utils/frontendSelectors.js +241 -80
- package/build/utils/pathMatching.d.ts +2 -4
- package/build/utils/pathMatching.js +2 -4
- package/build/utils/planMatchKeys.d.ts +38 -47
- package/build/utils/planMatchKeys.js +143 -81
- package/build/utils/rebaselineSnapshots.d.ts +24 -0
- package/build/utils/rebaselineSnapshots.js +65 -0
- package/build/utils/removedUiElements.d.ts +22 -0
- package/build/utils/removedUiElements.js +106 -0
- package/build/utils/reportVerification.d.ts +2 -6
- package/build/utils/reportVerification.js +61 -2
- package/build/utils/screenRoutes.d.ts +66 -0
- package/build/utils/screenRoutes.js +727 -0
- package/build/utils/sourceRouteExtractor.js +320 -112
- package/build/utils/testFileClassification.d.ts +11 -2
- package/build/utils/testFileClassification.js +44 -2
- package/build/utils/testFixtures.d.ts +5 -0
- package/build/utils/testFixtures.js +13 -0
- package/build/utils/utils.d.ts +0 -1
- package/build/utils/utils.js +0 -11
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +12 -12
- package/node_modules/playwright/lib/mcp/skyramp/assertHiddenTool.js +56 -0
- package/node_modules/playwright/lib/mcp/skyramp/assertTool.js +2 -1
- package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +10 -0
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +4 -1
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +160 -1
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +4 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +1 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderSignalProcessor.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder.js +5 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.-Id052Lr.js → index.B7KbSQcC.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/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderSignalProcessor.ts +7 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder.ts +6 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +4 -3
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/plugin.json +6 -0
- package/plugin/prompts/declaring-a-plan.md +20 -0
- package/plugin/prompts/generate-tests/context-fetching.md +4 -0
- package/plugin/prompts/generate-tests/execution-plan.md +63 -0
- package/plugin/prompts/generate-tests/generation.md +108 -0
- package/plugin/prompts/generate-tests/path-parameters.md +1 -0
- package/plugin/prompts/generate-tests/reasoning-protocol.md +17 -0
- package/plugin/prompts/generate-tests/tool-workflow-variants.md +61 -0
- package/plugin/prompts/generate-tests/tool-workflows.md +65 -0
- package/plugin/prompts/plan-tests.md +42 -0
- package/plugin/prompts/testbot-task1.md +82 -0
- package/plugin/skills/fix-test-import-errors/SKILL.md +98 -0
- package/build/prompts/test-recommendation/analysisOutputPrompt.d.ts +0 -84
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +0 -369
- package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +0 -7
- package/build/prompts/test-recommendation/fullRepoCatalog.js +0 -283
- package/build/prompts/test-recommendation/scopeAssessment.d.ts +0 -81
- package/build/prompts/test-recommendation/scopeAssessment.js +0 -359
- package/build/recommendation/budgeters/diversityBalancedBudgeter.d.ts +0 -7
- package/build/recommendation/budgeters/diversityBalancedBudgeter.js +0 -105
- package/build/recommendation/budgeters/fixedNBudgeter.d.ts +0 -7
- package/build/recommendation/budgeters/fixedNBudgeter.js +0 -11
- package/build/recommendation/budgeters/shared.d.ts +0 -32
- package/build/recommendation/budgeters/shared.js +0 -246
- package/build/recommendation/discriminators.d.ts +0 -37
- package/build/recommendation/discriminators.js +0 -379
- package/build/recommendation/diversity.d.ts +0 -47
- package/build/recommendation/diversity.js +0 -101
- package/build/recommendation/planRanker.d.ts +0 -65
- package/build/recommendation/planRanker.js +0 -83
- package/build/recommendation/testFixtures.d.ts +0 -25
- package/build/recommendation/testFixtures.js +0 -45
- package/build/types/FrontendIntegration.d.ts +0 -28
- package/build/types/FrontendIntegration.js +0 -22
- package/build/types/Recommendation.d.ts +0 -146
- package/build/types/Recommendation.js +0 -74
- package/build/utils/changedRoutes.d.ts +0 -29
- package/build/utils/changedRoutes.js +0 -87
- package/build/utils/frontendIntegration.d.ts +0 -9
- package/build/utils/frontendIntegration.js +0 -243
- package/build/utils/importerHop.d.ts +0 -135
- package/build/utils/importerHop.js +0 -489
- package/build/utils/pathAffinityClassification.d.ts +0 -49
- package/build/utils/pathAffinityClassification.js +0 -180
- package/build/utils/pythonMountPrefixes.d.ts +0 -25
- package/build/utils/pythonMountPrefixes.js +0 -347
- package/build/utils/repoScanner.d.ts +0 -34
- package/build/utils/repoScanner.js +0 -300
- package/build/utils/routeParsers.d.ts +0 -95
- package/build/utils/routeParsers.js +0 -951
- package/build/utils/scenarioDrafting.d.ts +0 -92
- package/build/utils/scenarioDrafting.js +0 -951
- package/build/utils/subjectEndpoints.d.ts +0 -19
- package/build/utils/subjectEndpoints.js +0 -98
- package/build/utils/uiPageEnumerator.d.ts +0 -172
- package/build/utils/uiPageEnumerator.js +0 -474
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import { MAX_RECOMMENDATIONS, MAX_TESTS_TO_GENERATE } from "./recommendationSections.js";
|
|
4
|
-
// .vue and .svelte cannot be route handlers in any framework — always frontend.
|
|
5
|
-
// Stylesheets cannot be either (SKYR-4292): `packages/excalidraw/css/styles.scss`
|
|
6
|
-
// classified as backend because `css/` is not in FRONTEND_DIR_PATTERN, so a
|
|
7
|
-
// stylesheet-only PR took the zero-classified backend branch and never saw the
|
|
8
|
-
// changed-behavior rule.
|
|
9
|
-
const ALWAYS_FRONTEND_EXT = /\.(vue|svelte|css|scss|less)$/i;
|
|
10
|
-
// .dart is always frontend in Flutter projects (no server-side Dart web framework
|
|
11
|
-
// in scope). Gated on hasFlutterPubspec so non-Flutter Dart files (rare, but
|
|
12
|
-
// possible — e.g. Dart-on-server backends) don't accidentally classify as UI.
|
|
13
|
-
const FLUTTER_DART_EXT = /\.dart$/i;
|
|
14
|
-
// .tsx/.jsx are usually UI components but can be Next.js API handlers (pages/api/*.tsx).
|
|
15
|
-
const LIKELY_FRONTEND_EXT = /\.(tsx|jsx)$/i;
|
|
16
|
-
// Ambiguous extensions need directory context to distinguish frontend from backend.
|
|
17
|
-
const AMBIGUOUS_FRONTEND_PATTERN = /\.(tsx?|jsx?|html?|erb|jsp|asp|jinja2?|twig)$/i;
|
|
18
|
-
// /api/ always signals backend — even .tsx files (e.g. Next.js pages/api/*.tsx handlers).
|
|
19
|
-
const STRICT_API_DIR_PATTERN = /\/(api)\//i;
|
|
20
|
-
// Framework route dirs that contain React page components in modern full-stack apps
|
|
21
|
-
// (Medusa Admin, Remix, React Router v6). These only block non-.tsx/.jsx files —
|
|
22
|
-
// a .tsx file in routes/ is a page component, not a route handler.
|
|
23
|
-
const FRAMEWORK_ROUTE_DIR_PATTERN = /\/(routes?|controllers?|routers?|handlers?|endpoints?|server)\//i;
|
|
24
|
-
const FRONTEND_DIR_PATTERN = /(^|\/)(components?|pages?|views?|layouts?|app|src\/app|frontend|client|public|styles?|templates?)\//i;
|
|
25
|
-
// A .json (or .yaml/.yml) file nested under an i18n/locale directory holds only
|
|
26
|
-
// user-facing copy — frontend regardless of nesting depth (subject to the
|
|
27
|
-
// /api/ check above), unlike generic JSON (package.json, tsconfig.json, data
|
|
28
|
-
// fixtures) which needs no such carve-out since it's never under one of these
|
|
29
|
-
// directory names.
|
|
30
|
-
const I18N_LOCALE_FILE_PATTERN = /(^|\/)(i18n|locales?|lang|translations?)\/.*\.(json|ya?ml)$/i;
|
|
31
|
-
/**
|
|
32
|
-
* Returns true if the file path looks like i18n/locale content (a JSON/YAML
|
|
33
|
-
* file under an i18n/, locale(s)/, lang/, or translation(s)/ directory).
|
|
34
|
-
* Exported so callers outside the frontend/backend classification itself
|
|
35
|
-
* (e.g. the production-importer integration check, which greps for static
|
|
36
|
-
* quoted imports) can special-case these files: real i18n loaders commonly
|
|
37
|
-
* fetch locale content by a templated runtime path rather than a static
|
|
38
|
-
* import, so the grep-based check would otherwise misreport them as
|
|
39
|
-
* unintegrated dead code (SKYR-3977).
|
|
40
|
-
*/
|
|
41
|
-
export function isI18nLocaleFile(filePath) {
|
|
42
|
-
return I18N_LOCALE_FILE_PATTERN.test(filePath);
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Returns true if the repository root's `pubspec.yaml` declares a Flutter
|
|
46
|
-
* SDK dependency (i.e., a `flutter:` key with `sdk: flutter` under
|
|
47
|
-
* `dependencies:`). This is the load-bearing signal that the project
|
|
48
|
-
* is a Flutter UI app or package, NOT a pure Dart server / CLI / library
|
|
49
|
-
* (those have a `pubspec.yaml` too but no `sdk: flutter` dep).
|
|
50
|
-
*
|
|
51
|
-
* Used as the gate for treating `.dart` files as frontend in
|
|
52
|
-
* `isFrontendFile`. Pure-Dart-backend repos correctly fall through to
|
|
53
|
-
* the existing classifier behaviour (no `.dart` recognition).
|
|
54
|
-
*
|
|
55
|
-
* Synchronous + best-effort: returns false on any read/parse error.
|
|
56
|
-
* Cheap because pubspec.yaml is small and we only call this once per
|
|
57
|
-
* tool invocation (computed at the budget-driving boundary, then passed
|
|
58
|
-
* through as an option).
|
|
59
|
-
*/
|
|
60
|
-
/**
|
|
61
|
-
* Common subdir locations for `pubspec.yaml` in real Flutter repos:
|
|
62
|
-
* - root (single-app — birdle, customer SPA)
|
|
63
|
-
* - app/, mobile/, frontend/, web/ (mixed-stack repos)
|
|
64
|
-
* - apps/<name>/, packages/<name>/ (monorepos — depth-2 walked)
|
|
65
|
-
*
|
|
66
|
-
* We stop at the first pubspec we find that declares `sdk: flutter`. Pure-Dart
|
|
67
|
-
* subprojects (e.g. a CLI tool in `tools/`) are correctly skipped because their
|
|
68
|
-
* pubspec doesn't have the Flutter SDK line.
|
|
69
|
-
*
|
|
70
|
-
* Less-common shapes that are NOT auto-discovered: `services/mobile/`,
|
|
71
|
-
* `clients/<name>/`, etc. If a customer's repo uses one of those, add the
|
|
72
|
-
* parent dir here or tell them to symlink `pubspec.yaml` at the root.
|
|
73
|
-
*/
|
|
74
|
-
const PUBSPEC_SEARCH_DIRS = [
|
|
75
|
-
".",
|
|
76
|
-
"app",
|
|
77
|
-
"mobile",
|
|
78
|
-
"frontend",
|
|
79
|
-
"web",
|
|
80
|
-
"client",
|
|
81
|
-
];
|
|
82
|
-
const PUBSPEC_MONOREPO_PARENTS = ["apps", "packages"];
|
|
83
|
-
export function hasFlutterSdkDep(repositoryPath) {
|
|
84
|
-
for (const subdir of PUBSPEC_SEARCH_DIRS) {
|
|
85
|
-
if (checkPubspec(path.join(repositoryPath, subdir, "pubspec.yaml"))) {
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
// Monorepo shape: apps/<name>/pubspec.yaml, packages/<name>/pubspec.yaml.
|
|
90
|
-
// One level of fan-out — enough for typical Flutter monorepos, cheap to walk.
|
|
91
|
-
for (const parent of PUBSPEC_MONOREPO_PARENTS) {
|
|
92
|
-
const parentPath = path.join(repositoryPath, parent);
|
|
93
|
-
let entries;
|
|
94
|
-
try {
|
|
95
|
-
entries = fs.readdirSync(parentPath, { withFileTypes: true });
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
for (const entry of entries) {
|
|
101
|
-
if (!entry.isDirectory())
|
|
102
|
-
continue;
|
|
103
|
-
if (checkPubspec(path.join(parentPath, entry.name, "pubspec.yaml"))) {
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
function checkPubspec(pubspecPath) {
|
|
111
|
-
try {
|
|
112
|
-
if (!fs.existsSync(pubspecPath))
|
|
113
|
-
return false;
|
|
114
|
-
const content = fs.readFileSync(pubspecPath, "utf8");
|
|
115
|
-
// Detect the `flutter:` SDK dep — proper YAML parsing would be overkill.
|
|
116
|
-
// The canonical pattern is:
|
|
117
|
-
// dependencies:
|
|
118
|
-
// flutter:
|
|
119
|
-
// sdk: flutter
|
|
120
|
-
// The `sdk: flutter` line under a `flutter:` key is unique to Flutter
|
|
121
|
-
// projects; pure-Dart projects use `sdk: dart` or no sdk: line at all.
|
|
122
|
-
return /^\s+sdk:\s*flutter\s*$/m.test(content);
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Returns true if the file path is a frontend file.
|
|
130
|
-
*
|
|
131
|
-
* Five tiers:
|
|
132
|
-
* 1. .vue / .svelte — always frontend; these file types cannot be route handlers.
|
|
133
|
-
* 2. .dart — always frontend IFF `hasFlutterSdkDep` is true (Flutter project).
|
|
134
|
-
* Sits above `STRICT_API_DIR_PATTERN` because there's no server-side Dart
|
|
135
|
-
* web framework in scope when the Flutter SDK dep is present.
|
|
136
|
-
* 3. i18n/locale JSON/YAML (e.g. locales/en/common.json) — frontend UNLESS in
|
|
137
|
-
* an API/backend directory (e.g. an i18n content API under api/), since
|
|
138
|
-
* unlike tiers 1-2 a locale file CAN be served by a backend route (SKYR-3977).
|
|
139
|
-
* 4. .tsx / .jsx — frontend unless in an API/backend directory (e.g. pages/api/).
|
|
140
|
-
* 5. Ambiguous extensions (.ts, .js, .css, .html, …) — require a recognised
|
|
141
|
-
* frontend directory AND must not be in an API/backend directory.
|
|
142
|
-
*
|
|
143
|
-
* The `hasFlutterSdkDep` opt is passed in by the budget-driving caller
|
|
144
|
-
* (analyzeChangesTool) which has repository context.
|
|
145
|
-
* Pure-path callers (test-recommendation-prompt, analysisOutputPrompt,
|
|
146
|
-
* scopeAssessment's own buildScopeAssessmentSection) default to false —
|
|
147
|
-
* they don't drive the budget.
|
|
148
|
-
*/
|
|
149
|
-
export function isFrontendFile(filePath, { hasFlutterSdkDep = false } = {}) {
|
|
150
|
-
if (ALWAYS_FRONTEND_EXT.test(filePath))
|
|
151
|
-
return true;
|
|
152
|
-
if (hasFlutterSdkDep && FLUTTER_DART_EXT.test(filePath))
|
|
153
|
-
return true;
|
|
154
|
-
// /api/ always means backend regardless of extension (Next.js API routes use .tsx).
|
|
155
|
-
// Must run before I18N_LOCALE_FILE_PATTERN — unlike .vue/.svelte/.dart, a locale
|
|
156
|
-
// JSON/YAML file CAN be served by a backend route (e.g. an i18n content API), so
|
|
157
|
-
// it doesn't get the same always-frontend exemption as those extensions.
|
|
158
|
-
if (STRICT_API_DIR_PATTERN.test(filePath))
|
|
159
|
-
return false;
|
|
160
|
-
if (isI18nLocaleFile(filePath))
|
|
161
|
-
return true;
|
|
162
|
-
// routes/, controllers/ etc. block .ts/.js but not .tsx/.jsx — Medusa Admin and
|
|
163
|
-
// React Router place page components under routes/, not API handlers.
|
|
164
|
-
if (!LIKELY_FRONTEND_EXT.test(filePath) && FRAMEWORK_ROUTE_DIR_PATTERN.test(filePath))
|
|
165
|
-
return false;
|
|
166
|
-
if (LIKELY_FRONTEND_EXT.test(filePath))
|
|
167
|
-
return true;
|
|
168
|
-
return AMBIGUOUS_FRONTEND_PATTERN.test(filePath) && FRONTEND_DIR_PATTERN.test(filePath);
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Returns true if the file path looks like a test file rather than UI source.
|
|
172
|
-
*
|
|
173
|
-
* Catches:
|
|
174
|
-
* - Skyramp-generated tests (`*_test.ts`, `*_smoke.ts`, `*_contract.ts`,
|
|
175
|
-
* `*_fuzz.ts`, `*_integration.ts`, `*_load.ts`, `*_e2e.ts`, `*_ui.ts`)
|
|
176
|
-
* - Skyramp scenario files (`scenario_*.json`)
|
|
177
|
-
* - Conventional Vitest/Jest/Playwright spec naming
|
|
178
|
-
* (`*.spec.{ts,tsx,js,jsx}`, `*.test.{ts,tsx,js,jsx}`)
|
|
179
|
-
* - Files inside `__tests__/` directories (Jest convention)
|
|
180
|
-
*
|
|
181
|
-
* Used by callers that want to filter test files OUT of frontend-source
|
|
182
|
-
* processing — `isFrontendFile` returns true for `.spec.ts` under a
|
|
183
|
-
* frontend directory because the rule is "tier-3 ambiguous + frontend dir,"
|
|
184
|
-
* but those tests aren't UI source we'd want to ground recommendations in.
|
|
185
|
-
*/
|
|
186
|
-
export function isTestFile(filePath) {
|
|
187
|
-
return (/(?:_test|_smoke|_contract|_fuzz|_integration|_load|_e2e|_ui)\.[^/]+$/.test(filePath) ||
|
|
188
|
-
/scenario_[^/]+\.json$/.test(filePath) ||
|
|
189
|
-
// Include the `.e2e.` dot form so Playwright/Cypress specs discovery treats as UI tests
|
|
190
|
-
// (UI_TEST_EXT matches `.(test|spec|e2e).`) are recognized here too — e.g. `login.e2e.ts`.
|
|
191
|
-
/\.(spec|test|e2e)\.(tsx?|jsx?)$/.test(filePath) ||
|
|
192
|
-
/(?:^|\/)__tests__\//.test(filePath));
|
|
193
|
-
}
|
|
194
|
-
// ── LLM scope assessment ──────────────────────────────────────────────────────
|
|
195
|
-
/**
|
|
196
|
-
* The changed-behavior budget rule, shared by every branch that can see a frontend
|
|
197
|
-
* diff (SKYR-4292).
|
|
198
|
-
*
|
|
199
|
-
* This replaces the zero-new-surface abstention override (SKYR-4099). That override
|
|
200
|
-
* named three abstain categories — cosmetic, deletion-only, and modification of an
|
|
201
|
-
* already-covered surface — and its only sanctioned action was a Budget Plan of 0. On
|
|
202
|
-
* a 20-change UI-only diff under a 20 budget (Wipro PR #24) the agent generalized it
|
|
203
|
-
* into a graded cut to 3, classified option additions no test asserted (Siebel,
|
|
204
|
-
* Windows, .NET, GO, MQ) as "modification of an already-covered surface" because the
|
|
205
|
-
* existing specs reached the wizard page, and shipped zero recommendations for the
|
|
206
|
-
* rest. The taxonomy is gone: every observable behavior the diff changes gets a
|
|
207
|
-
* candidate, coverage means an existing test ASSERTS the changed value (not that it
|
|
208
|
-
* reaches the component), and the total tracks the count of changed behaviors — the
|
|
209
|
-
* ranker in `skyramp_register_test_plan` trims to the ceiling and reports the drops.
|
|
210
|
-
*
|
|
211
|
-
* Backend-only diffs do not render this: their GENERATE list is server pre-ranked
|
|
212
|
-
* from classified endpoints (and SKYR-3820 handles a zero-classified diff), whereas a
|
|
213
|
-
* frontend GENERATE list is placeholders the agent must fill from the diff itself.
|
|
214
|
-
*/
|
|
215
|
-
function changedBehaviorSection(effectiveGenerate, budgetPlanTiming = "") {
|
|
216
|
-
return `**Fill the budget from changed behavior:** The ceiling above is the most this run may plan, not a quota to reach or a default to cut. Enumerate every observable behavior this diff changes — each option value added, removed, renamed or reordered; each label, element, route, state, validation rule or computed value that renders or behaves differently — and give each one its own candidate. Treat no change as too trivial to test: a changed option value that no test asserts is a test. A changed style value counts too — a spacing, size, color, radius, font weight, icon size or class name that now renders differently is a changed behavior; its candidate asserts the new computed style or class on that element, or records a \`browser_visual_snapshot\` of it. A removed option, element or feature is a changed behavior too: its candidate asserts the removed thing is absent from the page where it used to render — the removal is what this PR ships, so the removal is what gets tested.
|
|
217
|
-
|
|
218
|
-
A changed behavior is something a user can observe differently after this diff. Code the diff deletes, dedupes or simplifies without changing what renders is not a changed behavior — its coverage is maintenance of the tests that reach it — and a pre-existing defect you notice nearby is an \`issuesFound\` entry, not a slot in this budget. Only a comment, whitespace or equivalent-value edit (\`0px\` → \`0\`) changes nothing observable.
|
|
219
|
-
|
|
220
|
-
A changed behavior is covered only when an existing test asserts the changed value (including after a maintenance UPDATE you recorded). A test that merely reaches the component does not cover it — that change still needs its own candidate.
|
|
221
|
-
|
|
222
|
-
- Fewer changed behaviors than the ceiling → your Budget Plan total is that count, split as generate first (up to ${effectiveGenerate}) and the remainder additional. Do not pad with tests of behavior this diff did not change.
|
|
223
|
-
- More changed behaviors than the ceiling → keep every one in the candidate list you register; \`skyramp_register_test_plan\` trims to the ceiling and reports what it dropped.
|
|
224
|
-
|
|
225
|
-
**A requirement the diff did NOT implement counts too.** The enumeration above is of behaviors the diff changed; a \`requirement_conflict\` is the opposite — the PR title/description (or a requirements file it references) states a requirement and the diff failed to implement it. Give it its own candidate anyway, category \`requirement_conflict\`, asserting what the description requires so that it fails on the current code. When the description says a feature was removed and it still renders, the absence assertion IS that test — it fails today, which is the finding. It counts toward your total like any changed behavior, and it is the one candidate a total of zero must never swallow: abstaining past a stated requirement reports the code back to the author as if it were the requirement.
|
|
226
|
-
|
|
227
|
-
Never reduce the total below the number of changed behaviors you found. State your Budget Plan in the canonical format${budgetPlanTiming} — \`Budget Plan: <total> total (<generate> generate + <additional> additional), <ui_pct>% UI/E2E\` — and use those numbers throughout the rest of the prompt.`;
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Builds the PR scope assessment section.
|
|
231
|
-
*
|
|
232
|
-
* When `precomputedUIPct` is provided (0 = backend-only, 100 = UI-only) the server has
|
|
233
|
-
* already determined the split unambiguously, so Steps A–C are skipped. Backend-only
|
|
234
|
-
* (0) renders a single Budget Plan line; UI-only (100) renders that line plus the
|
|
235
|
-
* changed-behavior budget rule (SKYR-4292), because a frontend GENERATE list is
|
|
236
|
-
* placeholders the agent fills from the diff and the total must track what changed.
|
|
237
|
-
*
|
|
238
|
-
* For mixed PRs (`precomputedUIPct` is undefined, `hasFrontendChanges` is true) skip
|
|
239
|
-
* Steps A–C but keep Step D so the LLM can apply judgment to determine the UI%.
|
|
240
|
-
*
|
|
241
|
-
* Falls back to the full four-step assessment when no precomputed data is available.
|
|
242
|
-
*/
|
|
243
|
-
export function buildScopeAssessmentSection(maxTotal = MAX_RECOMMENDATIONS, maxGenerate = MAX_TESTS_TO_GENERATE, isUIOnly = false,
|
|
244
|
-
/** Server-determined UI/E2E percentage. `undefined` = mixed PR or unknown. */
|
|
245
|
-
precomputedUIPct,
|
|
246
|
-
/** Whether the diff contains frontend files (true ↔ mixed PR when precomputedUIPct is undefined). */
|
|
247
|
-
hasFrontendChanges,
|
|
248
|
-
/**
|
|
249
|
-
* PR-mode: whether the diff classified at least one new/modified/removed endpoint.
|
|
250
|
-
* When false on the backend branch (precomputedUIPct === 0), the Budget Plan
|
|
251
|
-
* defaults to 0 total with a conditional ceiling instead of a fixed mandate —
|
|
252
|
-
* SKYR-3820: a config-only PR must not be pushed to fill the budget with
|
|
253
|
-
* baseline tests for unchanged endpoints. Defaults to true so existing callers
|
|
254
|
-
* keep the unconditional fixed-budget rendering.
|
|
255
|
-
*/
|
|
256
|
-
hasClassifiedApiChanges = true) {
|
|
257
|
-
const effectiveGenerate = Math.min(maxGenerate, maxTotal);
|
|
258
|
-
const additional = Math.max(0, maxTotal - effectiveGenerate);
|
|
259
|
-
// Backend-only diff with zero classified endpoint changes (SKYR-3820): the
|
|
260
|
-
// budget defaults to 0 so the tool output agrees with the testbot prompt's
|
|
261
|
-
// zero-test report path, instead of mandating a fixed total the diff cannot
|
|
262
|
-
// justify. The ceiling stays claimable because a zero-classified diff can be
|
|
263
|
-
// a classification miss on a real behavior change (e.g. a DTO/serializer
|
|
264
|
-
// change the endpoint scanner cannot attribute — SKYR-3855's immich case).
|
|
265
|
-
if (precomputedUIPct === 0 && !hasClassifiedApiChanges) {
|
|
266
|
-
return `### PR Scope Assessment
|
|
267
|
-
Budget Plan: 0 total — no new, modified, or removed endpoints were classified from this diff, and no frontend files changed.
|
|
268
|
-
|
|
269
|
-
With a 0-total Budget Plan: generate zero tests, recommend zero tests, and follow the zero-test report path. Do NOT draft baseline or generic tests for unchanged endpoints to fill a budget — an empty diff surface is a valid, expected outcome.
|
|
270
|
-
|
|
271
|
-
**Exception — claim the ceiling only with evidence:** if your code review of the changed files shows an observable API behavior change the classifier missed (e.g. a DTO/serializer/service change that alters a response shape, a shared library/default-value or business-rule constant change that alters the behavior of an existing, unchanged endpoint (e.g. a default schedule, threshold, or config constant imported by a route handler elsewhere in the codebase), a deployment/config change that newly exposes or removes endpoints, or a schema-defined API contract change — a CRD type/kubebuilder validation marker, GraphQL schema, or gRPC proto edit that adds, removes, or re-validates what the server accepts or returns), raise your Budget Plan to cover exactly those affected endpoints, up to ${maxTotal} total (${effectiveGenerate} generate + ${additional} additional), 0% UI/E2E. Note: repositories whose entire API surface is schema-defined (e.g. a Kubernetes operator serving CRDs through the kube-apiserver) ALWAYS classify zero endpoints — for these, a schema change in the diff IS the endpoint change; evaluate this exception against the schema files instead of concluding there is nothing to test. Similarly, a changed file with zero classified endpoints is not by itself evidence of "no testable surface" — trace what imports the changed export (grep for its name) to check whether it feeds an existing endpoint's behavior before concluding the diff has no test value. **A requirement conflict is also grounds to raise the plan:** if the PR title/description (or a requirements file it references) states a requirement the diff did not implement, claim at least one slot for a \`requirement_conflict\` test asserting that requirement — it fails on the current code, which is the finding. Every test must name the changed file that justifies it. State your raised plan now in the canonical format — \`Budget Plan: <total> total (<generate> generate + <additional> additional), 0% UI/E2E\` — and use those exact numbers throughout the rest of the prompt; the raised generate count is your committed generate count.`;
|
|
272
|
-
}
|
|
273
|
-
// Unambiguous backend-only or UI-only: no LLM counting needed. Backend-only emits just
|
|
274
|
-
// the Budget Plan line; UI-only appends the changed-behavior budget rule (see above).
|
|
275
|
-
if (precomputedUIPct !== undefined) {
|
|
276
|
-
const uiSuffix = precomputedUIPct > 0 ? `, ${precomputedUIPct}% UI/E2E` : "";
|
|
277
|
-
// Backend-only (precomputedUIPct === 0): the server pre-ranked the GENERATE list
|
|
278
|
-
// from classified endpoints, so the numbers are fixed. UI-only: the list is
|
|
279
|
-
// placeholders the agent fills from the diff, so the line is a ceiling and the
|
|
280
|
-
// changed-behavior rule sets the Budget Plan — a fixed "Budget Plan" plus "use
|
|
281
|
-
// these exact numbers" here would contradict that rule (Copilot on mcp#835).
|
|
282
|
-
if (precomputedUIPct === 0) {
|
|
283
|
-
return `### PR Scope Assessment
|
|
284
|
-
Budget Plan: ${maxTotal} total (${effectiveGenerate} generate + ${additional} additional)
|
|
285
|
-
|
|
286
|
-
Use these exact numbers throughout the rest of the prompt.`;
|
|
287
|
-
}
|
|
288
|
-
return `### PR Scope Assessment
|
|
289
|
-
Budget ceiling: ${maxTotal} total (${effectiveGenerate} generate + ${additional} additional)${uiSuffix}
|
|
290
|
-
|
|
291
|
-
${changedBehaviorSection(effectiveGenerate)}`;
|
|
292
|
-
}
|
|
293
|
-
// Mixed PR: server can pre-compute the total but not the UI/E2E split — keep Step D.
|
|
294
|
-
if (hasFrontendChanges) {
|
|
295
|
-
return `### PR Scope Assessment — determine UI% before planning recommendations
|
|
296
|
-
|
|
297
|
-
Budget ceiling: **${maxTotal} total (${effectiveGenerate} generate + ${additional} additional)**
|
|
298
|
-
|
|
299
|
-
${changedBehaviorSection(effectiveGenerate, ", after Step D below sets your UI%")}
|
|
300
|
-
|
|
301
|
-
**Step D — Determine UI vs backend split for your Budget Plan:**
|
|
302
|
-
- Non-UI slots are backend tests; start from file-count ratio for UI%, then apply judgment:
|
|
303
|
-
- Cosmetic CSS/style changes alongside real changes inflate the frontend file count without adding test value → reduce UI%
|
|
304
|
-
- Frontend logic bugs (state management, calculation errors, form validation) in the diff → increase UI% even if few frontend files
|
|
305
|
-
- Frontend component calls a changed backend API → an E2E test covers both sides → count toward UI%
|
|
306
|
-
- Frontend files only in \`__tests__/\` or \`.stories.\` → exclude from the ratio
|
|
307
|
-
|
|
308
|
-
**Append your UI% now** and state your Budget Plan — total and split per the changed-behavior rule above, within the ceiling:
|
|
309
|
-
\`Budget Plan: <total> total (<generate> generate + <additional> additional), <ui_pct>% UI/E2E\`
|
|
310
|
-
|
|
311
|
-
Use those numbers throughout the rest of the prompt.`;
|
|
312
|
-
}
|
|
313
|
-
// Fallback (no diff context — full_repo else-branch or test): full four-step assessment.
|
|
314
|
-
const minTotal = Math.min(effectiveGenerate + 1, maxTotal);
|
|
315
|
-
const minAdditional = Math.max(0, minTotal - effectiveGenerate);
|
|
316
|
-
const baselineFormula = `${effectiveGenerate} (generate) + ${minAdditional} (min additional) = ${minTotal}`;
|
|
317
|
-
const stepD = isUIOnly
|
|
318
|
-
? `**Step D — UI/E2E confirmation (frontend-only PR):**
|
|
319
|
-
This is a frontend-only PR — set **100% UI/E2E** in your Budget Plan.
|
|
320
|
-
Budget generate slots toward directly recording and generating UI tests; budget additional slots toward more UI/E2E flows derived from the changed components.`
|
|
321
|
-
: `**Step D — Determine UI vs backend split:**
|
|
322
|
-
- Backend-only PR (0 frontend files changed or only CSS/copy): **0% UI/E2E** — focus on integration and contract tests
|
|
323
|
-
- Frontend-only PR: **100% UI/E2E** — all tests should be UI/E2E
|
|
324
|
-
- Mixed PR — non-UI slots are backend tests; start from file-count ratio for UI%, then apply judgment:
|
|
325
|
-
- Pure CSS/style changes inflate the frontend file count without adding test value → reduce UI%
|
|
326
|
-
- Frontend logic bugs (state management, calculation errors, form validation) in the diff → increase UI% even if few frontend files
|
|
327
|
-
- Frontend component calls a changed backend API → an E2E test covers both sides → count toward UI%
|
|
328
|
-
- Frontend files only in \`__tests__/\` or \`.stories.\` → exclude from the ratio`;
|
|
329
|
-
return `### PR Scope Assessment — complete this first, before planning any recommendations
|
|
330
|
-
|
|
331
|
-
Read the Changed Files list and endpoint changes above, then work through the four steps below. This determines your **Budget Plan** (total count + backend/frontend split) for the rest of the prompt.
|
|
332
|
-
|
|
333
|
-
**Step A — Classify changed files:**
|
|
334
|
-
Count each type from the diff context (ignore generated test files, lock files, and build artifacts):
|
|
335
|
-
- **Frontend files**: .vue / .svelte / .css / .scss / .less anywhere (always UI). .dart anywhere (always UI in a Flutter project — repo has a \`pubspec.yaml\` with \`sdk: flutter\`). .tsx / .jsx anywhere except in api/, routes/, routers/, controllers/, handlers/, endpoints/, or server/ directories. .ts / .js / .html / .erb / .jsp / .asp / .jinja2 / .twig only when in a frontend directory (components/, pages/, views/, layouts/, app/, frontend/, client/, styles/, templates/).
|
|
336
|
-
- **Backend files**: route handlers, controllers, services, models, API modules, middleware, config with business logic
|
|
337
|
-
- **Non-application** (exclude from test value): README and docs, lock files, CI config with no logic
|
|
338
|
-
|
|
339
|
-
**Step B — Assess semantic complexity (quality over quantity):**
|
|
340
|
-
Weigh changes by their test value, not file count:
|
|
341
|
-
- New API endpoint → HIGH test value: needs happy path + at least one error path (contributes ~2 to budget)
|
|
342
|
-
- Modified endpoint with formula / business logic change → HIGH: edge cases matter (contributes ~1–2)
|
|
343
|
-
- Auth middleware change → CRITICAL: flag for extra security tests regardless of file count
|
|
344
|
-
- Frontend state / validation / calculation logic → HIGH for UI tests even if zero backend endpoints changed
|
|
345
|
-
- Changed style values, labels or copy → each rendered difference is a changed behavior: assert the new value (computed style, class or text) — no change is too trivial to test
|
|
346
|
-
|
|
347
|
-
**Step C — Determine total recommendation count (${minTotal}–${maxTotal}):**
|
|
348
|
-
Start from the baseline formula: *${baselineFormula}*, then adjust:
|
|
349
|
-
- **Scale up** for: critical auth/data-integrity changes (+2), complex multi-step business workflows (+1 each), new endpoints with non-trivial validation (+1 each beyond the formula)
|
|
350
|
-
- **Scale down** for: already well-tested paths confirmed by existing test list, trivial CRUD with no validation
|
|
351
|
-
- **Hard cap**: ${maxTotal}
|
|
352
|
-
|
|
353
|
-
${stepD}
|
|
354
|
-
|
|
355
|
-
**State your Budget Plan now** (one line, before any recommendation):
|
|
356
|
-
\`Budget Plan: <total> total (<generate> generate + <additional> additional), <ui_pct>% UI/E2E\`
|
|
357
|
-
|
|
358
|
-
Use these exact numbers throughout the rest of the prompt.`;
|
|
359
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Budgeter } from "../../types/Recommendation.js";
|
|
2
|
-
/**
|
|
3
|
-
* Budgeter that guarantees cross-test-type coverage in the GENERATE set.
|
|
4
|
-
* `maxGenerate` acts as an upper cap (not a hard N). Fixes the single-type skew
|
|
5
|
-
* that starves contract/error-path tests. Opt-in via strategy selection.
|
|
6
|
-
*/
|
|
7
|
-
export declare const diversityBalancedBudgeter: Budgeter;
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { runBudget } from "./shared.js";
|
|
2
|
-
import { bucketByType, inferScenarioType, isProtectedCandidate, roundRobinFill, } from "../diversity.js";
|
|
3
|
-
const typeOf = (c) => inferScenarioType(c.scenario);
|
|
4
|
-
/**
|
|
5
|
-
* Pick `count` items guaranteeing each present test type at least one slot.
|
|
6
|
-
*
|
|
7
|
-
* Unlike roundRobinByType (which fills ALL protected items first and can
|
|
8
|
-
* starve other types at small N — the SKYR-3879 "0 contract executed" skew),
|
|
9
|
-
* this caps protected occupancy to `count - (numTypes - 1)` so at least one slot
|
|
10
|
-
* per remaining type is always reachable, then:
|
|
11
|
-
* Phase 1 — one item for each still-uncovered present type (by rank), then
|
|
12
|
-
* Phase 2 — round-robin the remainder across types until `count` is reached.
|
|
13
|
-
* Rank order is preserved within each type; degenerate cases match a rank slice.
|
|
14
|
-
*/
|
|
15
|
-
function floorBalancedPick(items, count) {
|
|
16
|
-
if (count <= 0)
|
|
17
|
-
return [];
|
|
18
|
-
if (count >= items.length)
|
|
19
|
-
return items.slice(0, count);
|
|
20
|
-
// Present types in first-appearance (rank) order.
|
|
21
|
-
const typesOrder = [];
|
|
22
|
-
const seen = new Set();
|
|
23
|
-
for (const it of items) {
|
|
24
|
-
const t = typeOf(it);
|
|
25
|
-
if (!seen.has(t)) {
|
|
26
|
-
seen.add(t);
|
|
27
|
-
typesOrder.push(t);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
// Two passes, because "protected" is broader than "is a caught bug".
|
|
31
|
-
//
|
|
32
|
-
// A `bug_caught` candidate may take a type-floor slot: a caught bug is the
|
|
33
|
-
// strongest thing a run produces, and it outranks type spread. Every OTHER
|
|
34
|
-
// protected candidate — anything else CRITICAL, and attack-surface
|
|
35
|
-
// security_boundary items — still yields to the floor, which is what stops
|
|
36
|
-
// the SKYR-3879 skew where three CRITICAL integration candidates took all
|
|
37
|
-
// three slots and no contract test was generated.
|
|
38
|
-
//
|
|
39
|
-
// The second pass replaces `maxProtected = count - (typesOrder.length - 1)`,
|
|
40
|
-
// a static cap computed from the whole pool. It reserved a slot for every
|
|
41
|
-
// other type whether or not the picks already covered it, so at the default
|
|
42
|
-
// budget of 3 with three types present it evaluated to 1. Counting the types
|
|
43
|
-
// still uncovered keeps the same floor and drops the arithmetic that made a
|
|
44
|
-
// second finding unreachable.
|
|
45
|
-
// The same bound the prompt states (`maxGen - 1`, and the whole budget only
|
|
46
|
-
// at a budget of 1). Without it the first pass took every caught bug up to
|
|
47
|
-
// `count`, which put the SKYR-3879 skew back: at the default budget of 3, two
|
|
48
|
-
// same-type bugs left no slot for the type floor, and a measured 9-20% of
|
|
49
|
-
// backend-only pools lost a test type. It also evicted attack-surface
|
|
50
|
-
// security_boundary items the prompt in this same branch says to preserve
|
|
51
|
-
// while any non-bug slot remains.
|
|
52
|
-
const bugSlots = count <= 1 ? count : count - 1;
|
|
53
|
-
const selected = [];
|
|
54
|
-
const deferred = [];
|
|
55
|
-
const pool = [];
|
|
56
|
-
let bugsTaken = 0;
|
|
57
|
-
for (const it of items) {
|
|
58
|
-
if (it.scenario?.category === "bug_caught" && bugsTaken < bugSlots && selected.length < count) {
|
|
59
|
-
selected.push(it);
|
|
60
|
-
bugsTaken++;
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
deferred.push(it);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
const takenTypes = new Set(selected.map(typeOf));
|
|
67
|
-
for (const it of deferred) {
|
|
68
|
-
const stillUncovered = typesOrder.filter((t) => !takenTypes.has(t) && t !== typeOf(it)).length;
|
|
69
|
-
if (isProtectedCandidate(it.priority, it.scenario) &&
|
|
70
|
-
selected.length < count &&
|
|
71
|
-
count - (selected.length + 1) >= stillUncovered) {
|
|
72
|
-
selected.push(it);
|
|
73
|
-
takenTypes.add(typeOf(it));
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
pool.push(it);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
const { buckets } = bucketByType(pool, typeOf);
|
|
80
|
-
const covered = new Set(selected.map(typeOf));
|
|
81
|
-
// Phase 1 — floor: one item per still-uncovered present type.
|
|
82
|
-
for (const t of typesOrder) {
|
|
83
|
-
if (selected.length >= count)
|
|
84
|
-
break;
|
|
85
|
-
if (covered.has(t))
|
|
86
|
-
continue;
|
|
87
|
-
const bucket = buckets.get(t);
|
|
88
|
-
if (bucket && bucket.length > 0) {
|
|
89
|
-
selected.push(bucket.shift());
|
|
90
|
-
covered.add(t);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
// Phase 2 — round-robin the remainder across types until full.
|
|
94
|
-
roundRobinFill(selected, typesOrder, buckets, count);
|
|
95
|
-
return selected;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Budgeter that guarantees cross-test-type coverage in the GENERATE set.
|
|
99
|
-
* `maxGenerate` acts as an upper cap (not a hard N). Fixes the single-type skew
|
|
100
|
-
* that starves contract/error-path tests. Opt-in via strategy selection.
|
|
101
|
-
*/
|
|
102
|
-
export const diversityBalancedBudgeter = {
|
|
103
|
-
name: "diversity-balanced",
|
|
104
|
-
select: (ranked, ctx) => runBudget(ranked, ctx, floorBalancedPick),
|
|
105
|
-
};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Budgeter } from "../../types/Recommendation.js";
|
|
2
|
-
/**
|
|
3
|
-
* Default budgeter — reproduces the pre-refactor behavior exactly: fill up to
|
|
4
|
-
* `maxGenerate` GENERATE slots by round-robin across the present test types
|
|
5
|
-
* (protected items first), the rest become ADDITIONAL.
|
|
6
|
-
*/
|
|
7
|
-
export declare const fixedNBudgeter: Budgeter;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { roundRobinByType } from "../diversity.js";
|
|
2
|
-
import { runBudget } from "./shared.js";
|
|
3
|
-
/**
|
|
4
|
-
* Default budgeter — reproduces the pre-refactor behavior exactly: fill up to
|
|
5
|
-
* `maxGenerate` GENERATE slots by round-robin across the present test types
|
|
6
|
-
* (protected items first), the rest become ADDITIONAL.
|
|
7
|
-
*/
|
|
8
|
-
export const fixedNBudgeter = {
|
|
9
|
-
name: "fixed-n",
|
|
10
|
-
select: (ranked, ctx) => runBudget(ranked, ctx, roundRobinByType),
|
|
11
|
-
};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Candidate, BudgetContext, SelectionResult } from "../../types/Recommendation.js";
|
|
2
|
-
/**
|
|
3
|
-
* What each PR class GUARANTEES in GENERATE (SKYR-4275). These are floors, not
|
|
4
|
-
* quotas: the slots above them are open to either kind, decided by `pick`.
|
|
5
|
-
* An open slot goes to whichever candidate `pick` chooses on rank, so a backend
|
|
6
|
-
* candidate takes one when it outranks the UI candidates, not merely because it
|
|
7
|
-
* exists. Measured against main on random mixed pools at `maxGenerate` 5 with no
|
|
8
|
-
* caught bugs: 35% get fewer backend tests than main did, average 3.53 to 3.24.
|
|
9
|
-
*
|
|
10
|
-
* Backend-only floors nothing on purpose: of 86 backend-only diffs in the eval
|
|
11
|
-
* corpus, ZERO generated a UI test, so a floor there would guarantee what
|
|
12
|
-
* already happens every time.
|
|
13
|
-
*
|
|
14
|
-
* At one slot there is no room for a floor plus an open slot, so each class
|
|
15
|
-
* keeps that slot for its own kind.
|
|
16
|
-
*
|
|
17
|
-
* The floors always sum to at most `maxGenerate`, which is what lets
|
|
18
|
-
* `applyFloors` satisfy both without one starving the other.
|
|
19
|
-
*/
|
|
20
|
-
export declare function generateFloors(ctx: BudgetContext): {
|
|
21
|
-
ui: number;
|
|
22
|
-
backend: number;
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* Shared budgeting pipeline. All Budgeters run the same external-dedup,
|
|
26
|
-
* attack-surface prioritization, and ADDITIONAL set-difference; they differ
|
|
27
|
-
* ONLY in how they pick the GENERATE items from the slot-ordered list (`pick`).
|
|
28
|
-
*
|
|
29
|
-
* With `pick = roundRobinByType` this reproduces the pre-refactor selection in
|
|
30
|
-
* diffExecutionPlan.ts exactly.
|
|
31
|
-
*/
|
|
32
|
-
export declare function runBudget(ranked: Candidate[], rawCtx: BudgetContext, pick: (items: Candidate[], count: number) => Candidate[]): SelectionResult;
|