@mergesignal/shared 0.12.3 → 0.14.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.
Files changed (178) hide show
  1. package/dist/assessment/authoredCommunication.d.ts +30 -0
  2. package/dist/assessment/authoredCommunication.d.ts.map +1 -0
  3. package/dist/assessment/authoredCommunication.js +18 -0
  4. package/dist/assessment/index.d.ts +6 -0
  5. package/dist/assessment/index.d.ts.map +1 -0
  6. package/dist/assessment/index.js +5 -0
  7. package/dist/assessment/literals.d.ts +44 -0
  8. package/dist/assessment/literals.d.ts.map +1 -0
  9. package/dist/assessment/literals.js +140 -0
  10. package/dist/assessment/schema.d.ts +311 -0
  11. package/dist/assessment/schema.d.ts.map +1 -0
  12. package/dist/assessment/schema.js +162 -0
  13. package/dist/assessment/types.d.ts +197 -0
  14. package/dist/assessment/types.d.ts.map +1 -0
  15. package/dist/assessment/types.js +1 -0
  16. package/dist/assessment/version.d.ts +11 -0
  17. package/dist/assessment/version.d.ts.map +1 -0
  18. package/dist/assessment/version.js +9 -0
  19. package/dist/assessmentLabels.d.ts +1 -1
  20. package/dist/assessmentLabels.d.ts.map +1 -1
  21. package/dist/assessmentLabels.js +2 -2
  22. package/dist/assessmentPresentationUtils.d.ts +1 -1
  23. package/dist/assessmentPresentationUtils.d.ts.map +1 -1
  24. package/dist/assessmentProjection.d.ts +1 -1
  25. package/dist/assessmentProjection.d.ts.map +1 -1
  26. package/dist/assessmentProjection.js +2 -2
  27. package/dist/deriveScanNarrative.d.ts.map +1 -1
  28. package/dist/deriveScanNarrative.js +2 -1
  29. package/dist/fixtures/assessmentFixtures.d.ts +1 -1
  30. package/dist/fixtures/assessmentFixtures.js +12 -12
  31. package/dist/fixtures/assessmentScopeFixtures.d.ts +1 -1
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +2 -0
  35. package/dist/lockfileEvidence.d.ts +22 -0
  36. package/dist/lockfileEvidence.d.ts.map +1 -0
  37. package/dist/lockfileEvidence.js +32 -0
  38. package/dist/presentation/dto/assessmentPresentationFields.d.ts +1 -1
  39. package/dist/presentation/dto/assessmentPresentationFields.d.ts.map +1 -1
  40. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts.map +1 -1
  41. package/dist/presentation/orchestration/buildScanPresentationBundle.js +2 -1
  42. package/dist/presentation/orchestration/scanPresentationBundle.d.ts +2 -1
  43. package/dist/presentation/orchestration/scanPresentationBundle.d.ts.map +1 -1
  44. package/dist/presentation/profile/buildProfileFromAssessment.d.ts +1 -1
  45. package/dist/presentation/profile/buildProfileFromAssessment.d.ts.map +1 -1
  46. package/dist/presentation/profile/buildProfileFromAssessment.js +14 -6
  47. package/dist/repoIntelligenceSchema.d.ts +5 -5
  48. package/dist/scanResultSchema.d.ts +8 -8
  49. package/dist/scanResultSchema.js +1 -1
  50. package/dist/scanSurfaceCopy.d.ts +1 -0
  51. package/dist/scanSurfaceCopy.d.ts.map +1 -1
  52. package/dist/scanSurfaceCopy.js +1 -0
  53. package/dist/types.d.ts +6 -2
  54. package/dist/types.d.ts.map +1 -1
  55. package/package.json +2 -3
  56. package/dist/actionsStepSummary.test.d.ts +0 -2
  57. package/dist/actionsStepSummary.test.d.ts.map +0 -1
  58. package/dist/actionsStepSummary.test.js +0 -53
  59. package/dist/assessmentProjection.verificationChannel.test.d.ts +0 -2
  60. package/dist/assessmentProjection.verificationChannel.test.d.ts.map +0 -1
  61. package/dist/assessmentProjection.verificationChannel.test.js +0 -110
  62. package/dist/deriveScanNarrative.test.d.ts +0 -2
  63. package/dist/deriveScanNarrative.test.d.ts.map +0 -1
  64. package/dist/deriveScanNarrative.test.js +0 -274
  65. package/dist/engineWireRegression.test.d.ts +0 -2
  66. package/dist/engineWireRegression.test.d.ts.map +0 -1
  67. package/dist/engineWireRegression.test.js +0 -77
  68. package/dist/fixtures/engineAbi4Fixtures.d.ts +0 -16
  69. package/dist/fixtures/engineAbi4Fixtures.d.ts.map +0 -1
  70. package/dist/fixtures/engineAbi4Fixtures.js +0 -26
  71. package/dist/fixtures/legacyEngineRepoIntelligence.d.ts +0 -28
  72. package/dist/fixtures/legacyEngineRepoIntelligence.d.ts.map +0 -1
  73. package/dist/fixtures/legacyEngineRepoIntelligence.js +0 -28
  74. package/dist/fixtures/repoIntelligenceFixtures.d.ts +0 -8
  75. package/dist/fixtures/repoIntelligenceFixtures.d.ts.map +0 -1
  76. package/dist/fixtures/repoIntelligenceFixtures.js +0 -123
  77. package/dist/fixtures/repoIntelligenceTestHelpers.d.ts +0 -5
  78. package/dist/fixtures/repoIntelligenceTestHelpers.d.ts.map +0 -1
  79. package/dist/fixtures/repoIntelligenceTestHelpers.js +0 -28
  80. package/dist/formatInsight.test.d.ts +0 -2
  81. package/dist/formatInsight.test.d.ts.map +0 -1
  82. package/dist/formatInsight.test.js +0 -102
  83. package/dist/narrativeParity.test.d.ts +0 -2
  84. package/dist/narrativeParity.test.d.ts.map +0 -1
  85. package/dist/narrativeParity.test.js +0 -35
  86. package/dist/narrativePresentation.test.d.ts +0 -2
  87. package/dist/narrativePresentation.test.d.ts.map +0 -1
  88. package/dist/narrativePresentation.test.js +0 -54
  89. package/dist/normalizeGeneratedText.test.d.ts +0 -2
  90. package/dist/normalizeGeneratedText.test.d.ts.map +0 -1
  91. package/dist/normalizeGeneratedText.test.js +0 -18
  92. package/dist/prRiskBand.test.d.ts +0 -2
  93. package/dist/prRiskBand.test.d.ts.map +0 -1
  94. package/dist/prRiskBand.test.js +0 -68
  95. package/dist/prRiskWire.test.d.ts +0 -2
  96. package/dist/prRiskWire.test.d.ts.map +0 -1
  97. package/dist/prRiskWire.test.js +0 -106
  98. package/dist/prRiskWiringRegression.test.d.ts +0 -2
  99. package/dist/prRiskWiringRegression.test.d.ts.map +0 -1
  100. package/dist/prRiskWiringRegression.test.js +0 -102
  101. package/dist/presentation/abi3Expression.test.d.ts +0 -13
  102. package/dist/presentation/abi3Expression.test.d.ts.map +0 -1
  103. package/dist/presentation/abi3Expression.test.js +0 -392
  104. package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts +0 -2
  105. package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts.map +0 -1
  106. package/dist/presentation/buildGitHubCheckRunOutput.test.js +0 -163
  107. package/dist/presentation/bundleAccessorParity.test.d.ts +0 -9
  108. package/dist/presentation/bundleAccessorParity.test.d.ts.map +0 -1
  109. package/dist/presentation/bundleAccessorParity.test.js +0 -126
  110. package/dist/presentation/cliParity.test.d.ts +0 -2
  111. package/dist/presentation/cliParity.test.d.ts.map +0 -1
  112. package/dist/presentation/cliParity.test.js +0 -49
  113. package/dist/presentation/compose/narrativeChannels.test.d.ts +0 -2
  114. package/dist/presentation/compose/narrativeChannels.test.d.ts.map +0 -1
  115. package/dist/presentation/compose/narrativeChannels.test.js +0 -42
  116. package/dist/presentation/decisionSurface.test.d.ts +0 -2
  117. package/dist/presentation/decisionSurface.test.d.ts.map +0 -1
  118. package/dist/presentation/decisionSurface.test.js +0 -290
  119. package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts +0 -2
  120. package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts.map +0 -1
  121. package/dist/presentation/fixtures/dashboardCardGolden.test.js +0 -38
  122. package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts +0 -11
  123. package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts.map +0 -1
  124. package/dist/presentation/fixtures/presentationPersonaFixtures.js +0 -208
  125. package/dist/presentation/fixtures/scanResultFixtures.d.ts +0 -5
  126. package/dist/presentation/fixtures/scanResultFixtures.d.ts.map +0 -1
  127. package/dist/presentation/fixtures/scanResultFixtures.js +0 -123
  128. package/dist/presentation/intent/postureVocabularyGuard.test.d.ts +0 -2
  129. package/dist/presentation/intent/postureVocabularyGuard.test.d.ts.map +0 -1
  130. package/dist/presentation/intent/postureVocabularyGuard.test.js +0 -23
  131. package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts +0 -2
  132. package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts.map +0 -1
  133. package/dist/presentation/orchestration/buildScanPresentationBundle.test.js +0 -99
  134. package/dist/presentation/presentation.test.d.ts +0 -2
  135. package/dist/presentation/presentation.test.d.ts.map +0 -1
  136. package/dist/presentation/presentation.test.js +0 -126
  137. package/dist/presentation/presentationPersonas.test.d.ts +0 -2
  138. package/dist/presentation/presentationPersonas.test.d.ts.map +0 -1
  139. package/dist/presentation/presentationPersonas.test.js +0 -164
  140. package/dist/presentation/surfaceParity.test.d.ts +0 -2
  141. package/dist/presentation/surfaceParity.test.d.ts.map +0 -1
  142. package/dist/presentation/surfaceParity.test.js +0 -156
  143. package/dist/presentation/verificationChannelParity.test.d.ts +0 -2
  144. package/dist/presentation/verificationChannelParity.test.d.ts.map +0 -1
  145. package/dist/presentation/verificationChannelParity.test.js +0 -57
  146. package/dist/productMessaging.test.d.ts +0 -2
  147. package/dist/productMessaging.test.d.ts.map +0 -1
  148. package/dist/productMessaging.test.js +0 -97
  149. package/dist/repoIntelligenceSchema.contract.test.d.ts +0 -2
  150. package/dist/repoIntelligenceSchema.contract.test.d.ts.map +0 -1
  151. package/dist/repoIntelligenceSchema.contract.test.js +0 -51
  152. package/dist/repoIntelligenceSemantics.test.d.ts +0 -2
  153. package/dist/repoIntelligenceSemantics.test.d.ts.map +0 -1
  154. package/dist/repoIntelligenceSemantics.test.js +0 -63
  155. package/dist/repoIntelligenceValidation.persist.test.d.ts +0 -2
  156. package/dist/repoIntelligenceValidation.persist.test.d.ts.map +0 -1
  157. package/dist/repoIntelligenceValidation.persist.test.js +0 -44
  158. package/dist/riskSignals.test.d.ts +0 -2
  159. package/dist/riskSignals.test.d.ts.map +0 -1
  160. package/dist/riskSignals.test.js +0 -32
  161. package/dist/riskVocabulary.test.d.ts +0 -2
  162. package/dist/riskVocabulary.test.d.ts.map +0 -1
  163. package/dist/riskVocabulary.test.js +0 -14
  164. package/dist/scanPresentationBundle.golden.test.d.ts +0 -2
  165. package/dist/scanPresentationBundle.golden.test.d.ts.map +0 -1
  166. package/dist/scanPresentationBundle.golden.test.js +0 -78
  167. package/dist/scanResultSchema.test.d.ts +0 -2
  168. package/dist/scanResultSchema.test.d.ts.map +0 -1
  169. package/dist/scanResultSchema.test.js +0 -162
  170. package/dist/scanSurfaceCopy.test.d.ts +0 -2
  171. package/dist/scanSurfaceCopy.test.d.ts.map +0 -1
  172. package/dist/scanSurfaceCopy.test.js +0 -14
  173. package/dist/selectTopAffectedAreas.test.d.ts +0 -2
  174. package/dist/selectTopAffectedAreas.test.d.ts.map +0 -1
  175. package/dist/selectTopAffectedAreas.test.js +0 -179
  176. package/dist/trustedScanGuards.test.d.ts +0 -2
  177. package/dist/trustedScanGuards.test.d.ts.map +0 -1
  178. package/dist/trustedScanGuards.test.js +0 -151
