@mergesignal/shared 0.9.0 → 0.10.0
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/dist/assessmentProjection.d.ts +10 -1
- package/dist/assessmentProjection.d.ts.map +1 -1
- package/dist/assessmentProjection.js +38 -7
- package/dist/assessmentProjection.verificationChannel.test.d.ts +2 -0
- package/dist/assessmentProjection.verificationChannel.test.d.ts.map +1 -0
- package/dist/assessmentProjection.verificationChannel.test.js +110 -0
- package/dist/fixtures/assessmentFixtures.d.ts +1 -1
- package/dist/fixtures/assessmentFixtures.d.ts.map +1 -1
- package/dist/fixtures/assessmentFixtures.js +10 -10
- package/dist/fixtures/assessmentScopeFixtures.d.ts +4 -2
- package/dist/fixtures/assessmentScopeFixtures.d.ts.map +1 -1
- package/dist/fixtures/assessmentScopeFixtures.js +13 -3
- package/dist/fixtures/engineAbi4Fixtures.d.ts +13 -1
- package/dist/fixtures/engineAbi4Fixtures.d.ts.map +1 -1
- package/dist/fixtures/engineAbi4Fixtures.js +21 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/prRiskWiringRegression.test.d.ts +2 -0
- package/dist/prRiskWiringRegression.test.d.ts.map +1 -0
- package/dist/prRiskWiringRegression.test.js +102 -0
- package/dist/presentation/dto/assessmentPresentationFields.d.ts +2 -0
- package/dist/presentation/dto/assessmentPresentationFields.d.ts.map +1 -1
- package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts.map +1 -1
- package/dist/presentation/fixtures/presentationPersonaFixtures.js +5 -1
- package/dist/presentation/fixtures/scanResultFixtures.d.ts.map +1 -1
- package/dist/presentation/fixtures/scanResultFixtures.js +28 -17
- package/dist/presentation/presentation.test.js +3 -1
- package/dist/presentation/presentationPersonas.test.js +2 -0
- package/dist/presentation/presenters/presentCliScanSummary.js +1 -1
- package/dist/presentation/presenters/presentDashboardCard.d.ts.map +1 -1
- package/dist/presentation/presenters/presentDashboardCard.js +3 -2
- package/dist/presentation/profile/buildProfileFromAssessment.d.ts.map +1 -1
- package/dist/presentation/profile/buildProfileFromAssessment.js +2 -2
- package/dist/presentation/projectAssessmentFields.d.ts.map +1 -1
- package/dist/presentation/projectAssessmentFields.js +4 -2
- package/dist/presentation/surfaceParity.test.js +6 -0
- package/dist/presentation/verificationChannelParity.test.d.ts +2 -0
- package/dist/presentation/verificationChannelParity.test.d.ts.map +1 -0
- package/dist/presentation/verificationChannelParity.test.js +57 -0
- package/dist/repoIntelligenceSchema.d.ts +3 -3
- package/dist/riskSignals.test.d.ts +2 -0
- package/dist/riskSignals.test.d.ts.map +1 -0
- package/dist/riskSignals.test.js +32 -0
- package/dist/scanPresentationBundle.golden.test.js +2 -2
- package/dist/scanResultSchema.d.ts +6 -0
- package/dist/scanResultSchema.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import type { ScanResult } from "./types.js";
|
|
2
2
|
import { type RepoIntelligence } from "./repoIntelligenceSchema.js";
|
|
3
|
-
import type { Assessment, AssessmentPresentationPublic, ReachVisibility } from "@mergesignal/contracts";
|
|
3
|
+
import type { Assessment, AssessmentPresentationPublic, ReachVisibility, VerificationIntensity } from "@mergesignal/contracts";
|
|
4
4
|
import type { PresentationStatus } from "./presentation/dto/types.js";
|
|
5
|
+
export type VerificationChannel = "runtime" | "artifact" | "none";
|
|
6
|
+
export declare function resolveVerificationChannel(intensity: VerificationIntensity): VerificationChannel;
|
|
7
|
+
export declare function collectRuntimeVerificationFocus(result: ScanResult): string[];
|
|
8
|
+
export declare function collectArtifactVerificationFocus(result: ScanResult): string[];
|
|
9
|
+
export declare function collectVerificationFocusForPresentation(presentation: AssessmentPresentationPublic, result: ScanResult): {
|
|
10
|
+
channel: VerificationChannel;
|
|
11
|
+
focus: string[];
|
|
12
|
+
};
|
|
13
|
+
/** @deprecated Use collectVerificationFocusForPresentation or channel-specific collectors. */
|
|
5
14
|
export declare function collectVerificationFocus(result: ScanResult): string[];
|
|
6
15
|
export declare function reachVisibilityLabel(visibility: ReachVisibility): string | undefined;
|
|
7
16
|
export declare function formatAssessmentHeadline(assessment: Assessment, status: PresentationStatus): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assessmentProjection.d.ts","sourceRoot":"","sources":["../src/assessmentProjection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC5B,eAAe,
|
|
1
|
+
{"version":3,"file":"assessmentProjection.d.ts","sourceRoot":"","sources":["../src/assessmentProjection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC5B,eAAe,EACf,qBAAqB,EACtB,MAAM,wBAAwB,CAAC;AAOhC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAmDlE,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,qBAAqB,GAC/B,mBAAmB,CAIrB;AAED,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAQ5E;AAED,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAS7E;AAED,wBAAgB,uCAAuC,CACrD,YAAY,EAAE,4BAA4B,EAC1C,MAAM,EAAE,UAAU,GACjB;IAAE,OAAO,EAAE,mBAAmB,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAWnD;AAED,8FAA8F;AAC9F,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAErE;AAED,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,eAAe,GAC1B,MAAM,GAAG,SAAS,CAWpB;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,kBAAkB,GACzB,MAAM,CAiDR;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAIlE;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,UAAU,GACjB,MAAM,EAAE,CAaV;AAED,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,4BAA4B,EAC1C,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAOV;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI,GAAG,MAAM,EAAE,CAI3E"}
|
|
@@ -32,12 +32,7 @@ function surfaceHintFromAssessment(assessment) {
|
|
|
32
32
|
}
|
|
33
33
|
return "application code";
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
const scope = result.assessment?.verificationScope;
|
|
37
|
-
if (scope?.focus?.length)
|
|
38
|
-
return [...scope.focus];
|
|
39
|
-
const packages = scope?.packages ?? [];
|
|
40
|
-
const parsed = safeParseRepoIntelligence(result.repoIntelligence);
|
|
35
|
+
function collectVerificationFocusFromPackages(packages, parsed) {
|
|
41
36
|
if (!parsed.ok)
|
|
42
37
|
return [];
|
|
43
38
|
const labels = [];
|
|
@@ -54,6 +49,42 @@ export function collectVerificationFocus(result) {
|
|
|
54
49
|
}
|
|
55
50
|
return labels;
|
|
56
51
|
}
|
|
52
|
+
export function resolveVerificationChannel(intensity) {
|
|
53
|
+
if (intensity === "none")
|
|
54
|
+
return "none";
|
|
55
|
+
if (intensity === "required")
|
|
56
|
+
return "runtime";
|
|
57
|
+
return "artifact";
|
|
58
|
+
}
|
|
59
|
+
export function collectRuntimeVerificationFocus(result) {
|
|
60
|
+
const scope = result.assessment?.verificationScope;
|
|
61
|
+
if (scope?.focus?.length)
|
|
62
|
+
return [...scope.focus];
|
|
63
|
+
const packages = scope?.packages ?? [];
|
|
64
|
+
return collectVerificationFocusFromPackages(packages, safeParseRepoIntelligence(result.repoIntelligence));
|
|
65
|
+
}
|
|
66
|
+
export function collectArtifactVerificationFocus(result) {
|
|
67
|
+
const artifactGrounded = result.assessment?.verificationScope?.artifactGrounded;
|
|
68
|
+
if (!artifactGrounded)
|
|
69
|
+
return [];
|
|
70
|
+
if (artifactGrounded.focus.length > 0)
|
|
71
|
+
return [...artifactGrounded.focus];
|
|
72
|
+
return collectVerificationFocusFromPackages(artifactGrounded.packages, safeParseRepoIntelligence(result.repoIntelligence));
|
|
73
|
+
}
|
|
74
|
+
export function collectVerificationFocusForPresentation(presentation, result) {
|
|
75
|
+
const channel = resolveVerificationChannel(presentation.verificationIntensity);
|
|
76
|
+
if (channel === "none") {
|
|
77
|
+
return { channel, focus: [] };
|
|
78
|
+
}
|
|
79
|
+
if (channel === "runtime") {
|
|
80
|
+
return { channel, focus: collectRuntimeVerificationFocus(result) };
|
|
81
|
+
}
|
|
82
|
+
return { channel, focus: collectArtifactVerificationFocus(result) };
|
|
83
|
+
}
|
|
84
|
+
/** @deprecated Use collectVerificationFocusForPresentation or channel-specific collectors. */
|
|
85
|
+
export function collectVerificationFocus(result) {
|
|
86
|
+
return collectRuntimeVerificationFocus(result);
|
|
87
|
+
}
|
|
57
88
|
export function reachVisibilityLabel(visibility) {
|
|
58
89
|
switch (visibility) {
|
|
59
90
|
case "hidden":
|
|
@@ -118,7 +149,7 @@ export function projectInsightLines(assessment, result) {
|
|
|
118
149
|
export function projectVerificationActions(assessment, presentation, result, max) {
|
|
119
150
|
if (presentation.verificationIntensity === "none")
|
|
120
151
|
return [];
|
|
121
|
-
const focus =
|
|
152
|
+
const { focus } = collectVerificationFocusForPresentation(presentation, result);
|
|
122
153
|
return focus.slice(0, max);
|
|
123
154
|
}
|
|
124
155
|
export function applicationAreaLabels(ri) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assessmentProjection.verificationChannel.test.d.ts","sourceRoot":"","sources":["../src/assessmentProjection.verificationChannel.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { artifactGroundedScopeFor, minimalReviewFocalPoint, verificationScopeFor, withAssessmentScope, } from "./fixtures/assessmentScopeFixtures.js";
|
|
3
|
+
import { assessmentFastifyRuntime, assessmentPrettier, assessmentTypescriptPatch, } from "./fixtures/assessmentFixtures.js";
|
|
4
|
+
import { collectArtifactVerificationFocus, collectRuntimeVerificationFocus, collectVerificationFocusForPresentation, resolveVerificationChannel, } from "./assessmentProjection.js";
|
|
5
|
+
import { toPublicPresentation } from "./assessmentPresentationUtils.js";
|
|
6
|
+
function scanResultWithAssessment(assessment) {
|
|
7
|
+
return {
|
|
8
|
+
totalScore: 30,
|
|
9
|
+
layerScores: {
|
|
10
|
+
security: 10,
|
|
11
|
+
maintainability: 10,
|
|
12
|
+
ecosystem: 10,
|
|
13
|
+
upgradeImpact: 0,
|
|
14
|
+
},
|
|
15
|
+
findings: [],
|
|
16
|
+
generatedAt: "2026-01-01T00:00:00.000Z",
|
|
17
|
+
assessment,
|
|
18
|
+
decision: {
|
|
19
|
+
recommendation: "safe",
|
|
20
|
+
confidence: "high",
|
|
21
|
+
reasoning: ["fixture"],
|
|
22
|
+
},
|
|
23
|
+
repoIntelligence: {},
|
|
24
|
+
changedPackages: assessment.reviewFocalPoint.anchors,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
describe("resolveVerificationChannel", () => {
|
|
28
|
+
it("maps intensity to presentation channel", () => {
|
|
29
|
+
expect(resolveVerificationChannel("none")).toBe("none");
|
|
30
|
+
expect(resolveVerificationChannel("advisory")).toBe("artifact");
|
|
31
|
+
expect(resolveVerificationChannel("required")).toBe("runtime");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe("collectVerificationFocusForPresentation", () => {
|
|
35
|
+
it("fastify required: runtime focus only", () => {
|
|
36
|
+
const result = scanResultWithAssessment(assessmentFastifyRuntime);
|
|
37
|
+
const presentation = toPublicPresentation(assessmentFastifyRuntime.presentation);
|
|
38
|
+
const projected = collectVerificationFocusForPresentation(presentation, result);
|
|
39
|
+
expect(projected.channel).toBe("runtime");
|
|
40
|
+
expect(projected.focus).toEqual(["routes", "middleware"]);
|
|
41
|
+
expect(collectRuntimeVerificationFocus(result)).toEqual(projected.focus);
|
|
42
|
+
expect(collectArtifactVerificationFocus(result)).toEqual([]);
|
|
43
|
+
});
|
|
44
|
+
it("typescript advisory: artifact focus only", () => {
|
|
45
|
+
const result = scanResultWithAssessment(assessmentTypescriptPatch);
|
|
46
|
+
const presentation = toPublicPresentation(assessmentTypescriptPatch.presentation);
|
|
47
|
+
const projected = collectVerificationFocusForPresentation(presentation, result);
|
|
48
|
+
expect(projected.channel).toBe("artifact");
|
|
49
|
+
expect(projected.focus).toEqual(["typecheck"]);
|
|
50
|
+
expect(collectArtifactVerificationFocus(result)).toEqual(["typecheck"]);
|
|
51
|
+
expect(collectRuntimeVerificationFocus(result)).toEqual([]);
|
|
52
|
+
});
|
|
53
|
+
it("prettier advisory: artifact focus only", () => {
|
|
54
|
+
const result = scanResultWithAssessment(assessmentPrettier);
|
|
55
|
+
const presentation = toPublicPresentation(assessmentPrettier.presentation);
|
|
56
|
+
const projected = collectVerificationFocusForPresentation(presentation, result);
|
|
57
|
+
expect(projected.channel).toBe("artifact");
|
|
58
|
+
expect(projected.focus).toEqual(["format"]);
|
|
59
|
+
});
|
|
60
|
+
it("none intensity: empty focus", () => {
|
|
61
|
+
const assessment = withAssessmentScope({
|
|
62
|
+
...assessmentTypescriptPatch,
|
|
63
|
+
presentation: {
|
|
64
|
+
...assessmentTypescriptPatch.presentation,
|
|
65
|
+
verificationIntensity: "none",
|
|
66
|
+
},
|
|
67
|
+
}, {
|
|
68
|
+
reviewFocalPoint: minimalReviewFocalPoint(["typescript"]),
|
|
69
|
+
verificationScope: verificationScopeFor(["fastify"], ["routes"], artifactGroundedScopeFor(["typescript"], ["typecheck"])),
|
|
70
|
+
});
|
|
71
|
+
const result = scanResultWithAssessment(assessment);
|
|
72
|
+
const projected = collectVerificationFocusForPresentation(toPublicPresentation(assessment.presentation), result);
|
|
73
|
+
expect(projected.channel).toBe("none");
|
|
74
|
+
expect(projected.focus).toEqual([]);
|
|
75
|
+
});
|
|
76
|
+
it("conflict fixture: advisory uses artifact only", () => {
|
|
77
|
+
const assessment = withAssessmentScope({
|
|
78
|
+
...assessmentTypescriptPatch,
|
|
79
|
+
presentation: {
|
|
80
|
+
...assessmentTypescriptPatch.presentation,
|
|
81
|
+
verificationIntensity: "advisory",
|
|
82
|
+
},
|
|
83
|
+
}, {
|
|
84
|
+
reviewFocalPoint: minimalReviewFocalPoint(["typescript"]),
|
|
85
|
+
verificationScope: verificationScopeFor(["fastify"], ["routes"], artifactGroundedScopeFor(["typescript"], ["typecheck"])),
|
|
86
|
+
});
|
|
87
|
+
const result = scanResultWithAssessment(assessment);
|
|
88
|
+
const projected = collectVerificationFocusForPresentation(toPublicPresentation(assessment.presentation), result);
|
|
89
|
+
expect(projected.channel).toBe("artifact");
|
|
90
|
+
expect(projected.focus).toEqual(["typecheck"]);
|
|
91
|
+
expect(projected.focus).not.toContain("routes");
|
|
92
|
+
});
|
|
93
|
+
it("conflict fixture: required uses runtime only", () => {
|
|
94
|
+
const assessment = withAssessmentScope({
|
|
95
|
+
...assessmentFastifyRuntime,
|
|
96
|
+
presentation: {
|
|
97
|
+
...assessmentFastifyRuntime.presentation,
|
|
98
|
+
verificationIntensity: "required",
|
|
99
|
+
},
|
|
100
|
+
}, {
|
|
101
|
+
reviewFocalPoint: minimalReviewFocalPoint(["fastify"]),
|
|
102
|
+
verificationScope: verificationScopeFor(["fastify"], ["routes", "middleware"], artifactGroundedScopeFor(["typescript"], ["typecheck"])),
|
|
103
|
+
});
|
|
104
|
+
const result = scanResultWithAssessment(assessment);
|
|
105
|
+
const projected = collectVerificationFocusForPresentation(toPublicPresentation(assessment.presentation), result);
|
|
106
|
+
expect(projected.channel).toBe("runtime");
|
|
107
|
+
expect(projected.focus).toEqual(["routes", "middleware"]);
|
|
108
|
+
expect(projected.focus).not.toContain("typecheck");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Assessment } from "@mergesignal/contracts";
|
|
2
2
|
export declare const assessmentTypescriptPatch: Assessment;
|
|
3
|
-
export declare const assessmentPrettier: Assessment;
|
|
4
3
|
export declare const assessmentEslint: Assessment;
|
|
4
|
+
export declare const assessmentPrettier: Assessment;
|
|
5
5
|
export declare const assessmentVitest: Assessment;
|
|
6
6
|
export declare const assessmentFastifyRuntime: Assessment;
|
|
7
7
|
export declare const assessmentBullmq: Assessment;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assessmentFixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/assessmentFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"assessmentFixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/assessmentFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AA2BzD,eAAO,MAAM,yBAAyB,EAAE,UAmBvC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,UAa9B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,UAchC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,UAuB9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,UAyBtC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,UA6B9B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,UAyBhC,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,gCAAgC,EAAE,UAgC9C,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,UA2BtC,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,UA6B5B,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,UAqBnC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { emptyReachScope, emptyVerificationScope, minimalReviewFocalPoint, reachScopeFor, verificationScopeFor, withAssessmentScope, } from "./assessmentScopeFixtures.js";
|
|
1
|
+
import { artifactGroundedScopeFor, emptyReachScope, emptyVerificationScope, minimalReviewFocalPoint, reachScopeFor, verificationScopeFor, withAssessmentScope, } from "./assessmentScopeFixtures.js";
|
|
2
2
|
const safeToolingPresentation = {
|
|
3
3
|
narrativeIntensity: "minimal",
|
|
4
4
|
reachVisibility: "hidden",
|
|
@@ -24,22 +24,22 @@ export const assessmentTypescriptPatch = withAssessmentScope({
|
|
|
24
24
|
}, {
|
|
25
25
|
reviewFocalPoint: minimalReviewFocalPoint(["typescript"]),
|
|
26
26
|
reachScope: emptyReachScope(),
|
|
27
|
-
verificationScope:
|
|
27
|
+
verificationScope: verificationScopeFor([], [], artifactGroundedScopeFor(["typescript"], ["typecheck"], ["package.json"])),
|
|
28
28
|
});
|
|
29
|
-
export const
|
|
29
|
+
export const assessmentEslint = withAssessmentScope({
|
|
30
30
|
...assessmentTypescriptPatch,
|
|
31
|
-
factors: ["tooling_maintenance"],
|
|
32
31
|
}, {
|
|
33
|
-
reviewFocalPoint: minimalReviewFocalPoint(["
|
|
32
|
+
reviewFocalPoint: minimalReviewFocalPoint(["eslint"]),
|
|
34
33
|
reachScope: emptyReachScope(),
|
|
35
|
-
verificationScope:
|
|
34
|
+
verificationScope: verificationScopeFor([], [], artifactGroundedScopeFor(["eslint"], ["lint"], [".eslintrc.cjs"])),
|
|
36
35
|
});
|
|
37
|
-
export const
|
|
36
|
+
export const assessmentPrettier = withAssessmentScope({
|
|
38
37
|
...assessmentTypescriptPatch,
|
|
38
|
+
factors: ["tooling_maintenance"],
|
|
39
39
|
}, {
|
|
40
|
-
reviewFocalPoint: minimalReviewFocalPoint(["
|
|
40
|
+
reviewFocalPoint: minimalReviewFocalPoint(["prettier"]),
|
|
41
41
|
reachScope: emptyReachScope(),
|
|
42
|
-
verificationScope:
|
|
42
|
+
verificationScope: verificationScopeFor([], [], artifactGroundedScopeFor(["prettier"], ["format"], [".prettierrc"])),
|
|
43
43
|
});
|
|
44
44
|
export const assessmentVitest = withAssessmentScope({
|
|
45
45
|
posture: "safe",
|
|
@@ -52,7 +52,7 @@ export const assessmentVitest = withAssessmentScope({
|
|
|
52
52
|
}, {
|
|
53
53
|
reviewFocalPoint: minimalReviewFocalPoint(["vitest"]),
|
|
54
54
|
reachScope: emptyReachScope(),
|
|
55
|
-
verificationScope:
|
|
55
|
+
verificationScope: verificationScopeFor([], [], artifactGroundedScopeFor(["vitest"], ["test_suite"], ["vitest.config.ts"])),
|
|
56
56
|
});
|
|
57
57
|
export const assessmentFastifyRuntime = withAssessmentScope({
|
|
58
58
|
posture: "needs_review",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Assessment, FocalElectionDimension, ReachScope, ReviewEpisodeShape, ReviewFocalPoint, VerificationScope } from "@mergesignal/contracts";
|
|
1
|
+
import type { ArtifactGroundedVerificationScope, Assessment, FocalElectionDimension, ReachScope, ReviewEpisodeShape, ReviewFocalPoint, VerificationScope } from "@mergesignal/contracts";
|
|
2
2
|
export declare function minimalReviewFocalPoint(anchors: string[], opts?: {
|
|
3
3
|
episodeShape?: ReviewEpisodeShape;
|
|
4
4
|
supportingPackages?: string[];
|
|
@@ -6,8 +6,10 @@ export declare function minimalReviewFocalPoint(anchors: string[], opts?: {
|
|
|
6
6
|
}): ReviewFocalPoint;
|
|
7
7
|
export declare function emptyReachScope(): ReachScope;
|
|
8
8
|
export declare function reachScopeFor(packages: string[], maxBucket?: ReachScope["maxBucket"]): ReachScope;
|
|
9
|
+
export declare function emptyArtifactGrounded(): ArtifactGroundedVerificationScope;
|
|
10
|
+
export declare function artifactGroundedScopeFor(packages: string[], focus?: string[], artifactPaths?: string[]): ArtifactGroundedVerificationScope;
|
|
9
11
|
export declare function emptyVerificationScope(): VerificationScope;
|
|
10
|
-
export declare function verificationScopeFor(packages: string[], focus?: string[]): VerificationScope;
|
|
12
|
+
export declare function verificationScopeFor(packages: string[], focus?: string[], artifactGrounded?: ArtifactGroundedVerificationScope): VerificationScope;
|
|
11
13
|
/** Attach focal/scope fields required by assessmentSchema ABI 2. */
|
|
12
14
|
export declare function withAssessmentScope(assessment: Omit<Assessment, "reviewFocalPoint" | "reachScope" | "verificationScope">, scope: {
|
|
13
15
|
reviewFocalPoint: ReviewFocalPoint;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assessmentScopeFixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/assessmentScopeFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,sBAAsB,EACtB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,wBAAwB,CAAC;AAEhC,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,CAAC,EAAE;IACL,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC,GACA,gBAAgB,CAoBlB;AAED,wBAAgB,eAAe,IAAI,UAAU,CAE5C;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAAE,EAClB,SAAS,GAAE,UAAU,CAAC,WAAW,CAAc,GAC9C,UAAU,CAEZ;AAED,wBAAgB,sBAAsB,IAAI,iBAAiB,
|
|
1
|
+
{"version":3,"file":"assessmentScopeFixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/assessmentScopeFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iCAAiC,EACjC,UAAU,EACV,sBAAsB,EACtB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,wBAAwB,CAAC;AAEhC,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,CAAC,EAAE;IACL,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC,GACA,gBAAgB,CAoBlB;AAED,wBAAgB,eAAe,IAAI,UAAU,CAE5C;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAAE,EAClB,SAAS,GAAE,UAAU,CAAC,WAAW,CAAc,GAC9C,UAAU,CAEZ;AAED,wBAAgB,qBAAqB,IAAI,iCAAiC,CAEzE;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAAE,EAClB,KAAK,GAAE,MAAM,EAAO,EACpB,aAAa,GAAE,MAAM,EAAO,GAC3B,iCAAiC,CAEnC;AAED,wBAAgB,sBAAsB,IAAI,iBAAiB,CAM1D;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAAE,EAClB,KAAK,GAAE,MAAM,EAAO,EACpB,gBAAgB,GAAE,iCAA2D,GAC5E,iBAAiB,CAEnB;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,IAAI,CACd,UAAU,EACV,kBAAkB,GAAG,YAAY,GAAG,mBAAmB,CACxD,EACD,KAAK,EAAE;IACL,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACvC,GACA,UAAU,CAOZ"}
|
|
@@ -25,11 +25,21 @@ export function emptyReachScope() {
|
|
|
25
25
|
export function reachScopeFor(packages, maxBucket = "moderate") {
|
|
26
26
|
return { packages, maxBucket };
|
|
27
27
|
}
|
|
28
|
+
export function emptyArtifactGrounded() {
|
|
29
|
+
return { packages: [], focus: [], artifactPaths: [] };
|
|
30
|
+
}
|
|
31
|
+
export function artifactGroundedScopeFor(packages, focus = [], artifactPaths = []) {
|
|
32
|
+
return { packages, focus, artifactPaths };
|
|
33
|
+
}
|
|
28
34
|
export function emptyVerificationScope() {
|
|
29
|
-
return {
|
|
35
|
+
return {
|
|
36
|
+
packages: [],
|
|
37
|
+
focus: [],
|
|
38
|
+
artifactGrounded: emptyArtifactGrounded(),
|
|
39
|
+
};
|
|
30
40
|
}
|
|
31
|
-
export function verificationScopeFor(packages, focus = []) {
|
|
32
|
-
return { packages, focus };
|
|
41
|
+
export function verificationScopeFor(packages, focus = [], artifactGrounded = emptyArtifactGrounded()) {
|
|
42
|
+
return { packages, focus, artifactGrounded };
|
|
33
43
|
}
|
|
34
44
|
/** Attach focal/scope fields required by assessmentSchema ABI 2. */
|
|
35
45
|
export function withAssessmentScope(assessment, scope) {
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import type { ScanResult } from "../types.js";
|
|
1
|
+
import type { LayerScores, ScanResult } from "../types.js";
|
|
2
|
+
type Abi4SplitScores = {
|
|
3
|
+
prRiskScore: number;
|
|
4
|
+
repositoryHealthScore: number;
|
|
5
|
+
layerScores?: LayerScores;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Attach differentiated ABI-4 prRisk + repositoryHealth wire blocks.
|
|
9
|
+
* `totalScore` mirrors repository health for legacy readers; presentation must
|
|
10
|
+
* consume `prRisk.score` via `deriveRiskSignals()`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function withAbi4SplitScores<T extends ScanResult>(result: T, scores: Abi4SplitScores): T;
|
|
2
13
|
/** Attach ABI-4 prRisk + repositoryHealth wire blocks for strict engine validation tests. */
|
|
3
14
|
export declare function withAbi4EngineScores<T extends ScanResult>(result: T): T;
|
|
15
|
+
export {};
|
|
4
16
|
//# sourceMappingURL=engineAbi4Fixtures.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engineAbi4Fixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/engineAbi4Fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"engineAbi4Fixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/engineAbi4Fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG3D,KAAK,eAAe,GAAG;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,UAAU,EACtD,MAAM,EAAE,CAAC,EACT,MAAM,EAAE,eAAe,GACtB,CAAC,CAeH;AAED,6FAA6F;AAC7F,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,UAAU,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAEvE"}
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import { normalizeEngineOutputAbi4 } from "../scanResultSchema.js";
|
|
2
|
+
/**
|
|
3
|
+
* Attach differentiated ABI-4 prRisk + repositoryHealth wire blocks.
|
|
4
|
+
* `totalScore` mirrors repository health for legacy readers; presentation must
|
|
5
|
+
* consume `prRisk.score` via `deriveRiskSignals()`.
|
|
6
|
+
*/
|
|
7
|
+
export function withAbi4SplitScores(result, scores) {
|
|
8
|
+
const layerScores = scores.layerScores ?? result.layerScores;
|
|
9
|
+
return {
|
|
10
|
+
...result,
|
|
11
|
+
totalScore: scores.repositoryHealthScore,
|
|
12
|
+
layerScores,
|
|
13
|
+
prRisk: {
|
|
14
|
+
score: scores.prRiskScore,
|
|
15
|
+
layerScores: result.prRisk?.layerScores ?? layerScores,
|
|
16
|
+
},
|
|
17
|
+
repositoryHealth: {
|
|
18
|
+
totalScore: scores.repositoryHealthScore,
|
|
19
|
+
layerScores,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
2
23
|
/** Attach ABI-4 prRisk + repositoryHealth wire blocks for strict engine validation tests. */
|
|
3
24
|
export function withAbi4EngineScores(result) {
|
|
4
25
|
return normalizeEngineOutputAbi4(result);
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,6 @@ export * from "./trustedScanGuards.js";
|
|
|
29
29
|
export * from "./actionsStepSummary.js";
|
|
30
30
|
export * from "./productMessaging.js";
|
|
31
31
|
export * from "./presentation/index.js";
|
|
32
|
-
export { emptyReachScope, emptyVerificationScope, minimalReviewFocalPoint, reachScopeFor, verificationScopeFor, withAssessmentScope, } from "./fixtures/assessmentScopeFixtures.js";
|
|
32
|
+
export { emptyReachScope, emptyVerificationScope, emptyArtifactGrounded, artifactGroundedScopeFor, minimalReviewFocalPoint, reachScopeFor, verificationScopeFor, withAssessmentScope, } from "./fixtures/assessmentScopeFixtures.js";
|
|
33
33
|
export { assessmentStub, assessmentTypescriptPatch, } from "./fixtures/assessmentFixtures.js";
|
|
34
34
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,OAAO,EAAE,wCAAwC,EAAE,MAAM,6BAA6B,CAAC;AACvF,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,uBAAuB,EACvB,aAAa,EACb,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,cAAc,EACd,yBAAyB,GAC1B,MAAM,kCAAkC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,OAAO,EAAE,wCAAwC,EAAE,MAAM,6BAA6B,CAAC;AACvF,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,aAAa,EACb,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,cAAc,EACd,yBAAyB,GAC1B,MAAM,kCAAkC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -29,5 +29,5 @@ export * from "./trustedScanGuards.js";
|
|
|
29
29
|
export * from "./actionsStepSummary.js";
|
|
30
30
|
export * from "./productMessaging.js";
|
|
31
31
|
export * from "./presentation/index.js";
|
|
32
|
-
export { emptyReachScope, emptyVerificationScope, minimalReviewFocalPoint, reachScopeFor, verificationScopeFor, withAssessmentScope, } from "./fixtures/assessmentScopeFixtures.js";
|
|
32
|
+
export { emptyReachScope, emptyVerificationScope, emptyArtifactGrounded, artifactGroundedScopeFor, minimalReviewFocalPoint, reachScopeFor, verificationScopeFor, withAssessmentScope, } from "./fixtures/assessmentScopeFixtures.js";
|
|
33
33
|
export { assessmentStub, assessmentTypescriptPatch, } from "./fixtures/assessmentFixtures.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prRiskWiringRegression.test.d.ts","sourceRoot":"","sources":["../src/prRiskWiringRegression.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { deriveScanNarrative } from "./deriveScanNarrative.js";
|
|
3
|
+
import { resolveRepositoryHealthScore } from "./prRiskWire.js";
|
|
4
|
+
import { buildScanPresentationBundle } from "./presentation/orchestration/buildScanPresentationBundle.js";
|
|
5
|
+
import { presentDashboardCard } from "./presentation/presenters/presentDashboardCard.js";
|
|
6
|
+
import { presentScanDetails } from "./presentation/presenters/presentScanDetails.js";
|
|
7
|
+
import { presentCliScanSummary } from "./presentation/presenters/presentCliScanSummary.js";
|
|
8
|
+
import { presentGitHubCheckRun } from "./presentation/presenters/presentGitHubCheckRun.js";
|
|
9
|
+
import { presentGitHubPrComment } from "./presentation/presenters/presentGitHubPrComment.js";
|
|
10
|
+
import { scanResultFastifyRuntime, scanResultTypescriptPatch, } from "./presentation/fixtures/scanResultFixtures.js";
|
|
11
|
+
import { scanResultPrettier } from "./presentation/fixtures/presentationPersonaFixtures.js";
|
|
12
|
+
const SCAN_ID = "22222222-2222-4222-8222-222222222222";
|
|
13
|
+
const ORIGIN = "https://app.example.com";
|
|
14
|
+
const validationPrShapes = [
|
|
15
|
+
{
|
|
16
|
+
id: "PR #27 fastify",
|
|
17
|
+
result: scanResultFastifyRuntime,
|
|
18
|
+
expectedPrRisk: 55,
|
|
19
|
+
expectedRepositoryHealth: 63,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "PR #28 typescript",
|
|
23
|
+
result: scanResultTypescriptPatch,
|
|
24
|
+
expectedPrRisk: 30,
|
|
25
|
+
expectedRepositoryHealth: 63,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "PR #29 prettier",
|
|
29
|
+
result: scanResultPrettier,
|
|
30
|
+
expectedPrRisk: 30,
|
|
31
|
+
expectedRepositoryHealth: 61,
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
function surfacesFor(result) {
|
|
35
|
+
const bundle = buildScanPresentationBundle({
|
|
36
|
+
result,
|
|
37
|
+
pipelineStatus: "done",
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
bundle,
|
|
41
|
+
card: presentDashboardCard(bundle),
|
|
42
|
+
details: presentScanDetails(bundle, { scanId: SCAN_ID }),
|
|
43
|
+
cli: presentCliScanSummary(bundle, { repoLabel: "mergesignal/app" }),
|
|
44
|
+
check: presentGitHubCheckRun(bundle, {
|
|
45
|
+
scanId: SCAN_ID,
|
|
46
|
+
webAppOrigin: ORIGIN,
|
|
47
|
+
}),
|
|
48
|
+
comment: presentGitHubPrComment(bundle),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
describe("PR Risk wiring regression (Investigation 1)", () => {
|
|
52
|
+
it.each(validationPrShapes)("$id: riskIndex follows prRisk.score, not repository health", ({ result, expectedPrRisk, expectedRepositoryHealth }) => {
|
|
53
|
+
expect(result.prRisk?.score).toBe(expectedPrRisk);
|
|
54
|
+
expect(result.repositoryHealth?.totalScore).toBe(expectedRepositoryHealth);
|
|
55
|
+
expect(result.totalScore).toBe(expectedRepositoryHealth);
|
|
56
|
+
expect(resolveRepositoryHealthScore(result)).toBe(expectedRepositoryHealth);
|
|
57
|
+
const facts = deriveScanNarrative(result);
|
|
58
|
+
expect(facts.riskSignals?.riskIndex).toBe(expectedPrRisk);
|
|
59
|
+
expect(facts.riskIndex).toBe(expectedPrRisk);
|
|
60
|
+
const { card, details, cli, check, comment } = surfacesFor(result);
|
|
61
|
+
expect(card.sortKey.riskIndex).toBe(expectedPrRisk);
|
|
62
|
+
expect(details.hero.prRiskScore).toBe(expectedPrRisk);
|
|
63
|
+
expect(details.signalSummary?.prRiskScore).toBe(expectedPrRisk);
|
|
64
|
+
expect(cli.metrics?.prRiskScore).toBe(expectedPrRisk);
|
|
65
|
+
expect(cli.metrics?.riskIndex).toBe(expectedPrRisk);
|
|
66
|
+
const prRiskSection = check.sections.find((s) => s.title === "PR Risk");
|
|
67
|
+
expect(prRiskSection?.bullets[0]).toMatch(new RegExp(`^${expectedPrRisk} \\(`));
|
|
68
|
+
expect(comment.introLines.some((l) => l.startsWith("PR Risk:"))).toBe(true);
|
|
69
|
+
expect(comment.introLines.find((l) => l.startsWith("PR Risk:"))).toContain(String(expectedPrRisk));
|
|
70
|
+
});
|
|
71
|
+
it("same repository health with different PR evidence yields different riskIndex", () => {
|
|
72
|
+
const fastify = surfacesFor(scanResultFastifyRuntime);
|
|
73
|
+
const typescript = surfacesFor(scanResultTypescriptPatch);
|
|
74
|
+
expect(resolveRepositoryHealthScore(scanResultFastifyRuntime)).toBe(resolveRepositoryHealthScore(scanResultTypescriptPatch));
|
|
75
|
+
expect(fastify.card.sortKey.riskIndex).not.toBe(typescript.card.sortKey.riskIndex);
|
|
76
|
+
expect(fastify.card.sortKey.riskIndex).toBe(55);
|
|
77
|
+
expect(typescript.card.sortKey.riskIndex).toBe(30);
|
|
78
|
+
});
|
|
79
|
+
it("historical fallback: missing prRisk uses totalScore for riskIndex", () => {
|
|
80
|
+
const historical = {
|
|
81
|
+
totalScore: 48,
|
|
82
|
+
layerScores: {
|
|
83
|
+
security: 10,
|
|
84
|
+
maintainability: 12,
|
|
85
|
+
ecosystem: 14,
|
|
86
|
+
upgradeImpact: 12,
|
|
87
|
+
},
|
|
88
|
+
findings: [],
|
|
89
|
+
generatedAt: "2026-01-01T00:00:00.000Z",
|
|
90
|
+
assessment: scanResultTypescriptPatch.assessment,
|
|
91
|
+
decision: scanResultTypescriptPatch.decision,
|
|
92
|
+
};
|
|
93
|
+
const facts = deriveScanNarrative(historical);
|
|
94
|
+
expect(facts.riskSignals?.riskIndex).toBe(48);
|
|
95
|
+
expect(facts.riskIndex).toBe(48);
|
|
96
|
+
const bundle = buildScanPresentationBundle({
|
|
97
|
+
result: historical,
|
|
98
|
+
pipelineStatus: "done",
|
|
99
|
+
});
|
|
100
|
+
expect(presentDashboardCard(bundle).sortKey.riskIndex).toBe(48);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MergeConcernKind, NarrativeIntensity, ReachVisibility } from "@mergesignal/contracts";
|
|
2
|
+
import type { VerificationChannel } from "../../assessmentProjection.js";
|
|
2
3
|
/** Assessment-aligned fields projected identically across surfaces. */
|
|
3
4
|
export type AssessmentPresentationFields = {
|
|
4
5
|
posture: "safe" | "needs_review" | "risky";
|
|
@@ -6,6 +7,7 @@ export type AssessmentPresentationFields = {
|
|
|
6
7
|
factors: string[];
|
|
7
8
|
reasoning: string[];
|
|
8
9
|
verificationFocus: string[];
|
|
10
|
+
verificationChannel: VerificationChannel;
|
|
9
11
|
reachVisibility: ReachVisibility;
|
|
10
12
|
narrativeIntensity: NarrativeIntensity;
|
|
11
13
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assessmentPresentationFields.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/assessmentPresentationFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EAEhB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"assessmentPresentationFields.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/assessmentPresentationFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EAEhB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,uEAAuE;AACvE,MAAM,MAAM,4BAA4B,GAAG;IACzC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,OAAO,CAAC;IAC3C,cAAc,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACxC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,EAAE,kBAAkB,CAAC;CACxC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentationPersonaFixtures.d.ts","sourceRoot":"","sources":["../../../src/presentation/fixtures/presentationPersonaFixtures.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"presentationPersonaFixtures.d.ts","sourceRoot":"","sources":["../../../src/presentation/fixtures/presentationPersonaFixtures.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAmBjD,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,yBAAyB,CAAC;AA+KjC,eAAO,MAAM,gBAAgB,EAAE,UAG7B,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,UAShC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,UAG7B,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,UAI/B,CAAC;AAEH,eAAO,MAAM,gBAAgB,EAAE,UAa7B,CAAC;AAEH,eAAO,MAAM,qBAAqB,EAAE,UAMnC,CAAC;AAEF,eAAO,MAAM,gCAAgC,EAAE,UAO9C,CAAC;AAEF,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,yBAAyB,GAC1B,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assessmentBullmq, assessmentEslint, assessmentMixedTypescriptFastify, assessmentNextAuth, assessmentPrettier, assessmentUnknownSafe, assessmentVitest, } from "../../fixtures/assessmentFixtures.js";
|
|
2
2
|
import { fixtureRepoIntelligenceFastify, fixtureRepoIntelligenceTypescript, } from "../../fixtures/repoIntelligenceFixtures.js";
|
|
3
|
+
import { withAbi4SplitScores } from "../../fixtures/engineAbi4Fixtures.js";
|
|
3
4
|
import { analysisPreparationWithValidRepoIntel } from "../../fixtures/repoIntelligenceTestHelpers.js";
|
|
4
5
|
import { scanResultFastifyRuntime, scanResultLimitedContext, scanResultTypescriptPatch, } from "./scanResultFixtures.js";
|
|
5
6
|
const basePrep = analysisPreparationWithValidRepoIntel();
|
|
@@ -165,9 +166,12 @@ export const scanResultEslint = scanBase(assessmentEslint, {
|
|
|
165
166
|
changedPackages: ["eslint"],
|
|
166
167
|
repoIntelligence: fixtureRepoIntelligenceEslint,
|
|
167
168
|
});
|
|
168
|
-
export const scanResultPrettier = scanBase(assessmentPrettier, {
|
|
169
|
+
export const scanResultPrettier = withAbi4SplitScores(scanBase(assessmentPrettier, {
|
|
169
170
|
changedPackages: ["prettier"],
|
|
170
171
|
repoIntelligence: fixtureRepoIntelligencePrettier,
|
|
172
|
+
}), {
|
|
173
|
+
prRiskScore: 30,
|
|
174
|
+
repositoryHealthScore: 61,
|
|
171
175
|
});
|
|
172
176
|
export const scanResultVitest = scanBase(assessmentVitest, {
|
|
173
177
|
changedPackages: ["vitest"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanResultFixtures.d.ts","sourceRoot":"","sources":["../../../src/presentation/fixtures/scanResultFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"scanResultFixtures.d.ts","sourceRoot":"","sources":["../../../src/presentation/fixtures/scanResultFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAoBjD,eAAO,MAAM,wBAAwB,EAAE,UA4CtC,CAAC;AASF,eAAO,MAAM,yBAAyB,EAAE,UA6BvC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,UAqCtC,CAAC"}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import { withAbi4SplitScores } from "../../fixtures/engineAbi4Fixtures.js";
|
|
1
2
|
import { assessmentFastifyRuntime, assessmentLimitedContext, assessmentTypescriptPatch, } from "../../fixtures/assessmentFixtures.js";
|
|
2
3
|
import { fixtureRepoIntelligenceFastify, fixtureRepoIntelligenceTypescript, } from "../../fixtures/repoIntelligenceFixtures.js";
|
|
3
4
|
import { analysisPreparationWithValidRepoIntel } from "../../fixtures/repoIntelligenceTestHelpers.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
const fastifyLayerScores = {
|
|
6
|
+
security: 10,
|
|
7
|
+
maintainability: 20,
|
|
8
|
+
ecosystem: 30,
|
|
9
|
+
upgradeImpact: 15,
|
|
10
|
+
};
|
|
11
|
+
export const scanResultFastifyRuntime = withAbi4SplitScores({
|
|
12
|
+
totalScore: 63,
|
|
13
|
+
layerScores: fastifyLayerScores,
|
|
12
14
|
findings: [
|
|
13
15
|
{
|
|
14
16
|
id: "finding-fastify-runtime",
|
|
@@ -43,15 +45,20 @@ export const scanResultFastifyRuntime = {
|
|
|
43
45
|
remediation: "Run auth smoke tests",
|
|
44
46
|
},
|
|
45
47
|
],
|
|
48
|
+
}, {
|
|
49
|
+
prRiskScore: 55,
|
|
50
|
+
repositoryHealthScore: 63,
|
|
51
|
+
layerScores: fastifyLayerScores,
|
|
52
|
+
});
|
|
53
|
+
const typescriptLayerScores = {
|
|
54
|
+
security: 5,
|
|
55
|
+
maintainability: 8,
|
|
56
|
+
ecosystem: 10,
|
|
57
|
+
upgradeImpact: 5,
|
|
46
58
|
};
|
|
47
|
-
export const scanResultTypescriptPatch = {
|
|
48
|
-
totalScore:
|
|
49
|
-
layerScores:
|
|
50
|
-
security: 5,
|
|
51
|
-
maintainability: 8,
|
|
52
|
-
ecosystem: 10,
|
|
53
|
-
upgradeImpact: 5,
|
|
54
|
-
},
|
|
59
|
+
export const scanResultTypescriptPatch = withAbi4SplitScores({
|
|
60
|
+
totalScore: 63,
|
|
61
|
+
layerScores: typescriptLayerScores,
|
|
55
62
|
findings: [],
|
|
56
63
|
generatedAt: "2026-01-01T00:00:00.000Z",
|
|
57
64
|
changedPackages: ["typescript"],
|
|
@@ -71,7 +78,11 @@ export const scanResultTypescriptPatch = {
|
|
|
71
78
|
edges: 340,
|
|
72
79
|
hotspots: [],
|
|
73
80
|
},
|
|
74
|
-
}
|
|
81
|
+
}, {
|
|
82
|
+
prRiskScore: 30,
|
|
83
|
+
repositoryHealthScore: 63,
|
|
84
|
+
layerScores: typescriptLayerScores,
|
|
85
|
+
});
|
|
75
86
|
export const scanResultLimitedContext = {
|
|
76
87
|
totalScore: 40,
|
|
77
88
|
layerScores: {
|
|
@@ -67,7 +67,9 @@ describe("presentDashboardCard personas", () => {
|
|
|
67
67
|
expect(card.verdict?.posture).toBe("safe");
|
|
68
68
|
expect(card.headline).toMatch(/patch upgrade/i);
|
|
69
69
|
expect(card.headline).not.toMatch(/needs review/i);
|
|
70
|
-
expect(card.verification.length).
|
|
70
|
+
expect(card.verification.length).toBeGreaterThan(0);
|
|
71
|
+
expect(card.verificationChannel).toBe("artifact");
|
|
72
|
+
expect(card.verification.some((v) => v.toLowerCase().includes("typecheck"))).toBe(true);
|
|
71
73
|
});
|
|
72
74
|
});
|
|
73
75
|
describe("presentScanDetails usage paths", () => {
|
|
@@ -16,6 +16,7 @@ const personas = [
|
|
|
16
16
|
density: "minimal",
|
|
17
17
|
headlineMatch: /patch upgrade/i,
|
|
18
18
|
headlineMustNot: /needs review/i,
|
|
19
|
+
verificationIncludes: ["typecheck"],
|
|
19
20
|
},
|
|
20
21
|
{
|
|
21
22
|
name: "eslint",
|
|
@@ -34,6 +35,7 @@ const personas = [
|
|
|
34
35
|
density: "minimal",
|
|
35
36
|
headlineMatch: /patch upgrade/i,
|
|
36
37
|
headlineMustNot: /needs review/i,
|
|
38
|
+
verificationIncludes: ["format"],
|
|
37
39
|
},
|
|
38
40
|
{
|
|
39
41
|
name: "vitest",
|
|
@@ -8,7 +8,7 @@ export function presentCliScanSummary(bundle, ctx = {}) {
|
|
|
8
8
|
const layerLine = layerScores
|
|
9
9
|
? `security=${layerScores.security} maintainability=${layerScores.maintainability} ecosystem=${layerScores.ecosystem} upgradeImpact=${layerScores.upgradeImpact}`
|
|
10
10
|
: "";
|
|
11
|
-
const prRiskScore = facts.riskIndex ?? 0;
|
|
11
|
+
const prRiskScore = facts.riskSignals?.riskIndex ?? facts.riskIndex ?? 0;
|
|
12
12
|
return {
|
|
13
13
|
...projectAssessmentFields(bundle),
|
|
14
14
|
header: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentDashboardCard.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentDashboardCard.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAErF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAoEzF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,sBAAsB,GAC7B,yBAAyB,
|
|
1
|
+
{"version":3,"file":"presentDashboardCard.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentDashboardCard.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAErF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAoEzF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,sBAAsB,GAC7B,yBAAyB,CA+C3B;AAED,wBAAgB,sCAAsC,IAAI,yBAAyB,CAelF;AAED,wBAAgB,4BAA4B,CAC1C,cAAc,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAC9C,yBAAyB,CAqB3B"}
|
|
@@ -59,8 +59,9 @@ export function presentDashboardCard(bundle) {
|
|
|
59
59
|
const { facts, profile } = bundle;
|
|
60
60
|
const channels = buildNarrativeChannels(bundle);
|
|
61
61
|
const posture = profile.status;
|
|
62
|
-
const riskIndex = facts.riskIndex != null &&
|
|
63
|
-
|
|
62
|
+
const riskIndex = facts.riskSignals?.riskIndex != null &&
|
|
63
|
+
Number.isFinite(facts.riskSignals.riskIndex)
|
|
64
|
+
? facts.riskSignals.riskIndex
|
|
64
65
|
: -1;
|
|
65
66
|
const insights = channels.insights.slice(0, INSIGHTS_CAP);
|
|
66
67
|
const scopeAreas = channels.scopeAreas.length > 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildProfileFromAssessment.d.ts","sourceRoot":"","sources":["../../../src/presentation/profile/buildProfileFromAssessment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC7B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"buildProfileFromAssessment.d.ts","sourceRoot":"","sources":["../../../src/presentation/profile/buildProfileFromAssessment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC7B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAoDpE,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,4BAA4B,EAC1C,MAAM,EAAE,UAAU,GACjB,mBAAmB,CAuBrB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { narrativeIntensityToDensity } from "../../assessmentPresentationUtils.js";
|
|
2
2
|
import { safeParseRepoIntelligence } from "../../repoIntelligenceSchema.js";
|
|
3
3
|
import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
|
|
4
|
-
import {
|
|
4
|
+
import { collectVerificationFocusForPresentation } from "../../assessmentProjection.js";
|
|
5
5
|
function focalAnchorPackage(assessment) {
|
|
6
6
|
const anchors = assessment.reviewFocalPoint.anchors;
|
|
7
7
|
if (!anchors.length)
|
|
@@ -14,7 +14,7 @@ function focalAnchorPackage(assessment) {
|
|
|
14
14
|
return token;
|
|
15
15
|
}
|
|
16
16
|
function buildInterpretation(assessment, presentation, result) {
|
|
17
|
-
const verificationLabels =
|
|
17
|
+
const { focus: verificationLabels } = collectVerificationFocusForPresentation(presentation, result);
|
|
18
18
|
const anchorPackage = focalAnchorPackage(assessment);
|
|
19
19
|
const suppressRuntimeNarrative = presentation.reachVisibility === "hidden";
|
|
20
20
|
let intent = "unknown_upgrade";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projectAssessmentFields.d.ts","sourceRoot":"","sources":["../../src/presentation/projectAssessmentFields.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAExF,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,sBAAsB,GAC7B,4BAA4B,
|
|
1
|
+
{"version":3,"file":"projectAssessmentFields.d.ts","sourceRoot":"","sources":["../../src/presentation/projectAssessmentFields.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAExF,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,sBAAsB,GAC7B,4BAA4B,CAgB9B"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { collectVerificationFocusForPresentation, projectReasoningLines, } from "../assessmentProjection.js";
|
|
2
2
|
export function projectAssessmentFields(bundle) {
|
|
3
3
|
const { assessment, presentation, result } = bundle;
|
|
4
|
+
const { channel, focus } = collectVerificationFocusForPresentation(presentation, result);
|
|
4
5
|
return {
|
|
5
6
|
posture: assessment.posture,
|
|
6
7
|
primaryConcern: assessment.primaryConcern,
|
|
7
8
|
factors: [...assessment.factors],
|
|
8
9
|
reasoning: projectReasoningLines(result),
|
|
9
|
-
verificationFocus:
|
|
10
|
+
verificationFocus: focus,
|
|
11
|
+
verificationChannel: channel,
|
|
10
12
|
reachVisibility: presentation.reachVisibility,
|
|
11
13
|
narrativeIntensity: presentation.narrativeIntensity,
|
|
12
14
|
};
|
|
@@ -85,6 +85,10 @@ describe("surfaceParity guardrail", () => {
|
|
|
85
85
|
expect(s.details.verificationFocus).toEqual(s.fields.verificationFocus);
|
|
86
86
|
expect(s.check.verificationFocus).toEqual(s.fields.verificationFocus);
|
|
87
87
|
expect(s.comment.verificationFocus).toEqual(s.fields.verificationFocus);
|
|
88
|
+
expect(s.card.verificationChannel).toBe(s.fields.verificationChannel);
|
|
89
|
+
expect(s.details.verificationChannel).toBe(s.fields.verificationChannel);
|
|
90
|
+
expect(s.check.verificationChannel).toBe(s.fields.verificationChannel);
|
|
91
|
+
expect(s.comment.verificationChannel).toBe(s.fields.verificationChannel);
|
|
88
92
|
});
|
|
89
93
|
it(`${persona.name}: headline matches across card and details`, () => {
|
|
90
94
|
const s = surfacesFor(persona.result);
|
|
@@ -133,6 +137,8 @@ describe("surfaceParity guardrail", () => {
|
|
|
133
137
|
const ch26 = channelsById.get("validation-pr-26");
|
|
134
138
|
expect(ch28.reachLabel).toBeUndefined();
|
|
135
139
|
expect(ch29.reachLabel).toBeUndefined();
|
|
140
|
+
expect(ch28.verification.length).toBeGreaterThan(0);
|
|
141
|
+
expect(ch29.verification.length).toBeGreaterThan(0);
|
|
136
142
|
expect(ch27.verification.length).toBeGreaterThan(0);
|
|
137
143
|
expect(ch30.verification.length).toBeGreaterThan(0);
|
|
138
144
|
expect(ch27.verification).not.toEqual(ch30.verification);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verificationChannelParity.test.d.ts","sourceRoot":"","sources":["../../src/presentation/verificationChannelParity.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { buildScanPresentationBundle } from "./orchestration/buildScanPresentationBundle.js";
|
|
3
|
+
import { presentDashboardCard } from "./presenters/presentDashboardCard.js";
|
|
4
|
+
import { presentScanDetails } from "./presenters/presentScanDetails.js";
|
|
5
|
+
import { presentGitHubCheckRun } from "./presenters/presentGitHubCheckRun.js";
|
|
6
|
+
import { presentGitHubPrComment } from "./presenters/presentGitHubPrComment.js";
|
|
7
|
+
import { projectAssessmentFields } from "./projectAssessmentFields.js";
|
|
8
|
+
import { scanResultFastifyRuntime, scanResultPrettier, scanResultTypescriptPatch, } from "./fixtures/presentationPersonaFixtures.js";
|
|
9
|
+
const SCAN_ID = "22222222-2222-4222-8222-222222222222";
|
|
10
|
+
const ORIGIN = "https://app.example.com";
|
|
11
|
+
describe("verification channel provenance parity", () => {
|
|
12
|
+
for (const persona of [
|
|
13
|
+
{
|
|
14
|
+
name: "fastify runtime",
|
|
15
|
+
result: scanResultFastifyRuntime,
|
|
16
|
+
channel: "runtime",
|
|
17
|
+
focusIncludes: "routes",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "typescript patch",
|
|
21
|
+
result: scanResultTypescriptPatch,
|
|
22
|
+
channel: "artifact",
|
|
23
|
+
focusIncludes: "typecheck",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "prettier",
|
|
27
|
+
result: scanResultPrettier,
|
|
28
|
+
channel: "artifact",
|
|
29
|
+
focusIncludes: "format",
|
|
30
|
+
},
|
|
31
|
+
]) {
|
|
32
|
+
it(`${persona.name}: verificationChannel and focus match across surfaces`, () => {
|
|
33
|
+
const bundle = buildScanPresentationBundle({
|
|
34
|
+
result: persona.result,
|
|
35
|
+
pipelineStatus: "done",
|
|
36
|
+
});
|
|
37
|
+
const fields = projectAssessmentFields(bundle);
|
|
38
|
+
const card = presentDashboardCard(bundle);
|
|
39
|
+
const details = presentScanDetails(bundle, { scanId: SCAN_ID });
|
|
40
|
+
const check = presentGitHubCheckRun(bundle, {
|
|
41
|
+
scanId: SCAN_ID,
|
|
42
|
+
webAppOrigin: ORIGIN,
|
|
43
|
+
});
|
|
44
|
+
const comment = presentGitHubPrComment(bundle);
|
|
45
|
+
expect(fields.verificationChannel).toBe(persona.channel);
|
|
46
|
+
expect(card.verificationChannel).toBe(persona.channel);
|
|
47
|
+
expect(details.verificationChannel).toBe(persona.channel);
|
|
48
|
+
expect(check.verificationChannel).toBe(persona.channel);
|
|
49
|
+
expect(comment.verificationChannel).toBe(persona.channel);
|
|
50
|
+
expect(fields.verificationFocus).toEqual(card.verificationFocus);
|
|
51
|
+
expect(details.verificationFocus).toEqual(fields.verificationFocus);
|
|
52
|
+
expect(check.verificationFocus).toEqual(fields.verificationFocus);
|
|
53
|
+
expect(comment.verificationFocus).toEqual(fields.verificationFocus);
|
|
54
|
+
expect(fields.verificationFocus.some((f) => f.toLowerCase().includes(persona.focusIncludes.toLowerCase()))).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
@@ -35,13 +35,13 @@ export declare const repoIntelligenceWireSchema: z.ZodObject<{
|
|
|
35
35
|
}, z.core.$strip>;
|
|
36
36
|
areas: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
37
37
|
dependencyClass: z.ZodOptional<z.ZodEnum<{
|
|
38
|
+
lint: "lint";
|
|
39
|
+
format: "format";
|
|
38
40
|
unknown: "unknown";
|
|
39
41
|
runtime: "runtime";
|
|
40
42
|
build: "build";
|
|
41
43
|
test: "test";
|
|
42
44
|
tooling: "tooling";
|
|
43
|
-
lint: "lint";
|
|
44
|
-
format: "format";
|
|
45
45
|
ci: "ci";
|
|
46
46
|
}>>;
|
|
47
47
|
packageRole: z.ZodOptional<z.ZodEnum<{
|
|
@@ -70,10 +70,10 @@ export declare const repoIntelligenceWireSchema: z.ZodObject<{
|
|
|
70
70
|
possible: "possible";
|
|
71
71
|
}>>;
|
|
72
72
|
expectedImpact: z.ZodOptional<z.ZodEnum<{
|
|
73
|
+
typecheck: "typecheck";
|
|
73
74
|
unknown: "unknown";
|
|
74
75
|
runtime: "runtime";
|
|
75
76
|
build_time: "build_time";
|
|
76
|
-
typecheck: "typecheck";
|
|
77
77
|
test_time: "test_time";
|
|
78
78
|
development_only: "development_only";
|
|
79
79
|
}>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"riskSignals.test.d.ts","sourceRoot":"","sources":["../src/riskSignals.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { deriveRiskSignals } from "./riskSignals.js";
|
|
3
|
+
import { resolveRepositoryHealthScore } from "./prRiskWire.js";
|
|
4
|
+
function baseResult(overrides = {}) {
|
|
5
|
+
return {
|
|
6
|
+
totalScore: 63,
|
|
7
|
+
layerScores: {
|
|
8
|
+
security: 10,
|
|
9
|
+
maintainability: 20,
|
|
10
|
+
ecosystem: 30,
|
|
11
|
+
upgradeImpact: 15,
|
|
12
|
+
},
|
|
13
|
+
findings: [],
|
|
14
|
+
generatedAt: "2026-01-01T00:00:00.000Z",
|
|
15
|
+
...overrides,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
describe("deriveRiskSignals", () => {
|
|
19
|
+
it("prefers prRisk.score over totalScore and repositoryHealth", () => {
|
|
20
|
+
const result = baseResult({
|
|
21
|
+
prRisk: { score: 55 },
|
|
22
|
+
repositoryHealth: { totalScore: 63 },
|
|
23
|
+
totalScore: 63,
|
|
24
|
+
});
|
|
25
|
+
expect(deriveRiskSignals(result)?.riskIndex).toBe(55);
|
|
26
|
+
expect(resolveRepositoryHealthScore(result)).toBe(63);
|
|
27
|
+
});
|
|
28
|
+
it("falls back to totalScore when prRisk is missing (historical scans)", () => {
|
|
29
|
+
const result = baseResult({ totalScore: 48 });
|
|
30
|
+
expect(deriveRiskSignals(result)?.riskIndex).toBe(48);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -58,8 +58,8 @@ describe("scanPresentationBundle golden — proof model", () => {
|
|
|
58
58
|
const { facts } = bundle;
|
|
59
59
|
expect(facts.availability.corpusGateReason).toBe("ok");
|
|
60
60
|
expect(facts.packageSemantics?.dependencyClass).toBe("tooling");
|
|
61
|
-
expect(facts.riskSignals?.riskIndex).toBe(
|
|
62
|
-
expect(facts.riskSignals?.band).toBe(scoreToBand(
|
|
61
|
+
expect(facts.riskSignals?.riskIndex).toBe(30);
|
|
62
|
+
expect(facts.riskSignals?.band).toBe(scoreToBand(30));
|
|
63
63
|
});
|
|
64
64
|
it("affected areas always carry linkage arrays (structure)", () => {
|
|
65
65
|
for (const { result } of validationPersonas) {
|
|
@@ -156,10 +156,16 @@ export declare const engineOutputScanResultSchema: z.ZodObject<{
|
|
|
156
156
|
very_low: "very_low";
|
|
157
157
|
moderate: "moderate";
|
|
158
158
|
}>;
|
|
159
|
+
entryPointFiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
159
160
|
}, z.core.$strip>;
|
|
160
161
|
verificationScope: z.ZodObject<{
|
|
161
162
|
packages: z.ZodArray<z.ZodString>;
|
|
162
163
|
focus: z.ZodArray<z.ZodString>;
|
|
164
|
+
artifactGrounded: z.ZodOptional<z.ZodObject<{
|
|
165
|
+
packages: z.ZodArray<z.ZodString>;
|
|
166
|
+
focus: z.ZodArray<z.ZodString>;
|
|
167
|
+
artifactPaths: z.ZodArray<z.ZodString>;
|
|
168
|
+
}, z.core.$strip>>;
|
|
163
169
|
}, z.core.$strip>;
|
|
164
170
|
posture: z.ZodEnum<{
|
|
165
171
|
safe: "safe";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanResultSchema.d.ts","sourceRoot":"","sources":["../src/scanResultSchema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtE,wGAAwG;AACxG,eAAO,MAAM,eAAe,EAAG,GAAY,CAAC;AAE5C,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,EAAG,GAAY,CAAC;AAmBnD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAc/D;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8Bb,CAAC;AAUjB;;;GAGG;AACH,eAAO,MAAM,4BAA4B
|
|
1
|
+
{"version":3,"file":"scanResultSchema.d.ts","sourceRoot":"","sources":["../src/scanResultSchema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtE,wGAAwG;AACxG,eAAO,MAAM,eAAe,EAAG,GAAY,CAAC;AAE5C,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,EAAG,GAAY,CAAC;AAmBnD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAc/D;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8Bb,CAAC;AAUjB;;;GAGG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAMvC,CAAC;AAEH,MAAM,MAAM,kCAAkC,GAAG;IAC/C,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,uBAAuB,CAAC;CACjC,CAAC;AAEF,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,OAAO,GACZ,kCAAkC,GAAG,kCAAkC,CAezE;AAED,uFAAuF;AACvF,wBAAgB,kCAAkC,CAChD,IAAI,EAAE,OAAO,GACZ,uBAAuB,CAMzB;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,GACZ,sBAAsB,GAAG,sBAAsB,CAajD;AAED,yEAAyE;AACzE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAMhE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mergesignal/shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "MergeSignal shared scan contract, presentation policy, and trusted validation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"test": "vitest run"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@mergesignal/contracts": "
|
|
41
|
+
"@mergesignal/contracts": "catalog:",
|
|
42
42
|
"zod": "^4.4.3"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|