@skyramp/mcp 0.3.6 → 0.3.8
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/prompts/code-reuse.js +17 -2
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +1 -1
- package/build/prompts/modularization/ui-test-modularization.js +9 -6
- package/build/prompts/pom-aware-code-reuse.js +1 -1
- package/build/prompts/shared-helper-policy.js +5 -5
- package/build/prompts/test-recommendation/diffExecutionPlan.js +40 -9
- package/build/prompts/test-recommendation/fullRepoCatalog.js +3 -2
- package/build/prompts/test-recommendation/recommendationSections.js +3 -3
- package/build/prompts/test-recommendation/scopeAssessment.d.ts +2 -2
- package/build/prompts/test-recommendation/scopeAssessment.js +58 -76
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +14 -1
- package/build/prompts/testbot/testbot-prompts.js +25 -8
- package/build/recommendation/budgeters/diversityBalancedBudgeter.js +39 -5
- package/build/recommendation/budgeters/shared.d.ts +21 -8
- package/build/recommendation/budgeters/shared.js +134 -56
- package/build/recommendation/planRanker.d.ts +5 -3
- package/build/recommendation/planRanker.js +1 -1
- package/build/services/TestGenerationService.js +15 -1
- package/build/tools/code-refactor/assertion-state.d.ts +91 -0
- package/build/tools/code-refactor/assertion-state.js +375 -0
- package/build/tools/code-refactor/codeReuseTool.js +6 -4
- package/build/tools/code-refactor/enhanceAssertionsTool.js +73 -18
- package/build/tools/code-refactor/retrofit-state.d.ts +53 -0
- package/build/tools/code-refactor/retrofit-state.js +162 -0
- package/build/tools/code-refactor/reuse-outcome.d.ts +7 -0
- package/build/tools/code-refactor/reuse-state.d.ts +9 -0
- package/build/tools/code-refactor/reuse-state.js +42 -4
- package/build/tools/code-refactor/utils-verify-gates.js +69 -15
- package/build/tools/executeSkyrampTestTool.js +19 -14
- package/build/tools/submitReportTool.js +87 -9
- package/build/tools/test-management/registerTestPlanTool.d.ts +3 -3
- package/build/tools/test-management/registerTestPlanTool.js +29 -8
- package/build/types/AssertionOutcome.d.ts +68 -0
- package/build/types/AssertionOutcome.js +1 -0
- package/build/types/RepositoryAnalysis.d.ts +10 -10
- package/build/types/ReuseOutcome.d.ts +16 -0
- package/build/types/TestRecommendation.d.ts +11 -1
- package/build/types/TestRecommendation.js +34 -0
- package/build/types/TestTypes.d.ts +4 -0
- package/build/types/TestTypes.js +8 -0
- package/build/types/TestbotReport.d.ts +13 -0
- package/build/types/index.d.ts +1 -1
- package/build/utils/AnalysisStateManager.d.ts +20 -7
- package/build/utils/assertion-verify/api-shared-lints.d.ts +5 -0
- package/build/utils/assertion-verify/api-shared-lints.js +315 -0
- package/build/utils/assertion-verify/contract-lints.d.ts +3 -0
- package/build/utils/assertion-verify/contract-lints.js +87 -0
- package/build/utils/assertion-verify/format.d.ts +5 -0
- package/build/utils/assertion-verify/format.js +65 -0
- package/build/utils/assertion-verify/helper-imports.d.ts +6 -0
- package/build/utils/assertion-verify/helper-imports.js +178 -0
- package/build/utils/assertion-verify/index.d.ts +3 -0
- package/build/utils/assertion-verify/index.js +7 -0
- package/build/utils/assertion-verify/integration-lints.d.ts +3 -0
- package/build/utils/assertion-verify/integration-lints.js +36 -0
- package/build/utils/assertion-verify/js-regex-blank.d.ts +1 -0
- package/build/utils/assertion-verify/js-regex-blank.js +153 -0
- package/build/utils/assertion-verify/lint-types.d.ts +33 -0
- package/build/utils/assertion-verify/lint-types.js +57 -0
- package/build/utils/assertion-verify/marker.d.ts +27 -0
- package/build/utils/assertion-verify/marker.js +61 -0
- package/build/utils/assertion-verify/metrics.d.ts +30 -0
- package/build/utils/assertion-verify/metrics.js +341 -0
- package/build/utils/assertion-verify/python-strip.d.ts +6 -0
- package/build/utils/assertion-verify/python-strip.js +75 -0
- package/build/utils/assertion-verify/strip-dispatch.d.ts +19 -0
- package/build/utils/assertion-verify/strip-dispatch.js +42 -0
- package/build/utils/assertion-verify/ui-lints.d.ts +8 -0
- package/build/utils/assertion-verify/ui-lints.js +244 -0
- package/build/utils/assertion-verify/verify.d.ts +61 -0
- package/build/utils/assertion-verify/verify.js +215 -0
- package/build/utils/featureFlags.d.ts +12 -2
- package/build/utils/featureFlags.js +33 -3
- package/build/utils/pathSignatures.d.ts +4 -1
- package/build/utils/pathSignatures.js +14 -2
- package/build/utils/planOnlyMode.d.ts +33 -0
- package/build/utils/planOnlyMode.js +40 -0
- package/build/utils/reportVerification.d.ts +9 -0
- package/build/utils/reportVerification.js +39 -6
- package/build/utils/scenarioDrafting.d.ts +1 -1
- package/build/utils/scenarioDrafting.js +1 -1
- package/build/utils/utils-verify/allow.d.ts +22 -4
- package/build/utils/utils-verify/allow.js +8 -2
- package/build/utils/utils-verify/call-sites.d.ts +40 -1
- package/build/utils/utils-verify/call-sites.js +196 -30
- package/build/utils/utils-verify/importers.d.ts +31 -0
- package/build/utils/utils-verify/importers.js +78 -0
- package/build/utils/utils-verify/index.d.ts +1 -0
- package/build/utils/utils-verify/index.js +1 -0
- package/build/utils/utils-verify/language-spec.d.ts +13 -2
- package/build/utils/utils-verify/language-spec.js +12 -2
- package/build/utils/utils-verify/parse.d.ts +31 -3
- package/build/utils/utils-verify/parse.js +190 -9
- package/build/utils/utils-verify/retrofit-equivalence.d.ts +43 -0
- package/build/utils/utils-verify/retrofit-equivalence.js +218 -0
- package/build/utils/utils-verify/stage.d.ts +6 -0
- package/build/utils/utils-verify/stage.js +12 -2
- package/build/utils/utils-verify/verify.d.ts +54 -4
- package/build/utils/utils-verify/verify.js +224 -12
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-CZfp96qZ.js → codeMirrorModule-LNgEKtdV.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-gpLo02E0.js → defaultSettingsView-Bwr1eMKC.js} +135 -135
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.Bq1r1URj.js → index.-Id052Lr.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.VEfqi1qN.js → uiMode.BPopbasy.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +2 -2
- package/build/tools/code-refactor/enhance-state.d.ts +0 -49
- package/build/tools/code-refactor/enhance-state.js +0 -109
|
@@ -12,9 +12,12 @@ import { StateManager, runArtifactDir, getTestsRepoDir, } from "../utils/Analysi
|
|
|
12
12
|
import { toolError, testFileMatches } from "../utils/utils.js";
|
|
13
13
|
import { matchesApprovedPlan } from "../utils/planMatchKeys.js";
|
|
14
14
|
import { isTestbotEnabled } from "../utils/featureFlags.js";
|
|
15
|
-
import { findInvalidSourceCitations, findUnchangedFileClaims, listChangedFiles, listChangedFilesAcross, } from "../utils/reportVerification.js";
|
|
15
|
+
import { findInvalidSourceCitations, findUnchangedFileClaims, listChangedFiles, listChangedFilesAcross, listChangedFilesAbs, } from "../utils/reportVerification.js";
|
|
16
|
+
import { isPlanOnlyMode } from "../utils/planOnlyMode.js";
|
|
16
17
|
import { getReportLanguage, isEnforcedReportLanguage, findLanguageViolations, findLanguageNearMisses, reportLanguageDisplayName, } from "../utils/reportLanguage.js";
|
|
17
|
-
import {
|
|
18
|
+
import { canonicalTestPath, findAssertionRecordByFileName, rederiveAssertionOutcome, } from "./code-refactor/assertion-state.js";
|
|
19
|
+
import { rederiveReuseOutcome, reuseChainSkipped, samePath, } from "./code-refactor/reuse-state.js";
|
|
20
|
+
import { retrofitGate, } from "./code-refactor/retrofit-state.js";
|
|
18
21
|
// SKYR-3879 Path B: which testTypes the register-plan checkpoint gates. Mirrors
|
|
19
22
|
// the generation tools actually wired to planGuard (batch-scenario/integration,
|
|
20
23
|
// contract) — UI and E2E are on a separate blueprint-grounded pipeline and are
|
|
@@ -341,7 +344,8 @@ const issueFoundSchema = z
|
|
|
341
344
|
.optional()
|
|
342
345
|
.describe("Issue severity. critical = feature broken/unusable (e.g. page doesn't load, data corruption). " +
|
|
343
346
|
"high = incorrect behavior (e.g. wrong calculation, stale data returned). " +
|
|
344
|
-
"medium = minor functional gap. low = cosmetic or informational."
|
|
347
|
+
"medium = minor functional gap. low = cosmetic or informational. " +
|
|
348
|
+
"Floor: a requirement conflict — the PR title/description (or a requirements file it references) states a requirement the implemented behavior contradicts — is 'high' or 'critical', never 'medium' or below."),
|
|
345
349
|
category: z
|
|
346
350
|
.nativeEnum(IssueFoundCategory)
|
|
347
351
|
.describe("Issue classification. bug = a product/code defect, e.g. found by a test or in the diff. " +
|
|
@@ -641,7 +645,26 @@ function computeReportMetrics(params) {
|
|
|
641
645
|
* gets `chainSkipped: true` (SKYR-4220), the one claim that is exactly about the
|
|
642
646
|
* reuse tool NOT having run. Applies to every test type: UI tests carry the POM
|
|
643
647
|
* fields, API tests the shared-helper ones. */
|
|
644
|
-
|
|
648
|
+
/** Attach the assertion-enhancement summary to a report row.
|
|
649
|
+
*
|
|
650
|
+
* How the row finds its record: records are keyed by the test file's full
|
|
651
|
+
* path, but a report row only carries a file NAME. So the match compares
|
|
652
|
+
* basenames, then checks testType and repository. When more than one record
|
|
653
|
+
* still matches, the row gets NO summary — attaching the wrong spec's
|
|
654
|
+
* numbers is worse than attaching none.
|
|
655
|
+
*
|
|
656
|
+
* The numbers are server-derived and re-computed from the delivered file at
|
|
657
|
+
* report time — the report narrative is LLM-authored, these numbers are not.
|
|
658
|
+
* What a reader can conclude: `executionCount: 0` = generated but never
|
|
659
|
+
* executed; no `assertions` field at all = the enhance tool never ran for
|
|
660
|
+
* the file and it never executed. */
|
|
661
|
+
async function attachAssertionOutcome(test, outcomes, checkouts) {
|
|
662
|
+
const record = findAssertionRecordByFileName(outcomes, test, checkouts);
|
|
663
|
+
if (!record)
|
|
664
|
+
return test;
|
|
665
|
+
return { ...test, assertions: await rederiveAssertionOutcome(record) };
|
|
666
|
+
}
|
|
667
|
+
async function attachReuseOutcome(test, outcomes, handOffs, retrofits = []) {
|
|
645
668
|
// POM records describe browser specs; a basename collision with an API test's
|
|
646
669
|
// fileName must not attach them there. A utils-path record is attachable anywhere.
|
|
647
670
|
const record = outcomes?.[path.basename(test.fileName)];
|
|
@@ -652,6 +675,18 @@ async function attachReuseOutcome(test, outcomes, handOffs) {
|
|
|
652
675
|
? record
|
|
653
676
|
: undefined;
|
|
654
677
|
const derived = found ? await rederiveReuseOutcome(found) : undefined;
|
|
678
|
+
// Pre-existing generated tests this spec's reuse pass rewired (SKYR-4276 A4): the
|
|
679
|
+
// report names each with its recorded execution, so a reviewer sees that the
|
|
680
|
+
// module became a dependency of code they already owned — and that it still runs.
|
|
681
|
+
if (derived?.helpers && found?.testFilePath) {
|
|
682
|
+
const specPath = found.testFilePath;
|
|
683
|
+
const mine = retrofits.filter((r) => samePath(r.testFile, specPath));
|
|
684
|
+
if (mine.length > 0)
|
|
685
|
+
derived.helpers.retrofits = mine.map((r) => ({
|
|
686
|
+
file: path.basename(r.file),
|
|
687
|
+
...(r.execution ? { execution: r.execution } : {}),
|
|
688
|
+
}));
|
|
689
|
+
}
|
|
655
690
|
// The RAW record: a colliding record of any kind means reuse ran for this basename,
|
|
656
691
|
// and the chain claim must not be made on the filtered view.
|
|
657
692
|
const chainSkipped = await reuseChainSkipped(test.fileName, test.testType, record, handOffs);
|
|
@@ -1036,6 +1071,11 @@ export function registerSubmitReportTool(server) {
|
|
|
1036
1071
|
"Add a testMaintenanceDetails entry with the missing field(s), drafted from the execution output you already saw.");
|
|
1037
1072
|
return errorResult;
|
|
1038
1073
|
}
|
|
1074
|
+
// SKYR-4276 A4: retrofits (pre-existing generated tests the reuse pass edited)
|
|
1075
|
+
// that still stand in the working tree, resolved once here and attached to the
|
|
1076
|
+
// rows below; an unexecuted one refuses the report (inside the SKYR-3883 block,
|
|
1077
|
+
// which already enumerates the working tree).
|
|
1078
|
+
let retrofitViews = [];
|
|
1039
1079
|
// SKYR-3883: in a testbot run, refuse to ship a report that claims file work
|
|
1040
1080
|
// the working tree doesn't reflect. The delivery step can only commit what the
|
|
1041
1081
|
// agent actually created/edited, so a report claiming otherwise erodes trust
|
|
@@ -1071,6 +1111,9 @@ export function registerSubmitReportTool(server) {
|
|
|
1071
1111
|
newTests: dedupedNewTests,
|
|
1072
1112
|
verdicts: stateData.maintenanceVerdicts ?? [],
|
|
1073
1113
|
primaryRepository: fullState?.metadata?.repository,
|
|
1114
|
+
// SKYR-4250: captured at prompt-render time, since planOnly is a
|
|
1115
|
+
// prompt-only argument (mirrors the SKYR-4185 language capture).
|
|
1116
|
+
planOnly: isPlanOnlyMode(),
|
|
1074
1117
|
});
|
|
1075
1118
|
if (unbacked.length > 0) {
|
|
1076
1119
|
// Offer the attribution-correction path explicitly and show what DID change:
|
|
@@ -1096,6 +1139,17 @@ export function registerSubmitReportTool(server) {
|
|
|
1096
1139
|
`Files with actual working-tree changes:\n${changedList || " (none)"}`);
|
|
1097
1140
|
return errorResult;
|
|
1098
1141
|
}
|
|
1142
|
+
const changedFilesAbs = await listChangedFilesAbs([
|
|
1143
|
+
repoRoot,
|
|
1144
|
+
getTestsRepoDir(),
|
|
1145
|
+
...Object.values(fullState?.relatedRepos ?? {}).map((section) => section.repositoryPath),
|
|
1146
|
+
]);
|
|
1147
|
+
const gate = await retrofitGate(stateData, changedFilesAbs, repoRoot);
|
|
1148
|
+
retrofitViews = gate.views;
|
|
1149
|
+
if (gate.refusal) {
|
|
1150
|
+
errorResult = toolError(gate.refusal);
|
|
1151
|
+
return errorResult;
|
|
1152
|
+
}
|
|
1099
1153
|
}
|
|
1100
1154
|
catch (err) {
|
|
1101
1155
|
// Never block a valid report because verification itself failed (path not a
|
|
@@ -1133,7 +1187,16 @@ export function registerSubmitReportTool(server) {
|
|
|
1133
1187
|
// generation — downstream scoring scripts don't expect them and fail if
|
|
1134
1188
|
// they encounter these string fields while traversing the object.
|
|
1135
1189
|
// Also normalize each item's `repository` (blank → undefined).
|
|
1136
|
-
|
|
1190
|
+
// Checkout roots let the assertion-record matcher verify a candidate
|
|
1191
|
+
// record actually lives in the row's repo (basename collisions across
|
|
1192
|
+
// repos must not publish one spec's proof-of-work under another's name).
|
|
1193
|
+
const assertionCheckouts = await stateManager
|
|
1194
|
+
.listRepoCheckouts()
|
|
1195
|
+
.catch(() => []);
|
|
1196
|
+
const sanitizedNewTests = await Promise.all(dedupedNewTests.map(async ({ scenarioFile: _sf, traceFile: _tf, frontendTrace: _ft, ...rest }) => {
|
|
1197
|
+
const row = await attachReuseOutcome(normalizeRepository(rest), stateData.reuseOutcomes, stateData.reuseHandOffs, retrofitViews);
|
|
1198
|
+
return attachAssertionOutcome(row, stateData.assertionOutcomes ?? {}, assertionCheckouts);
|
|
1199
|
+
}));
|
|
1137
1200
|
const report = {
|
|
1138
1201
|
businessCaseAnalysis: params.businessCaseAnalysis,
|
|
1139
1202
|
newTestsCreated: sanitizedNewTests,
|
|
@@ -1142,10 +1205,25 @@ export function registerSubmitReportTool(server) {
|
|
|
1142
1205
|
// an internal-only field, needed for matching but never meant to reach the report.
|
|
1143
1206
|
// TODO(multi-repo maintenance): map(normalizeRepository) once testMaintenanceSchema
|
|
1144
1207
|
// has a repository field (see TODO above).
|
|
1145
|
-
testMaintenance: testMaintenance
|
|
1146
|
-
...row
|
|
1147
|
-
|
|
1148
|
-
|
|
1208
|
+
testMaintenance: testMaintenance
|
|
1209
|
+
? await Promise.all(testMaintenance.map(async ({ testFilePath, ...row }) => {
|
|
1210
|
+
// Maintenance rows still carry the absolute path here, so the
|
|
1211
|
+
// assertion summary uses an EXACT canonical-path lookup — no
|
|
1212
|
+
// basename ambiguity. This is what carries the maintenance
|
|
1213
|
+
// honesty labels (nothing-to-verify vs verified) into the
|
|
1214
|
+
// report instead of leaving them as tool text the agent can
|
|
1215
|
+
// paraphrase.
|
|
1216
|
+
const record = stateData.assertionOutcomes?.[canonicalTestPath(testFilePath)];
|
|
1217
|
+
const assertions = record
|
|
1218
|
+
? await rederiveAssertionOutcome(record)
|
|
1219
|
+
: undefined;
|
|
1220
|
+
return {
|
|
1221
|
+
...row,
|
|
1222
|
+
fileName: path.basename(testFilePath),
|
|
1223
|
+
...(assertions ? { assertions } : {}),
|
|
1224
|
+
};
|
|
1225
|
+
}))
|
|
1226
|
+
: undefined,
|
|
1149
1227
|
// videoPath is filled from the run's execution records; testFilePath is the
|
|
1150
1228
|
// match-only key and is stripped from the wire format, the same line drawn for
|
|
1151
1229
|
// testMaintenance's own testFilePath above (downstream scoring scripts traverse
|
|
@@ -6,7 +6,7 @@ import { ChangedRoute } from "../../utils/changedRoutes.js";
|
|
|
6
6
|
declare const registerCandidateSchema: z.ZodObject<{
|
|
7
7
|
scenarioName: z.ZodString;
|
|
8
8
|
description: z.ZodString;
|
|
9
|
-
category: z.ZodEnum<["new_endpoint", "bug_caught", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
|
|
9
|
+
category: z.ZodEnum<["new_endpoint", "bug_caught", "requirement_conflict", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
|
|
10
10
|
priority: z.ZodEnum<["high", "medium", "low"]>;
|
|
11
11
|
testType: z.ZodEffects<z.ZodNativeEnum<typeof TestType>, TestType, TestType>;
|
|
12
12
|
steps: z.ZodArray<z.ZodObject<{
|
|
@@ -124,7 +124,7 @@ declare const registerCandidateSchema: z.ZodObject<{
|
|
|
124
124
|
priority: "high" | "medium" | "low";
|
|
125
125
|
testType: TestType;
|
|
126
126
|
scenarioName: string;
|
|
127
|
-
category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
127
|
+
category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
128
128
|
steps: {
|
|
129
129
|
path: string;
|
|
130
130
|
method: HttpMethod;
|
|
@@ -160,7 +160,7 @@ declare const registerCandidateSchema: z.ZodObject<{
|
|
|
160
160
|
priority: "high" | "medium" | "low";
|
|
161
161
|
testType: TestType;
|
|
162
162
|
scenarioName: string;
|
|
163
|
-
category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
163
|
+
category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
164
164
|
steps: {
|
|
165
165
|
path: string;
|
|
166
166
|
method: HttpMethod;
|
|
@@ -12,7 +12,7 @@ import { CandidateSource, computeCandidateId, scenarioMergeKey, DiscriminatorKin
|
|
|
12
12
|
import { selectPlan } from "../../recommendation/planRanker.js";
|
|
13
13
|
import { resolveSubjectEndpoints } from "../../utils/subjectEndpoints.js";
|
|
14
14
|
import { collectChangedRouteLines } from "../../utils/changedRoutes.js";
|
|
15
|
-
import {
|
|
15
|
+
import { generateFloors } from "../../recommendation/budgeters/shared.js";
|
|
16
16
|
import { inferScenarioType } from "../../recommendation/diversity.js";
|
|
17
17
|
import { validateDiscriminator } from "../../recommendation/discriminators.js";
|
|
18
18
|
import { isAttackSurfaceSecurityBoundary } from "../../prompts/test-recommendation/recommendationShared.js";
|
|
@@ -76,7 +76,7 @@ const discriminatorClaimSchema = z.object({
|
|
|
76
76
|
const registerCandidateSchema = z.object({
|
|
77
77
|
scenarioName: z.string().min(1).describe("Name for this test/scenario. Reused as the match key against the generation tool's scenarioName — keep it identical when you later call the generation tool."),
|
|
78
78
|
description: z.string().min(1),
|
|
79
|
-
category: z.enum(SCENARIO_CATEGORIES).describe("Test category — drives ranking (never your own priority tag)."),
|
|
79
|
+
category: z.enum(SCENARIO_CATEGORIES).describe("Test category — drives ranking (never your own priority tag). `bug_caught` targets a `<bug_found>` flaw from code review. `requirement_conflict` asserts a requirement the PR title/description (or a requirements file it references) states and the implemented behavior contradicts — it fails on the current code by design. Both rank in the top tier but are carved out independently, so a requirement conflict never loses its slot to a code-review bug."),
|
|
80
80
|
priority: z.enum(["high", "medium", "low"]).describe("Your own assessment — recorded, but ranking is derived from category, not this field."),
|
|
81
81
|
testType: z.nativeEnum(TestType).refine((t) => PLAN_TEST_TYPES.has(t), {
|
|
82
82
|
message: `testType must be one of ${[...PLAN_TEST_TYPES].join(", ")}`,
|
|
@@ -340,7 +340,26 @@ function renderGenerationDirective(plan) {
|
|
|
340
340
|
(nonUICount > 0 ? ` Generate the ${tests(nonUICount)} of other types as well.` : ""),
|
|
341
341
|
];
|
|
342
342
|
}
|
|
343
|
-
|
|
343
|
+
/** SKYR-4292: the server cannot count the diff's changed behaviors, so an
|
|
344
|
+
* under-budget plan is flagged (not rejected) with what a missing candidate looks
|
|
345
|
+
* like. Wipro PR #24 registered 3 candidates against a 20-slot UI-only budget and
|
|
346
|
+
* the tool answered "AUTHORITATIVE and FINAL" with no pushback. */
|
|
347
|
+
function renderBudgetCheck(plan, maxTotal) {
|
|
348
|
+
const filled = plan.generate.length + plan.additional.length;
|
|
349
|
+
if (filled >= maxTotal)
|
|
350
|
+
return [];
|
|
351
|
+
return [
|
|
352
|
+
"",
|
|
353
|
+
`### Budget check: ${filled} of ${maxTotal} slots filled`,
|
|
354
|
+
"Every observable behavior this diff changes — each option value added, removed, renamed or reordered; each label, " +
|
|
355
|
+
"element, route, state, validation rule, computed value or style value (spacing, size, color, radius, font weight, " +
|
|
356
|
+
"class name) that renders differently; each removed element, asserted absent — needs a candidate above, however " +
|
|
357
|
+
"small the change. A change is covered only when an existing test asserts the changed value, not when a test merely " +
|
|
358
|
+
"reaches the component. If any changed behavior has no candidate, call skyramp_register_test_plan again with your " +
|
|
359
|
+
"full updated list. If the diff truly changes fewer behaviors than the budget, this plan stands as-is.",
|
|
360
|
+
];
|
|
361
|
+
}
|
|
362
|
+
function renderPlanText(plan, dropped, maxTotal) {
|
|
344
363
|
const lines = [];
|
|
345
364
|
lines.push(`## Approved Test Plan (${plan.planId})`);
|
|
346
365
|
lines.push("");
|
|
@@ -402,6 +421,7 @@ function renderPlanText(plan, dropped) {
|
|
|
402
421
|
}
|
|
403
422
|
lines.push("");
|
|
404
423
|
lines.push(...renderGenerationDirective(plan));
|
|
424
|
+
lines.push(...renderBudgetCheck(plan, maxTotal));
|
|
405
425
|
return lines.join("\n");
|
|
406
426
|
}
|
|
407
427
|
// ── Tool registration ───────────────────────────────────────────────────────
|
|
@@ -544,10 +564,11 @@ export function registerRegisterTestPlanTool(server) {
|
|
|
544
564
|
// changes would silently plan zero UI tests (runs 32419025266,
|
|
545
565
|
// 32431042133, 32431106864) — reject so the agent self-corrects in one
|
|
546
566
|
// step, exactly like the empty-candidates rejection above.
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
errorResult = toolError(
|
|
550
|
-
"
|
|
567
|
+
const uiFloor = generateFloors(budgetContext).ui;
|
|
568
|
+
if (uiFloor > 0 && !allCandidates.some((c) => inferScenarioType(c.scenario) === TestType.UI)) {
|
|
569
|
+
errorResult = toolError(`This run has frontend changes, so the plan MUST include UI tests — at least ${uiFloor} of the ` +
|
|
570
|
+
"GENERATE slots are held for them, and more are open to UI candidates on rank. No submitted or " +
|
|
571
|
+
"recovered candidate has testType 'ui'. Resubmit your full candidate list including UI candidates " +
|
|
551
572
|
"for the changed frontend (see uiContext.changedFrontendFiles).");
|
|
552
573
|
return errorResult;
|
|
553
574
|
}
|
|
@@ -576,7 +597,7 @@ export function registerRegisterTestPlanTool(server) {
|
|
|
576
597
|
return errorResult;
|
|
577
598
|
}
|
|
578
599
|
return {
|
|
579
|
-
content: [{ type: "text", text: renderPlanText(approvedPlan, result.dropped) }],
|
|
600
|
+
content: [{ type: "text", text: renderPlanText(approvedPlan, result.dropped, budgetContext.maxTotal) }],
|
|
580
601
|
};
|
|
581
602
|
}
|
|
582
603
|
catch (error) {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { TestType } from "./TestTypes.js";
|
|
2
|
+
import type { EnhanceType } from "../prompts/enhance-assertions/sharedAssertionRules.js";
|
|
3
|
+
/**
|
|
4
|
+
* Server-derived assertion-enhancement state. Lives in types/ (not beside
|
|
5
|
+
* assertion-state.ts) because AnalysisStateManager embeds AssertionRecord in
|
|
6
|
+
* the run state while assertion-state.ts imports AnalysisStateManager — same
|
|
7
|
+
* layering as ReuseOutcome.
|
|
8
|
+
*/
|
|
9
|
+
/** A verdict a verify pass can produce and store. */
|
|
10
|
+
export type AssertionVerdict = "passed" | "failed";
|
|
11
|
+
/** What the report can say about verification: the stored verdicts plus
|
|
12
|
+
* "never-verified" — the report-time reading of a record that has no verdict
|
|
13
|
+
* yet (instructions were handed out or the spec was executed, but no verify
|
|
14
|
+
* pass ever ran). Only real verdicts are ever STORED (AssertionRecord);
|
|
15
|
+
* "never-verified" exists only on the wire type below. */
|
|
16
|
+
export type AssertionVerification = AssertionVerdict | "never-verified";
|
|
17
|
+
/** One spec's enhancement record in the run state, keyed by the spec's
|
|
18
|
+
* CANONICAL ABSOLUTE PATH (see assertion-state.ts `canonicalTestPath`).
|
|
19
|
+
*
|
|
20
|
+
* Two creation paths, which is why the baseline group is optional while the
|
|
21
|
+
* identity fields are not:
|
|
22
|
+
* - instruction hand-out (recordAssertionBaseline) writes the full record;
|
|
23
|
+
* - execution counting (recordAssertionExecution) creates a minimal record
|
|
24
|
+
* for a spec that executed without ever being enhanced — identity fields
|
|
25
|
+
* plus executionCount, no baseline group.
|
|
26
|
+
*/
|
|
27
|
+
export interface AssertionRecord {
|
|
28
|
+
/** Canonical path the record is for — kept in the record as a
|
|
29
|
+
* defense-in-depth ownership check on reads and writes. */
|
|
30
|
+
testFilePath: string;
|
|
31
|
+
testType: TestType;
|
|
32
|
+
/** Times skyramp_execute_test ran this spec (recorded server-side after the
|
|
33
|
+
* gates pass). Proof-of-work substrate: generated-but-never-executed and
|
|
34
|
+
* executed-but-never-enhanced both become visible. */
|
|
35
|
+
executionCount: number;
|
|
36
|
+
/** Absent on execution-only records (no hand-out happened). */
|
|
37
|
+
enhanceType?: EnhanceType;
|
|
38
|
+
/** File hash + metrics at instruction hand-out time — the verify baseline.
|
|
39
|
+
* Present as a group iff a hand-out happened. */
|
|
40
|
+
fileSha256?: string;
|
|
41
|
+
/** Copy of the generated file at hand-out time, saved under the run artifact
|
|
42
|
+
* dir (never the repo, so it cannot leak into the customer commit). Lets the
|
|
43
|
+
* agent diff/restore and gives evals a ground-truth pre-enhancement artifact. */
|
|
44
|
+
baselineFilePath?: string;
|
|
45
|
+
baselineCount?: number;
|
|
46
|
+
baselineStrength?: number;
|
|
47
|
+
baselineFingerprints?: string[];
|
|
48
|
+
issuedAt?: string;
|
|
49
|
+
/** Verdict of the last verify pass; absent until one runs. */
|
|
50
|
+
verification?: AssertionVerdict;
|
|
51
|
+
/** Reason from an accepted `assertions complete` decline marker. */
|
|
52
|
+
declinedReason?: string;
|
|
53
|
+
}
|
|
54
|
+
/** Report-facing proof-of-work summary attached to `newTestsCreated` rows —
|
|
55
|
+
* the compact wire projection of AssertionRecord (which stays internal: it
|
|
56
|
+
* carries fingerprints, hashes, and baseline paths the report must not).
|
|
57
|
+
* Server-derived, re-derived from the delivered file at report time, never
|
|
58
|
+
* supplied by the LLM. A missing `assertions` field on a row means no record
|
|
59
|
+
* exists at all — the enhance tool never ran and the spec never executed. */
|
|
60
|
+
export interface AssertionOutcome {
|
|
61
|
+
verification: AssertionVerification;
|
|
62
|
+
assertionsBaseline?: number;
|
|
63
|
+
assertionsFinal?: number;
|
|
64
|
+
strengthBaseline?: number;
|
|
65
|
+
strengthFinal?: number;
|
|
66
|
+
declinedReason?: string;
|
|
67
|
+
executionCount: number;
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1119,7 +1119,7 @@ export declare const scenarioStepSchema: z.ZodObject<{
|
|
|
1119
1119
|
export declare const draftedScenarioSchema: z.ZodObject<{
|
|
1120
1120
|
scenarioName: z.ZodString;
|
|
1121
1121
|
description: z.ZodString;
|
|
1122
|
-
category: z.ZodEnum<["new_endpoint", "bug_caught", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
|
|
1122
|
+
category: z.ZodEnum<["new_endpoint", "bug_caught", "requirement_conflict", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
|
|
1123
1123
|
priority: z.ZodEnum<["high", "medium", "low"]>;
|
|
1124
1124
|
steps: z.ZodArray<z.ZodObject<{
|
|
1125
1125
|
order: z.ZodNumber;
|
|
@@ -1231,7 +1231,7 @@ export declare const draftedScenarioSchema: z.ZodObject<{
|
|
|
1231
1231
|
description: string;
|
|
1232
1232
|
priority: "high" | "medium" | "low";
|
|
1233
1233
|
scenarioName: string;
|
|
1234
|
-
category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1234
|
+
category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1235
1235
|
steps: {
|
|
1236
1236
|
path: string;
|
|
1237
1237
|
method: string;
|
|
@@ -1268,7 +1268,7 @@ export declare const draftedScenarioSchema: z.ZodObject<{
|
|
|
1268
1268
|
description: string;
|
|
1269
1269
|
priority: "high" | "medium" | "low";
|
|
1270
1270
|
scenarioName: string;
|
|
1271
|
-
category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1271
|
+
category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1272
1272
|
steps: {
|
|
1273
1273
|
path: string;
|
|
1274
1274
|
method: string;
|
|
@@ -1607,7 +1607,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
|
|
|
1607
1607
|
draftedScenarios: z.ZodArray<z.ZodObject<{
|
|
1608
1608
|
scenarioName: z.ZodString;
|
|
1609
1609
|
description: z.ZodString;
|
|
1610
|
-
category: z.ZodEnum<["new_endpoint", "bug_caught", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
|
|
1610
|
+
category: z.ZodEnum<["new_endpoint", "bug_caught", "requirement_conflict", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"]>;
|
|
1611
1611
|
priority: z.ZodEnum<["high", "medium", "low"]>;
|
|
1612
1612
|
steps: z.ZodArray<z.ZodObject<{
|
|
1613
1613
|
order: z.ZodNumber;
|
|
@@ -1719,7 +1719,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
|
|
|
1719
1719
|
description: string;
|
|
1720
1720
|
priority: "high" | "medium" | "low";
|
|
1721
1721
|
scenarioName: string;
|
|
1722
|
-
category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1722
|
+
category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1723
1723
|
steps: {
|
|
1724
1724
|
path: string;
|
|
1725
1725
|
method: string;
|
|
@@ -1756,7 +1756,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
|
|
|
1756
1756
|
description: string;
|
|
1757
1757
|
priority: "high" | "medium" | "low";
|
|
1758
1758
|
scenarioName: string;
|
|
1759
|
-
category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1759
|
+
category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1760
1760
|
steps: {
|
|
1761
1761
|
path: string;
|
|
1762
1762
|
method: string;
|
|
@@ -1799,7 +1799,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
|
|
|
1799
1799
|
description: string;
|
|
1800
1800
|
priority: "high" | "medium" | "low";
|
|
1801
1801
|
scenarioName: string;
|
|
1802
|
-
category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1802
|
+
category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1803
1803
|
steps: {
|
|
1804
1804
|
path: string;
|
|
1805
1805
|
method: string;
|
|
@@ -1842,7 +1842,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
|
|
|
1842
1842
|
description: string;
|
|
1843
1843
|
priority: "high" | "medium" | "low";
|
|
1844
1844
|
scenarioName: string;
|
|
1845
|
-
category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1845
|
+
category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
1846
1846
|
steps: {
|
|
1847
1847
|
path: string;
|
|
1848
1848
|
method: string;
|
|
@@ -2743,7 +2743,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
|
|
|
2743
2743
|
description: string;
|
|
2744
2744
|
priority: "high" | "medium" | "low";
|
|
2745
2745
|
scenarioName: string;
|
|
2746
|
-
category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
2746
|
+
category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
2747
2747
|
steps: {
|
|
2748
2748
|
path: string;
|
|
2749
2749
|
method: string;
|
|
@@ -2956,7 +2956,7 @@ export declare const repositoryAnalysisSchema: z.ZodObject<{
|
|
|
2956
2956
|
description: string;
|
|
2957
2957
|
priority: "high" | "medium" | "low";
|
|
2958
2958
|
scenarioName: string;
|
|
2959
|
-
category: "new_endpoint" | "bug_caught" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
2959
|
+
category: "new_endpoint" | "bug_caught" | "requirement_conflict" | "business_rule" | "security_boundary" | "data_integrity" | "breaking_change" | "auth" | "error_handling" | "workflow" | "data_validation" | "crud";
|
|
2960
2960
|
steps: {
|
|
2961
2961
|
path: string;
|
|
2962
2962
|
method: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TestExecutionStatus } from "./TestExecution.js";
|
|
1
2
|
/**
|
|
2
3
|
* The code-reuse observability shape carried in a TestbotReport — the wire contract
|
|
3
4
|
* between this server and the consumers that render it (test-bot.git). It covers
|
|
@@ -85,6 +86,14 @@ export declare enum HelperVerificationOutcome {
|
|
|
85
86
|
/** The test neither wrote nor imports a shared utils file. */
|
|
86
87
|
NoUtilsFile = "no-utils-file"
|
|
87
88
|
}
|
|
89
|
+
/** A pre-existing Skyramp-generated test the reuse pass edited to import from the
|
|
90
|
+
* shared utils file. `execution` is the recorded post-edit run; a retrofit without
|
|
91
|
+
* one cannot be reported (skyramp_submit_report refuses). */
|
|
92
|
+
export interface RetrofitOutcome {
|
|
93
|
+
/** BASENAME of the edited test. */
|
|
94
|
+
file: string;
|
|
95
|
+
execution?: TestExecutionStatus;
|
|
96
|
+
}
|
|
88
97
|
/** Shared-helper reuse for an API test, re-derived from the delivered files at report
|
|
89
98
|
* time — nothing here is read back from what the agent or the verify pass recorded.
|
|
90
99
|
* Absent altogether when no utils file exists for the test (the same omission rule
|
|
@@ -105,6 +114,13 @@ export interface HelperReuseOutcome {
|
|
|
105
114
|
* per method+path, status-code-only assertions, method+resource names — with
|
|
106
115
|
* documented declines (`reuse-verify: allow …`) counted as holding. */
|
|
107
116
|
verification?: HelperVerificationOutcome;
|
|
117
|
+
/** Pre-existing generated tests this spec's reuse pass rewired onto the utils
|
|
118
|
+
* file, each with its recorded execution. Omitted when none. */
|
|
119
|
+
retrofits?: RetrofitOutcome[];
|
|
120
|
+
/** Whether the delivered test plus the helpers it imports still hold every
|
|
121
|
+
* assertion (and page-error guard) present when modularization was handed out.
|
|
122
|
+
* Present only when that baseline exists. */
|
|
123
|
+
assertionsPreserved?: boolean;
|
|
108
124
|
}
|
|
109
125
|
/**
|
|
110
126
|
* What lands in a report's `reuse` field for a generated test. Every member is
|
|
@@ -12,7 +12,7 @@ export declare enum Novelty {
|
|
|
12
12
|
EXISTING = "existing"
|
|
13
13
|
}
|
|
14
14
|
/** All categories including internal ones. */
|
|
15
|
-
export declare const SCENARIO_CATEGORIES: readonly ["new_endpoint", "bug_caught", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"];
|
|
15
|
+
export declare const SCENARIO_CATEGORIES: readonly ["new_endpoint", "bug_caught", "requirement_conflict", "business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"];
|
|
16
16
|
export type ScenarioCategory = typeof SCENARIO_CATEGORIES[number];
|
|
17
17
|
/** Categories valid for tool submissions (excludes internal-only categories). */
|
|
18
18
|
export declare const TEST_CATEGORIES: readonly ["business_rule", "security_boundary", "data_integrity", "breaking_change", "auth", "error_handling", "workflow", "data_validation", "crud"];
|
|
@@ -21,3 +21,13 @@ export type TestCategory = typeof TEST_CATEGORIES[number];
|
|
|
21
21
|
export declare const CATEGORY_PRIORITY: Record<ScenarioCategory, PriorityTier>;
|
|
22
22
|
/** Map internal-only categories to their external equivalent for tool submission. */
|
|
23
23
|
export declare function externalCategory(cat: ScenarioCategory): TestCategory;
|
|
24
|
+
/**
|
|
25
|
+
* Categories whose scenarios target a specific identified defect — a code flaw
|
|
26
|
+
* (`bug_caught`) or a stated requirement the code contradicts
|
|
27
|
+
* (`requirement_conflict`). They survive external-test dedup: an existing test on
|
|
28
|
+
* the same endpoint exercises the surface, not the flaw, so removing them would
|
|
29
|
+
* drop the only test that fails on the defect.
|
|
30
|
+
*/
|
|
31
|
+
export declare const FLAW_TARGETING_CATEGORIES: readonly ["bug_caught", "requirement_conflict"];
|
|
32
|
+
/** Whether `category` targets a specific identified defect (see {@link FLAW_TARGETING_CATEGORIES}). */
|
|
33
|
+
export declare function isFlawTargetingCategory(category: ScenarioCategory | undefined): boolean;
|
|
@@ -17,6 +17,14 @@ export var Novelty;
|
|
|
17
17
|
const INTERNAL_CATEGORIES = [
|
|
18
18
|
"new_endpoint", // MEDIUM - diff-direct scenario; where a test came from, not a guarantee of a slot
|
|
19
19
|
"bug_caught", // CRITICAL - tests targeting a specific <bug_found> flaw identified during enrichment
|
|
20
|
+
// CRITICAL - tests asserting a requirement the PR title/description (or a
|
|
21
|
+
// requirements file it references) states, which the implemented behavior
|
|
22
|
+
// contradicts. Separate from bug_caught deliberately (SKYR-4291): labelled
|
|
23
|
+
// bug_caught, a requirement-vs-code mismatch competed with the code-review
|
|
24
|
+
// flaws for the same promotion and lost it on severity. Its own category means
|
|
25
|
+
// its own carve-out here, first place in the promotion order SKYR-4275's bound
|
|
26
|
+
// hands out, and its own coverage gate.
|
|
27
|
+
"requirement_conflict",
|
|
20
28
|
];
|
|
21
29
|
/** External categories valid for tool submissions, ordered by priority. */
|
|
22
30
|
const CATEGORIES = [
|
|
@@ -59,6 +67,12 @@ export const CATEGORY_PRIORITY = {
|
|
|
59
67
|
// fill GENERATE.
|
|
60
68
|
new_endpoint: PriorityTier.MEDIUM,
|
|
61
69
|
bug_caught: PriorityTier.CRITICAL, // tests targeting a <bug_found> flaw — always in GENERATE
|
|
70
|
+
// A stated requirement the implementation contradicts is the point of the PR,
|
|
71
|
+
// so it sits in the same top tier as bug_caught. Being its own category, it is
|
|
72
|
+
// carved out separately in planRanker and takes the FIRST slot of the promotion
|
|
73
|
+
// bound SKYR-4275 sets — the code-review flaws promote into what is left, so a
|
|
74
|
+
// requirement conflict is never the finding that loses on severity.
|
|
75
|
+
requirement_conflict: PriorityTier.CRITICAL,
|
|
62
76
|
business_rule: PriorityTier.HIGH, // formula/business-logic bugs are high priority
|
|
63
77
|
security_boundary: PriorityTier.HIGH,
|
|
64
78
|
data_integrity: PriorityTier.HIGH,
|
|
@@ -75,5 +89,25 @@ export function externalCategory(cat) {
|
|
|
75
89
|
return "crud";
|
|
76
90
|
if (cat === "bug_caught")
|
|
77
91
|
return "business_rule";
|
|
92
|
+
// The stated requirement IS the business rule the test asserts — same landing
|
|
93
|
+
// spot as bug_caught, so a requirement-conflict test reads as a rule check in
|
|
94
|
+
// the customer-facing report rather than an unrecognised label.
|
|
95
|
+
if (cat === "requirement_conflict")
|
|
96
|
+
return "business_rule";
|
|
78
97
|
return cat;
|
|
79
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Categories whose scenarios target a specific identified defect — a code flaw
|
|
101
|
+
* (`bug_caught`) or a stated requirement the code contradicts
|
|
102
|
+
* (`requirement_conflict`). They survive external-test dedup: an existing test on
|
|
103
|
+
* the same endpoint exercises the surface, not the flaw, so removing them would
|
|
104
|
+
* drop the only test that fails on the defect.
|
|
105
|
+
*/
|
|
106
|
+
export const FLAW_TARGETING_CATEGORIES = [
|
|
107
|
+
"bug_caught",
|
|
108
|
+
"requirement_conflict",
|
|
109
|
+
];
|
|
110
|
+
/** Whether `category` targets a specific identified defect (see {@link FLAW_TARGETING_CATEGORIES}). */
|
|
111
|
+
export function isFlawTargetingCategory(category) {
|
|
112
|
+
return category !== undefined && FLAW_TARGETING_CATEGORIES.includes(category);
|
|
113
|
+
}
|
|
@@ -246,3 +246,7 @@ export declare const codeRefactoringSchema: z.ZodObject<{
|
|
|
246
246
|
codeReuse?: boolean | undefined;
|
|
247
247
|
modularizeCode?: boolean | undefined;
|
|
248
248
|
}>;
|
|
249
|
+
/** Test types skyramp_enhance_assertions supports — single-sourced so the
|
|
250
|
+
* tool's schema enum and generation-time baseline seeding cannot drift
|
|
251
|
+
* (contract additionally requires provider mode at the seeding site). */
|
|
252
|
+
export declare const ASSERTION_ENHANCEABLE_TEST_TYPES: readonly [TestType.INTEGRATION, TestType.CONTRACT, TestType.UI];
|
package/build/types/TestTypes.js
CHANGED
|
@@ -274,3 +274,11 @@ export const codeRefactoringSchema = z.object({
|
|
|
274
274
|
.default(false)
|
|
275
275
|
.describe("Whether to modularize the code"),
|
|
276
276
|
});
|
|
277
|
+
/** Test types skyramp_enhance_assertions supports — single-sourced so the
|
|
278
|
+
* tool's schema enum and generation-time baseline seeding cannot drift
|
|
279
|
+
* (contract additionally requires provider mode at the seeding site). */
|
|
280
|
+
export const ASSERTION_ENHANCEABLE_TEST_TYPES = [
|
|
281
|
+
TestType.INTEGRATION,
|
|
282
|
+
TestType.CONTRACT,
|
|
283
|
+
TestType.UI,
|
|
284
|
+
];
|
|
@@ -2,6 +2,7 @@ import type { DriftAction } from "./TestAnalysis.js";
|
|
|
2
2
|
import type { TestExecutionStatus } from "./TestExecution.js";
|
|
3
3
|
import type { HttpMethod, TestType } from "./TestTypes.js";
|
|
4
4
|
import type { ReuseOutcome } from "./ReuseOutcome.js";
|
|
5
|
+
import type { AssertionOutcome } from "./AssertionOutcome.js";
|
|
5
6
|
/** Classification of an issuesFound entry. Bug is a product/code defect; the
|
|
6
7
|
* other values are tooling noise that the Testbot report renders in its
|
|
7
8
|
* Configuration Errors section instead of Issues Found. */
|
|
@@ -39,6 +40,14 @@ export interface TestbotReport {
|
|
|
39
40
|
* rest of this file mirrors Zod schemas by hand, which is why only this field
|
|
40
41
|
* is imported). Absent in reports from MCP versions predating it. */
|
|
41
42
|
reuse?: ReuseOutcome;
|
|
43
|
+
/** Assertion-enhancement proof-of-work summary, present when
|
|
44
|
+
* skyramp_enhance_assertions or skyramp_execute_test recorded state for
|
|
45
|
+
* this test. Server-derived like `reuse` (imported for the same reason);
|
|
46
|
+
* cross-checks the LLM-authored narrative against server-observed facts:
|
|
47
|
+
* verification status, before/after assertion metrics, decline reason,
|
|
48
|
+
* and how many times the test actually executed. Absent in reports from
|
|
49
|
+
* MCP versions predating it. */
|
|
50
|
+
assertions?: AssertionOutcome;
|
|
42
51
|
}[];
|
|
43
52
|
/** Omitted when no existing tests were found (agent omits the field). */
|
|
44
53
|
testMaintenance?: {
|
|
@@ -51,6 +60,10 @@ export interface TestbotReport {
|
|
|
51
60
|
beforeDetails: string;
|
|
52
61
|
afterStatus: TestExecutionStatus;
|
|
53
62
|
afterDetails: string;
|
|
63
|
+
/** See newTestsCreated[].assertions — maintenance rows are matched by
|
|
64
|
+
* exact canonical path, so this carries the maintenance honesty labels
|
|
65
|
+
* (nothing-to-verify vs verified) into the report. */
|
|
66
|
+
assertions?: AssertionOutcome;
|
|
54
67
|
}[];
|
|
55
68
|
testResults: {
|
|
56
69
|
testType: TestType;
|
package/build/types/index.d.ts
CHANGED
|
@@ -4,5 +4,5 @@ export { TestType, HttpMethod } from "./TestTypes.js";
|
|
|
4
4
|
export type { TestbotReport } from "./TestbotReport.js";
|
|
5
5
|
export { IssueFoundCategory } from "./TestbotReport.js";
|
|
6
6
|
export { ReuseDeclinedBy, ReuseVerificationOutcome, HelperVerificationOutcome, } from "./ReuseOutcome.js";
|
|
7
|
-
export type { ReuseOutcome, ReuseSkippedEntry, ReuseMissedEntry, HelperReuseOutcome, } from "./ReuseOutcome.js";
|
|
7
|
+
export type { ReuseOutcome, ReuseSkippedEntry, ReuseMissedEntry, HelperReuseOutcome, RetrofitOutcome, } from "./ReuseOutcome.js";
|
|
8
8
|
export type { RelatedRepository, TestbotPromptOptions, } from "./TestbotPromptOptions.js";
|