@@ -1,102 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { formatInsight, renderInsightsAsMarkdown } from "./formatInsight.js";
3
- function makeInsight(overrides = {}) {
4
- return {
5
- type: "behavioral_change",
6
- priority: "high",
7
- confidence: "confirmed",
8
- scope: "changed",
9
- message: "default message",
10
- context: "default context",
11
- remediation: "default remediation",
12
- ...overrides,
13
- };
14
- }
15
- describe("formatInsight", () => {
16
- describe("typographic dash normalization", () => {
17
- it("replaces em dashes with ASCII hyphens in message", () => {
18
- const result = formatInsight(makeInsight({ message: "A \u2014 B" }));
19
- expect(result.message).toBe("A - B");
20
- });
21
- it("replaces en dashes with ASCII hyphens in message", () => {
22
- const result = formatInsight(makeInsight({ message: "A \u2013 B" }));
23
- expect(result.message).toBe("A - B");
24
- });
25
- it("normalizes all three fields: message, context, remediation", () => {
26
- const result = formatInsight(makeInsight({
27
- message: "A \u2014 B",
28
- context: "C \u2013 D",
29
- remediation: "E \u2014 F",
30
- }));
31
- expect(result.message).toBe("A - B");
32
- expect(result.where).toBe("C - D");
33
- expect(result.action).toBe("E - F");
34
- });
35
- it("handles multiple dashes in a single field", () => {
36
- const result = formatInsight(makeInsight({ message: "X \u2014 Y \u2013 Z" }));
37
- expect(result.message).toBe("X - Y - Z");
38
- });
39
- });
40
- describe("XSS payload pass-through", () => {
41
- it("does not alter script tag payloads — escaping is the renderer's job", () => {
42
- const payload = "<script>alert(1)</script>";
43
- const result = formatInsight(makeInsight({ message: payload }));
44
- expect(result.message).toBe(payload);
45
- });
46
- it("does not alter img onerror payloads", () => {
47
- const payload = "<img src=x onerror=alert(1)>";
48
- const result = formatInsight(makeInsight({ context: payload }));
49
- expect(result.where).toBe(payload);
50
- });
51
- it("does not alter javascript: URL payloads", () => {
52
- const payload = "[x](javascript:alert(1))";
53
- const result = formatInsight(makeInsight({ remediation: payload }));
54
- expect(result.action).toBe(payload);
55
- });
56
- });
57
- describe("edge cases", () => {
58
- it("handles empty strings in all fields without throwing", () => {
59
- const result = formatInsight(makeInsight({ message: "", context: "", remediation: "" }));
60
- expect(result.message).toBe("");
61
- expect(result.where).toBe("");
62
- expect(result.action).toBe("");
63
- });
64
- it("maps fields correctly: context → where, remediation → action", () => {
65
- const result = formatInsight(makeInsight({ message: "msg", context: "ctx", remediation: "fix" }));
66
- expect(result.message).toBe("msg");
67
- expect(result.where).toBe("ctx");
68
- expect(result.action).toBe("fix");
69
- });
70
- });
71
- });
72
- describe("renderInsightsAsMarkdown", () => {
73
- const decision = {
74
- recommendation: "needs_review",
75
- confidence: "high",
76
- reasoning: ["reason 1"],
77
- };
78
- it("does not alter or escape XSS payloads — sanitization is deferred to MarkdownContent", () => {
79
- const xssPayload = "<script>alert(1)</script>";
80
- const insight = makeInsight({ message: xssPayload });
81
- const output = renderInsightsAsMarkdown([insight], decision);
82
- expect(output).toContain(xssPayload);
83
- });
84
- it("includes the decision title and all insight fields", () => {
85
- const insight = makeInsight({
86
- message: "msg text",
87
- context: "ctx text",
88
- remediation: "fix text",
89
- });
90
- const output = renderInsightsAsMarkdown([insight], decision);
91
- expect(output).toContain("Needs review");
92
- expect(output).toContain("msg text");
93
- expect(output).toContain("ctx text");
94
- expect(output).toContain("fix text");
95
- });
96
- it("normalizes typographic dashes in rendered markdown output", () => {
97
- const insight = makeInsight({ message: "A \u2014 B" });
98
- const output = renderInsightsAsMarkdown([insight], decision);
99
- expect(output).toContain("A - B");
100
- expect(output).not.toContain("\u2014");
101
- });
102
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=narrativeParity.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"narrativeParity.test.d.ts","sourceRoot":"","sources":["../src/narrativeParity.test.ts"],"names":[],"mappings":""}
@@ -1,35 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { buildScanPresentationBundle } from "./presentation/orchestration/buildScanPresentationBundle.js";
3
- import { presentDashboardCard } from "./presentation/presenters/presentDashboardCard.js";
4
- import { presentScanDetails } from "./presentation/presenters/presentScanDetails.js";
5
- import { presentGitHubPrComment } from "./presentation/presenters/presentGitHubPrComment.js";
6
- import { renderGitHubCheckRunMarkdown } from "./presentation/render/renderGitHubCheckRunMarkdown.js";
7
- import { presentGitHubCheckRun } from "./presentation/presenters/presentGitHubCheckRun.js";
8
- import { scanResultFastifyRuntime } from "./presentation/fixtures/scanResultFixtures.js";
9
- const ORIGIN = "https://app.example.com";
10
- const SCAN_ID = "22222222-2222-4222-8222-222222222222";
11
- describe("narrative parity across consumers", () => {
12
- it("card, detail, GitHub share primary package and reachability story", () => {
13
- const bundle = buildScanPresentationBundle({
14
- result: scanResultFastifyRuntime,
15
- pipelineStatus: "done",
16
- });
17
- const card = presentDashboardCard(bundle);
18
- const detail = presentScanDetails(bundle, {
19
- scanId: SCAN_ID,
20
- });
21
- const checkRun = renderGitHubCheckRunMarkdown(presentGitHubCheckRun(bundle, {
22
- scanId: SCAN_ID,
23
- webAppOrigin: ORIGIN,
24
- baseline: false,
25
- }));
26
- const prComment = presentGitHubPrComment(bundle);
27
- expect(detail.narrative.primaryPackage).toBe("fastify");
28
- expect(card.reachVisibility).toBe("prominent");
29
- expect(card.verdict?.scopeLabel).toBeTruthy();
30
- const asciiOnly = /^[\x00-\x7F]*$/;
31
- expect(asciiOnly.test(checkRun)).toBe(true);
32
- expect(checkRun.toLowerCase()).toContain("fastify");
33
- expect(prComment.title.toLowerCase()).toContain("review");
34
- });
35
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=narrativePresentation.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"narrativePresentation.test.d.ts","sourceRoot":"","sources":["../src/narrativePresentation.test.ts"],"names":[],"mappings":""}
@@ -1,54 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { deriveScanNarrative } from "./deriveScanNarrative.js";
3
- import { fixtureRepoIntelligenceFastify } from "./fixtures/repoIntelligenceFixtures.js";
4
- import { analysisPreparationWithValidRepoIntel } from "./fixtures/repoIntelligenceTestHelpers.js";
5
- import { composeVerificationPrompt, formatChangedPackagesShort, formatUsageSummaryLine, summarizePackageUsage, } from "./narrativePresentation.js";
6
- const baseResult = {
7
- totalScore: 10,
8
- layerScores: {
9
- security: 1,
10
- maintainability: 2,
11
- ecosystem: 3,
12
- upgradeImpact: 4,
13
- },
14
- findings: [],
15
- generatedAt: "2026-01-01T00:00:00.000Z",
16
- };
17
- describe("narrativePresentation", () => {
18
- it("formats changed packages with second name when only one other", () => {
19
- const facts = deriveScanNarrative({
20
- ...baseResult,
21
- changedPackages: ["lodash", "axios"],
22
- });
23
- expect(formatChangedPackagesShort(facts, 2)).toBe("lodash, axios");
24
- });
25
- it("summarizes package usage paths from facts", () => {
26
- const facts = deriveScanNarrative({
27
- ...baseResult,
28
- changedPackages: ["fastify"],
29
- analysisPreparation: analysisPreparationWithValidRepoIntel(),
30
- repoIntelligence: fixtureRepoIntelligenceFastify,
31
- });
32
- const usage = summarizePackageUsage(facts);
33
- expect(usage?.pathCount).toBeGreaterThan(0);
34
- expect(formatUsageSummaryLine(facts)).toMatch(/Used in/);
35
- });
36
- it("composes verification from remediation when present", () => {
37
- const facts = deriveScanNarrative({
38
- ...baseResult,
39
- changedPackages: ["pkg"],
40
- insights: [
41
- {
42
- type: "usage_risk",
43
- priority: "high",
44
- confidence: "confirmed",
45
- scope: "changed",
46
- message: "Risk in billing",
47
- context: "billing",
48
- remediation: "Run export job smoke test",
49
- },
50
- ],
51
- });
52
- expect(composeVerificationPrompt(facts)).toBe("Run export job smoke test");
53
- });
54
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=normalizeGeneratedText.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"normalizeGeneratedText.test.d.ts","sourceRoot":"","sources":["../src/normalizeGeneratedText.test.ts"],"names":[],"mappings":""}
@@ -1,18 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { normalizeGeneratedText } from "./normalizeGeneratedText.js";
3
- import { scanSurfaceCopyFlat } from "./scanSurfaceCopy.js";
4
- describe("normalizeGeneratedText", () => {
5
- it("replaces typographic dashes with ASCII hyphen-minus", () => {
6
- expect(normalizeGeneratedText("foo – bar — baz")).toBe("foo - bar - baz");
7
- expect(normalizeGeneratedText("a&mdash;b&ndash;c")).toBe("a-b-c");
8
- });
9
- it("leaves ASCII text unchanged", () => {
10
- expect(normalizeGeneratedText("Safe - Needs review")).toBe("Safe - Needs review");
11
- });
12
- it("scanSurfaceCopy flat strings are ASCII-safe", () => {
13
- const asciiOnly = /^[\x00-\x7F]*$/;
14
- for (const value of Object.values(scanSurfaceCopyFlat())) {
15
- expect(asciiOnly.test(value), value).toBe(true);
16
- }
17
- });
18
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=prRiskBand.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prRiskBand.test.d.ts","sourceRoot":"","sources":["../src/prRiskBand.test.ts"],"names":[],"mappings":""}
@@ -1,68 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { PR_RISK_BAND_ABI, PR_RISK_BAND_THRESHOLDS, formatPrRiskSummary, prRiskBandAriaFragment, prRiskBandLabel, prRiskBandToGaugeBand, scoreToBand, scoreToBandLabel, } from "./prRiskBand.js";
3
- describe("PR_RISK_BAND_ABI", () => {
4
- it("is stable v1", () => {
5
- expect(PR_RISK_BAND_ABI).toBe("1");
6
- expect(PR_RISK_BAND_THRESHOLDS).toEqual([19, 44, 64, 84, 100]);
7
- });
8
- });
9
- describe("scoreToBand", () => {
10
- it("maps approved boundary cut points", () => {
11
- expect(scoreToBand(0)).toBe("very_low");
12
- expect(scoreToBand(19)).toBe("very_low");
13
- expect(scoreToBand(20)).toBe("low");
14
- expect(scoreToBand(44)).toBe("low");
15
- expect(scoreToBand(45)).toBe("medium");
16
- expect(scoreToBand(64)).toBe("medium");
17
- expect(scoreToBand(65)).toBe("high");
18
- expect(scoreToBand(84)).toBe("high");
19
- expect(scoreToBand(85)).toBe("critical");
20
- expect(scoreToBand(100)).toBe("critical");
21
- });
22
- it("returns null for invalid input", () => {
23
- expect(scoreToBand(null)).toBeNull();
24
- expect(scoreToBand(NaN)).toBeNull();
25
- });
26
- it("clamps out-of-range values", () => {
27
- expect(scoreToBand(150)).toBe("critical");
28
- expect(scoreToBand(-5)).toBe("very_low");
29
- expect(scoreToBand(71.6)).toBe("high");
30
- });
31
- });
32
- describe("scoreToBandLabel", () => {
33
- it("returns presentation labels", () => {
34
- expect(scoreToBandLabel(10)).toBe("Very Low");
35
- expect(scoreToBandLabel(55)).toBe("Medium");
36
- expect(scoreToBandLabel(90)).toBe("Critical");
37
- });
38
- });
39
- describe("prRiskBandLabel", () => {
40
- it("labels enum values", () => {
41
- expect(prRiskBandLabel("medium")).toBe("Medium");
42
- });
43
- });
44
- describe("prRiskBandAriaFragment", () => {
45
- it("prefixes PR Risk", () => {
46
- expect(prRiskBandAriaFragment(55)).toBe("PR Risk: Medium");
47
- });
48
- });
49
- describe("formatPrRiskSummary", () => {
50
- it("returns score and band label from riskSignals", () => {
51
- expect(formatPrRiskSummary({
52
- riskSignals: { riskIndex: 55, band: "medium", layers: [] },
53
- riskIndex: 55,
54
- })).toEqual({ prRiskScore: 55, prRiskBandLabel: "Medium" });
55
- });
56
- it("returns undefined when risk index is missing", () => {
57
- expect(formatPrRiskSummary({ riskSignals: null, riskIndex: null })).toBeUndefined();
58
- });
59
- });
60
- describe("prRiskBandToGaugeBand", () => {
61
- it("projects to three gauge buckets", () => {
62
- expect(prRiskBandToGaugeBand("very_low")).toBe("low");
63
- expect(prRiskBandToGaugeBand("low")).toBe("low");
64
- expect(prRiskBandToGaugeBand("medium")).toBe("moderate");
65
- expect(prRiskBandToGaugeBand("high")).toBe("high");
66
- expect(prRiskBandToGaugeBand("critical")).toBe("high");
67
- });
68
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=prRiskWire.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prRiskWire.test.d.ts","sourceRoot":"","sources":["../src/prRiskWire.test.ts"],"names":[],"mappings":""}
@@ -1,106 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { resolvePrRiskLayerScores, resolvePrRiskScore, resolvePrRiskScoreFromRow, resolveRepositoryHealthScore, resolveRepositoryHealthScoreFromRow, } from "./prRiskWire.js";
3
- function baseResult(overrides = {}) {
4
- return {
5
- totalScore: 50,
6
- layerScores: {
7
- security: 10,
8
- maintainability: 20,
9
- ecosystem: 30,
10
- upgradeImpact: 40,
11
- },
12
- findings: [],
13
- generatedAt: "2026-01-01T00:00:00.000Z",
14
- ...overrides,
15
- };
16
- }
17
- describe("resolvePrRiskScore", () => {
18
- it("prefers prRisk.score", () => {
19
- const result = baseResult({
20
- prRisk: { score: 72 },
21
- totalScore: 50,
22
- });
23
- expect(resolvePrRiskScore(result)).toBe(72);
24
- });
25
- it("falls back to denormalized column", () => {
26
- expect(resolvePrRiskScore(null, { prRiskScore: 61 })).toBe(61);
27
- });
28
- it("falls back to legacy total_score column before JSON totalScore", () => {
29
- expect(resolvePrRiskScore(null, { legacyTotalScore: 48 })).toBe(48);
30
- });
31
- it("falls back to JSON totalScore for historical scans", () => {
32
- expect(resolvePrRiskScore(baseResult({ totalScore: 48 }))).toBe(48);
33
- });
34
- it("returns null when no score available", () => {
35
- expect(resolvePrRiskScore(baseResult({ totalScore: NaN }))).toBeNull();
36
- });
37
- });
38
- describe("resolvePrRiskScoreFromRow", () => {
39
- it("uses pr_risk_score for new rows without legacy column", () => {
40
- expect(resolvePrRiskScoreFromRow({
41
- pr_risk_score: 55,
42
- total_score: null,
43
- })).toBe(55);
44
- });
45
- it("uses legacy total_score only when pr_risk_score is absent", () => {
46
- expect(resolvePrRiskScoreFromRow({
47
- pr_risk_score: null,
48
- total_score: 40,
49
- })).toBe(40);
50
- });
51
- });
52
- describe("resolvePrRiskLayerScores", () => {
53
- it("prefers prRisk.layerScores", () => {
54
- const layers = {
55
- security: 1,
56
- maintainability: 2,
57
- ecosystem: 3,
58
- upgradeImpact: 4,
59
- };
60
- const result = baseResult({ prRisk: { score: 10, layerScores: layers } });
61
- expect(resolvePrRiskLayerScores(result)).toEqual(layers);
62
- });
63
- });
64
- describe("resolveRepositoryHealthScore", () => {
65
- it("prefers repositoryHealth.totalScore", () => {
66
- const result = baseResult({
67
- repositoryHealth: { totalScore: 80 },
68
- totalScore: 50,
69
- });
70
- expect(resolveRepositoryHealthScore(result)).toBe(80);
71
- });
72
- it("falls back to denormalized column", () => {
73
- expect(resolveRepositoryHealthScore(null, { repositoryHealthScore: 66 })).toBe(66);
74
- });
75
- it("falls back to legacy total_score column for non-PR scans", () => {
76
- expect(resolveRepositoryHealthScore(null, { legacyTotalScore: 55 })).toBe(55);
77
- });
78
- it("uses JSON totalScore only for non-PR scans", () => {
79
- expect(resolveRepositoryHealthScore(baseResult({ totalScore: 55 }))).toBe(55);
80
- });
81
- it("does not use legacy scores for PR scans", () => {
82
- expect(resolveRepositoryHealthScore(null, {
83
- legacyTotalScore: 55,
84
- isPrScan: true,
85
- })).toBeNull();
86
- expect(resolveRepositoryHealthScore(baseResult({ totalScore: 55 }), {
87
- isPrScan: true,
88
- })).toBeNull();
89
- });
90
- });
91
- describe("resolveRepositoryHealthScoreFromRow", () => {
92
- it("uses repository_health_score for new rows", () => {
93
- expect(resolveRepositoryHealthScoreFromRow({
94
- repository_health_score: 70,
95
- total_score: null,
96
- github_pr_number: null,
97
- })).toBe(70);
98
- });
99
- it("does not use total_score for PR scans", () => {
100
- expect(resolveRepositoryHealthScoreFromRow({
101
- repository_health_score: null,
102
- total_score: 40,
103
- github_pr_number: 12,
104
- })).toBeNull();
105
- });
106
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=prRiskWiringRegression.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"prRiskWiringRegression.test.d.ts","sourceRoot":"","sources":["../src/prRiskWiringRegression.test.ts"],"names":[],"mappings":""}
@@ -1,102 +0,0 @@
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,13 +0,0 @@
1
- /**
2
- * Wave 2.D — ABI 3 expression validation.
3
- *
4
- * Validates that:
5
- * 1. projectReasoningLines reads from assessment.reasoning (ABI 3) when present
6
- * 2. projectVerificationActions returns assessment.verificationScope.guidance when present
7
- * 3. PR comment includes reviewFocalPoint.electionSummary when present
8
- * 4. Check run "Why" section uses assessment.reasoning lines directly
9
- * 5. No prohibited vocabulary appears in rendered surfaces
10
- * 6. assessment.reasoning takes priority over decision.reasoning
11
- */
12
- export {};
13
- //# sourceMappingURL=abi3Expression.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"abi3Expression.test.d.ts","sourceRoot":"","sources":["../../src/presentation/abi3Expression.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}