@mergesignal/shared 0.4.3 → 0.6.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 (181) hide show
  1. package/dist/actionsStepSummary.d.ts +1 -22
  2. package/dist/actionsStepSummary.d.ts.map +1 -1
  3. package/dist/actionsStepSummary.js +19 -716
  4. package/dist/actionsStepSummary.test.js +34 -329
  5. package/dist/assessmentLabels.d.ts +6 -0
  6. package/dist/assessmentLabels.d.ts.map +1 -0
  7. package/dist/assessmentLabels.js +33 -0
  8. package/dist/assessmentProjection.d.ts +12 -0
  9. package/dist/assessmentProjection.d.ts.map +1 -0
  10. package/dist/assessmentProjection.js +100 -0
  11. package/dist/assessmentSchema.d.ts +155 -0
  12. package/dist/assessmentSchema.d.ts.map +1 -0
  13. package/dist/assessmentSchema.js +110 -0
  14. package/dist/cardObservationCatalog.d.ts +2 -0
  15. package/dist/cardObservationCatalog.d.ts.map +1 -1
  16. package/dist/cardObservationCatalog.js +47 -1
  17. package/dist/engineWireRegression.test.js +22 -2
  18. package/dist/fixtures/assessmentFixtures.d.ts +13 -0
  19. package/dist/fixtures/assessmentFixtures.d.ts.map +1 -0
  20. package/dist/fixtures/assessmentFixtures.js +164 -0
  21. package/dist/formatCardExposureDisplay.d.ts +0 -4
  22. package/dist/formatCardExposureDisplay.d.ts.map +1 -1
  23. package/dist/formatCardExposureDisplay.js +0 -15
  24. package/dist/formatInsight.d.ts +3 -2
  25. package/dist/formatInsight.d.ts.map +1 -1
  26. package/dist/formatInsight.js +26 -1
  27. package/dist/index.d.ts +7 -11
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +7 -11
  30. package/dist/narrativeParity.test.js +4 -34
  31. package/dist/presentGitHubPrComment.d.ts +1 -3
  32. package/dist/presentGitHubPrComment.d.ts.map +1 -1
  33. package/dist/presentGitHubPrComment.js +0 -17
  34. package/dist/presentation/buildGitHubCheckRunOutput.d.ts +20 -0
  35. package/dist/presentation/buildGitHubCheckRunOutput.d.ts.map +1 -0
  36. package/dist/presentation/buildGitHubCheckRunOutput.js +42 -0
  37. package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts +2 -0
  38. package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts.map +1 -0
  39. package/dist/presentation/buildGitHubCheckRunOutput.test.js +188 -0
  40. package/dist/presentation/compose/narrativeChannels.d.ts.map +1 -1
  41. package/dist/presentation/compose/narrativeChannels.js +13 -43
  42. package/dist/presentation/compose/narrativeCompose.d.ts.map +1 -1
  43. package/dist/presentation/compose/narrativeCompose.js +2 -5
  44. package/dist/presentation/dto/assessmentPresentationFields.d.ts +13 -0
  45. package/dist/presentation/dto/assessmentPresentationFields.d.ts.map +1 -0
  46. package/dist/presentation/dto/assessmentPresentationFields.js +1 -0
  47. package/dist/presentation/dto/dashboardCardPresentation.d.ts +2 -1
  48. package/dist/presentation/dto/dashboardCardPresentation.d.ts.map +1 -1
  49. package/dist/presentation/dto/githubAndCliPresentation.d.ts +4 -3
  50. package/dist/presentation/dto/githubAndCliPresentation.d.ts.map +1 -1
  51. package/dist/presentation/dto/scanDetailsPresentation.d.ts +2 -1
  52. package/dist/presentation/dto/scanDetailsPresentation.d.ts.map +1 -1
  53. package/dist/presentation/fixtures/dashboardCardGolden.test.js +21 -14
  54. package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts.map +1 -1
  55. package/dist/presentation/fixtures/presentationPersonaFixtures.js +31 -24
  56. package/dist/presentation/fixtures/scanResultFixtures.d.ts.map +1 -1
  57. package/dist/presentation/fixtures/scanResultFixtures.js +14 -3
  58. package/dist/presentation/index.d.ts +2 -2
  59. package/dist/presentation/index.d.ts.map +1 -1
  60. package/dist/presentation/index.js +2 -2
  61. package/dist/presentation/orchestration/buildScanCardPresentation.d.ts.map +1 -1
  62. package/dist/presentation/orchestration/buildScanCardPresentation.js +8 -3
  63. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts +1 -1
  64. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts.map +1 -1
  65. package/dist/presentation/orchestration/buildScanPresentationBundle.js +12 -10
  66. package/dist/presentation/orchestration/scanPresentationBundle.d.ts +8 -3
  67. package/dist/presentation/orchestration/scanPresentationBundle.d.ts.map +1 -1
  68. package/dist/presentation/presentationPersonas.test.js +6 -6
  69. package/dist/presentation/presenters/presentCliScanSummary.d.ts.map +1 -1
  70. package/dist/presentation/presenters/presentCliScanSummary.js +2 -0
  71. package/dist/presentation/presenters/presentDashboardCard.d.ts +1 -0
  72. package/dist/presentation/presenters/presentDashboardCard.d.ts.map +1 -1
  73. package/dist/presentation/presenters/presentDashboardCard.js +18 -0
  74. package/dist/presentation/presenters/presentGitHubCheckRun.d.ts.map +1 -1
  75. package/dist/presentation/presenters/presentGitHubCheckRun.js +2 -0
  76. package/dist/presentation/presenters/presentGitHubPrComment.d.ts.map +1 -1
  77. package/dist/presentation/presenters/presentGitHubPrComment.js +15 -21
  78. package/dist/presentation/presenters/presentScanDetails.d.ts.map +1 -1
  79. package/dist/presentation/presenters/presentScanDetails.js +52 -129
  80. package/dist/presentation/profile/buildProfileFromAssessment.d.ts +5 -0
  81. package/dist/presentation/profile/buildProfileFromAssessment.d.ts.map +1 -0
  82. package/dist/presentation/profile/buildProfileFromAssessment.js +58 -0
  83. package/dist/presentation/projectAssessmentFields.d.ts +4 -0
  84. package/dist/presentation/projectAssessmentFields.d.ts.map +1 -0
  85. package/dist/presentation/projectAssessmentFields.js +13 -0
  86. package/dist/presentation/surfaceParity.test.d.ts +2 -0
  87. package/dist/presentation/surfaceParity.test.d.ts.map +1 -0
  88. package/dist/presentation/surfaceParity.test.js +74 -0
  89. package/dist/productMessaging.d.ts +4 -4
  90. package/dist/productMessaging.d.ts.map +1 -1
  91. package/dist/productMessaging.js +4 -4
  92. package/dist/productMessaging.test.js +2 -2
  93. package/dist/repoIntelligenceSchema.d.ts +2 -2
  94. package/dist/resolvePipelineStatus.d.ts +15 -0
  95. package/dist/resolvePipelineStatus.d.ts.map +1 -0
  96. package/dist/resolvePipelineStatus.js +37 -0
  97. package/dist/scanCardPresentationState.d.ts +10 -0
  98. package/dist/scanCardPresentationState.d.ts.map +1 -0
  99. package/dist/scanCardPresentationState.js +4 -0
  100. package/dist/{scanDetailViewModel.d.ts → scanDetailViewModelTypes.d.ts} +60 -32
  101. package/dist/scanDetailViewModelTypes.d.ts.map +1 -0
  102. package/dist/scanDetailViewModelTypes.js +4 -0
  103. package/dist/scanResultSchema.d.ts +75 -2
  104. package/dist/scanResultSchema.d.ts.map +1 -1
  105. package/dist/scanResultSchema.js +4 -2
  106. package/dist/scanResultSchema.test.js +16 -0
  107. package/dist/scanSurfaceCopy.d.ts +2 -0
  108. package/dist/scanSurfaceCopy.d.ts.map +1 -1
  109. package/dist/scanSurfaceCopy.js +2 -0
  110. package/dist/trustedScanGuards.test.js +7 -0
  111. package/dist/types.d.ts +3 -0
  112. package/dist/types.d.ts.map +1 -1
  113. package/package.json +1 -1
  114. package/dist/collectNarrativeWhyBullets.d.ts +0 -12
  115. package/dist/collectNarrativeWhyBullets.d.ts.map +0 -1
  116. package/dist/collectNarrativeWhyBullets.js +0 -104
  117. package/dist/deriveCardDisplaySummary.d.ts +0 -16
  118. package/dist/deriveCardDisplaySummary.d.ts.map +0 -1
  119. package/dist/deriveCardDisplaySummary.js +0 -56
  120. package/dist/deriveCardDisplaySummary.test.d.ts +0 -2
  121. package/dist/deriveCardDisplaySummary.test.d.ts.map +0 -1
  122. package/dist/deriveCardDisplaySummary.test.js +0 -89
  123. package/dist/deriveCardOperationalObservations.d.ts +0 -19
  124. package/dist/deriveCardOperationalObservations.d.ts.map +0 -1
  125. package/dist/deriveCardOperationalObservations.js +0 -180
  126. package/dist/deriveCardOperationalObservations.test.d.ts +0 -2
  127. package/dist/deriveCardOperationalObservations.test.d.ts.map +0 -1
  128. package/dist/deriveCardOperationalObservations.test.js +0 -268
  129. package/dist/deriveRiskScoreSummary.d.ts +0 -33
  130. package/dist/deriveRiskScoreSummary.d.ts.map +0 -1
  131. package/dist/deriveRiskScoreSummary.js +0 -90
  132. package/dist/deriveRiskScoreSummary.test.d.ts +0 -2
  133. package/dist/deriveRiskScoreSummary.test.d.ts.map +0 -1
  134. package/dist/deriveRiskScoreSummary.test.js +0 -47
  135. package/dist/deriveScanDetailRecommendations.d.ts +0 -63
  136. package/dist/deriveScanDetailRecommendations.d.ts.map +0 -1
  137. package/dist/deriveScanDetailRecommendations.js +0 -725
  138. package/dist/deriveScanDetailRecommendations.test.d.ts +0 -2
  139. package/dist/deriveScanDetailRecommendations.test.d.ts.map +0 -1
  140. package/dist/deriveScanDetailRecommendations.test.js +0 -181
  141. package/dist/deriveScanSummaryText.d.ts +0 -7
  142. package/dist/deriveScanSummaryText.d.ts.map +0 -1
  143. package/dist/deriveScanSummaryText.js +0 -24
  144. package/dist/deriveScanSummaryText.test.d.ts +0 -2
  145. package/dist/deriveScanSummaryText.test.d.ts.map +0 -1
  146. package/dist/deriveScanSummaryText.test.js +0 -77
  147. package/dist/formatCardEvidenceCounts.d.ts +0 -7
  148. package/dist/formatCardEvidenceCounts.d.ts.map +0 -1
  149. package/dist/formatCardEvidenceCounts.js +0 -19
  150. package/dist/prCheckRunPresentation.d.ts +0 -68
  151. package/dist/prCheckRunPresentation.d.ts.map +0 -1
  152. package/dist/prCheckRunPresentation.js +0 -319
  153. package/dist/prCheckRunPresentation.test.d.ts +0 -2
  154. package/dist/prCheckRunPresentation.test.d.ts.map +0 -1
  155. package/dist/prCheckRunPresentation.test.js +0 -278
  156. package/dist/presentation/intent/derivePresentationInterpretation.d.ts +0 -4
  157. package/dist/presentation/intent/derivePresentationInterpretation.d.ts.map +0 -1
  158. package/dist/presentation/intent/derivePresentationInterpretation.js +0 -202
  159. package/dist/presentation/intent/formatPresentationCopy.d.ts +0 -8
  160. package/dist/presentation/intent/formatPresentationCopy.d.ts.map +0 -1
  161. package/dist/presentation/intent/formatPresentationCopy.js +0 -140
  162. package/dist/presentation/intent/presentationIntent.test.d.ts +0 -2
  163. package/dist/presentation/intent/presentationIntent.test.d.ts.map +0 -1
  164. package/dist/presentation/intent/presentationIntent.test.js +0 -29
  165. package/dist/presentation/intent/verificationFocusLabels.d.ts +0 -2
  166. package/dist/presentation/intent/verificationFocusLabels.d.ts.map +0 -1
  167. package/dist/presentation/intent/verificationFocusLabels.js +0 -45
  168. package/dist/presentation/profile/derivePresentationProfile.d.ts +0 -12
  169. package/dist/presentation/profile/derivePresentationProfile.d.ts.map +0 -1
  170. package/dist/presentation/profile/derivePresentationProfile.js +0 -85
  171. package/dist/scanCardSummary.d.ts +0 -58
  172. package/dist/scanCardSummary.d.ts.map +0 -1
  173. package/dist/scanCardSummary.js +0 -146
  174. package/dist/scanCardSummary.test.d.ts +0 -2
  175. package/dist/scanCardSummary.test.d.ts.map +0 -1
  176. package/dist/scanCardSummary.test.js +0 -234
  177. package/dist/scanDetailViewModel.d.ts.map +0 -1
  178. package/dist/scanDetailViewModel.js +0 -636
  179. package/dist/scanDetailViewModel.test.d.ts +0 -2
  180. package/dist/scanDetailViewModel.test.d.ts.map +0 -1
  181. package/dist/scanDetailViewModel.test.js +0 -530
