@mergesignal/shared 0.6.0 → 0.8.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/affectedAreaLinkage.d.ts +14 -0
- package/dist/affectedAreaLinkage.d.ts.map +1 -0
- package/dist/affectedAreaLinkage.js +100 -0
- package/dist/assessmentLabels.d.ts +1 -1
- package/dist/assessmentLabels.d.ts.map +1 -1
- package/dist/assessmentPresentationUtils.d.ts +6 -0
- package/dist/assessmentPresentationUtils.d.ts.map +1 -0
- package/dist/assessmentPresentationUtils.js +17 -0
- package/dist/assessmentProjection.d.ts +2 -2
- package/dist/assessmentProjection.d.ts.map +1 -1
- package/dist/assessmentProjection.js +42 -13
- package/dist/deriveScanNarrative.d.ts.map +1 -1
- package/dist/deriveScanNarrative.js +92 -9
- package/dist/deriveScanNarrative.test.js +74 -0
- package/dist/engineWireRegression.test.js +1 -1
- package/dist/fixtures/assessmentFixtures.d.ts +2 -1
- package/dist/fixtures/assessmentFixtures.d.ts.map +1 -1
- package/dist/fixtures/assessmentFixtures.js +78 -23
- package/dist/fixtures/assessmentScopeFixtures.d.ts +17 -0
- package/dist/fixtures/assessmentScopeFixtures.d.ts.map +1 -0
- package/dist/fixtures/assessmentScopeFixtures.js +42 -0
- package/dist/formatCardExposureDisplay.d.ts +5 -3
- package/dist/formatCardExposureDisplay.d.ts.map +1 -1
- package/dist/formatCardExposureDisplay.js +12 -5
- package/dist/formatCardExposureDisplay.test.js +9 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/presentation/buildGitHubCheckRunOutput.test.js +16 -41
- package/dist/presentation/compose/narrativeChannels.js +1 -1
- package/dist/presentation/dto/assessmentPresentationFields.d.ts +1 -2
- package/dist/presentation/dto/assessmentPresentationFields.d.ts.map +1 -1
- package/dist/presentation/fixtures/scanResultFixtures.d.ts.map +1 -1
- package/dist/presentation/fixtures/scanResultFixtures.js +19 -1
- package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts.map +1 -1
- package/dist/presentation/orchestration/buildScanPresentationBundle.js +12 -5
- package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts +2 -0
- package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts.map +1 -0
- package/dist/presentation/orchestration/buildScanPresentationBundle.test.js +99 -0
- package/dist/presentation/orchestration/scanPresentationBundle.d.ts +1 -1
- package/dist/presentation/orchestration/scanPresentationBundle.d.ts.map +1 -1
- package/dist/presentation/presentation.test.js +1 -1
- package/dist/presentation/presentationPersonas.test.js +0 -5
- package/dist/presentation/profile/buildProfileFromAssessment.d.ts +1 -1
- package/dist/presentation/profile/buildProfileFromAssessment.d.ts.map +1 -1
- package/dist/presentation/profile/buildProfileFromAssessment.js +13 -2
- package/dist/presentation/surfaceParity.test.js +73 -5
- package/dist/repoIntelligenceSchema.d.ts +3 -3
- package/dist/riskSignals.d.ts +19 -0
- package/dist/riskSignals.d.ts.map +1 -0
- package/dist/riskSignals.js +32 -0
- package/dist/scanNarrativeFacts.d.ts +25 -7
- package/dist/scanNarrativeFacts.d.ts.map +1 -1
- package/dist/scanNarrativeFacts.js +4 -1
- package/dist/scanPresentationBundle.golden.test.d.ts +2 -0
- package/dist/scanPresentationBundle.golden.test.d.ts.map +1 -0
- package/dist/scanPresentationBundle.golden.test.js +78 -0
- package/dist/scanResultSchema.d.ts +53 -4
- package/dist/scanResultSchema.d.ts.map +1 -1
- package/dist/scanResultSchema.js +1 -1
- package/dist/scanResultSchema.test.js +26 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -2
- package/dist/assessmentSchema.d.ts +0 -155
- package/dist/assessmentSchema.d.ts.map +0 -1
- package/dist/assessmentSchema.js +0 -110
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanPresentationBundle.d.ts","sourceRoot":"","sources":["../../../src/presentation/orchestration/scanPresentationBundle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC7B,MAAM,
|
|
1
|
+
{"version":3,"file":"scanPresentationBundle.d.ts","sourceRoot":"","sources":["../../../src/presentation/orchestration/scanPresentationBundle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC7B,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,MAAM,MAAM,sBAAsB,GAAG;IACnC,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,4BAA4B,CAAC;IAC3C,OAAO,EAAE,mBAAmB,CAAC;IAC7B;;;OAGG;IACH,KAAK,EAAE,kBAAkB,CAAC;IAC1B;;;OAGG;IACH,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC"}
|
|
@@ -66,7 +66,7 @@ describe("presentDashboardCard personas", () => {
|
|
|
66
66
|
expect(card.verdict?.posture).toBe("safe");
|
|
67
67
|
expect(card.headline).toMatch(/patch upgrade/i);
|
|
68
68
|
expect(card.headline).not.toMatch(/needs review/i);
|
|
69
|
-
expect(card.verification.length).
|
|
69
|
+
expect(card.verification.length).toBe(0);
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
72
|
describe("presentation parity across surfaces", () => {
|
|
@@ -16,8 +16,6 @@ const personas = [
|
|
|
16
16
|
density: "minimal",
|
|
17
17
|
headlineMatch: /patch upgrade/i,
|
|
18
18
|
headlineMustNot: /needs review/i,
|
|
19
|
-
verificationIncludes: ["CI", "typecheck"],
|
|
20
|
-
verificationMustNot: /auth flow/i,
|
|
21
19
|
},
|
|
22
20
|
{
|
|
23
21
|
name: "eslint",
|
|
@@ -27,7 +25,6 @@ const personas = [
|
|
|
27
25
|
density: "minimal",
|
|
28
26
|
headlineMatch: /patch upgrade/i,
|
|
29
27
|
headlineMustNot: /needs review/i,
|
|
30
|
-
verificationIncludes: ["ci", "lint"],
|
|
31
28
|
},
|
|
32
29
|
{
|
|
33
30
|
name: "prettier",
|
|
@@ -37,7 +34,6 @@ const personas = [
|
|
|
37
34
|
density: "minimal",
|
|
38
35
|
headlineMatch: /patch upgrade/i,
|
|
39
36
|
headlineMustNot: /needs review/i,
|
|
40
|
-
verificationIncludes: ["format"],
|
|
41
37
|
},
|
|
42
38
|
{
|
|
43
39
|
name: "vitest",
|
|
@@ -47,7 +43,6 @@ const personas = [
|
|
|
47
43
|
density: "minimal",
|
|
48
44
|
headlineMatch: /patch upgrade|dependency upgrade/i,
|
|
49
45
|
headlineMustNot: /needs review/i,
|
|
50
|
-
verificationIncludes: ["test"],
|
|
51
46
|
},
|
|
52
47
|
{
|
|
53
48
|
name: "fastify runtime",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Assessment, AssessmentPresentationPublic } from "
|
|
1
|
+
import type { Assessment, AssessmentPresentationPublic } from "@mergesignal/contracts";
|
|
2
2
|
import type { ScanResult } from "../../types.js";
|
|
3
3
|
import type { PresentationProfile } from "./presentationProfile.js";
|
|
4
4
|
export declare function buildProfileFromAssessment(assessment: Assessment, presentation: AssessmentPresentationPublic, result: ScanResult): PresentationProfile;
|
|
@@ -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,
|
|
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;AAiDpE,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,4BAA4B,EAC1C,MAAM,EAAE,UAAU,GACjB,mBAAmB,CAuBrB"}
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import { narrativeIntensityToDensity } from "../../
|
|
1
|
+
import { narrativeIntensityToDensity } from "../../assessmentPresentationUtils.js";
|
|
2
2
|
import { safeParseRepoIntelligence } from "../../repoIntelligenceSchema.js";
|
|
3
3
|
import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
|
|
4
4
|
import { collectVerificationFocus } from "../../assessmentProjection.js";
|
|
5
|
+
function focalAnchorPackage(assessment) {
|
|
6
|
+
const anchors = assessment.reviewFocalPoint.anchors;
|
|
7
|
+
if (!anchors.length)
|
|
8
|
+
return null;
|
|
9
|
+
const token = anchors[0];
|
|
10
|
+
if (token === "dependency_graph")
|
|
11
|
+
return null;
|
|
12
|
+
if (token.includes("+"))
|
|
13
|
+
return token.split("+")[0];
|
|
14
|
+
return token;
|
|
15
|
+
}
|
|
5
16
|
function buildInterpretation(assessment, presentation, result) {
|
|
6
17
|
const verificationLabels = collectVerificationFocus(result);
|
|
7
|
-
const anchorPackage =
|
|
18
|
+
const anchorPackage = focalAnchorPackage(assessment);
|
|
8
19
|
const suppressRuntimeNarrative = presentation.reachVisibility === "hidden";
|
|
9
20
|
let intent = "unknown_upgrade";
|
|
10
21
|
if (assessment.primaryConcern === "insufficient_evidence") {
|
|
@@ -8,14 +8,34 @@ import { renderGitHubCheckRunMarkdown } from "./render/renderGitHubCheckRunMarkd
|
|
|
8
8
|
import { renderGitHubPrCommentMarkdown } from "./render/renderGitHubPrCommentMarkdown.js";
|
|
9
9
|
import { scanResultBullmq, scanResultEslint, scanResultFastifyRuntime, scanResultMixedTypescriptFastify, scanResultPrettier, scanResultTypescriptPatch, scanResultVitest, } from "./fixtures/presentationPersonaFixtures.js";
|
|
10
10
|
import { projectAssessmentFields } from "./projectAssessmentFields.js";
|
|
11
|
+
import { buildNarrativeChannels } from "./compose/narrativeChannels.js";
|
|
11
12
|
const SCAN_ID = "22222222-2222-4222-8222-222222222222";
|
|
12
13
|
const ORIGIN = "https://app.example.com";
|
|
14
|
+
const validationPersonasCore = [
|
|
15
|
+
{
|
|
16
|
+
id: "validation-pr-26",
|
|
17
|
+
name: "PR #26 mixed",
|
|
18
|
+
result: scanResultMixedTypescriptFastify,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "validation-pr-27",
|
|
22
|
+
name: "PR #27 fastify",
|
|
23
|
+
result: scanResultFastifyRuntime,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "validation-pr-28",
|
|
27
|
+
name: "PR #28 typescript",
|
|
28
|
+
result: scanResultTypescriptPatch,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "validation-pr-29",
|
|
32
|
+
name: "PR #29 prettier",
|
|
33
|
+
result: scanResultPrettier,
|
|
34
|
+
},
|
|
35
|
+
{ id: "validation-pr-30", name: "PR #30 bullmq", result: scanResultBullmq },
|
|
36
|
+
];
|
|
13
37
|
const validationPersonas = [
|
|
14
|
-
|
|
15
|
-
{ name: "PR #27 fastify", result: scanResultFastifyRuntime },
|
|
16
|
-
{ name: "PR #28 typescript", result: scanResultTypescriptPatch },
|
|
17
|
-
{ name: "PR #29 prettier", result: scanResultPrettier },
|
|
18
|
-
{ name: "PR #30 bullmq", result: scanResultBullmq },
|
|
38
|
+
...validationPersonasCore,
|
|
19
39
|
{ name: "eslint", result: scanResultEslint },
|
|
20
40
|
{ name: "vitest", result: scanResultVitest },
|
|
21
41
|
];
|
|
@@ -71,4 +91,52 @@ describe("surfaceParity guardrail", () => {
|
|
|
71
91
|
expect(s.details.hero.headline).toBe(s.card.headline);
|
|
72
92
|
});
|
|
73
93
|
}
|
|
94
|
+
it("validation personas #26–#30: pairwise distinct reviewFocalPoint and headlines", () => {
|
|
95
|
+
const focalPoints = [];
|
|
96
|
+
const headlines = [];
|
|
97
|
+
for (const persona of validationPersonasCore) {
|
|
98
|
+
const s = surfacesFor(persona.result);
|
|
99
|
+
focalPoints.push(persona.result.assessment.reviewFocalPoint);
|
|
100
|
+
headlines.push(s.card.headline);
|
|
101
|
+
expect(s.check.title).toBe(s.card.headline);
|
|
102
|
+
}
|
|
103
|
+
for (let i = 0; i < headlines.length; i++) {
|
|
104
|
+
for (let j = i + 1; j < headlines.length; j++) {
|
|
105
|
+
expect(headlines[i]).not.toBe(headlines[j]);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (let i = 0; i < focalPoints.length; i++) {
|
|
109
|
+
for (let j = i + 1; j < focalPoints.length; j++) {
|
|
110
|
+
expect(focalPoints[i]).not.toEqual(focalPoints[j]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
it("PR #26: headline uses focal fastify, not changedPackages[0] typescript", () => {
|
|
115
|
+
const result = scanResultMixedTypescriptFastify;
|
|
116
|
+
expect(result.changedPackages?.[0]).toBe("typescript");
|
|
117
|
+
const s = surfacesFor(result);
|
|
118
|
+
expect(s.card.headline.toLowerCase()).toContain("fastify");
|
|
119
|
+
expect(s.card.headline.toLowerCase()).not.toContain("typescript");
|
|
120
|
+
});
|
|
121
|
+
it("validation personas #28–#30: reach and verification channels differ by scope", () => {
|
|
122
|
+
const channelsById = new Map(validationPersonasCore.map((p) => {
|
|
123
|
+
const bundle = buildScanPresentationBundle({
|
|
124
|
+
result: p.result,
|
|
125
|
+
pipelineStatus: "done",
|
|
126
|
+
});
|
|
127
|
+
return [p.id, buildNarrativeChannels(bundle)];
|
|
128
|
+
}));
|
|
129
|
+
const ch28 = channelsById.get("validation-pr-28");
|
|
130
|
+
const ch29 = channelsById.get("validation-pr-29");
|
|
131
|
+
const ch27 = channelsById.get("validation-pr-27");
|
|
132
|
+
const ch30 = channelsById.get("validation-pr-30");
|
|
133
|
+
const ch26 = channelsById.get("validation-pr-26");
|
|
134
|
+
expect(ch28.reachLabel).toBeUndefined();
|
|
135
|
+
expect(ch29.reachLabel).toBeUndefined();
|
|
136
|
+
expect(ch27.verification.length).toBeGreaterThan(0);
|
|
137
|
+
expect(ch30.verification.length).toBeGreaterThan(0);
|
|
138
|
+
expect(ch27.verification).not.toEqual(ch30.verification);
|
|
139
|
+
expect(ch26.verification).not.toEqual(ch27.verification);
|
|
140
|
+
expect(ch26.scopeAreas.length).toBeGreaterThan(0);
|
|
141
|
+
});
|
|
74
142
|
});
|
|
@@ -35,16 +35,17 @@ 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
|
-
format: "format";
|
|
39
38
|
unknown: "unknown";
|
|
40
39
|
runtime: "runtime";
|
|
41
40
|
build: "build";
|
|
42
41
|
test: "test";
|
|
43
42
|
tooling: "tooling";
|
|
44
43
|
lint: "lint";
|
|
44
|
+
format: "format";
|
|
45
45
|
ci: "ci";
|
|
46
46
|
}>>;
|
|
47
47
|
packageRole: z.ZodOptional<z.ZodEnum<{
|
|
48
|
+
queue: "queue";
|
|
48
49
|
unknown: "unknown";
|
|
49
50
|
typechecker: "typechecker";
|
|
50
51
|
compiler: "compiler";
|
|
@@ -54,7 +55,6 @@ export declare const repoIntelligenceWireSchema: z.ZodObject<{
|
|
|
54
55
|
test_runner: "test_runner";
|
|
55
56
|
http_framework: "http_framework";
|
|
56
57
|
auth: "auth";
|
|
57
|
-
queue: "queue";
|
|
58
58
|
orm: "orm";
|
|
59
59
|
}>>;
|
|
60
60
|
lockfileKind: z.ZodOptional<z.ZodEnum<{
|
|
@@ -79,8 +79,8 @@ export declare const repoIntelligenceWireSchema: z.ZodObject<{
|
|
|
79
79
|
}>>;
|
|
80
80
|
evidenceStrength: z.ZodOptional<z.ZodEnum<{
|
|
81
81
|
low: "low";
|
|
82
|
-
medium: "medium";
|
|
83
82
|
high: "high";
|
|
83
|
+
medium: "medium";
|
|
84
84
|
}>>;
|
|
85
85
|
confidenceReason: z.ZodOptional<z.ZodString>;
|
|
86
86
|
verificationFocus: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type CardExposureCategory } from "./formatCardExposureDisplay.js";
|
|
2
|
+
import type { ScanResult, ScoreLayer } from "./types.js";
|
|
3
|
+
export type RiskSignalLayer = {
|
|
4
|
+
layer: ScoreLayer;
|
|
5
|
+
score: number;
|
|
6
|
+
exposure: CardExposureCategory;
|
|
7
|
+
};
|
|
8
|
+
/** Canonical exposure classification for totalScore and layerScores. */
|
|
9
|
+
export type RiskSignals = {
|
|
10
|
+
riskIndex: number | null;
|
|
11
|
+
exposure: CardExposureCategory | null;
|
|
12
|
+
layers: RiskSignalLayer[];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Single owner for numeric risk index + exposure bucket classification.
|
|
16
|
+
* Surfaces must consume `facts.riskSignals` in future phases — do not re-derive bands.
|
|
17
|
+
*/
|
|
18
|
+
export declare function deriveRiskSignals(result: ScanResult): RiskSignals | null;
|
|
19
|
+
//# sourceMappingURL=riskSignals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"riskSignals.d.ts","sourceRoot":"","sources":["../src/riskSignals.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAe,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAEF,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACtC,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B,CAAC;AASF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,CAwBxE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { deriveCardExposureCategory, } from "./formatCardExposureDisplay.js";
|
|
2
|
+
const LAYER_ORDER = [
|
|
3
|
+
"security",
|
|
4
|
+
"maintainability",
|
|
5
|
+
"ecosystem",
|
|
6
|
+
"upgradeImpact",
|
|
7
|
+
];
|
|
8
|
+
/**
|
|
9
|
+
* Single owner for numeric risk index + exposure bucket classification.
|
|
10
|
+
* Surfaces must consume `facts.riskSignals` in future phases — do not re-derive bands.
|
|
11
|
+
*/
|
|
12
|
+
export function deriveRiskSignals(result) {
|
|
13
|
+
const riskIndex = typeof result.totalScore === "number" && Number.isFinite(result.totalScore)
|
|
14
|
+
? result.totalScore
|
|
15
|
+
: null;
|
|
16
|
+
const layerScores = result.layerScores;
|
|
17
|
+
if (riskIndex == null && layerScores == null) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const exposure = deriveCardExposureCategory(riskIndex);
|
|
21
|
+
const layers = layerScores
|
|
22
|
+
? LAYER_ORDER.map((layer) => {
|
|
23
|
+
const score = layerScores[layer];
|
|
24
|
+
return {
|
|
25
|
+
layer,
|
|
26
|
+
score,
|
|
27
|
+
exposure: deriveCardExposureCategory(score) ?? "minimal",
|
|
28
|
+
};
|
|
29
|
+
})
|
|
30
|
+
: [];
|
|
31
|
+
return { riskIndex, exposure, layers };
|
|
32
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ObservationSignalFamily } from "./cardObservationCatalog.js";
|
|
2
2
|
import type { MergePosture } from "./riskVocabulary.js";
|
|
3
|
+
import type { RiskSignals } from "./riskSignals.js";
|
|
4
|
+
import type { AnalysisContextWarning } from "./types.js";
|
|
3
5
|
export type NarrativeAvailabilityMode = "pr_intelligence" | "insights" | "graph_fallback" | "denormalized";
|
|
4
6
|
export type RuntimeSurfaceKind = "runtime" | "build" | "test" | "unknown";
|
|
5
7
|
export type ReachabilityKind = "on_runtime_paths" | "build_only" | "test_only" | "unreachable" | "unknown";
|
|
@@ -20,6 +22,23 @@ export type PackageRoleKind = "typechecker" | "compiler" | "bundler" | "linter"
|
|
|
20
22
|
export type PackageRuntimeImpact = "none" | "possible" | "confirmed" | "unknown";
|
|
21
23
|
export type PackageExpectedImpact = "runtime" | "build_time" | "typecheck" | "test_time" | "development_only" | "unknown";
|
|
22
24
|
export type PackageEvidenceStrength = "high" | "medium" | "low";
|
|
25
|
+
export type CorpusGateReason = "ok" | "no_code_intelligence" | "repo_intelligence_invalid" | "repo_intelligence_absent";
|
|
26
|
+
/** Enriched affected area with proof-chain linkage (Phase 3). */
|
|
27
|
+
export type AffectedAreaFact = {
|
|
28
|
+
id: string;
|
|
29
|
+
label: string;
|
|
30
|
+
packages: string[];
|
|
31
|
+
findingIds: string[];
|
|
32
|
+
paths: string[];
|
|
33
|
+
evidenceStrength: PackageEvidenceStrength | null;
|
|
34
|
+
hotspotPackages: string[];
|
|
35
|
+
verificationFocus: string[];
|
|
36
|
+
};
|
|
37
|
+
export type ScanNarrativeConfidenceFacts = {
|
|
38
|
+
decision: "low" | "medium" | "high" | null;
|
|
39
|
+
assessment: "low" | "medium" | "high" | null;
|
|
40
|
+
limitedContext: boolean;
|
|
41
|
+
};
|
|
23
42
|
/** Engine semantic projection for one changed package (wire copy only). */
|
|
24
43
|
export type ChangedPackageSemantics = {
|
|
25
44
|
packageName: string;
|
|
@@ -52,6 +71,8 @@ export type ScanNarrativeFacts = {
|
|
|
52
71
|
tier3: boolean;
|
|
53
72
|
};
|
|
54
73
|
repoIntelligenceParse: RepoIntelligenceParseStatus;
|
|
74
|
+
preparationWarnings: AnalysisContextWarning[];
|
|
75
|
+
corpusGateReason: CorpusGateReason;
|
|
55
76
|
};
|
|
56
77
|
changedPackages: {
|
|
57
78
|
primary: string | null;
|
|
@@ -83,10 +104,7 @@ export type ScanNarrativeFacts = {
|
|
|
83
104
|
changedPackageCount?: number;
|
|
84
105
|
factors: string[];
|
|
85
106
|
} | null;
|
|
86
|
-
affectedAreas:
|
|
87
|
-
id: string;
|
|
88
|
-
label: string;
|
|
89
|
-
}>;
|
|
107
|
+
affectedAreas: AffectedAreaFact[];
|
|
90
108
|
hotspots: Array<{
|
|
91
109
|
packageName: string;
|
|
92
110
|
source: "code" | "graph";
|
|
@@ -103,9 +121,7 @@ export type ScanNarrativeFacts = {
|
|
|
103
121
|
remediation?: string;
|
|
104
122
|
affectedFiles?: string[];
|
|
105
123
|
}>;
|
|
106
|
-
confidence:
|
|
107
|
-
decision: "low" | "medium" | "high" | null;
|
|
108
|
-
};
|
|
124
|
+
confidence: ScanNarrativeConfidenceFacts;
|
|
109
125
|
repositoryContext: Array<{
|
|
110
126
|
family: ObservationSignalFamily;
|
|
111
127
|
source: RepositoryContextSource;
|
|
@@ -115,7 +131,9 @@ export type ScanNarrativeFacts = {
|
|
|
115
131
|
};
|
|
116
132
|
}>;
|
|
117
133
|
mergePosture: MergePosture | null;
|
|
134
|
+
/** @deprecated Use `riskSignals.riskIndex` — kept for frozen surface presenters. */
|
|
118
135
|
riskIndex: number | null;
|
|
136
|
+
riskSignals: RiskSignals | null;
|
|
119
137
|
};
|
|
120
138
|
export declare const EMPTY_SCAN_NARRATIVE_FACTS: ScanNarrativeFacts;
|
|
121
139
|
/** Derivation caps — presenters apply tighter channel limits. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanNarrativeFacts.d.ts","sourceRoot":"","sources":["../src/scanNarrativeFacts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"scanNarrativeFacts.d.ts","sourceRoot":"","sources":["../src/scanNarrativeFacts.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,8 @@ export const EMPTY_SCAN_NARRATIVE_FACTS = {
|
|
|
4
4
|
codeIntelligenceAvailable: false,
|
|
5
5
|
tiersPresent: { tier1: false, tier2: false, tier3: false },
|
|
6
6
|
repoIntelligenceParse: "absent",
|
|
7
|
+
preparationWarnings: [],
|
|
8
|
+
corpusGateReason: "repo_intelligence_absent",
|
|
7
9
|
},
|
|
8
10
|
changedPackages: { primary: null, others: [], all: [] },
|
|
9
11
|
packageUsage: [],
|
|
@@ -16,10 +18,11 @@ export const EMPTY_SCAN_NARRATIVE_FACTS = {
|
|
|
16
18
|
affectedAreas: [],
|
|
17
19
|
hotspots: [],
|
|
18
20
|
reviewerGuidance: [],
|
|
19
|
-
confidence: { decision: null },
|
|
21
|
+
confidence: { decision: null, assessment: null, limitedContext: false },
|
|
20
22
|
repositoryContext: [],
|
|
21
23
|
mergePosture: null,
|
|
22
24
|
riskIndex: null,
|
|
25
|
+
riskSignals: null,
|
|
23
26
|
};
|
|
24
27
|
/** Derivation caps — presenters apply tighter channel limits. */
|
|
25
28
|
export const NARRATIVE_FACT_MAX_PATHS = 20;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanPresentationBundle.golden.test.d.ts","sourceRoot":"","sources":["../src/scanPresentationBundle.golden.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { deriveCardExposureCategory } from "./formatCardExposureDisplay.js";
|
|
3
|
+
import { buildScanPresentationBundle } from "./presentation/orchestration/buildScanPresentationBundle.js";
|
|
4
|
+
import { scanResultBullmq, scanResultEslint, scanResultFastifyRuntime, scanResultLimitedContext, scanResultMixedTypescriptFastify, scanResultPrettier, scanResultTypescriptPatch, scanResultVitest, } from "./presentation/fixtures/presentationPersonaFixtures.js";
|
|
5
|
+
const validationPersonas = [
|
|
6
|
+
{ id: "fastify-runtime", result: scanResultFastifyRuntime },
|
|
7
|
+
{ id: "typescript-patch", result: scanResultTypescriptPatch },
|
|
8
|
+
{ id: "mixed-ts-fastify", result: scanResultMixedTypescriptFastify },
|
|
9
|
+
{ id: "prettier", result: scanResultPrettier },
|
|
10
|
+
{ id: "bullmq", result: scanResultBullmq },
|
|
11
|
+
{ id: "eslint", result: scanResultEslint },
|
|
12
|
+
{ id: "vitest", result: scanResultVitest },
|
|
13
|
+
{ id: "limited-context", result: scanResultLimitedContext },
|
|
14
|
+
];
|
|
15
|
+
function bundleFor(result) {
|
|
16
|
+
return buildScanPresentationBundle({
|
|
17
|
+
result,
|
|
18
|
+
pipelineStatus: "done",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
describe("scanPresentationBundle golden — proof model", () => {
|
|
22
|
+
it.each(validationPersonas)("builds bundle for $id", ({ result }) => {
|
|
23
|
+
const bundle = bundleFor(result);
|
|
24
|
+
expect(bundle).not.toBeNull();
|
|
25
|
+
expect(bundle.facts.availability.corpusGateReason).toBeDefined();
|
|
26
|
+
expect(bundle.facts.confidence).toMatchObject({
|
|
27
|
+
limitedContext: expect.any(Boolean),
|
|
28
|
+
});
|
|
29
|
+
expect(bundle.facts.riskSignals).not.toBeNull();
|
|
30
|
+
expect(bundle.facts.riskSignals.layers).toHaveLength(4);
|
|
31
|
+
});
|
|
32
|
+
it("fastify-runtime — linkage and diagnostics", () => {
|
|
33
|
+
const bundle = bundleFor(scanResultFastifyRuntime);
|
|
34
|
+
const { facts } = bundle;
|
|
35
|
+
expect(facts.availability.corpusGateReason).toBe("ok");
|
|
36
|
+
expect(facts.confidence.limitedContext).toBe(false);
|
|
37
|
+
expect(facts.confidence.assessment).toBe("medium");
|
|
38
|
+
const apiArea = facts.affectedAreas.find((a) => a.id === "api");
|
|
39
|
+
expect(apiArea?.packages).toContain("fastify");
|
|
40
|
+
expect(apiArea?.findingIds).toContain("finding-fastify-runtime");
|
|
41
|
+
expect(apiArea?.paths.length).toBeGreaterThan(0);
|
|
42
|
+
expect(apiArea?.hotspotPackages).toContain("fastify");
|
|
43
|
+
expect(apiArea?.verificationFocus.length).toBeGreaterThan(0);
|
|
44
|
+
expect(facts.riskSignals?.riskIndex).toBe(55);
|
|
45
|
+
expect(facts.riskSignals?.exposure).toBe(deriveCardExposureCategory(55));
|
|
46
|
+
expect(facts.riskIndex).toBe(facts.riskSignals?.riskIndex);
|
|
47
|
+
});
|
|
48
|
+
it("limited-context — diagnostics without synthetic confidence", () => {
|
|
49
|
+
const bundle = bundleFor(scanResultLimitedContext);
|
|
50
|
+
const { facts } = bundle;
|
|
51
|
+
expect(facts.availability.corpusGateReason).toBe("no_code_intelligence");
|
|
52
|
+
expect(facts.availability.preparationWarnings.some((w) => w.code === "base_lockfile_missing")).toBe(true);
|
|
53
|
+
expect(facts.confidence.assessment).toBe("low");
|
|
54
|
+
expect(facts.confidence.limitedContext).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
it("typescript-patch — tooling semantics with risk signals", () => {
|
|
57
|
+
const bundle = bundleFor(scanResultTypescriptPatch);
|
|
58
|
+
const { facts } = bundle;
|
|
59
|
+
expect(facts.availability.corpusGateReason).toBe("ok");
|
|
60
|
+
expect(facts.packageSemantics?.dependencyClass).toBe("tooling");
|
|
61
|
+
expect(facts.riskSignals?.riskIndex).toBe(18);
|
|
62
|
+
expect(facts.riskSignals?.exposure).toBe(deriveCardExposureCategory(18));
|
|
63
|
+
});
|
|
64
|
+
it("affected areas always carry linkage arrays (structure)", () => {
|
|
65
|
+
for (const { result } of validationPersonas) {
|
|
66
|
+
const bundle = bundleFor(result);
|
|
67
|
+
if (!bundle)
|
|
68
|
+
continue;
|
|
69
|
+
for (const area of bundle.facts.affectedAreas) {
|
|
70
|
+
expect(Array.isArray(area.packages)).toBe(true);
|
|
71
|
+
expect(Array.isArray(area.findingIds)).toBe(true);
|
|
72
|
+
expect(Array.isArray(area.paths)).toBe(true);
|
|
73
|
+
expect(Array.isArray(area.hotspotPackages)).toBe(true);
|
|
74
|
+
expect(Array.isArray(area.verificationFocus)).toBe(true);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -21,8 +21,8 @@ export declare const scanResultSchema: z.ZodObject<{
|
|
|
21
21
|
methodologyVersion: z.ZodOptional<z.ZodString>;
|
|
22
22
|
confidence: z.ZodOptional<z.ZodEnum<{
|
|
23
23
|
low: "low";
|
|
24
|
-
medium: "medium";
|
|
25
24
|
high: "high";
|
|
25
|
+
medium: "medium";
|
|
26
26
|
}>>;
|
|
27
27
|
signals: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
28
28
|
contributions: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
@@ -40,8 +40,8 @@ export declare const scanResultSchema: z.ZodObject<{
|
|
|
40
40
|
}>;
|
|
41
41
|
confidence: z.ZodOptional<z.ZodEnum<{
|
|
42
42
|
low: "low";
|
|
43
|
-
medium: "medium";
|
|
44
43
|
high: "high";
|
|
44
|
+
medium: "medium";
|
|
45
45
|
}>>;
|
|
46
46
|
reasoning: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
47
47
|
}, z.core.$loose>>;
|
|
@@ -62,8 +62,8 @@ export declare const engineOutputScanResultSchema: z.ZodObject<{
|
|
|
62
62
|
findings: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodUnknown>, z.ZodNull]>>, z.ZodTransform<unknown[], unknown[] | null | undefined>>;
|
|
63
63
|
confidence: z.ZodOptional<z.ZodEnum<{
|
|
64
64
|
low: "low";
|
|
65
|
-
medium: "medium";
|
|
66
65
|
high: "high";
|
|
66
|
+
medium: "medium";
|
|
67
67
|
}>>;
|
|
68
68
|
signals: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
69
69
|
contributions: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
@@ -80,8 +80,8 @@ export declare const engineOutputScanResultSchema: z.ZodObject<{
|
|
|
80
80
|
}>;
|
|
81
81
|
confidence: z.ZodOptional<z.ZodEnum<{
|
|
82
82
|
low: "low";
|
|
83
|
-
medium: "medium";
|
|
84
83
|
high: "high";
|
|
84
|
+
medium: "medium";
|
|
85
85
|
}>>;
|
|
86
86
|
reasoning: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
87
87
|
}, z.core.$loose>>;
|
|
@@ -89,6 +89,55 @@ export declare const engineOutputScanResultSchema: z.ZodObject<{
|
|
|
89
89
|
methodologyVersion: z.ZodString;
|
|
90
90
|
generatedAt: z.ZodString;
|
|
91
91
|
assessment: z.ZodObject<{
|
|
92
|
+
reviewFocalPoint: z.ZodObject<{
|
|
93
|
+
episodeShape: z.ZodEnum<{
|
|
94
|
+
single_anchor: "single_anchor";
|
|
95
|
+
multi_anchor: "multi_anchor";
|
|
96
|
+
parent_supporting: "parent_supporting";
|
|
97
|
+
coupled_pair: "coupled_pair";
|
|
98
|
+
tooling_bundle: "tooling_bundle";
|
|
99
|
+
structural: "structural";
|
|
100
|
+
}>;
|
|
101
|
+
anchors: z.ZodArray<z.ZodString>;
|
|
102
|
+
supportingPackages: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
103
|
+
election: z.ZodObject<{
|
|
104
|
+
grounding: z.ZodArray<z.ZodObject<{
|
|
105
|
+
packageName: z.ZodString;
|
|
106
|
+
reason: z.ZodString;
|
|
107
|
+
decidedBy: z.ZodEnum<{
|
|
108
|
+
concern: "concern";
|
|
109
|
+
reach: "reach";
|
|
110
|
+
changeSeverity: "changeSeverity";
|
|
111
|
+
role_salience_tiebreak: "role_salience_tiebreak";
|
|
112
|
+
}>;
|
|
113
|
+
evidenceRefs: z.ZodArray<z.ZodString>;
|
|
114
|
+
}, z.core.$strip>>;
|
|
115
|
+
exclusions: z.ZodArray<z.ZodObject<{
|
|
116
|
+
packageName: z.ZodString;
|
|
117
|
+
reason: z.ZodString;
|
|
118
|
+
lostOn: z.ZodEnum<{
|
|
119
|
+
concern: "concern";
|
|
120
|
+
reach: "reach";
|
|
121
|
+
changeSeverity: "changeSeverity";
|
|
122
|
+
role_salience_tiebreak: "role_salience_tiebreak";
|
|
123
|
+
}>;
|
|
124
|
+
evidenceRefs: z.ZodArray<z.ZodString>;
|
|
125
|
+
}, z.core.$strip>>;
|
|
126
|
+
}, z.core.$strip>;
|
|
127
|
+
}, z.core.$strip>;
|
|
128
|
+
reachScope: z.ZodObject<{
|
|
129
|
+
packages: z.ZodArray<z.ZodString>;
|
|
130
|
+
maxBucket: z.ZodEnum<{
|
|
131
|
+
low: "low";
|
|
132
|
+
high: "high";
|
|
133
|
+
very_low: "very_low";
|
|
134
|
+
moderate: "moderate";
|
|
135
|
+
}>;
|
|
136
|
+
}, z.core.$strip>;
|
|
137
|
+
verificationScope: z.ZodObject<{
|
|
138
|
+
packages: z.ZodArray<z.ZodString>;
|
|
139
|
+
focus: z.ZodArray<z.ZodString>;
|
|
140
|
+
}, z.core.$strip>;
|
|
92
141
|
posture: z.ZodEnum<{
|
|
93
142
|
safe: "safe";
|
|
94
143
|
needs_review: "needs_review";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanResultSchema.d.ts","sourceRoot":"","sources":["../src/scanResultSchema.ts"],"names":[],"mappings":"
|
|
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;AASnD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4Bb,CAAC;AAUjB;;;GAGG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAIvC,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,CAazE;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/dist/scanResultSchema.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { assessmentSchema } from "@mergesignal/contracts";
|
|
1
2
|
import { z } from "zod";
|
|
2
|
-
import { assessmentSchema } from "./assessmentSchema.js";
|
|
3
3
|
/** Bump when persisted `result` JSON validation rules change materially (relaxed / legacy-tolerant). */
|
|
4
4
|
export const SCAN_RESULT_ABI = "2";
|
|
5
5
|
/** Bump when strict fresh-engine-output validation rules change materially. */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
2
|
import { safeParseScanResult, parseScanResultOrThrow, scanResultSchema, safeParseEngineOutputScanResult, parseEngineOutputScanResultOrThrow, } from "./scanResultSchema.js";
|
|
3
|
+
import { emptyReachScope, emptyVerificationScope, minimalReviewFocalPoint, } from "./fixtures/assessmentScopeFixtures.js";
|
|
3
4
|
const minimalValid = {
|
|
4
5
|
totalScore: 42,
|
|
5
6
|
layerScores: {
|
|
@@ -78,6 +79,9 @@ describe("scanResultSchema", () => {
|
|
|
78
79
|
});
|
|
79
80
|
});
|
|
80
81
|
const minimalAssessment = {
|
|
82
|
+
reviewFocalPoint: minimalReviewFocalPoint(["typescript"]),
|
|
83
|
+
reachScope: emptyReachScope(),
|
|
84
|
+
verificationScope: emptyVerificationScope(),
|
|
81
85
|
posture: "safe",
|
|
82
86
|
confidence: "high",
|
|
83
87
|
primaryConcern: null,
|
|
@@ -118,4 +122,26 @@ describe("engineOutputScanResultSchema (strict, fresh engine only)", () => {
|
|
|
118
122
|
});
|
|
119
123
|
expect(r.ok).toBe(false);
|
|
120
124
|
});
|
|
125
|
+
it("rejects ABI-1 assessment missing focal/scope fields on fresh engine path", () => {
|
|
126
|
+
const legacyAssessment = {
|
|
127
|
+
posture: "safe",
|
|
128
|
+
confidence: "high",
|
|
129
|
+
primaryConcern: null,
|
|
130
|
+
concerns: [],
|
|
131
|
+
factors: ["tooling_maintenance"],
|
|
132
|
+
changeClasses: ["tooling_maintenance"],
|
|
133
|
+
presentation: {
|
|
134
|
+
narrativeIntensity: "minimal",
|
|
135
|
+
reachVisibility: "hidden",
|
|
136
|
+
verificationIntensity: "advisory",
|
|
137
|
+
insightEmissionFloor: "none",
|
|
138
|
+
reportMode: "high_signal_pr",
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
const r = safeParseEngineOutputScanResult({
|
|
142
|
+
...withMethodology,
|
|
143
|
+
assessment: legacyAssessment,
|
|
144
|
+
});
|
|
145
|
+
expect(r.ok).toBe(false);
|
|
146
|
+
});
|
|
121
147
|
});
|
package/dist/types.d.ts
CHANGED