@skyramp/mcp 0.4.2 → 0.4.3-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/execution/wrapperConfig.d.ts +4 -0
- package/build/execution/wrapperConfig.js +14 -2
- package/build/prompts/code-reuse.d.ts +5 -1
- package/build/prompts/code-reuse.js +16 -10
- package/build/prompts/modularization/ui-test-modularization.js +9 -2
- package/build/prompts/pom-aware-code-reuse.d.ts +4 -1
- package/build/prompts/pom-aware-code-reuse.js +38 -6
- package/build/prompts/reuse-hand-off.d.ts +4 -0
- package/build/prompts/reuse-hand-off.js +10 -5
- package/build/prompts/shared-helper-policy.d.ts +5 -0
- package/build/prompts/shared-helper-policy.js +5 -0
- package/build/prompts/test-maintenance/driftAnalysisShared.js +2 -0
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +2 -1
- package/build/prompts/testbot/testbot-prompts.js +33 -16
- package/build/recommendation/fakeVerifyContext.d.ts +3 -0
- package/build/recommendation/fakeVerifyContext.js +16 -0
- package/build/recommendation/runVerifiers.js +2 -0
- package/build/recommendation/types.d.ts +12 -0
- package/build/recommendation/verifierContracts.d.ts +24 -2
- package/build/recommendation/verifierContracts.js +32 -4
- package/build/recommendation/verifiers/changedLinesClaimed.d.ts +17 -0
- package/build/recommendation/verifiers/changedLinesClaimed.js +88 -0
- package/build/recommendation/verifiers/expectedValueSourced.js +8 -0
- package/build/recommendation/verifiers/requirementSourced.js +141 -11
- package/build/services/TestGenerationService.js +4 -0
- package/build/tools/code-refactor/caller-gate.d.ts +105 -0
- package/build/tools/code-refactor/caller-gate.js +174 -0
- package/build/tools/code-refactor/changed-helper-report.d.ts +62 -0
- package/build/tools/code-refactor/changed-helper-report.js +114 -0
- package/build/tools/code-refactor/codeReuseTool.js +131 -62
- package/build/tools/code-refactor/enhanceAssertionsTool.js +2 -2
- package/build/tools/code-refactor/helper-callers.d.ts +65 -0
- package/build/tools/code-refactor/helper-callers.js +85 -0
- package/build/tools/code-refactor/modularizationTool.js +10 -0
- package/build/tools/code-refactor/pom-pass-state.d.ts +51 -0
- package/build/tools/code-refactor/pom-pass-state.js +115 -0
- package/build/tools/code-refactor/retrofit-state.d.ts +3 -2
- package/build/tools/code-refactor/retrofit-state.js +11 -12
- package/build/tools/code-refactor/reuse-outcome.d.ts +6 -0
- package/build/tools/code-refactor/reuse-record-store.d.ts +52 -0
- package/build/tools/code-refactor/reuse-record-store.js +126 -0
- package/build/tools/code-refactor/reuse-state.d.ts +18 -6
- package/build/tools/code-refactor/reuse-state.js +78 -131
- package/build/tools/code-refactor/utils-verify-gates.js +3 -3
- package/build/tools/code-refactor/verify-gates.d.ts +4 -0
- package/build/tools/code-refactor/verify-gates.js +8 -1
- package/build/tools/generate-tests/generateContractRestTool.js +7 -7
- package/build/tools/generate-tests/generateIntegrationRestTool.js +8 -8
- package/build/tools/generate-tests/generateUIRestTool.js +3 -3
- package/build/tools/submitReportTool.js +91 -29
- package/build/tools/test-management/actionsTool.js +7 -0
- package/build/tools/test-management/registerTestPlanTool.d.ts +1 -0
- package/build/tools/test-management/registerTestPlanTool.js +70 -22
- package/build/types/RepositoryAnalysis.d.ts +20 -20
- package/build/types/ReuseOutcome.d.ts +96 -0
- package/build/types/TestExecution.d.ts +11 -0
- package/build/types/TestExecution.js +19 -0
- package/build/types/index.d.ts +1 -1
- package/build/utils/branchDiff.d.ts +1 -1
- package/build/utils/branchDiff.js +1 -1
- package/build/utils/changedRuns.d.ts +13 -0
- package/build/utils/changedRuns.js +56 -0
- package/build/utils/featureFlags.d.ts +8 -10
- package/build/utils/featureFlags.js +23 -21
- package/build/utils/normalizeSkyrampImports.d.ts +4 -1
- package/build/utils/normalizeSkyrampImports.js +24 -17
- package/build/utils/pom-scope/ownership.d.ts +19 -0
- package/build/utils/pom-scope/ownership.js +31 -0
- package/build/utils/pom-scope/pom-files.d.ts +8 -0
- package/build/utils/pom-scope/pom-files.js +8 -0
- package/build/utils/pom-scope/scoring.js +13 -1
- package/build/utils/pom-scope/strip.d.ts +10 -0
- package/build/utils/pom-scope/strip.js +9 -0
- package/build/utils/pom-verify/verify.js +13 -2
- package/build/utils/reuseRouting.d.ts +30 -2
- package/build/utils/reuseRouting.js +36 -15
- package/build/utils/testDependencyPolicy.js +4 -16
- package/build/utils/utils-verify/action-sites.d.ts +2 -2
- package/build/utils/utils-verify/body-reach.d.ts +2 -2
- package/build/utils/utils-verify/body-reach.js +4 -1
- package/build/utils/utils-verify/call-sites.d.ts +16 -6
- package/build/utils/utils-verify/call-sites.js +12 -6
- package/build/utils/utils-verify/changed-helpers.d.ts +38 -0
- package/build/utils/utils-verify/changed-helpers.js +91 -0
- package/build/utils/utils-verify/head.d.ts +48 -0
- package/build/utils/utils-verify/head.js +115 -0
- package/build/utils/utils-verify/importers.d.ts +59 -10
- package/build/utils/utils-verify/importers.js +121 -31
- package/build/utils/utils-verify/in-house.js +26 -31
- package/build/utils/utils-verify/index.d.ts +3 -0
- package/build/utils/utils-verify/index.js +3 -0
- package/build/utils/utils-verify/language-spec.js +10 -2
- package/build/utils/utils-verify/locate.d.ts +3 -0
- package/build/utils/utils-verify/locate.js +6 -6
- package/build/utils/utils-verify/parse.d.ts +13 -1
- package/build/utils/utils-verify/parse.js +122 -49
- package/build/utils/utils-verify/retrofit-equivalence.d.ts +0 -5
- package/build/utils/utils-verify/retrofit-equivalence.js +2 -34
- package/build/utils/utils-verify/stage.js +4 -16
- package/build/utils/utils-verify/verify.d.ts +9 -3
- package/build/utils/utils-verify/verify.js +26 -12
- package/node_modules/playwright/lib/dom-analyzer/blueprint.js +1 -0
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.js +1 -0
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.test.js +6 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +303 -150
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-LNgEKtdV.js → codeMirrorModule-Bou8kKzE.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-Bwr1eMKC.js → defaultSettingsView-DTp2-8Si.js} +3 -3
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.BAkLd5DX.js → index.ZYuEFHsi.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.BPopbasy.js → uiMode.GLqsx5cI.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/pollingRecorderSource.ts +1 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +2 -2
- package/plugin/prompts/code-reuse/hand-off.md +13 -1
- package/plugin/prompts/plan-tests.md +2 -2
- package/plugin/skills/enhance-assertions/reference/shared-rules.md +1 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { logger } from "../../utils/logger.js";
|
|
3
|
+
import { isPomFirstTarget } from "../../utils/reuseRouting.js";
|
|
4
|
+
import { ReuseVerificationOutcome } from "./reuse-outcome.js";
|
|
5
|
+
import { freshRecord, owns, readHandOff, readReuseRecord, runStateWritable, updateReuseOutcome, } from "./reuse-record-store.js";
|
|
6
|
+
/**
|
|
7
|
+
* The page-object pass as run state, for a target both reuse settings serve
|
|
8
|
+
* (isPomFirstTarget): whether the pass has finished for a spec, the mark that
|
|
9
|
+
* finishes it, the checkpoint that holds modularization until it has, and the
|
|
10
|
+
* merge of the two halves one such spec's report row carries.
|
|
11
|
+
*/
|
|
12
|
+
/** Whether a hand-off's chain starts with the page-object pass. Derived from what the
|
|
13
|
+
* hand-off already stores, never recorded beside it: generation writes the hand-off
|
|
14
|
+
* from exactly these three values, and maintenance arms none for such a target. */
|
|
15
|
+
export function pomFirstHandOff(handOff) {
|
|
16
|
+
return isPomFirstTarget(handOff.language, handOff.testType);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* With both reuse settings on (isPomFirstTarget), where this spec's page-object pass
|
|
20
|
+
* stands. `undefined` while it is open; `"finished"` once its verify pass PASSED;
|
|
21
|
+
* `"no-layer"` when discovery found no page-object layer to reuse. From then on the
|
|
22
|
+
* reuse tool serves the SkyrampUtils flow for the spec, and the page-object gates
|
|
23
|
+
* have already judged the spec while every step they did not substitute was still raw
|
|
24
|
+
* and inline — the state they were written for.
|
|
25
|
+
*
|
|
26
|
+
* A FAILED verdict keeps the pass open: the page-object report says to fix and
|
|
27
|
+
* re-verify, and `pendingReuseVerification` refuses the tools after it until that
|
|
28
|
+
* happens. A record owned by another spec, or no run state, reads as open; the caller
|
|
29
|
+
* then serves the page-object pass, which is what the spec gets without composition.
|
|
30
|
+
*/
|
|
31
|
+
export async function pomPassOutcome(testFile, explicitStateFile) {
|
|
32
|
+
try {
|
|
33
|
+
const record = await readReuseRecord(testFile, explicitStateFile);
|
|
34
|
+
if (!record?.pomPassFinished || !owns(record, testFile))
|
|
35
|
+
return undefined;
|
|
36
|
+
return record.verification === ReuseVerificationOutcome.SkippedNoPom
|
|
37
|
+
? "no-layer"
|
|
38
|
+
: "finished";
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** Mark the page-object pass finished for a spec (see `pomPassOutcome`). Written by
|
|
45
|
+
* the reuse tool at the two points that finish it: a PASSED page-object verify, and
|
|
46
|
+
* discovery that found no page-object layer. */
|
|
47
|
+
export async function recordPomPassFinished(testFile, explicitStateFile) {
|
|
48
|
+
await updateReuseOutcome(testFile, (existing) => owns(existing, testFile)
|
|
49
|
+
? { pomPassFinished: true }
|
|
50
|
+
: { ...freshRecord(), testFilePath: testFile, pomPassFinished: true }, explicitStateFile);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Does this spec owe its page-object pass before `skyramp_modularization`? Only a spec
|
|
54
|
+
* generation handed off with the page-object pass first. Modularizing first would move
|
|
55
|
+
* the raw steps the page-object pass maps into helpers it cannot see, and its gates
|
|
56
|
+
* would then judge a spec with nothing left inline. Fails open without a hand-off, on
|
|
57
|
+
* an unreadable or unwritable state file.
|
|
58
|
+
*/
|
|
59
|
+
export async function pendingPomPass(testFile, explicitStateFile) {
|
|
60
|
+
try {
|
|
61
|
+
const handOff = (await readHandOff(testFile, explicitStateFile))?.handOff;
|
|
62
|
+
if (!handOff || !pomFirstHandOff(handOff))
|
|
63
|
+
return undefined;
|
|
64
|
+
if ((await pomPassOutcome(testFile, explicitStateFile)) !== undefined)
|
|
65
|
+
return undefined;
|
|
66
|
+
if (!(await runStateWritable(explicitStateFile)))
|
|
67
|
+
return undefined;
|
|
68
|
+
return (`${path.basename(testFile)} takes the page-object pass BEFORE modularization. Call skyramp_reuse_code with ` +
|
|
69
|
+
`{ testFile: "${testFile}", language: "${handOff.language}", framework: "${handOff.framework}", testType: "${handOff.testType}" }, ` +
|
|
70
|
+
`follow its steps to a PASSED verification (or its "No reusable POM layer detected" answer), then call skyramp_modularization again.`);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
logger.warning("Could not check for a pending page-object pass — proceeding", {
|
|
74
|
+
testFile,
|
|
75
|
+
error: String(err),
|
|
76
|
+
});
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* One row, both halves. The outcome fields are disjoint by construction (the
|
|
82
|
+
* page-object fields sit at the top level, the shared-helper ones under `helpers` and
|
|
83
|
+
* `inHouse`). The two blocking verdicts become ONE, because the report refuses per
|
|
84
|
+
* spec and one `verify: true` call re-checks both halves (the reuse tool's combined
|
|
85
|
+
* verify). Declines are retired only where the half that owns them was measured: the
|
|
86
|
+
* page-object half measures a `kept inline` decline only when its zero-reuse gate
|
|
87
|
+
* walked the candidates, which it does not at non-zero reuse.
|
|
88
|
+
*/
|
|
89
|
+
export function combineRederivations(pom, helper, carriesKeptInline,
|
|
90
|
+
/** The call that re-checks both halves — the shared-helper verify call, which the
|
|
91
|
+
* reuse tool routes to its combined verify for such a spec. */
|
|
92
|
+
combinedVerifyCall) {
|
|
93
|
+
const outcome = pom.outcome || helper.outcome
|
|
94
|
+
? { ...(pom.outcome ?? {}), ...(helper.outcome ?? {}) }
|
|
95
|
+
: undefined;
|
|
96
|
+
const verdicts = [pom.blocking, helper.blocking].filter((v) => v !== undefined);
|
|
97
|
+
const blocking = verdicts.length === 0
|
|
98
|
+
? undefined
|
|
99
|
+
: {
|
|
100
|
+
file: verdicts[0].file,
|
|
101
|
+
failures: verdicts.flatMap((v) => v.failures),
|
|
102
|
+
detail: verdicts.map((v) => v.detail).join("\n\n"),
|
|
103
|
+
verifyCall: combinedVerifyCall ?? verdicts[0].verifyCall,
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
outcome,
|
|
107
|
+
...(blocking ? { blocking } : {}),
|
|
108
|
+
carriedConsumed: [
|
|
109
|
+
...(pom.carriedConsumed ?? []),
|
|
110
|
+
...(helper.carriedConsumed ?? []),
|
|
111
|
+
],
|
|
112
|
+
carriedEvaluated: !!helper.carriedEvaluated &&
|
|
113
|
+
(!!pom.carriedEvaluated || !carriesKeptInline),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -6,8 +6,9 @@ import { type StagingResult } from "../../utils/utils-verify/index.js";
|
|
|
6
6
|
* sibling-inline advisory). Staging carries the edit into the output commit, so the
|
|
7
7
|
* report must disclose it — with an execution that ran the edited bytes.
|
|
8
8
|
*
|
|
9
|
-
* Whether an execution counts is answered by TIME, not by a stored hash
|
|
10
|
-
*
|
|
9
|
+
* Whether an execution counts is answered by TIME, not by a stored hash (the rule
|
|
10
|
+
* itself is `freshExecution`, shared with the changed-helper gate): the execution
|
|
11
|
+
* record already carries `executedAt`, and the file's mtime says when it
|
|
11
12
|
* was last edited — an execution older than the last edit ran bytes that no longer
|
|
12
13
|
* exist. That also covers a bare editor write with no tool call in between, which a
|
|
13
14
|
* record-time hash cannot see. (Same identity pattern as VideoRecord's
|
|
@@ -6,14 +6,16 @@ import { stageUtilsArtifacts, } from "../../utils/utils-verify/index.js";
|
|
|
6
6
|
import { importFormViolations, locateUtilsFiles, retrofitMismatches, utilsSpecForFile, } from "../../utils/utils-verify/index.js";
|
|
7
7
|
import { realpath } from "../../utils/utils-verify/index.js";
|
|
8
8
|
import { canonPath, samePath, utilsFileLabel } from "./reuse-state.js";
|
|
9
|
+
import { freshExecution } from "../../types/TestExecution.js";
|
|
9
10
|
/**
|
|
10
11
|
* Retrofit tracking (SKYR-4276 A4): a RETROFIT is a pre-existing Skyramp-generated
|
|
11
12
|
* test the reuse pass edited to import from the shared utils file (STEP 5c / the
|
|
12
13
|
* sibling-inline advisory). Staging carries the edit into the output commit, so the
|
|
13
14
|
* report must disclose it — with an execution that ran the edited bytes.
|
|
14
15
|
*
|
|
15
|
-
* Whether an execution counts is answered by TIME, not by a stored hash
|
|
16
|
-
*
|
|
16
|
+
* Whether an execution counts is answered by TIME, not by a stored hash (the rule
|
|
17
|
+
* itself is `freshExecution`, shared with the changed-helper gate): the execution
|
|
18
|
+
* record already carries `executedAt`, and the file's mtime says when it
|
|
17
19
|
* was last edited — an execution older than the last edit ran bytes that no longer
|
|
18
20
|
* exist. That also covers a bare editor write with no tool call in between, which a
|
|
19
21
|
* record-time hash cannot see. (Same identity pattern as VideoRecord's
|
|
@@ -70,17 +72,14 @@ export async function standingRetrofits(state, changedFilesAbs) {
|
|
|
70
72
|
if (!changed.has(canonPath(file)))
|
|
71
73
|
continue;
|
|
72
74
|
const recorded = state.existingTests?.find((t) => samePath(t.testFile, file))?.executionAfter;
|
|
73
|
-
let
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
/* fail open: an unreadable mtime must not demand a re-run */
|
|
82
|
-
}
|
|
75
|
+
let mtime = 0; // fail open: an unreadable mtime must not demand a re-run
|
|
76
|
+
try {
|
|
77
|
+
mtime = (await stat(file)).mtimeMs;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
/* see above */
|
|
83
81
|
}
|
|
82
|
+
const execution = freshExecution(recorded, mtime);
|
|
84
83
|
out.push({ file, utilsFile: r.utilsFile, testFile: r.testFile, execution });
|
|
85
84
|
}
|
|
86
85
|
return uncrossAttributions(out);
|
|
@@ -60,6 +60,12 @@ export interface ReuseRecord {
|
|
|
60
60
|
* enforceable unit, as with every other debt here). Read by the reuse tool on a
|
|
61
61
|
* hand-off target: modularize-first flows owe modularization BEFORE reuse. */
|
|
62
62
|
modularized?: true;
|
|
63
|
+
/** Both reuse settings on (isPomFirstTarget): the page-object pass finished for this
|
|
64
|
+
* spec — its verify pass PASSED, or it found no page-object layer. Sticky: a later
|
|
65
|
+
* page-object verdict (the combined verify of the SkyrampUtils flow re-checks the
|
|
66
|
+
* page-object calls) does not reopen the pass, so routing never flips back to it.
|
|
67
|
+
* Not recoverable from the spec: it is a fact about the run's order. */
|
|
68
|
+
pomPassFinished?: true;
|
|
63
69
|
/** The spec as it stood when `skyramp_modularization` handed out its instructions —
|
|
64
70
|
* after the enhancer, before any code moved (SKYR-4276 A7/A8). The verify pass
|
|
65
71
|
* compares the delivered spec plus the helpers it imports against this. */
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { StateManager, type ReuseHandOff, type UnifiedAnalysisState } from "../../utils/AnalysisStateManager.js";
|
|
2
|
+
import type { ReuseRecord } from "./reuse-outcome.js";
|
|
3
|
+
/**
|
|
4
|
+
* The run-state store behind every reuse record and hand-off: one read, one
|
|
5
|
+
* read-modify-write, one ownership rule, one canonical path comparison. The reuse
|
|
6
|
+
* checkpoints (reuse-state.ts) and the page-object-pass state (pom-pass-state.ts)
|
|
7
|
+
* both build on these and nothing else touches the file.
|
|
8
|
+
*
|
|
9
|
+
* Every write is best-effort. Reuse observability must never be able to fail a
|
|
10
|
+
* refactor that otherwise succeeded, so a missing/unreadable/unwritable state
|
|
11
|
+
* file degrades to "no reuse summary in the report" — the same outcome as running
|
|
12
|
+
* outside a Testbot run, where there is no run state to record into.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Read-modify-write one spec's outcome. `patch` receives the entry recorded so
|
|
16
|
+
* far, so a caller that needs prior state (the verify pass, which accumulates
|
|
17
|
+
* flagged members) uses the same path as one that does not.
|
|
18
|
+
*
|
|
19
|
+
* Fields the patch omits are preserved: the prompt-generation call knows the
|
|
20
|
+
* candidate count, each verify pass knows the counts and verdict.
|
|
21
|
+
*/
|
|
22
|
+
export declare function updateReuseOutcome(testFile: string, patch: (existing: ReuseRecord) => Partial<ReuseRecord>, explicitStateFile?: string): Promise<boolean>;
|
|
23
|
+
/** The run state as one read — the state manager (for a write-back) and what it
|
|
24
|
+
* holds — or `undefined` when there is no run state to read. */
|
|
25
|
+
export declare function readRunState(explicitStateFile?: string): Promise<{
|
|
26
|
+
manager: StateManager<UnifiedAnalysisState>;
|
|
27
|
+
data: UnifiedAnalysisState;
|
|
28
|
+
} | undefined>;
|
|
29
|
+
/** This spec's hand-off, keyed by realpath, with the run state it was read from —
|
|
30
|
+
* the ONE read every hand-off checkpoint makes. `handOff` is `undefined` when the
|
|
31
|
+
* file was never handed off; the whole result is when there is no run state. */
|
|
32
|
+
export declare function readHandOff(testFile: string, explicitStateFile?: string): Promise<{
|
|
33
|
+
data: UnifiedAnalysisState;
|
|
34
|
+
handOff: ReuseHandOff | undefined;
|
|
35
|
+
} | undefined>;
|
|
36
|
+
/** Read one spec's record, or `undefined` when there is no run state, no record, or the
|
|
37
|
+
* state file cannot be read. Shares `updateReuseOutcome`'s resolve-read-basename plumbing
|
|
38
|
+
* so the two cannot disagree about which key a spec maps to. */
|
|
39
|
+
export declare function readReuseRecord(testFile: string, explicitStateFile?: string): Promise<ReuseRecord | undefined>;
|
|
40
|
+
/** Every field cleared — the patch spread onto a record owned by another spec. */
|
|
41
|
+
export declare function freshRecord(): Partial<ReuseRecord>;
|
|
42
|
+
/** Whether `record` describes `testFile` (or nobody yet). Basename-keyed run state
|
|
43
|
+
* means two specs can share one record; only the owner's history carries over. */
|
|
44
|
+
export declare function owns(record: ReuseRecord, testFile: string): boolean;
|
|
45
|
+
/** Canonical comparison: a record written through a symlinked path (macOS /var →
|
|
46
|
+
* /private/var) must still be recognised as this spec's when checked by realpath. */
|
|
47
|
+
export declare function samePath(a: string, b: string): boolean;
|
|
48
|
+
/** Exported for retrofit-state: the same canonicalisation the records use. */
|
|
49
|
+
export declare function canonPath(p: string): string;
|
|
50
|
+
/** Whether the run state file can be written — the precondition for any debt this
|
|
51
|
+
* module holds, since only a write clears it. */
|
|
52
|
+
export declare function runStateWritable(explicitStateFile?: string): Promise<boolean>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { access, constants as fsConstants } from "fs/promises";
|
|
3
|
+
import { realpathSync } from "fs";
|
|
4
|
+
import { logger } from "../../utils/logger.js";
|
|
5
|
+
import { resolveRunStatePath, StateManager, } from "../../utils/AnalysisStateManager.js";
|
|
6
|
+
import { realpath } from "../../utils/utils-verify/index.js";
|
|
7
|
+
/**
|
|
8
|
+
* The run-state store behind every reuse record and hand-off: one read, one
|
|
9
|
+
* read-modify-write, one ownership rule, one canonical path comparison. The reuse
|
|
10
|
+
* checkpoints (reuse-state.ts) and the page-object-pass state (pom-pass-state.ts)
|
|
11
|
+
* both build on these and nothing else touches the file.
|
|
12
|
+
*
|
|
13
|
+
* Every write is best-effort. Reuse observability must never be able to fail a
|
|
14
|
+
* refactor that otherwise succeeded, so a missing/unreadable/unwritable state
|
|
15
|
+
* file degrades to "no reuse summary in the report" — the same outcome as running
|
|
16
|
+
* outside a Testbot run, where there is no run state to record into.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Read-modify-write one spec's outcome. `patch` receives the entry recorded so
|
|
20
|
+
* far, so a caller that needs prior state (the verify pass, which accumulates
|
|
21
|
+
* flagged members) uses the same path as one that does not.
|
|
22
|
+
*
|
|
23
|
+
* Fields the patch omits are preserved: the prompt-generation call knows the
|
|
24
|
+
* candidate count, each verify pass knows the counts and verdict.
|
|
25
|
+
*/
|
|
26
|
+
export async function updateReuseOutcome(testFile, patch, explicitStateFile) {
|
|
27
|
+
try {
|
|
28
|
+
const state = await readRunState(explicitStateFile);
|
|
29
|
+
if (!state)
|
|
30
|
+
return false;
|
|
31
|
+
const { manager, data } = state;
|
|
32
|
+
const key = path.basename(testFile);
|
|
33
|
+
const existing = data.reuseOutcomes?.[key] ?? {};
|
|
34
|
+
await manager.appendData({
|
|
35
|
+
...data,
|
|
36
|
+
reuseOutcomes: {
|
|
37
|
+
...data.reuseOutcomes,
|
|
38
|
+
[key]: { ...existing, ...patch(existing) },
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
logger.warning("Could not record POM-reuse outcome — report will omit the reuse summary", {
|
|
45
|
+
testFile,
|
|
46
|
+
error: String(err),
|
|
47
|
+
});
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** The run state as one read — the state manager (for a write-back) and what it
|
|
52
|
+
* holds — or `undefined` when there is no run state to read. */
|
|
53
|
+
export async function readRunState(explicitStateFile) {
|
|
54
|
+
const stateFile = resolveRunStatePath(explicitStateFile);
|
|
55
|
+
if (!stateFile)
|
|
56
|
+
return undefined;
|
|
57
|
+
const manager = StateManager.fromStatePath(stateFile);
|
|
58
|
+
const data = await manager.readData();
|
|
59
|
+
return data ? { manager, data } : undefined;
|
|
60
|
+
}
|
|
61
|
+
/** This spec's hand-off, keyed by realpath, with the run state it was read from —
|
|
62
|
+
* the ONE read every hand-off checkpoint makes. `handOff` is `undefined` when the
|
|
63
|
+
* file was never handed off; the whole result is when there is no run state. */
|
|
64
|
+
export async function readHandOff(testFile, explicitStateFile) {
|
|
65
|
+
const state = await readRunState(explicitStateFile);
|
|
66
|
+
if (!state)
|
|
67
|
+
return undefined;
|
|
68
|
+
return {
|
|
69
|
+
data: state.data,
|
|
70
|
+
handOff: state.data.reuseHandOffs?.[await realpath(testFile)],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** Read one spec's record, or `undefined` when there is no run state, no record, or the
|
|
74
|
+
* state file cannot be read. Shares `updateReuseOutcome`'s resolve-read-basename plumbing
|
|
75
|
+
* so the two cannot disagree about which key a spec maps to. */
|
|
76
|
+
export async function readReuseRecord(testFile, explicitStateFile) {
|
|
77
|
+
return (await readRunState(explicitStateFile))?.data.reuseOutcomes?.[path.basename(testFile)];
|
|
78
|
+
}
|
|
79
|
+
/** Every field cleared — the patch spread onto a record owned by another spec. */
|
|
80
|
+
export function freshRecord() {
|
|
81
|
+
return {
|
|
82
|
+
candidatesDetected: undefined,
|
|
83
|
+
verification: undefined,
|
|
84
|
+
flagged: undefined,
|
|
85
|
+
language: undefined,
|
|
86
|
+
utils: undefined,
|
|
87
|
+
modularized: undefined,
|
|
88
|
+
pomPassFinished: undefined,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/** Whether `record` describes `testFile` (or nobody yet). Basename-keyed run state
|
|
92
|
+
* means two specs can share one record; only the owner's history carries over. */
|
|
93
|
+
export function owns(record, testFile) {
|
|
94
|
+
return !record.testFilePath || samePath(record.testFilePath, testFile);
|
|
95
|
+
}
|
|
96
|
+
/** Canonical comparison: a record written through a symlinked path (macOS /var →
|
|
97
|
+
* /private/var) must still be recognised as this spec's when checked by realpath. */
|
|
98
|
+
export function samePath(a, b) {
|
|
99
|
+
return canon(a) === canon(b);
|
|
100
|
+
}
|
|
101
|
+
/** Exported for retrofit-state: the same canonicalisation the records use. */
|
|
102
|
+
export function canonPath(p) {
|
|
103
|
+
return canon(p);
|
|
104
|
+
}
|
|
105
|
+
function canon(p) {
|
|
106
|
+
try {
|
|
107
|
+
return realpathSync(p);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return path.resolve(p);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** Whether the run state file can be written — the precondition for any debt this
|
|
114
|
+
* module holds, since only a write clears it. */
|
|
115
|
+
export async function runStateWritable(explicitStateFile) {
|
|
116
|
+
const stateFile = resolveRunStatePath(explicitStateFile);
|
|
117
|
+
if (!stateFile)
|
|
118
|
+
return false;
|
|
119
|
+
try {
|
|
120
|
+
await access(stateFile, fsConstants.W_OK);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -2,7 +2,20 @@ import { type ReuseHandOff, type ReuseHandOffSource } from "../../utils/Analysis
|
|
|
2
2
|
import { type VerifyResult } from "../../utils/pom-verify/index.js";
|
|
3
3
|
import { type InHouseDeclaration, type ReuseOutcome, type ReuseRecord } from "./reuse-outcome.js";
|
|
4
4
|
import { type UtilsVerifyResult, type UtilsViolationKind } from "../../utils/utils-verify/index.js";
|
|
5
|
-
import type { ReuseVerificationFailure } from "../../types/ReuseOutcome.js";
|
|
5
|
+
import type { HelperReuseOutcome, ReuseVerificationFailure } from "../../types/ReuseOutcome.js";
|
|
6
|
+
/** The canonical path comparison the records use — declared in the store, re-exported
|
|
7
|
+
* here for the readers that always imported it from this module. */
|
|
8
|
+
export { samePath, canonPath } from "./reuse-record-store.js";
|
|
9
|
+
/**
|
|
10
|
+
* Persistence for the server-derived POM-reuse outcome, keyed by test-file
|
|
11
|
+
* basename (what `newTestsCreated[].fileName` carries, so
|
|
12
|
+
* `skyramp_submit_report` can match without path normalization).
|
|
13
|
+
*
|
|
14
|
+
* Every write is best-effort. Reuse observability must never be able to fail a
|
|
15
|
+
* refactor that otherwise succeeded, so a missing/unreadable/unwritable state
|
|
16
|
+
* file degrades to "no reuse summary in the report" — the same outcome as running
|
|
17
|
+
* outside a Testbot run, where there is no run state to record into.
|
|
18
|
+
*/
|
|
6
19
|
/**
|
|
7
20
|
* Record the tier-1 candidate count STEP 1 detected, plus the identity needed to
|
|
8
21
|
* re-derive the counts from the delivered spec later.
|
|
@@ -80,11 +93,6 @@ export declare function recordUtilsReuseIssued(testFile: string, language: strin
|
|
|
80
93
|
/** A verify pass that threw. Recorded so the debt FAILS OPEN — a verifier that cannot
|
|
81
94
|
* run must not leave a debt nothing can pay — and cleared by the next successful pass. */
|
|
82
95
|
export declare function recordUtilsVerifyError(testFile: string, explicitStateFile?: string): Promise<void>;
|
|
83
|
-
/** Canonical comparison: a record written through a symlinked path (macOS /var →
|
|
84
|
-
* /private/var) must still be recognised as this spec's when checked by realpath. */
|
|
85
|
-
export declare function samePath(a: string, b: string): boolean;
|
|
86
|
-
/** Exported for retrofit-state: the same canonicalisation the records use. */
|
|
87
|
-
export declare function canonPath(p: string): string;
|
|
88
96
|
/**
|
|
89
97
|
* The label a report row shows for a set of utils files: each file's path relative
|
|
90
98
|
* to its repository root, posix-separated, joined with `, `.
|
|
@@ -233,6 +241,10 @@ export interface ReuseRederivation {
|
|
|
233
241
|
outcome: ReuseOutcome | undefined;
|
|
234
242
|
blocking?: ReuseBlockingVerdict;
|
|
235
243
|
}
|
|
244
|
+
/** The orphan fields of a report row: the helpers nothing imports, or the statement
|
|
245
|
+
* that the walk could not tell. Exactly one of the two, and neither when the walk ran
|
|
246
|
+
* and found none. Exported for its tests — the derivation is the whole behaviour. */
|
|
247
|
+
export declare function orphanFields(r: UtilsVerifyResult): Pick<HelperReuseOutcome, "orphanedHelpers" | "orphanScanIncomplete">;
|
|
236
248
|
/**
|
|
237
249
|
* The reason behind a failed shared-helper verdict, one entry per blocking kind in
|
|
238
250
|
* the order the verifier found them. The verify pass had these in hand and dropped
|