@@ -1,348 +1,53 @@
1
- import { describe, it, expect } from "vitest";
2
- import { ACTIONS_SUMMARY_DEFAULT_MAX_CHARS, ACTIONS_SUMMARY_DEFAULT_MAX_LINES, actionsSummaryDefaultFoldPrefix, buildActionsStepSummaryMarkdown, humanizeEngineSurfaceText, layerDriverSummary, layerRiskBandLabel, sortPRInsightsForDisplay, sortRecommendationsForDisplay, } from "./actionsStepSummary.js";
1
+ import { describe, expect, it } from "vitest";
2
+ import { assessmentFastifyRuntime } from "./fixtures/assessmentFixtures.js";
3
+ import { buildActionsStepSummaryMarkdown } from "./actionsStepSummary.js";
3
4
  import { scanSurfaceCopyFlat } from "./scanSurfaceCopy.js";
4
5
  const copy = scanSurfaceCopyFlat();
5
- function makeInsight(overrides) {
6
- return {
7
- type: "usage_risk",
8
- priority: "high",
9
- confidence: "likely",
10
- scope: "changed",
11
- context: "ctx",
12
- remediation: "fix",
13
- ...overrides,
14
- };
15
- }
16
- describe("layerRiskBandLabel", () => {
17
- it("maps numeric layer scores to Low / Moderate / High", () => {
18
- expect(layerRiskBandLabel(0)).toBe("Low");
19
- expect(layerRiskBandLabel(19.9)).toBe("Low");
20
- expect(layerRiskBandLabel(25)).toBe("Moderate");
21
- expect(layerRiskBandLabel(50)).toBe("High");
22
- expect(layerRiskBandLabel(null)).toBe("—");
23
- });
24
- });
25
- describe("humanizeEngineSurfaceText", () => {
26
- it("rewrites known graph metric tokens without adding new facts", () => {
27
- expect(humanizeEngineSurfaceText("graph.depth")).toBe("Indirect dependency depth");
28
- expect(humanizeEngineSurfaceText("graph.transitive volume")).toBe("Transitive dependency volume");
29
- expect(humanizeEngineSurfaceText("graph.fan in max")).toBe("Many import paths converge on a few widely shared packages");
30
- });
31
- it("rewrites ecosystem surface and duplicate-path tokens", () => {
32
- expect(humanizeEngineSurfaceText("Ecosystem.package surface")).toBe("Broad declared package footprint");
33
- expect(humanizeEngineSurfaceText("Graph.duplicates")).toBe("Overlapping dependency paths to the same packages");
34
- });
35
- it("merges dependency depth phrasing into one canonical reviewer line", () => {
36
- expect(humanizeEngineSurfaceText("dependency chain depth")).toBe("Indirect dependency depth");
37
- expect(humanizeEngineSurfaceText("dependency depth")).toBe("Indirect dependency depth");
38
- });
39
- it("leaves ordinary explain titles mostly intact", () => {
40
- expect(humanizeEngineSurfaceText("Duplicate majors on runtime boundary")).toBe("Duplicate majors on runtime boundary");
41
- });
42
- });
43
- describe("sortPRInsightsForDisplay", () => {
44
- it("orders by priority, confidence, then changed scope", () => {
45
- const a = makeInsight({
46
- message: "a",
47
- priority: "medium",
48
- confidence: "confirmed",
49
- scope: "all",
50
- });
51
- const b = makeInsight({
52
- message: "b",
53
- priority: "high",
54
- confidence: "likely",
55
- scope: "changed",
56
- });
57
- const c = makeInsight({
58
- message: "c",
59
- priority: "high",
60
- confidence: "confirmed",
61
- scope: "all",
62
- });
63
- expect(sortPRInsightsForDisplay([a, b, c]).map((i) => i.message)).toEqual([
64
- "c",
65
- "b",
66
- "a",
67
- ]);
68
- });
69
- });
70
- describe("sortRecommendationsForDisplay", () => {
71
- it("orders by priorityScore then impact", () => {
72
- const recs = [
73
- {
74
- id: "1",
75
- title: "Low prio",
76
- rationale: "",
77
- impact: "low",
78
- priorityScore: 10,
79
- },
80
- {
81
- id: "2",
82
- title: "High prio",
83
- rationale: "",
84
- impact: "high",
85
- priorityScore: 90,
86
- },
87
- ];
88
- expect(sortRecommendationsForDisplay(recs).map((r) => r.id)).toEqual([
89
- "2",
90
- "1",
91
- ]);
92
- });
93
- });
6
+ const sampleResult = {
7
+ totalScore: 55,
8
+ layerScores: {
9
+ security: 10,
10
+ maintainability: 20,
11
+ ecosystem: 30,
12
+ upgradeImpact: 15,
13
+ },
14
+ findings: [],
15
+ generatedAt: "2026-01-01T00:00:00.000Z",
16
+ changedPackages: ["fastify"],
17
+ assessment: assessmentFastifyRuntime,
18
+ decision: {
19
+ recommendation: "needs_review",
20
+ confidence: "medium",
21
+ reasoning: [
22
+ "Changed package has confirmed usage on runtime application paths in this repository.",
23
+ ],
24
+ },
25
+ };
94
26
  describe("buildActionsStepSummaryMarkdown", () => {
95
- const trustedBase = {
96
- totalScore: 42,
97
- layerScores: {
98
- security: 10,
99
- maintainability: 50,
100
- ecosystem: 30,
101
- upgradeImpact: 40,
102
- },
103
- findings: [],
104
- recommendations: [],
105
- generatedAt: "2026-01-01T00:00:00.000Z",
106
- methodologyVersion: "engine-test-fixture/v1",
107
- decision: {
108
- recommendation: "needs_review",
109
- confidence: "high",
110
- reasoning: ["Transitive dependency depth increased on hot paths."],
111
- },
112
- insights: [],
113
- };
114
- it("trusted: includes posture, risk index direction, and score breakdown", () => {
115
- const md = buildActionsStepSummaryMarkdown({
116
- result: trustedBase,
117
- profile: "trusted",
118
- copy,
119
- });
120
- expect(md).toMatch(/# MergeSignal dependency review — Needs review/);
121
- expect(md).toMatch(/Risk index 42\/100/);
122
- expect(md).toContain(copy["actions.riskIndexDirectionShort"]);
123
- expect(md).toContain("| Layer | Score | Layer risk |");
124
- expect(md).toContain("| Security | 10 | Low |");
125
- expect(md).toMatch(/<details>/);
126
- expect(md).not.toContain(copy["actions.layerScoreGlossary"]);
127
- });
128
- it("trusted: surfaces top insights and moves remainder to details", () => {
129
- const insights = [
130
- makeInsight({ message: "First critical", priority: "critical" }),
131
- makeInsight({ message: "Second high", priority: "high" }),
132
- makeInsight({ message: "Third medium", priority: "medium" }),
133
- makeInsight({ message: "Fourth overflow", priority: "medium" }),
134
- ];
135
- const md = buildActionsStepSummaryMarkdown({
136
- result: { ...trustedBase, insights },
137
- profile: "trusted",
138
- copy,
139
- });
140
- expect(md).toContain("First critical");
141
- expect(md).toContain(copy["actions.moreInsightsDetailsSummary"]);
142
- expect(md).toContain("Fourth overflow");
143
- });
144
- it("trusted: no decision uses posture-unavailable copy", () => {
145
- const { decision: _d, ...rest } = trustedBase;
146
- const md = buildActionsStepSummaryMarkdown({
147
- result: rest,
148
- profile: "trusted",
149
- copy,
150
- });
151
- expect(md).toContain(copy["actions.mergePostureUnavailableShort"]);
152
- });
153
- it("development + stub shows demo banner and score breakdown", () => {
27
+ it("renders development profile with demo header", () => {
154
28
  const md = buildActionsStepSummaryMarkdown({
155
- result: {
156
- ...trustedBase,
157
- methodologyVersion: "engine-stub/v2",
158
- decision: {
159
- recommendation: "safe",
160
- confidence: "high",
161
- reasoning: [],
162
- },
163
- },
29
+ result: sampleResult,
164
30
  profile: "development",
165
31
  copy,
166
32
  });
167
- expect(md).toContain(copy["actions.demoSummaryBanner"]);
168
- expect(md).toContain("| Layer | Score | Layer risk |");
169
- expect(md).toContain(copy["actions.layerScoreGlossary"]);
170
- });
171
- it("default-fold budget: heavy fixture stays within caps", () => {
172
- const long = "x".repeat(400);
173
- const insights = Array.from({ length: 12 }, (_, i) => makeInsight({
174
- message: `${long} ${i}`,
175
- priority: i === 0 ? "critical" : "medium",
176
- }));
177
- const recs = Array.from({ length: 8 }, (_, i) => ({
178
- id: `r${i}`,
179
- title: `Rec ${i}`,
180
- rationale: long,
181
- impact: "high",
182
- priorityScore: 100 - i,
183
- }));
184
- const md = buildActionsStepSummaryMarkdown({
185
- result: {
186
- ...trustedBase,
187
- insights,
188
- recommendations: recs,
189
- explain: {
190
- reasons: Array.from({ length: 10 }, (_, i) => ({
191
- id: `e${i}`,
192
- layer: "security",
193
- title: `Explain ${i}`,
194
- scoreImpact: 1,
195
- })),
196
- },
197
- },
198
- profile: "trusted",
199
- copy,
200
- });
201
- const prefix = actionsSummaryDefaultFoldPrefix(md);
202
- expect(prefix.split("\n").length).toBeLessThanOrEqual(ACTIONS_SUMMARY_DEFAULT_MAX_LINES + 4);
203
- expect(prefix.length).toBeLessThanOrEqual(ACTIONS_SUMMARY_DEFAULT_MAX_CHARS + 400);
204
- });
205
- it("includes scan-specific layer drivers from explain.reasons", () => {
206
- const md = buildActionsStepSummaryMarkdown({
207
- result: {
208
- ...trustedBase,
209
- layerScores: { ...trustedBase.layerScores, maintainability: 72 },
210
- explain: {
211
- reasons: [
212
- {
213
- id: "x",
214
- layer: "maintainability",
215
- title: "Duplicate majors on runtime boundary",
216
- scoreImpact: 12,
217
- },
218
- {
219
- id: "y",
220
- layer: "maintainability",
221
- title: "Churn in devDependencies cluster",
222
- scoreImpact: 5,
223
- },
224
- ],
225
- },
226
- },
227
- profile: "trusted",
228
- copy,
229
- });
230
- expect(md).toContain(copy["actions.layerDriversHeading"]);
231
- expect(md).toContain("Duplicate majors on runtime boundary");
232
- });
233
- it("layerDriverSummary returns null when no explain data for layer", () => {
234
- expect(layerDriverSummary(trustedBase, "security", 120)).toBeNull();
235
- });
236
- it("generic recommendation titles lead with rationale in default fold", () => {
237
- const md = buildActionsStepSummaryMarkdown({
238
- result: {
239
- ...trustedBase,
240
- insights: [],
241
- recommendations: [
242
- {
243
- id: "r1",
244
- title: "Reduce transitive dependency surface area",
245
- rationale: "This PR adds lodash twice at incompatible semver ranges pulled through different path aliases, widening the install tree.",
246
- impact: "high",
247
- priorityScore: 90,
248
- },
249
- ],
250
- },
251
- profile: "trusted",
252
- copy,
253
- });
254
- expect(md).toContain(copy["actions.recReviewLeadPrefix"]);
255
- expect(md).toContain("incompatible semver");
256
- expect(md).toContain("*Focus:*");
257
- expect(md).toContain("Reduce transitive dependency surface area");
258
- });
259
- it("applies rec review prefix only to the first rationale-first recommendation", () => {
260
- const md = buildActionsStepSummaryMarkdown({
261
- result: {
262
- ...trustedBase,
263
- insights: [],
264
- recommendations: [
265
- {
266
- id: "r1",
267
- title: "Reduce transitive dependency surface area",
268
- rationale: "This PR adds lodash twice at incompatible semver ranges pulled through different path aliases, widening the install tree.",
269
- impact: "high",
270
- priorityScore: 90,
271
- },
272
- {
273
- id: "r2",
274
- title: "Minimize dependency footprint in the service layer",
275
- rationale: "Several packages are pulled in only for types while runtime bundles still resolve overlapping majors, which increases upgrade coupling across services.",
276
- impact: "high",
277
- priorityScore: 85,
278
- },
279
- ],
280
- },
281
- profile: "trusted",
282
- copy,
283
- });
284
- const prefix = copy["actions.recReviewLeadPrefix"];
285
- const first = md.indexOf(prefix);
286
- const second = md.indexOf(prefix, first + 1);
287
- expect(first).toBeGreaterThan(-1);
288
- expect(second).toBe(-1);
33
+ expect(md).toContain("#");
34
+ expect(md.toLowerCase()).toContain("fastify");
289
35
  });
290
- it("humanizes graph metric tokens in explain-driven layer drivers", () => {
36
+ it("renders trusted profile without demo header", () => {
291
37
  const md = buildActionsStepSummaryMarkdown({
292
- result: {
293
- ...trustedBase,
294
- layerScores: { ...trustedBase.layerScores, maintainability: 72 },
295
- explain: {
296
- reasons: [
297
- {
298
- id: "gdepth",
299
- layer: "maintainability",
300
- title: "graph.depth",
301
- scoreImpact: 12,
302
- },
303
- ],
304
- },
305
- },
38
+ result: sampleResult,
306
39
  profile: "trusted",
307
40
  copy,
308
41
  });
309
- expect(md).toContain("Indirect dependency depth");
310
- expect(md).not.toContain("graph.depth");
42
+ expect(md).not.toContain(copy["actions.demoSummaryBanner"] ?? "");
43
+ expect(md).toContain("MergeSignal");
311
44
  });
312
- it("includes graph details when graphInsights present", () => {
45
+ it("returns failure message when assessment is missing", () => {
313
46
  const md = buildActionsStepSummaryMarkdown({
314
- result: {
315
- ...trustedBase,
316
- graphInsights: {
317
- maxDepth: 7,
318
- nodes: 900,
319
- edges: 1200,
320
- deepest: [
321
- {
322
- kind: "deep",
323
- packageName: "lodash",
324
- direct: false,
325
- depth: 7,
326
- via: ["a", "b"],
327
- },
328
- ],
329
- hotspots: [
330
- {
331
- kind: "hotspot",
332
- packageName: "x",
333
- direct: true,
334
- depth: 1,
335
- },
336
- ],
337
- vulnerable: [],
338
- },
339
- },
47
+ result: { ...sampleResult, assessment: undefined },
340
48
  profile: "trusted",
341
49
  copy,
342
50
  });
343
- expect(md).toContain(copy["actions.dependencyGraphDetailsSummary"]);
344
- expect(md).toContain("moderately deep");
345
- expect(md).toContain("fairly broad");
346
- expect(md).toMatch(/> \*max chain length 7 · 900 resolved packages\*/);
51
+ expect(md).toContain("could not be completed");
347
52
  });
348
53
  });
@@ -0,0 +1,6 @@
1
+ import type { MergeConcernKind } from "./assessmentSchema.js";
2
+ /** Display labels for factor tokens (mirrors engine factor-registry labels). */
3
+ export declare const ASSESSMENT_FACTOR_LABELS: Record<string, string>;
4
+ export declare const MERGE_CONCERN_LABELS: Record<MergeConcernKind, string>;
5
+ export declare function labelAssessmentFactor(token: string): string;
6
+ //# sourceMappingURL=assessmentLabels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assessmentLabels.d.ts","sourceRoot":"","sources":["../src/assessmentLabels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAmB3D,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAcjE,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D"}
@@ -0,0 +1,33 @@
1
+ /** Display labels for factor tokens (mirrors engine factor-registry labels). */
2
+ export const ASSESSMENT_FACTOR_LABELS = {
3
+ confirmed_runtime_usage: "Confirmed runtime usage",
4
+ queue_infrastructure: "Queue infrastructure",
5
+ http_framework_infrastructure: "HTTP framework infrastructure",
6
+ auth_infrastructure: "Authentication infrastructure",
7
+ moderate_reach: "Moderate repository reach",
8
+ high_reach: "High repository reach",
9
+ low_reach: "Low repository reach",
10
+ tooling_maintenance: "Tooling maintenance change",
11
+ test_infra_change: "Test infrastructure change",
12
+ security_advisory: "Security advisory",
13
+ breaking_or_major_change: "Breaking or major version change",
14
+ behavioral_change_risk: "Behavioral change risk",
15
+ ecosystem_unknown_package: "Unknown ecosystem package",
16
+ insufficient_collection_evidence: "Insufficient collection evidence",
17
+ graph_baseline_only: "Repository graph baseline only",
18
+ verification_required: "Verification focus required",
19
+ runtime_upgrade: "Runtime dependency upgrade",
20
+ runtime_patch: "Runtime dependency patch",
21
+ };
22
+ export const MERGE_CONCERN_LABELS = {
23
+ confirmed_runtime_usage: "Changed package has confirmed usage on runtime application paths in this repository.",
24
+ security_finding: "Security advisory or vulnerability finding affects changed dependencies.",
25
+ breaking_or_major: "Breaking change or major version bump warrants explicit review.",
26
+ behavioral_change: "Behavioral change risk detected on changed dependencies.",
27
+ ecosystem_risk: "Changed package has limited classification evidence; review dependency signals.",
28
+ insufficient_evidence: "Collection evidence is partial; confidence is reduced but review posture is preserved when runtime signals exist.",
29
+ graph_baseline_only: "Repository dependency graph baseline evaluated; no PR-specific merge concerns detected on changed packages.",
30
+ };
31
+ export function labelAssessmentFactor(token) {
32
+ return ASSESSMENT_FACTOR_LABELS[token] ?? token.replace(/_/g, " ");
33
+ }
@@ -0,0 +1,12 @@
1
+ import type { ScanResult } from "./types.js";
2
+ import { type RepoIntelligence } from "./repoIntelligenceSchema.js";
3
+ import type { Assessment, AssessmentPresentationPublic, ReachVisibility } from "./assessmentSchema.js";
4
+ import type { PresentationStatus } from "./presentation/dto/types.js";
5
+ export declare function collectVerificationFocus(result: ScanResult): string[];
6
+ export declare function reachVisibilityLabel(visibility: ReachVisibility): string | undefined;
7
+ export declare function formatAssessmentHeadline(assessment: Assessment, result: ScanResult, status: PresentationStatus): string;
8
+ export declare function projectReasoningLines(result: ScanResult): string[];
9
+ export declare function projectInsightLines(assessment: Assessment, result: ScanResult): string[];
10
+ export declare function projectVerificationActions(assessment: Assessment, presentation: AssessmentPresentationPublic, result: ScanResult, max: number): string[];
11
+ export declare function applicationAreaLabels(ri: RepoIntelligence | null): string[];
12
+ //# sourceMappingURL=assessmentProjection.d.ts.map
@@ -0,0 +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,EAChB,MAAM,uBAAuB,CAAC;AAO/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAgBrE;AAED,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,eAAe,GAC1B,MAAM,GAAG,SAAS,CAWpB;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,kBAAkB,GACzB,MAAM,CAqDR;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,CAIV;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI,GAAG,MAAM,EAAE,CAI3E"}
@@ -0,0 +1,100 @@
1
+ import { safeParseRepoIntelligence, } from "./repoIntelligenceSchema.js";
2
+ import { labelAssessmentFactor, MERGE_CONCERN_LABELS, } from "./assessmentLabels.js";
3
+ import { scanSurfaceCopy } from "./scanSurfaceCopy.js";
4
+ import { applyPostureVocabularyGuardHeadline } from "./presentation/intent/applyPostureVocabularyGuard.js";
5
+ export function collectVerificationFocus(result) {
6
+ const parsed = safeParseRepoIntelligence(result.repoIntelligence);
7
+ if (!parsed.ok)
8
+ return [];
9
+ const changed = new Set(result.changedPackages ?? []);
10
+ const labels = [];
11
+ const seen = new Set();
12
+ for (const pkg of changed) {
13
+ const row = parsed.value.packages[pkg];
14
+ for (const focus of row?.verificationFocus ?? []) {
15
+ const t = focus.trim();
16
+ if (!t || seen.has(t))
17
+ continue;
18
+ seen.add(t);
19
+ labels.push(t);
20
+ }
21
+ }
22
+ return labels;
23
+ }
24
+ export function reachVisibilityLabel(visibility) {
25
+ switch (visibility) {
26
+ case "hidden":
27
+ return undefined;
28
+ case "contextual":
29
+ return scanSurfaceCopy.scanDetail.reachChip.limited;
30
+ case "prominent":
31
+ return scanSurfaceCopy.scanDetail.reachChip.moderate;
32
+ default:
33
+ return undefined;
34
+ }
35
+ }
36
+ export function formatAssessmentHeadline(assessment, result, status) {
37
+ const copy = scanSurfaceCopy.presentation;
38
+ const runtimePkg = assessment.concerns.find((c) => c.kind === "confirmed_runtime_usage")?.packages?.[0];
39
+ const primaryPkg = runtimePkg ??
40
+ result.changedPackages?.[0] ??
41
+ assessment.concerns.find((c) => c.packages?.length)?.packages?.[0] ??
42
+ "dependency";
43
+ if (assessment.primaryConcern === "insufficient_evidence") {
44
+ return applyPostureVocabularyGuardHeadline(copy.limitedContextHeadline, status, primaryPkg);
45
+ }
46
+ const changeClass = assessment.changeClasses[0];
47
+ if (assessment.posture === "safe" &&
48
+ (changeClass === "tooling_maintenance" || changeClass === "test_infra")) {
49
+ const headline = copy.toolingPatchHeadline.replace("{package}", primaryPkg);
50
+ return applyPostureVocabularyGuardHeadline(headline, status, primaryPkg);
51
+ }
52
+ if (assessment.primaryConcern === "confirmed_runtime_usage") {
53
+ const surface = assessment.factors.includes("queue_infrastructure")
54
+ ? "workers and queues"
55
+ : assessment.factors.includes("auth_infrastructure")
56
+ ? "authentication flows"
57
+ : "application code";
58
+ const headline = copy.runtimeUpgradeHeadline
59
+ .replace("{package}", primaryPkg)
60
+ .replace("{surface}", surface);
61
+ return applyPostureVocabularyGuardHeadline(headline, status, primaryPkg);
62
+ }
63
+ if (assessment.posture === "safe") {
64
+ return applyPostureVocabularyGuardHeadline(copy.safeNeutralHeadline.replace("{package}", primaryPkg), status, primaryPkg);
65
+ }
66
+ return applyPostureVocabularyGuardHeadline(copy.defaultUpgradeHeadline.replace("{package}", primaryPkg), status, primaryPkg);
67
+ }
68
+ export function projectReasoningLines(result) {
69
+ const fromDecision = result.decision?.reasoning?.filter(Boolean) ?? [];
70
+ if (fromDecision.length > 0)
71
+ return fromDecision.slice(0, 3);
72
+ return [];
73
+ }
74
+ export function projectInsightLines(assessment, result) {
75
+ const reasoning = projectReasoningLines(result);
76
+ if (reasoning.length > 0)
77
+ return reasoning;
78
+ const lines = [];
79
+ if (assessment.primaryConcern) {
80
+ lines.push(MERGE_CONCERN_LABELS[assessment.primaryConcern]);
81
+ }
82
+ for (const token of assessment.factors) {
83
+ const label = labelAssessmentFactor(token);
84
+ if (!lines.includes(label))
85
+ lines.push(label);
86
+ }
87
+ return lines.slice(0, 3);
88
+ }
89
+ export function projectVerificationActions(assessment, presentation, result, max) {
90
+ if (presentation.verificationIntensity === "none")
91
+ return [];
92
+ const focus = collectVerificationFocus(result);
93
+ return focus.slice(0, max);
94
+ }
95
+ export function applicationAreaLabels(ri) {
96
+ if (!ri)
97
+ return [];
98
+ const areas = ri.affectedAreas ?? ri.applicationAreas ?? [];
99
+ return areas.map((a) => a.label).filter(Boolean);
100
+ }