@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,47 +0,0 @@
|
|
|
1
|
-
import { DraftedScenario } from "../types/RepositoryAnalysis.js";
|
|
2
|
-
import { PriorityTier } from "../types/TestRecommendation.js";
|
|
3
|
-
/**
|
|
4
|
-
* Reorder a rank-ordered list so each attack-surface security_boundary scenario
|
|
5
|
-
* sits immediately before the first ordinary direct-auth boundary — keeping
|
|
6
|
-
* destructive sibling auth tests bundled ahead of their read counterparts.
|
|
7
|
-
*/
|
|
8
|
-
export declare function prioritizeAttackSurfaceBundles<T extends {
|
|
9
|
-
scenario: DraftedScenario;
|
|
10
|
-
}>(items: T[]): T[];
|
|
11
|
-
/** The test-type inference used everywhere GENERATE slots are distributed:
|
|
12
|
-
* explicit testType, else single-step ⇒ contract, multi-step ⇒ integration
|
|
13
|
-
* (the same inference used when rendering plan items). */
|
|
14
|
-
export declare function inferScenarioType(s: DraftedScenario): string;
|
|
15
|
-
/** Protected items always take a GENERATE slot before any round-robin:
|
|
16
|
-
* CRITICAL-priority and attack-surface security_boundary scenarios. */
|
|
17
|
-
export declare function isProtectedCandidate(priority: PriorityTier, scenario: DraftedScenario): boolean;
|
|
18
|
-
/** Bucket items by type, preserving rank order within each bucket and
|
|
19
|
-
* first-appearance order across buckets (so the highest-ranked type wins
|
|
20
|
-
* round 1 of any subsequent round-robin). */
|
|
21
|
-
export declare function bucketByType<T>(items: T[], typeOf: (item: T) => string): {
|
|
22
|
-
order: string[];
|
|
23
|
-
buckets: Map<string, T[]>;
|
|
24
|
-
};
|
|
25
|
-
/** Round-robin one item per non-empty bucket per round (skipping exhausted
|
|
26
|
-
* buckets, so their freed slots spill over to the next type) until
|
|
27
|
-
* `selected` reaches `count` or every bucket is empty. Mutates `selected`
|
|
28
|
-
* and the buckets. */
|
|
29
|
-
export declare function roundRobinFill<T>(selected: T[], order: string[], buckets: Map<string, T[]>, count: number): void;
|
|
30
|
-
/**
|
|
31
|
-
* Select `count` items from a rank-ordered list, distributing GENERATE slots
|
|
32
|
-
* EVENLY across the test types present, with spillover.
|
|
33
|
-
*
|
|
34
|
-
* Policy (kept identical to the multi-repo prose in testbot-prompts.ts's
|
|
35
|
-
* "Cross-repo test generation" block — change both together):
|
|
36
|
-
* - Protected items first (see isProtectedCandidate) — they must stay in GENERATE.
|
|
37
|
-
* - Bucket the rest by inferred test type and round-robin (see bucketByType /
|
|
38
|
-
* roundRobinFill), preserving rank order within each bucket.
|
|
39
|
-
*
|
|
40
|
-
* Degenerate cases match the previous pure rank-order slice exactly: a single type
|
|
41
|
-
* present, or `count >= items.length`, returns the same items in the same order —
|
|
42
|
-
* so backend-only / single-type runs are unchanged (no regression).
|
|
43
|
-
*/
|
|
44
|
-
export declare function roundRobinByType<T extends {
|
|
45
|
-
scenario: DraftedScenario;
|
|
46
|
-
priority: PriorityTier;
|
|
47
|
-
}>(rankOrdered: T[], count: number): T[];
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { PriorityTier } from "../types/TestRecommendation.js";
|
|
2
|
-
import { isAttackSurfaceSecurityBoundary, isOrdinaryDirectAuthBoundary, } from "../prompts/test-recommendation/recommendationShared.js";
|
|
3
|
-
/**
|
|
4
|
-
* Reorder a rank-ordered list so each attack-surface security_boundary scenario
|
|
5
|
-
* sits immediately before the first ordinary direct-auth boundary — keeping
|
|
6
|
-
* destructive sibling auth tests bundled ahead of their read counterparts.
|
|
7
|
-
*/
|
|
8
|
-
export function prioritizeAttackSurfaceBundles(items) {
|
|
9
|
-
const reordered = [];
|
|
10
|
-
for (const item of items) {
|
|
11
|
-
if (isAttackSurfaceSecurityBoundary(item.scenario)) {
|
|
12
|
-
const firstDirectAuthIndex = reordered.findIndex((candidate) => isOrdinaryDirectAuthBoundary(candidate.scenario));
|
|
13
|
-
if (firstDirectAuthIndex >= 0) {
|
|
14
|
-
reordered.splice(firstDirectAuthIndex, 0, item);
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
reordered.push(item);
|
|
19
|
-
}
|
|
20
|
-
return reordered;
|
|
21
|
-
}
|
|
22
|
-
/** The test-type inference used everywhere GENERATE slots are distributed:
|
|
23
|
-
* explicit testType, else single-step ⇒ contract, multi-step ⇒ integration
|
|
24
|
-
* (the same inference used when rendering plan items). */
|
|
25
|
-
export function inferScenarioType(s) {
|
|
26
|
-
return s.testType ?? (s.steps.length === 1 ? "contract" : "integration");
|
|
27
|
-
}
|
|
28
|
-
/** Protected items always take a GENERATE slot before any round-robin:
|
|
29
|
-
* CRITICAL-priority and attack-surface security_boundary scenarios. */
|
|
30
|
-
export function isProtectedCandidate(priority, scenario) {
|
|
31
|
-
return (priority === PriorityTier.CRITICAL ||
|
|
32
|
-
isAttackSurfaceSecurityBoundary(scenario));
|
|
33
|
-
}
|
|
34
|
-
/** Bucket items by type, preserving rank order within each bucket and
|
|
35
|
-
* first-appearance order across buckets (so the highest-ranked type wins
|
|
36
|
-
* round 1 of any subsequent round-robin). */
|
|
37
|
-
export function bucketByType(items, typeOf) {
|
|
38
|
-
const order = [];
|
|
39
|
-
const buckets = new Map();
|
|
40
|
-
for (const item of items) {
|
|
41
|
-
const t = typeOf(item);
|
|
42
|
-
if (!buckets.has(t)) {
|
|
43
|
-
buckets.set(t, []);
|
|
44
|
-
order.push(t);
|
|
45
|
-
}
|
|
46
|
-
buckets.get(t).push(item);
|
|
47
|
-
}
|
|
48
|
-
return { order, buckets };
|
|
49
|
-
}
|
|
50
|
-
/** Round-robin one item per non-empty bucket per round (skipping exhausted
|
|
51
|
-
* buckets, so their freed slots spill over to the next type) until
|
|
52
|
-
* `selected` reaches `count` or every bucket is empty. Mutates `selected`
|
|
53
|
-
* and the buckets. */
|
|
54
|
-
export function roundRobinFill(selected, order, buckets, count) {
|
|
55
|
-
while (selected.length < count &&
|
|
56
|
-
order.some((t) => (buckets.get(t)?.length ?? 0) > 0)) {
|
|
57
|
-
for (const t of order) {
|
|
58
|
-
if (selected.length >= count)
|
|
59
|
-
break;
|
|
60
|
-
const bucket = buckets.get(t);
|
|
61
|
-
if (bucket && bucket.length > 0)
|
|
62
|
-
selected.push(bucket.shift());
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Select `count` items from a rank-ordered list, distributing GENERATE slots
|
|
68
|
-
* EVENLY across the test types present, with spillover.
|
|
69
|
-
*
|
|
70
|
-
* Policy (kept identical to the multi-repo prose in testbot-prompts.ts's
|
|
71
|
-
* "Cross-repo test generation" block — change both together):
|
|
72
|
-
* - Protected items first (see isProtectedCandidate) — they must stay in GENERATE.
|
|
73
|
-
* - Bucket the rest by inferred test type and round-robin (see bucketByType /
|
|
74
|
-
* roundRobinFill), preserving rank order within each bucket.
|
|
75
|
-
*
|
|
76
|
-
* Degenerate cases match the previous pure rank-order slice exactly: a single type
|
|
77
|
-
* present, or `count >= items.length`, returns the same items in the same order —
|
|
78
|
-
* so backend-only / single-type runs are unchanged (no regression).
|
|
79
|
-
*/
|
|
80
|
-
export function roundRobinByType(rankOrdered, count) {
|
|
81
|
-
if (count <= 0)
|
|
82
|
-
return [];
|
|
83
|
-
// Everything fits → no need to bucket; identical to the old slice.
|
|
84
|
-
if (count >= rankOrdered.length)
|
|
85
|
-
return rankOrdered.slice(0, count);
|
|
86
|
-
// Protected items occupy GENERATE slots first, in rank order.
|
|
87
|
-
const selected = [];
|
|
88
|
-
const remaining = [];
|
|
89
|
-
for (const item of rankOrdered) {
|
|
90
|
-
if (selected.length < count &&
|
|
91
|
-
isProtectedCandidate(item.priority, item.scenario)) {
|
|
92
|
-
selected.push(item);
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
remaining.push(item);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const { order, buckets } = bucketByType(remaining, (item) => inferScenarioType(item.scenario));
|
|
99
|
-
roundRobinFill(selected, order, buckets, count);
|
|
100
|
-
return selected;
|
|
101
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { Candidate, BudgetContext, Demotion, SelectionResult } from "../types/Recommendation.js";
|
|
2
|
-
import { ScenarioCategory } from "../types/TestRecommendation.js";
|
|
3
|
-
/** Options for {@link rankCandidates}. Reserved for phase-2 tuning. */
|
|
4
|
-
export interface RankOptions {
|
|
5
|
-
/**
|
|
6
|
-
* Categories that take the top carve-out tier ahead of everything else.
|
|
7
|
-
* Defaults to the `CATEGORY_PRIORITY === "CRITICAL"` categories (bug_caught and
|
|
8
|
-
* requirement_conflict — new_endpoint is MEDIUM, not carved out). The two CRITICAL
|
|
9
|
-
* categories are carved out independently, so a requirement conflict never competes
|
|
10
|
-
* with a code-review bug for one slot. Exposed so phase 2 can tune the carve-out
|
|
11
|
-
* WITHOUT reintroducing the agent's priority tag as a ranking input.
|
|
12
|
-
*/
|
|
13
|
-
carveOutCategories?: ScenarioCategory[];
|
|
14
|
-
/**
|
|
15
|
-
* Raw PR diff text. When supplied, a candidate whose scenario references a
|
|
16
|
-
* method+path that appears on an actual changed diff line (`+`/`-`) floats
|
|
17
|
-
* ahead of a same-tier candidate that doesn't — e.g. the one endpoint really
|
|
18
|
-
* removed by a PR outranks other same-category "verify-removed-*" candidates
|
|
19
|
-
* for endpoints merely adjacent in the same file (SKYR-4026).
|
|
20
|
-
*/
|
|
21
|
-
diffText?: string;
|
|
22
|
-
}
|
|
23
|
-
/** Context for {@link selectPlan}: the budget context plus the demotion channel
|
|
24
|
-
* the register-plan tool fills from discriminator verification. */
|
|
25
|
-
export interface SelectPlanContext extends BudgetContext {
|
|
26
|
-
/** Demoted claims (failed discriminator verification) to surface on the result. */
|
|
27
|
-
demotions?: Demotion[];
|
|
28
|
-
/** Threaded into {@link rankCandidates} as `RankOptions.diffText`. */
|
|
29
|
-
diffText?: string;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Rank test candidates for the register-plan checkpoint. Pure and fully
|
|
33
|
-
* deterministic: the same set of candidates always yields the same order,
|
|
34
|
-
* independent of input order (the final tiebreak is the stable `candidateId`).
|
|
35
|
-
*
|
|
36
|
-
* Ordering (highest first):
|
|
37
|
-
* 1. Carve-out — CRITICAL-category scenarios (bug_caught, requirement_conflict), preserving the
|
|
38
|
-
* protected-first convention of `roundRobinByType` /
|
|
39
|
-
* `prioritizeAttackSurfaceBundles`.
|
|
40
|
-
* 2. Verified discriminators — candidates whose declared discriminator survived
|
|
41
|
-
* `validateDiscriminator` (marked via `verifiedDiscriminator`) float ahead of
|
|
42
|
-
* unverified peers in the same tier.
|
|
43
|
-
* 3. Diff-hunk proximity — candidates referencing a method+path that appears
|
|
44
|
-
* on an actual changed diff line float ahead of same-tier candidates that
|
|
45
|
-
* don't (SKYR-4026). Only applied when `opts.diffText` is supplied.
|
|
46
|
-
* 4. `CATEGORY_PRIORITY` mapped over `scenario.category` (CRITICAL > HIGH >
|
|
47
|
-
* MEDIUM > LOW).
|
|
48
|
-
* 5. Stable tiebreak on `candidateId` for determinism.
|
|
49
|
-
*
|
|
50
|
-
* IMPORTANT: the agent-supplied `priority` field (`scenario.priority`) is
|
|
51
|
-
* DELIBERATELY IGNORED here. Round-1 experiments found it systematically
|
|
52
|
-
* miscalibrated — the agent labels genuinely discriminating tests "low" and
|
|
53
|
-
* generic happy-path tests "high" — so ranking on it inverts the intended order.
|
|
54
|
-
* Priority in this pipeline is derived from the scenario's category and from the
|
|
55
|
-
* verified discriminator, never from the agent's self-assessment.
|
|
56
|
-
*/
|
|
57
|
-
export declare function rankCandidates(candidates: Candidate[], opts?: RankOptions): Candidate[];
|
|
58
|
-
/**
|
|
59
|
-
* Convenience composition: rank the candidates, split GENERATE vs ADDITIONAL via
|
|
60
|
-
* the diversity-balanced budgeter, and surface the caller-supplied demotions on
|
|
61
|
-
* the result. The caller (the register-plan tool) runs `validateDiscriminator`
|
|
62
|
-
* first — setting `verifiedDiscriminator` on the candidates that passed and
|
|
63
|
-
* passing the failed claims through `ctx.demotions`.
|
|
64
|
-
*/
|
|
65
|
-
export declare function selectPlan(candidates: Candidate[], ctx: SelectPlanContext, opts?: RankOptions): SelectionResult;
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { CATEGORY_PRIORITY, PriorityTier } from "../types/TestRecommendation.js";
|
|
2
|
-
import { diversityBalancedBudgeter } from "./budgeters/diversityBalancedBudgeter.js";
|
|
3
|
-
import { collectChangedRouteLines, findStepOnChangedRoute } from "../utils/changedRoutes.js";
|
|
4
|
-
const PRIORITY_RANK = {
|
|
5
|
-
CRITICAL: 0,
|
|
6
|
-
HIGH: 1,
|
|
7
|
-
MEDIUM: 2,
|
|
8
|
-
LOW: 3,
|
|
9
|
-
};
|
|
10
|
-
const DEFAULT_CARVE_OUT_CATEGORIES = Object.keys(CATEGORY_PRIORITY).filter((category) => CATEGORY_PRIORITY[category] === PriorityTier.CRITICAL);
|
|
11
|
-
/**
|
|
12
|
-
* Rank test candidates for the register-plan checkpoint. Pure and fully
|
|
13
|
-
* deterministic: the same set of candidates always yields the same order,
|
|
14
|
-
* independent of input order (the final tiebreak is the stable `candidateId`).
|
|
15
|
-
*
|
|
16
|
-
* Ordering (highest first):
|
|
17
|
-
* 1. Carve-out — CRITICAL-category scenarios (bug_caught, requirement_conflict), preserving the
|
|
18
|
-
* protected-first convention of `roundRobinByType` /
|
|
19
|
-
* `prioritizeAttackSurfaceBundles`.
|
|
20
|
-
* 2. Verified discriminators — candidates whose declared discriminator survived
|
|
21
|
-
* `validateDiscriminator` (marked via `verifiedDiscriminator`) float ahead of
|
|
22
|
-
* unverified peers in the same tier.
|
|
23
|
-
* 3. Diff-hunk proximity — candidates referencing a method+path that appears
|
|
24
|
-
* on an actual changed diff line float ahead of same-tier candidates that
|
|
25
|
-
* don't (SKYR-4026). Only applied when `opts.diffText` is supplied.
|
|
26
|
-
* 4. `CATEGORY_PRIORITY` mapped over `scenario.category` (CRITICAL > HIGH >
|
|
27
|
-
* MEDIUM > LOW).
|
|
28
|
-
* 5. Stable tiebreak on `candidateId` for determinism.
|
|
29
|
-
*
|
|
30
|
-
* IMPORTANT: the agent-supplied `priority` field (`scenario.priority`) is
|
|
31
|
-
* DELIBERATELY IGNORED here. Round-1 experiments found it systematically
|
|
32
|
-
* miscalibrated — the agent labels genuinely discriminating tests "low" and
|
|
33
|
-
* generic happy-path tests "high" — so ranking on it inverts the intended order.
|
|
34
|
-
* Priority in this pipeline is derived from the scenario's category and from the
|
|
35
|
-
* verified discriminator, never from the agent's self-assessment.
|
|
36
|
-
*/
|
|
37
|
-
export function rankCandidates(candidates, opts = {}) {
|
|
38
|
-
const carveOutSet = new Set(opts.carveOutCategories ?? DEFAULT_CARVE_OUT_CATEGORIES);
|
|
39
|
-
const changedRoutes = opts.diffText ? collectChangedRouteLines(opts.diffText) : [];
|
|
40
|
-
const onHunk = new Set(candidates
|
|
41
|
-
.filter((c) => scenarioOnChangedHunk(c.scenario, changedRoutes))
|
|
42
|
-
.map((c) => c.candidateId));
|
|
43
|
-
return [...candidates].sort((a, b) => compareRank(a, b, carveOutSet, onHunk));
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Convenience composition: rank the candidates, split GENERATE vs ADDITIONAL via
|
|
47
|
-
* the diversity-balanced budgeter, and surface the caller-supplied demotions on
|
|
48
|
-
* the result. The caller (the register-plan tool) runs `validateDiscriminator`
|
|
49
|
-
* first — setting `verifiedDiscriminator` on the candidates that passed and
|
|
50
|
-
* passing the failed claims through `ctx.demotions`.
|
|
51
|
-
*/
|
|
52
|
-
export function selectPlan(candidates, ctx, opts = {}) {
|
|
53
|
-
const ranked = rankCandidates(candidates, { ...opts, diffText: opts.diffText ?? ctx.diffText });
|
|
54
|
-
const result = diversityBalancedBudgeter.select(ranked, ctx);
|
|
55
|
-
return { ...result, demotions: ctx.demotions ?? [] };
|
|
56
|
-
}
|
|
57
|
-
function compareRank(a, b, carveOutSet, onHunk) {
|
|
58
|
-
const carveA = carveOutSet.has(a.scenario?.category) ? 0 : 1;
|
|
59
|
-
const carveB = carveOutSet.has(b.scenario?.category) ? 0 : 1;
|
|
60
|
-
if (carveA !== carveB)
|
|
61
|
-
return carveA - carveB;
|
|
62
|
-
const verifiedA = a.verifiedDiscriminator ? 0 : 1;
|
|
63
|
-
const verifiedB = b.verifiedDiscriminator ? 0 : 1;
|
|
64
|
-
if (verifiedA !== verifiedB)
|
|
65
|
-
return verifiedA - verifiedB;
|
|
66
|
-
const hunkA = onHunk.has(a.candidateId) ? 0 : 1;
|
|
67
|
-
const hunkB = onHunk.has(b.candidateId) ? 0 : 1;
|
|
68
|
-
if (hunkA !== hunkB)
|
|
69
|
-
return hunkA - hunkB;
|
|
70
|
-
const catRankA = categoryRank(a);
|
|
71
|
-
const catRankB = categoryRank(b);
|
|
72
|
-
if (catRankA !== catRankB)
|
|
73
|
-
return catRankA - catRankB;
|
|
74
|
-
return (a.candidateId ?? "").localeCompare(b.candidateId ?? "");
|
|
75
|
-
}
|
|
76
|
-
function categoryRank(candidate) {
|
|
77
|
-
const tier = CATEGORY_PRIORITY[candidate.scenario?.category] ?? PriorityTier.LOW;
|
|
78
|
-
return PRIORITY_RANK[tier];
|
|
79
|
-
}
|
|
80
|
-
/** Whether any step of `scenario` targets a method+path on the changed hunk. */
|
|
81
|
-
function scenarioOnChangedHunk(scenario, changedRoutes) {
|
|
82
|
-
return !!findStepOnChangedRoute(scenario.steps, changedRoutes);
|
|
83
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { ScenarioStep } from "../types/RepositoryAnalysis.js";
|
|
2
|
-
import { Novelty, PriorityTier, ScenarioCategory } from "../types/TestRecommendation.js";
|
|
3
|
-
import { Candidate } from "../types/Recommendation.js";
|
|
4
|
-
/** Build a minimal ScenarioStep. */
|
|
5
|
-
export declare function mkStep(method?: string, path?: string): ScenarioStep;
|
|
6
|
-
interface CandidateOpts {
|
|
7
|
-
priority?: PriorityTier;
|
|
8
|
-
testType?: string;
|
|
9
|
-
steps?: number;
|
|
10
|
-
category?: ScenarioCategory;
|
|
11
|
-
description?: string;
|
|
12
|
-
novelty?: Novelty;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Build a ranked Candidate for budgeter/ranker tests. Steps default to a
|
|
16
|
-
* distinct path per candidate (so coverage keys don't collide) with the final
|
|
17
|
-
* step as a mutation (so resolvePrimaryStep picks a stable primary step).
|
|
18
|
-
*
|
|
19
|
-
* Note the two distinct priority fields: `scenario.priority` is the agent's
|
|
20
|
-
* own drafted label (lowercase — untrusted, ranking never reads it), while
|
|
21
|
-
* `Candidate.priority` is the server-computed PriorityTier the rankers
|
|
22
|
-
* actually order by.
|
|
23
|
-
*/
|
|
24
|
-
export declare function mkCandidate(name: string, opts?: CandidateOpts): Candidate;
|
|
25
|
-
export {};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { Novelty, PriorityTier, } from "../types/TestRecommendation.js";
|
|
2
|
-
import { CandidateSource, computeCandidateId, } from "../types/Recommendation.js";
|
|
3
|
-
/** Build a minimal ScenarioStep. */
|
|
4
|
-
export function mkStep(method = "GET", path = "/r") {
|
|
5
|
-
return {
|
|
6
|
-
order: 0,
|
|
7
|
-
method,
|
|
8
|
-
path,
|
|
9
|
-
description: "",
|
|
10
|
-
interactionType: "success",
|
|
11
|
-
expectedStatusCode: 200,
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Build a ranked Candidate for budgeter/ranker tests. Steps default to a
|
|
16
|
-
* distinct path per candidate (so coverage keys don't collide) with the final
|
|
17
|
-
* step as a mutation (so resolvePrimaryStep picks a stable primary step).
|
|
18
|
-
*
|
|
19
|
-
* Note the two distinct priority fields: `scenario.priority` is the agent's
|
|
20
|
-
* own drafted label (lowercase — untrusted, ranking never reads it), while
|
|
21
|
-
* `Candidate.priority` is the server-computed PriorityTier the rankers
|
|
22
|
-
* actually order by.
|
|
23
|
-
*/
|
|
24
|
-
export function mkCandidate(name, opts = {}) {
|
|
25
|
-
const stepCount = opts.steps ?? (opts.testType === "contract" ? 1 : 2);
|
|
26
|
-
const steps = Array.from({ length: stepCount }, (_, i) => mkStep(i === stepCount - 1 ? "POST" : "GET", `/${name}`));
|
|
27
|
-
const scenario = {
|
|
28
|
-
scenarioName: name,
|
|
29
|
-
description: opts.description ?? name,
|
|
30
|
-
category: opts.category ?? "workflow",
|
|
31
|
-
priority: "high",
|
|
32
|
-
steps,
|
|
33
|
-
chainingKeys: [],
|
|
34
|
-
requiresAuth: false,
|
|
35
|
-
estimatedComplexity: "moderate",
|
|
36
|
-
...(opts.testType ? { testType: opts.testType } : {}),
|
|
37
|
-
};
|
|
38
|
-
return {
|
|
39
|
-
scenario,
|
|
40
|
-
priority: opts.priority ?? PriorityTier.HIGH,
|
|
41
|
-
novelty: opts.novelty ?? Novelty.EXISTING,
|
|
42
|
-
source: CandidateSource.AGENT,
|
|
43
|
-
candidateId: computeCandidateId(scenario),
|
|
44
|
-
};
|
|
45
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Frontend component integration check types — SKYR-3855.
|
|
3
|
-
*
|
|
4
|
-
* Produced by `checkFrontendFileIntegration` (src/utils/frontendIntegration.ts)
|
|
5
|
-
* and surfaced to the agent as `uiContext.frontendFileIntegration` in the
|
|
6
|
-
* `skyramp_analyze_changes` output and state file.
|
|
7
|
-
*/
|
|
8
|
-
/** String enum so the JSON wire format (state file, tool output) stays unchanged. */
|
|
9
|
-
export declare enum IntegrationReason {
|
|
10
|
-
/** Framework route/page/entrypoint file — reachable by convention, grep skipped. */
|
|
11
|
-
RouteOrEntrypoint = "route-or-entrypoint",
|
|
12
|
-
/** i18n/locale content file — often loaded via a runtime-templated path rather
|
|
13
|
-
* than a static quoted import, so the grep-based check is skipped (SKYR-3977). */
|
|
14
|
-
I18nLocaleContent = "i18n-locale-content",
|
|
15
|
-
/** At least one production file imports/references the component's module path. */
|
|
16
|
-
Imported = "imported",
|
|
17
|
-
/** No production importer found — the component has no DOM presence in the running app. */
|
|
18
|
-
NoImporters = "no-importers",
|
|
19
|
-
/** The check could not run (missing file, scan failure) — reported integrated (fail-open). */
|
|
20
|
-
ScanError = "scan-error"
|
|
21
|
-
}
|
|
22
|
-
export interface FrontendFileIntegration {
|
|
23
|
-
file: string;
|
|
24
|
-
integrated: boolean;
|
|
25
|
-
/** Production files that import/reference the component, relative to repositoryPath. Capped at 10. */
|
|
26
|
-
importers: string[];
|
|
27
|
-
reason: IntegrationReason;
|
|
28
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Frontend component integration check types — SKYR-3855.
|
|
3
|
-
*
|
|
4
|
-
* Produced by `checkFrontendFileIntegration` (src/utils/frontendIntegration.ts)
|
|
5
|
-
* and surfaced to the agent as `uiContext.frontendFileIntegration` in the
|
|
6
|
-
* `skyramp_analyze_changes` output and state file.
|
|
7
|
-
*/
|
|
8
|
-
/** String enum so the JSON wire format (state file, tool output) stays unchanged. */
|
|
9
|
-
export var IntegrationReason;
|
|
10
|
-
(function (IntegrationReason) {
|
|
11
|
-
/** Framework route/page/entrypoint file — reachable by convention, grep skipped. */
|
|
12
|
-
IntegrationReason["RouteOrEntrypoint"] = "route-or-entrypoint";
|
|
13
|
-
/** i18n/locale content file — often loaded via a runtime-templated path rather
|
|
14
|
-
* than a static quoted import, so the grep-based check is skipped (SKYR-3977). */
|
|
15
|
-
IntegrationReason["I18nLocaleContent"] = "i18n-locale-content";
|
|
16
|
-
/** At least one production file imports/references the component's module path. */
|
|
17
|
-
IntegrationReason["Imported"] = "imported";
|
|
18
|
-
/** No production importer found — the component has no DOM presence in the running app. */
|
|
19
|
-
IntegrationReason["NoImporters"] = "no-importers";
|
|
20
|
-
/** The check could not run (missing file, scan failure) — reported integrated (fail-open). */
|
|
21
|
-
IntegrationReason["ScanError"] = "scan-error";
|
|
22
|
-
})(IntegrationReason || (IntegrationReason = {}));
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { DraftedScenario, SubjectEndpoint } from "../types/RepositoryAnalysis.js";
|
|
2
|
-
import { Novelty, PriorityTier } from "../types/TestRecommendation.js";
|
|
3
|
-
/**
|
|
4
|
-
* The structural bug-shape a candidate claims to probe. Verified against the
|
|
5
|
-
* candidate's `steps[]` by `validateDiscriminator` (discriminators.ts); a claim
|
|
6
|
-
* that does not survive verification is demoted, never used to reject.
|
|
7
|
-
*/
|
|
8
|
-
export declare enum DiscriminatorKind {
|
|
9
|
-
BOUNDARY_EQUALITY = "boundary_equality",
|
|
10
|
-
MULTI_RECORD_AGGREGATE = "multi_record_aggregate",
|
|
11
|
-
STATE_TRANSITION = "state_transition",
|
|
12
|
-
NEGATIVE_MATCH = "negative_match"
|
|
13
|
-
}
|
|
14
|
-
/** Who drafted a candidate: the LLM loop, or the MCP server's own pre-ranking. */
|
|
15
|
-
export declare enum CandidateSource {
|
|
16
|
-
AGENT = "agent",
|
|
17
|
-
SERVER = "server"
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* A ranked test candidate — the existing scored scenario plus the fields the
|
|
21
|
-
* budgeting stage reads. Matches the shape produced today by the scoring block
|
|
22
|
-
* in test-recommendation-prompt.ts (`{ scenario, priority, novelty }`), extended
|
|
23
|
-
* for the SKYR-3879 Path B register-plan checkpoint with provenance, a stable id
|
|
24
|
-
* and the verified-discriminator marker the ranker reads.
|
|
25
|
-
*/
|
|
26
|
-
export interface Candidate {
|
|
27
|
-
scenario: DraftedScenario;
|
|
28
|
-
priority: PriorityTier;
|
|
29
|
-
novelty: Novelty;
|
|
30
|
-
/** Who drafted this candidate: the LLM loop or the MCP server. */
|
|
31
|
-
source: CandidateSource;
|
|
32
|
-
/**
|
|
33
|
-
* Stable, deterministic id: slugified `scenarioName` + short content hash.
|
|
34
|
-
* Reproducible across calls (same scenario → same id) so the generation-tool
|
|
35
|
-
* plan guard can match a generated test back to its approved candidate.
|
|
36
|
-
*/
|
|
37
|
-
candidateId: string;
|
|
38
|
-
/**
|
|
39
|
-
* Set by the caller AFTER `validateDiscriminator` succeeds for a declared
|
|
40
|
-
* discriminator. Present only when the structural claim was verified against
|
|
41
|
-
* `steps[]` and the diff anchor; the ranker floats these ahead of unverified
|
|
42
|
-
* peers. Absent = no verified discriminator (unclaimed or demoted).
|
|
43
|
-
*/
|
|
44
|
-
verifiedDiscriminator?: DiscriminatorKind;
|
|
45
|
-
}
|
|
46
|
-
/** Environment-derived inputs a Budgeter needs to split GENERATE vs ADDITIONAL. */
|
|
47
|
-
export interface BudgetContext {
|
|
48
|
-
/** Upper bound on GENERATE items (a strategy parameter, formerly the hard cap). */
|
|
49
|
-
maxGenerate: number;
|
|
50
|
-
/** Total recommendations (GENERATE + ADDITIONAL); today's `topN`. */
|
|
51
|
-
maxTotal: number;
|
|
52
|
-
isUIOnlyPR: boolean;
|
|
53
|
-
hasFrontendChanges: boolean;
|
|
54
|
-
/** Method-aware coverage keys of external tests; matching candidates are dropped. */
|
|
55
|
-
externalCoverage: Set<string>;
|
|
56
|
-
/**
|
|
57
|
-
* Whether the PR's own diff changes any test file. External coverage is
|
|
58
|
-
* discovered from the working tree, so a test the PR itself adds is
|
|
59
|
-
* indistinguishable from pre-existing coverage; when this is true the
|
|
60
|
-
* covered-candidate reserve stays shut rather than forcing duplicates of a
|
|
61
|
-
* test the author already wrote.
|
|
62
|
-
*/
|
|
63
|
-
diffChangesTestFiles: boolean;
|
|
64
|
-
}
|
|
65
|
-
/** A demoted claim: which candidate, and the human-readable reason returned to
|
|
66
|
-
* the agent so it can fix the claim (never a silent drop of the claim itself).
|
|
67
|
-
* A demotion records that a declared discriminator claim failed to verify.
|
|
68
|
-
* It does not say whether the candidate is still in the plan. Budgeting
|
|
69
|
-
* decides that separately. A candidate can be both demoted and dropped. */
|
|
70
|
-
export interface Demotion {
|
|
71
|
-
candidateId: string;
|
|
72
|
-
reason: string;
|
|
73
|
-
}
|
|
74
|
-
/** A candidate the selection stage removed, and why. A demotion annotates a
|
|
75
|
-
* claim. A drop removes a candidate from `generate` and `additional`. The
|
|
76
|
-
* two are independent. The same candidate can carry both. */
|
|
77
|
-
export interface Dropped {
|
|
78
|
-
candidateId: string;
|
|
79
|
-
reason: string;
|
|
80
|
-
}
|
|
81
|
-
/** Result of budgeting: the GENERATE set, the ADDITIONAL set, how many UI
|
|
82
|
-
* placeholder slots the render layer should add (mixed/UI-only PRs), the
|
|
83
|
-
* demoted-claim channel (the missing rejection surface for register-plan),
|
|
84
|
-
* and the dropped-candidate channel (SKYR-4214: every candidate the
|
|
85
|
-
* selection stage removed outright, with a human-actionable reason). */
|
|
86
|
-
export interface SelectionResult {
|
|
87
|
-
generate: Candidate[];
|
|
88
|
-
additional: Candidate[];
|
|
89
|
-
reservedUISlots: number;
|
|
90
|
-
demotions: Demotion[];
|
|
91
|
-
dropped: Dropped[];
|
|
92
|
-
}
|
|
93
|
-
/** A pluggable budgeting algorithm. Deterministic and pure. */
|
|
94
|
-
export interface Budgeter {
|
|
95
|
-
readonly name: string;
|
|
96
|
-
select(ranked: Candidate[], ctx: BudgetContext): SelectionResult;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Slug of a scenario name alone (no content hash) — stable across independent
|
|
100
|
-
* drafts of "the same" scenario even when their steps[] differ (e.g. the
|
|
101
|
-
* server's auto-drafted version vs. the agent's own re-drafted version of a
|
|
102
|
-
* scenario with an identical name). Used to dedupe by scenario identity where
|
|
103
|
-
* `computeCandidateId`'s content-sensitive hash would wrongly treat two drafts
|
|
104
|
-
* of the same named scenario as distinct (SKYR-4026).
|
|
105
|
-
*/
|
|
106
|
-
export declare function scenarioNameSlug(scenarioName: string | undefined): string;
|
|
107
|
-
/**
|
|
108
|
-
* Merge-dedup identity key for a scenario name — like {@link scenarioNameSlug}
|
|
109
|
-
* but NOT truncated and with no shared fallback for a missing name. The 48-char
|
|
110
|
-
* truncation in `scenarioNameSlug` is fine for `computeCandidateId` (combined
|
|
111
|
-
* with a content hash for uniqueness), but used bare as a Map key it would
|
|
112
|
-
* silently collapse two distinct scenarios sharing a long common prefix, and
|
|
113
|
-
* collapse every candidate with no scenarioName onto the same key. Returns ""
|
|
114
|
-
* for a missing name so the caller can fall back to something already unique
|
|
115
|
-
* (e.g. candidateId) instead of a shared literal.
|
|
116
|
-
*/
|
|
117
|
-
export declare function scenarioMergeKey(scenarioName: string | undefined): string;
|
|
118
|
-
/**
|
|
119
|
-
* Deterministic candidate id: a slug of the scenario name plus an 8-char content
|
|
120
|
-
* hash. Pure and reproducible — the same scenario always yields the same id, so
|
|
121
|
-
* a candidate registered in the plan can be matched back when its test is later
|
|
122
|
-
* generated. The hash covers the salient, stable structural fields (name,
|
|
123
|
-
* category, testType, and each step's method/path/interactionType/status) so
|
|
124
|
-
* cosmetic re-serialization does not change the id while a real edit does.
|
|
125
|
-
*/
|
|
126
|
-
export declare function computeCandidateId(scenario: DraftedScenario): string;
|
|
127
|
-
/** One approved plan item as persisted in `UnifiedAnalysisState.approvedPlan`.
|
|
128
|
-
* Deliberately slim (no steps/requestBody/etc.) — `matchKeys` already carries
|
|
129
|
-
* everything a downstream matcher needs.
|
|
130
|
-
*
|
|
131
|
-
* `subjectEndpoints` is the one exception. `matchKeys` holds one key per
|
|
132
|
-
* step and is deliberately broad, so a generated test can be matched back
|
|
133
|
-
* to its plan item. It does not say which step is the subject. Dedup
|
|
134
|
-
* judges the subject, not the whole step list, so the plan item must
|
|
135
|
-
* record it too. See `DraftedScenario.subjectEndpoints` for the
|
|
136
|
-
* absent-vs-empty contract this field carries over unchanged. */
|
|
137
|
-
export interface ApprovedPlanItem {
|
|
138
|
-
candidateId: string;
|
|
139
|
-
scenarioName: string;
|
|
140
|
-
testType: string;
|
|
141
|
-
category: string;
|
|
142
|
-
source: CandidateSource;
|
|
143
|
-
verifiedDiscriminator?: string;
|
|
144
|
-
subjectEndpoints?: SubjectEndpoint[];
|
|
145
|
-
matchKeys: string[];
|
|
146
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import * as crypto from "crypto";
|
|
2
|
-
/**
|
|
3
|
-
* The structural bug-shape a candidate claims to probe. Verified against the
|
|
4
|
-
* candidate's `steps[]` by `validateDiscriminator` (discriminators.ts); a claim
|
|
5
|
-
* that does not survive verification is demoted, never used to reject.
|
|
6
|
-
*/
|
|
7
|
-
export var DiscriminatorKind;
|
|
8
|
-
(function (DiscriminatorKind) {
|
|
9
|
-
DiscriminatorKind["BOUNDARY_EQUALITY"] = "boundary_equality";
|
|
10
|
-
DiscriminatorKind["MULTI_RECORD_AGGREGATE"] = "multi_record_aggregate";
|
|
11
|
-
DiscriminatorKind["STATE_TRANSITION"] = "state_transition";
|
|
12
|
-
DiscriminatorKind["NEGATIVE_MATCH"] = "negative_match";
|
|
13
|
-
})(DiscriminatorKind || (DiscriminatorKind = {}));
|
|
14
|
-
/** Who drafted a candidate: the LLM loop, or the MCP server's own pre-ranking. */
|
|
15
|
-
export var CandidateSource;
|
|
16
|
-
(function (CandidateSource) {
|
|
17
|
-
CandidateSource["AGENT"] = "agent";
|
|
18
|
-
CandidateSource["SERVER"] = "server";
|
|
19
|
-
})(CandidateSource || (CandidateSource = {}));
|
|
20
|
-
/**
|
|
21
|
-
* Slug of a scenario name alone (no content hash) — stable across independent
|
|
22
|
-
* drafts of "the same" scenario even when their steps[] differ (e.g. the
|
|
23
|
-
* server's auto-drafted version vs. the agent's own re-drafted version of a
|
|
24
|
-
* scenario with an identical name). Used to dedupe by scenario identity where
|
|
25
|
-
* `computeCandidateId`'s content-sensitive hash would wrongly treat two drafts
|
|
26
|
-
* of the same named scenario as distinct (SKYR-4026).
|
|
27
|
-
*/
|
|
28
|
-
export function scenarioNameSlug(scenarioName) {
|
|
29
|
-
return ((scenarioName ?? "")
|
|
30
|
-
.toLowerCase()
|
|
31
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
32
|
-
.replace(/^-+|-+$/g, "")
|
|
33
|
-
.slice(0, 48) || "scenario");
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Merge-dedup identity key for a scenario name — like {@link scenarioNameSlug}
|
|
37
|
-
* but NOT truncated and with no shared fallback for a missing name. The 48-char
|
|
38
|
-
* truncation in `scenarioNameSlug` is fine for `computeCandidateId` (combined
|
|
39
|
-
* with a content hash for uniqueness), but used bare as a Map key it would
|
|
40
|
-
* silently collapse two distinct scenarios sharing a long common prefix, and
|
|
41
|
-
* collapse every candidate with no scenarioName onto the same key. Returns ""
|
|
42
|
-
* for a missing name so the caller can fall back to something already unique
|
|
43
|
-
* (e.g. candidateId) instead of a shared literal.
|
|
44
|
-
*/
|
|
45
|
-
export function scenarioMergeKey(scenarioName) {
|
|
46
|
-
return (scenarioName ?? "")
|
|
47
|
-
.toLowerCase()
|
|
48
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
49
|
-
.replace(/^-+|-+$/g, "");
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Deterministic candidate id: a slug of the scenario name plus an 8-char content
|
|
53
|
-
* hash. Pure and reproducible — the same scenario always yields the same id, so
|
|
54
|
-
* a candidate registered in the plan can be matched back when its test is later
|
|
55
|
-
* generated. The hash covers the salient, stable structural fields (name,
|
|
56
|
-
* category, testType, and each step's method/path/interactionType/status) so
|
|
57
|
-
* cosmetic re-serialization does not change the id while a real edit does.
|
|
58
|
-
*/
|
|
59
|
-
export function computeCandidateId(scenario) {
|
|
60
|
-
const slug = scenarioNameSlug(scenario.scenarioName);
|
|
61
|
-
const canonical = {
|
|
62
|
-
scenarioName: scenario.scenarioName ?? "",
|
|
63
|
-
category: scenario.category ?? "",
|
|
64
|
-
testType: scenario.testType ?? "",
|
|
65
|
-
steps: (scenario.steps ?? []).map((s) => ({
|
|
66
|
-
method: s?.method ?? "",
|
|
67
|
-
path: s?.path ?? "",
|
|
68
|
-
interactionType: s?.interactionType ?? "",
|
|
69
|
-
expectedStatusCode: s?.expectedStatusCode ?? 0,
|
|
70
|
-
})),
|
|
71
|
-
};
|
|
72
|
-
const hash = crypto.createHash("sha256").update(JSON.stringify(canonical)).digest("hex").slice(0, 8);
|
|
73
|
-
return `${slug}-${hash}`;
|
|
74
|
-
}
|