@skyramp/mcp 0.4.1-rc.1 → 0.4.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/prompts/enhance-assertions/contractProviderAssertionsPrompt.js +2 -1
- package/build/prompts/enhance-assertions/integrationAssertionsPrompt.js +2 -1
- package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +1 -1
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +41 -22
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +17 -9
- package/build/prompts/test-recommendation/diffExecutionPlan.js +0 -2
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +8 -2
- package/build/prompts/testbot/testbot-prompts.js +12 -7
- package/build/recommendation/registerPlan.d.ts +5 -1
- package/build/recommendation/registerPlan.js +5 -0
- package/build/recommendation/types.d.ts +25 -2
- package/build/recommendation/verifierContracts.d.ts +16 -4
- package/build/recommendation/verifierContracts.js +20 -4
- package/build/recommendation/verifiers/coverage.d.ts +10 -0
- package/build/recommendation/verifiers/coverage.js +144 -22
- package/build/recommendation/verifiers/deliveredMatchesPlan.d.ts +22 -0
- package/build/recommendation/verifiers/deliveredMatchesPlan.js +43 -0
- package/build/recommendation/verifiers/existingCoverage.js +53 -0
- package/build/recommendation/verifiers/expectedValueSourced.js +111 -14
- package/build/services/TestGenerationService.js +3 -1
- package/build/tools/code-refactor/codeReuseTool.js +1 -1
- package/build/tools/code-refactor/reuse-outcome.d.ts +1 -1
- package/build/tools/code-refactor/reuse-state.d.ts +85 -7
- package/build/tools/code-refactor/reuse-state.js +239 -34
- package/build/tools/code-refactor/utils-verify-gates.d.ts +5 -0
- package/build/tools/code-refactor/utils-verify-gates.js +103 -11
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +2 -1
- package/build/tools/submitReportTool.js +259 -38
- package/build/tools/test-management/actionsTool.js +5 -0
- package/build/tools/test-management/analyzeChangesTool.d.ts +53 -0
- package/build/tools/test-management/analyzeChangesTool.js +55 -2
- package/build/tools/test-management/registerTestPlanTool.d.ts +2 -1
- package/build/tools/test-management/registerTestPlanTool.js +29 -14
- package/build/types/ReuseOutcome.d.ts +73 -7
- package/build/types/TestAnalysis.d.ts +6 -0
- package/build/types/TestbotReport.d.ts +15 -1
- package/build/utils/AnalysisStateManager.d.ts +7 -1
- package/build/utils/AnalysisStateManager.js +5 -1
- package/build/utils/assertion-verify/api-shared-lints.js +71 -34
- package/build/utils/assertion-verify/format.js +2 -2
- package/build/utils/assertion-verify/helper-imports.d.ts +7 -0
- package/build/utils/assertion-verify/helper-imports.js +119 -27
- package/build/utils/assertion-verify/lint-types.d.ts +31 -2
- package/build/utils/assertion-verify/lint-types.js +66 -0
- package/build/utils/assertion-verify/metrics.d.ts +13 -0
- package/build/utils/assertion-verify/metrics.js +16 -0
- package/build/utils/assertion-verify/verify.d.ts +11 -6
- package/build/utils/assertion-verify/verify.js +56 -15
- package/build/utils/canonicalJson.d.ts +11 -0
- package/build/utils/canonicalJson.js +17 -0
- package/build/utils/utils-verify/action-key.d.ts +27 -0
- package/build/utils/utils-verify/action-key.js +292 -0
- package/build/utils/utils-verify/allow.d.ts +8 -1
- package/build/utils/utils-verify/allow.js +14 -1
- package/build/utils/utils-verify/call-sites.d.ts +76 -8
- package/build/utils/utils-verify/call-sites.js +256 -70
- package/build/utils/utils-verify/language-spec.d.ts +3 -2
- package/build/utils/utils-verify/parse.d.ts +22 -3
- package/build/utils/utils-verify/parse.js +123 -52
- package/build/utils/utils-verify/verify.d.ts +33 -3
- package/build/utils/utils-verify/verify.js +126 -12
- package/build/utils/workspaceAuth.d.ts +59 -19
- package/build/utils/workspaceAuth.js +228 -31
- package/package.json +1 -1
- package/plugin/prompts/generate-tests/execution-plan.md +1 -1
- package/plugin/prompts/generate-tests/generation.md +1 -0
- package/plugin/prompts/plan-tests.md +33 -16
|
@@ -2,6 +2,8 @@ import { allChangedFiles } from "../types.js";
|
|
|
2
2
|
import { changedPathSet, normalizeCitedPath } from "./citedPath.js";
|
|
3
3
|
import { EXPECTED_VALUE_SOURCED_CONTRACT as CONTRACT } from "../verifierContracts.js";
|
|
4
4
|
import { appearsIn, searchable } from "../pullRequestText.js";
|
|
5
|
+
import { escapeRegExp } from "../../utils/regex.js";
|
|
6
|
+
import { renderCase } from "./coverage.js";
|
|
5
7
|
function readSource(raw) {
|
|
6
8
|
const source = typeof raw === "string" ? raw.trim() : "";
|
|
7
9
|
if (source.length === 0)
|
|
@@ -28,6 +30,71 @@ function readSource(raw) {
|
|
|
28
30
|
function caseLabel(change, entry) {
|
|
29
31
|
return `${String(change?.id ?? "?")}:${String(entry?.param ?? "?")}`;
|
|
30
32
|
}
|
|
33
|
+
/** Every number the pull request writes, as a number. One value has many
|
|
34
|
+
* spellings — `1e21`, `1e+21`, `1E21`, the digits in full — and matching the
|
|
35
|
+
* value's OWN spelling refused every other one, so a pull request stating the
|
|
36
|
+
* limit as `1e21` did not state `1e21`.
|
|
37
|
+
*
|
|
38
|
+
* A token glued to a word or to another number is not a number the text states:
|
|
39
|
+
* that is what keeps `3600` from stating 60, `159.9989` from stating 159.998, and
|
|
40
|
+
* the `60` of `1e60` from being a number at all. The sign belongs to the token, so
|
|
41
|
+
* `-60` states -60 and not 60, while `+60` states 60. `Number` reads the rest:
|
|
42
|
+
* `1599.80` is 1599.8 and `10.0` is 10. */
|
|
43
|
+
function statedNumbers(prText) {
|
|
44
|
+
const stated = [];
|
|
45
|
+
for (const match of prText.matchAll(/[+-]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g)) {
|
|
46
|
+
const token = match[0];
|
|
47
|
+
const start = match.index ?? 0;
|
|
48
|
+
const end = start + token.length;
|
|
49
|
+
const before = prText[start - 1] ?? "";
|
|
50
|
+
const after = prText[end] ?? "";
|
|
51
|
+
if (/[\dA-Za-z.]/.test(before))
|
|
52
|
+
continue;
|
|
53
|
+
if (/[\dA-Za-z]/.test(after))
|
|
54
|
+
continue;
|
|
55
|
+
// A dot that carries more digits makes this the first part of something
|
|
56
|
+
// longer — a version, a list — rather than a number of its own.
|
|
57
|
+
if (after === "." && /\d/.test(prText[end + 1] ?? ""))
|
|
58
|
+
continue;
|
|
59
|
+
const parsed = Number(token);
|
|
60
|
+
if (Number.isFinite(parsed))
|
|
61
|
+
stated.push(parsed);
|
|
62
|
+
}
|
|
63
|
+
return stated;
|
|
64
|
+
}
|
|
65
|
+
/** Whether the pull request writes this word or phrase, on its own rather than
|
|
66
|
+
* inside a longer word. A plain substring let `active` read itself out of
|
|
67
|
+
* `inactive`. The boundary is a letter or a digit, so a phrase that ends in
|
|
68
|
+
* punctuation still matches the sentence that carries it. */
|
|
69
|
+
function statesTheString(prText, value) {
|
|
70
|
+
const folded = value.replace(/\s+/g, " ").trim().toLowerCase();
|
|
71
|
+
if (!folded)
|
|
72
|
+
return false;
|
|
73
|
+
return new RegExp(`(?<![\\p{L}\\p{N}])${escapeRegExp(folded)}(?![\\p{L}\\p{N}])`, "u").test(prText);
|
|
74
|
+
}
|
|
75
|
+
/** Whether the pull request writes this value, anywhere in the title or the
|
|
76
|
+
* description. A string or a literal — `true`, `false`, `null` — has to appear as
|
|
77
|
+
* a word of its own; a number is compared against the numbers the text writes,
|
|
78
|
+
* whatever spelling either side uses.
|
|
79
|
+
*
|
|
80
|
+
* TRUE for a value that is not there to state: a case whose expectation is only a
|
|
81
|
+
* `derived` rule declares no value, and the rule is checked through the change's
|
|
82
|
+
* quote instead. */
|
|
83
|
+
function statesTheValue(prText, value) {
|
|
84
|
+
if (typeof value === "string")
|
|
85
|
+
return statesTheString(prText, value);
|
|
86
|
+
// Three values with no spelling of their own. An empty spelling list used to read
|
|
87
|
+
// as "stated", so a case could claim `true` from a pull request that writes the
|
|
88
|
+
// opposite.
|
|
89
|
+
if (value === true || value === false || value === null)
|
|
90
|
+
return statesTheString(prText, String(value));
|
|
91
|
+
// Nothing to state: `undefined` is the derived-only case, and a non-finite
|
|
92
|
+
// number has no spelling a pull request could carry.
|
|
93
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
94
|
+
return true;
|
|
95
|
+
return statedNumbers(prText).includes(value);
|
|
96
|
+
}
|
|
97
|
+
const text = (value) => (typeof value === "string" ? value.trim() : "");
|
|
31
98
|
/** Verifier 10. A case that states what the response must carry says where that
|
|
32
99
|
* value came from, and the value did not come from the code under test.
|
|
33
100
|
*
|
|
@@ -52,6 +119,7 @@ export const expectedValueSourced = {
|
|
|
52
119
|
const changed = changedPathSet(allChangedFiles(ctx), true);
|
|
53
120
|
const readable = typeof ctx?.citedFileExists === "function" ? ctx.citedFileExists : undefined;
|
|
54
121
|
const prText = searchable(ctx?.pullRequest ?? { title: "", description: "" });
|
|
122
|
+
const raised = new Set();
|
|
55
123
|
const changes = Array.isArray(registration.changes) ? registration.changes : [];
|
|
56
124
|
for (const change of changes) {
|
|
57
125
|
const changeId = String(change?.id ?? "?");
|
|
@@ -60,7 +128,11 @@ export const expectedValueSourced = {
|
|
|
60
128
|
// `undefined` is "this case states no expected value"; `null` is a stated
|
|
61
129
|
// expectation that the field comes back null, which needs a source like
|
|
62
130
|
// any other value.
|
|
63
|
-
|
|
131
|
+
// A case states an expectation either as a value or as the rule behind one,
|
|
132
|
+
// and BOTH are read from somewhere: crediting `derived` without reading its
|
|
133
|
+
// `expectedFrom` let a rule "read" off the running app satisfy the check
|
|
134
|
+
// this verifier exists to be.
|
|
135
|
+
if (!entry || (entry.expectedValue === undefined && text(entry.derived).length === 0))
|
|
64
136
|
return;
|
|
65
137
|
const label = caseLabel(change, entry);
|
|
66
138
|
const where = `${label} (source "${String(entry.expectedFrom ?? "")}")`;
|
|
@@ -69,27 +141,45 @@ export const expectedValueSourced = {
|
|
|
69
141
|
// answer that addressed two of them and closed, and the id an answer is
|
|
70
142
|
// carried forward by must therefore name the case as well as the change.
|
|
71
143
|
const param = String(entry?.param ?? "").trim() || `case-${index + 1}`;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
144
|
+
// The CASE, not its parameter. Two bound cases on one parameter each invent
|
|
145
|
+
// their own value, and keying on the parameter published one of them and
|
|
146
|
+
// carried its answer to the other. The rendering is the case's content, so
|
|
147
|
+
// it is the same string whatever order the cases arrive in — the identity
|
|
148
|
+
// `coverage:cases` already uses.
|
|
149
|
+
const caseId = renderCase({ param, value: entry.value, absent: entry.absent, expect: String(entry.expect ?? "") });
|
|
150
|
+
const raise = (kind, objection, evidence) => {
|
|
151
|
+
if (raised.has(`${kind}:${changeId}:${caseId}`))
|
|
152
|
+
return;
|
|
153
|
+
raised.add(`${kind}:${changeId}:${caseId}`);
|
|
154
|
+
objections.push({
|
|
155
|
+
objectionId: `expectedValueSourced:${kind}:${changeId}:${caseId}`,
|
|
156
|
+
verifier: "expectedValueSourced",
|
|
157
|
+
message: objection.message,
|
|
158
|
+
evidence,
|
|
159
|
+
suggestion: objection.suggestion,
|
|
160
|
+
});
|
|
161
|
+
};
|
|
79
162
|
if (source.kind === "none") {
|
|
80
163
|
raise("unsourced", CONTRACT.objections.missingSource, `expected value with no usable \`expectedFrom\`: ${where}`);
|
|
81
164
|
}
|
|
82
165
|
else if (source.kind === "code") {
|
|
83
|
-
raise("code", CONTRACT.objections.sourcedFromCode,
|
|
166
|
+
raise("code", CONTRACT.objections.sourcedFromCode,
|
|
167
|
+
// A derived-only case pins no value, so quoting one read `expected
|
|
168
|
+
// undefined` — a sentence naming nothing the agent can act on.
|
|
169
|
+
`expected value read from the code under test: ${label} (${entry.expectedValue === undefined
|
|
170
|
+
? `derived as "${text(entry.derived)}"`
|
|
171
|
+
: `expected ${JSON.stringify(entry.expectedValue)}`})`);
|
|
84
172
|
}
|
|
85
173
|
else if (source.kind === "unresolvable") {
|
|
86
174
|
// A path no repository file can answer, so the citation opens nothing.
|
|
87
175
|
raise("unreadable", CONTRACT.objections.unreadableSource, `source file not in the checkout: ${where}`);
|
|
88
176
|
}
|
|
89
177
|
else if (source.kind === "pullRequest") {
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
// objected to five correct
|
|
178
|
+
// Two readings, and the quote settles which one applies. A rule the pull
|
|
179
|
+
// request states decides values it never writes — "over 60 is rejected"
|
|
180
|
+
// states no 61, and demanding the value itself objected to five correct
|
|
181
|
+
// cases in run 34414643135 — so a case whose source states a rule puts
|
|
182
|
+
// that rule in `derived` and declares no number.
|
|
93
183
|
const quote = String(change?.quote ?? "").trim();
|
|
94
184
|
if (quote.length === 0) {
|
|
95
185
|
raise("noQuote", CONTRACT.objections.quoteMissing, `${label} reads its value from the pull request; change ${changeId} states no \`quote\``);
|
|
@@ -97,6 +187,11 @@ export const expectedValueSourced = {
|
|
|
97
187
|
else if (!appearsIn(prText, quote)) {
|
|
98
188
|
raise("noQuote", CONTRACT.objections.quoteMissing, `${label} reads its value from the pull request; change ${changeId} quotes \`${quote}\`, which is in neither the title nor the description`);
|
|
99
189
|
}
|
|
190
|
+
else if (!statesTheValue(prText, entry.expectedValue)) {
|
|
191
|
+
// `derived` is the case saying it pins NO number, so a case that still
|
|
192
|
+
// carries one is not excused by it: the number is what has to be stated.
|
|
193
|
+
raise("valueNotQuoted", CONTRACT.objections.valueNotQuoted, `${label} reads ${JSON.stringify(entry.expectedValue)} from the pull request, which writes no such value${text(entry.derived) ? " — and `derived` does not stand in for a value the case still declares" : ""}`);
|
|
194
|
+
}
|
|
100
195
|
}
|
|
101
196
|
else if (!source.file) {
|
|
102
197
|
// A kind that names no file and is not the pull request has nothing to check.
|
|
@@ -129,7 +224,9 @@ export const expectedValueSourced = {
|
|
|
129
224
|
const cited = changes.filter((change) => citedIds.includes(String(change?.id ?? "").trim()));
|
|
130
225
|
if (cited.length === 0)
|
|
131
226
|
continue;
|
|
132
|
-
|
|
227
|
+
// `derived` counts: it states the rule the value follows from, which is a
|
|
228
|
+
// stated value in every sense except that no number is written down.
|
|
229
|
+
const statesAValue = cited.some((change) => (Array.isArray(change?.cases) ? change.cases : []).some((entry) => entry && (entry.expectedValue !== undefined || text(entry.derived).length > 0)));
|
|
133
230
|
if (statesAValue)
|
|
134
231
|
continue;
|
|
135
232
|
const plannedTestId = String(plannedTest?.plannedTestId ?? "").trim();
|
|
@@ -140,7 +237,7 @@ export const expectedValueSourced = {
|
|
|
140
237
|
message: CONTRACT.objections.failWithoutValue.message,
|
|
141
238
|
evidence: `planned test ${plannedTestId || "(unnamed)"} expects to fail and cites ${cited
|
|
142
239
|
.map((change) => String(change?.id ?? "?"))
|
|
143
|
-
.join(", ")}; no case on those changes states an \`expectedValue
|
|
240
|
+
.join(", ")}; no case on those changes states an \`expectedValue\` or a \`derived\` rule`,
|
|
144
241
|
suggestion: CONTRACT.objections.failWithoutValue.suggestion,
|
|
145
242
|
});
|
|
146
243
|
}
|
|
@@ -438,7 +438,9 @@ The generated test file remains unchanged and ready to use as-is.
|
|
|
438
438
|
if (generateOptions.authHeader === undefined) {
|
|
439
439
|
try {
|
|
440
440
|
const repoPath = generateOptions.outputDir || process.cwd();
|
|
441
|
-
const resolved = await resolveAuthFromWorkspace(repoPath, generateOptions.authHeader, generateOptions.authScheme
|
|
441
|
+
const resolved = await resolveAuthFromWorkspace(repoPath, generateOptions.authHeader, generateOptions.authScheme,
|
|
442
|
+
// The directory the test is written into names the service it is for.
|
|
443
|
+
generateOptions.outputDir);
|
|
442
444
|
if (resolved) {
|
|
443
445
|
generateOptions.authHeader = resolved.authHeader;
|
|
444
446
|
if (resolved.authScheme !== undefined) {
|
|
@@ -31,7 +31,7 @@ const codeReuseSchema = z.object({
|
|
|
31
31
|
verify: z
|
|
32
32
|
.boolean()
|
|
33
33
|
.default(false)
|
|
34
|
-
.describe("Verify a previously refactored test instead of returning the reuse prompt. POM path (browser tests): checks the test's POM calls against source; requires the discovery pass — this tool called for the same testFile without `verify` — to have run first. SkyrampUtils path (every other test type): stages the shared utils file for the output commit, checks its invariants, and — when skyramp_modularization recorded a baseline for this spec — compares the delivered assertion count and page.on('pageerror') guard against that hand-out, rejecting a net loss (API helpers: one helper per method+path, status-code-only assertions, method+resource names; browser helpers: actions and structural waits only, intent names). Call it after the reuse edits are written; skyramp_enhance_assertions and skyramp_execute_test refuse until it has passed."),
|
|
34
|
+
.describe("Verify a previously refactored test instead of returning the reuse prompt. POM path (browser tests): checks the test's POM calls against source; requires the discovery pass — this tool called for the same testFile without `verify` — to have run first. SkyrampUtils path (every other test type): stages the shared utils file for the output commit, checks its invariants, and — when skyramp_modularization recorded a baseline for this spec — compares the delivered assertion count and page.on('pageerror') guard against that hand-out, rejecting a net loss (API helpers: one helper per method+path, status-code-only assertions, method+resource names; browser helpers: actions and structural waits only, intent names, one helper per action sequence; every family: one name is defined once per module, with one parameter count across modules). Call it after the reuse edits are written; skyramp_enhance_assertions and skyramp_execute_test refuse until it has passed."),
|
|
35
35
|
testType: z
|
|
36
36
|
.nativeEnum(TestType)
|
|
37
37
|
.optional()
|
|
@@ -21,7 +21,7 @@ export interface FlaggedMember {
|
|
|
21
21
|
* moment the spec changes — and it does change: the execution fix-up may restore
|
|
22
22
|
* `<testFile>.raw.bak` over the spec by plain `cp`, invisible to this tool. The
|
|
23
23
|
* counts and the decline list are therefore derived at report time (see
|
|
24
|
-
* `
|
|
24
|
+
* `rederiveReuse`), never read back from here.
|
|
25
25
|
*
|
|
26
26
|
* And keeping the two types disjoint makes the report boundary opt-IN. When the
|
|
27
27
|
* record extended the wire type, every field added here shipped to the
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { type VerifyResult } from "../../utils/pom-verify/index.js";
|
|
2
2
|
import { type ReuseOutcome, type ReuseRecord } from "./reuse-outcome.js";
|
|
3
3
|
import { type UtilsVerifyResult } from "../../utils/utils-verify/index.js";
|
|
4
|
+
import type { ReuseVerificationFailure } from "../../types/ReuseOutcome.js";
|
|
4
5
|
/**
|
|
5
6
|
* Record the tier-1 candidate count STEP 1 detected, plus the identity needed to
|
|
6
7
|
* re-derive the counts from the delivered spec later.
|
|
7
8
|
*
|
|
8
9
|
* The identity is written here and not only by {@link recordVerifyOutcome} because
|
|
9
10
|
* the verify pass is a call the agent decides whether to make. When it skips it,
|
|
10
|
-
* a record holding the candidate count alone makes `
|
|
11
|
+
* a record holding the candidate count alone makes `rederiveReuse` bail out,
|
|
11
12
|
* and the report states "N candidate POM files detected" with no reuse numbers —
|
|
12
13
|
* indistinguishable from a run where reuse was measured and simply not summarized.
|
|
13
14
|
* Recording it unconditionally is what makes the zero-reuse case reportable without
|
|
@@ -55,9 +56,11 @@ export declare function recordVerifyOutcome(testFile: string, r: VerifyResult, g
|
|
|
55
56
|
* NOT terminal. A PASS is always reachable in one edit plus one call (substitute the
|
|
56
57
|
* members, or document each candidate with a `// kept inline:` marker, which the gate
|
|
57
58
|
* accepts), and an agent that resolves neither can still walk past these tools to
|
|
58
|
-
* `skyramp_submit_report
|
|
59
|
-
*
|
|
60
|
-
*
|
|
59
|
+
* `skyramp_submit_report`. That tool refuses a blocking verdict on the delivered
|
|
60
|
+
* files too, but its refusal is BOUNDED (`REUSE_SUBMIT_MAX_REFUSALS`): past the bound
|
|
61
|
+
* it accepts the report with the fault recorded in the row. So the worst case still
|
|
62
|
+
* degrades to the reporting half rather than to a run that produces nothing — which
|
|
63
|
+
* is what a bound here would have been protecting against.
|
|
61
64
|
*/
|
|
62
65
|
export declare function pendingReuseVerification(testFile: string, explicitStateFile?: string): Promise<string | undefined>;
|
|
63
66
|
/**
|
|
@@ -81,7 +84,17 @@ export declare function recordUtilsVerifyError(testFile: string, explicitStateFi
|
|
|
81
84
|
export declare function samePath(a: string, b: string): boolean;
|
|
82
85
|
/** Exported for retrofit-state: the same canonicalisation the records use. */
|
|
83
86
|
export declare function canonPath(p: string): string;
|
|
84
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* The label a report row shows for a set of utils files: each file's path relative
|
|
89
|
+
* to its repository root, posix-separated, joined with `, `.
|
|
90
|
+
*
|
|
91
|
+
* Repository-relative, never absolute — an absolute path carries the runner's
|
|
92
|
+
* temporary directory and means nothing to a customer. Not a basename either: a
|
|
93
|
+
* fullstack delivery writes one module per test directory under the same
|
|
94
|
+
* conventional name, and two rows labelled `skyrampUtils.ts` cannot be told apart
|
|
95
|
+
* (a renderer keyed on the label then merges them into one aggregate). Outside a
|
|
96
|
+
* repository, or for a file the root does not contain, the basename stands in.
|
|
97
|
+
*/
|
|
85
98
|
export declare function utilsFileLabel(files: string[]): string;
|
|
86
99
|
/** Verdict of one utils verify pass. The verdict alone is stored — counts and the
|
|
87
100
|
* file list are re-derived from the delivered files at report time. */
|
|
@@ -148,6 +161,64 @@ export declare function reuseChainSkipped(fileName: string, testType: string, re
|
|
|
148
161
|
testType: string;
|
|
149
162
|
language: string;
|
|
150
163
|
}> | undefined): Promise<true | undefined>;
|
|
164
|
+
/**
|
|
165
|
+
* A blocking reuse verdict measured on the DELIVERED files at report time — the
|
|
166
|
+
* same predicate the live `verify: true` pass refuses on, re-run on what ships.
|
|
167
|
+
*
|
|
168
|
+
* Verification is a one-time checkpoint on a file that keeps changing: the execution
|
|
169
|
+
* fix loop deletes a failing assertion after the gate recorded `passed`, and a
|
|
170
|
+
* post-verification full-file rewrite erases verified page-object reuse. Until this
|
|
171
|
+
* existed, the report re-derived the verdict, found the fault, wrote it into a field
|
|
172
|
+
* and shipped the work — the run committed the fault, detected it, disclosed it and
|
|
173
|
+
* delivered it. `skyramp_submit_report` refuses on this instead, and the agent
|
|
174
|
+
* repairs (or documents the decline) and resubmits.
|
|
175
|
+
*
|
|
176
|
+
* Only a BLOCKING verdict lands here. Advisories (`scenario-name`, untyped
|
|
177
|
+
* parameters, sibling inline call sites, single importers) never do: they are
|
|
178
|
+
* report-time signals whose number means something only on the final file, and
|
|
179
|
+
* they must stay free to be computed and reported at submit.
|
|
180
|
+
*/
|
|
181
|
+
export interface ReuseBlockingVerdict {
|
|
182
|
+
/** ABSOLUTE PATH of the spec the verdict is about — the refusal is agent-facing,
|
|
183
|
+
* and two specs sharing a basename (one per repository) must stay two verdicts. */
|
|
184
|
+
file: string;
|
|
185
|
+
/** The blocking kinds found, each with its count and detail — what the row ships
|
|
186
|
+
* with once the refusal bound is reached. */
|
|
187
|
+
failures: ReuseVerificationFailure[];
|
|
188
|
+
/** What failed and how to repair it — the text the live verify pass would have
|
|
189
|
+
* returned for the delivered file, so the remediation is the one the agent knows. */
|
|
190
|
+
detail: string;
|
|
191
|
+
/** The exact `skyramp_reuse_code` verify call to make after the repair, so the
|
|
192
|
+
* repaired files are re-checked. */
|
|
193
|
+
verifyCall: string;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* How many times skyramp_submit_report refuses a report over one spec's blocking
|
|
197
|
+
* verdict before accepting it with the fault recorded in the row.
|
|
198
|
+
*
|
|
199
|
+
* The refusal is a repair loop, and a repair loop at the last step of a run needs a
|
|
200
|
+
* terminal state (SKYR-4059: an unbounded loop destroyed verified work). Without one,
|
|
201
|
+
* an agent that cannot reach a pass — out of turns, or a `guard-removed` it cannot
|
|
202
|
+
* restore, which no marker documents away — delivers no report at all, and a
|
|
203
|
+
* customer with no pull request is a worse outcome than one with a disclosed fault.
|
|
204
|
+
* Two refusals cover the common case, where the repair is one edit and one verify
|
|
205
|
+
* call away; the third call ships the row with `verificationFailures` set.
|
|
206
|
+
*/
|
|
207
|
+
export declare const REUSE_SUBMIT_MAX_REFUSALS = 2;
|
|
208
|
+
/** What one report row's re-derivation established: the outcome to publish, and a
|
|
209
|
+
* blocking verdict when the delivered files fail the live check. */
|
|
210
|
+
export interface ReuseRederivation {
|
|
211
|
+
outcome: ReuseOutcome | undefined;
|
|
212
|
+
blocking?: ReuseBlockingVerdict;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* The reason behind a failed shared-helper verdict, one entry per blocking kind in
|
|
216
|
+
* the order the verifier found them. The verify pass had these in hand and dropped
|
|
217
|
+
* them after computing `ok`; without them a `failed` row is a verdict nobody can act
|
|
218
|
+
* on. The detail text is the verifier's own — for an assertion loss it carries the
|
|
219
|
+
* counts (baseline, delivered, in the test, in the helpers) that decided the verdict.
|
|
220
|
+
*/
|
|
221
|
+
export declare function verificationFailures(r: UtilsVerifyResult): ReuseVerificationFailure[];
|
|
151
222
|
/**
|
|
152
223
|
* Re-derive a recorded outcome from the spec as it stands NOW, so the report
|
|
153
224
|
* describes the delivered artifact rather than the state at verify time.
|
|
@@ -169,8 +240,15 @@ export declare function reuseChainSkipped(fileName: string, testType: string, re
|
|
|
169
240
|
*
|
|
170
241
|
* Returns the recorded outcome minus internals when re-derivation is impossible
|
|
171
242
|
* (no path recorded — now only the no-POM-layer path, which has no spec to measure
|
|
172
|
-
* and no candidates to contrast against), and
|
|
243
|
+
* and no candidates to contrast against), and no outcome when it fails outright.
|
|
173
244
|
* Failing closed matters: falling back to the recorded counts is exactly the false
|
|
174
245
|
* claim this exists to prevent.
|
|
246
|
+
*
|
|
247
|
+
* Beside the outcome comes the blocking verdict the delivered files earn under the
|
|
248
|
+
* live check — see {@link ReuseBlockingVerdict}. One measurement feeds both: the row
|
|
249
|
+
* the report publishes and the refusal `skyramp_submit_report` returns. That half
|
|
250
|
+
* fails OPEN like every reuse check: a re-derivation that throws yields no outcome
|
|
251
|
+
* and no verdict. A verifier that cannot run is not evidence against the file, and
|
|
252
|
+
* refusing a report on a malfunction would deliver nothing to the customer.
|
|
175
253
|
*/
|
|
176
|
-
export declare function
|
|
254
|
+
export declare function rederiveReuse(record: ReuseRecord): Promise<ReuseRederivation>;
|