@mergesignal/shared 0.2.2 → 0.2.3

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 (50) hide show
  1. package/dist/cardObservationCatalog.d.ts +55 -0
  2. package/dist/cardObservationCatalog.d.ts.map +1 -0
  3. package/dist/cardObservationCatalog.js +320 -0
  4. package/dist/cardSummaryCopy.d.ts +12 -0
  5. package/dist/cardSummaryCopy.d.ts.map +1 -0
  6. package/dist/cardSummaryCopy.js +36 -0
  7. package/dist/deriveCardDisplaySummary.d.ts +16 -0
  8. package/dist/deriveCardDisplaySummary.d.ts.map +1 -0
  9. package/dist/deriveCardDisplaySummary.js +56 -0
  10. package/dist/deriveCardDisplaySummary.test.d.ts +2 -0
  11. package/dist/deriveCardDisplaySummary.test.d.ts.map +1 -0
  12. package/dist/deriveCardDisplaySummary.test.js +89 -0
  13. package/dist/deriveCardOperationalObservations.d.ts +19 -0
  14. package/dist/deriveCardOperationalObservations.d.ts.map +1 -0
  15. package/dist/deriveCardOperationalObservations.js +180 -0
  16. package/dist/deriveCardOperationalObservations.test.d.ts +2 -0
  17. package/dist/deriveCardOperationalObservations.test.d.ts.map +1 -0
  18. package/dist/deriveCardOperationalObservations.test.js +268 -0
  19. package/dist/formatCardAreaLabels.d.ts +8 -0
  20. package/dist/formatCardAreaLabels.d.ts.map +1 -0
  21. package/dist/formatCardAreaLabels.js +65 -0
  22. package/dist/formatCardEvidenceCounts.d.ts +7 -0
  23. package/dist/formatCardEvidenceCounts.d.ts.map +1 -0
  24. package/dist/formatCardEvidenceCounts.js +19 -0
  25. package/dist/formatCardExposureDisplay.d.ts +20 -0
  26. package/dist/formatCardExposureDisplay.d.ts.map +1 -0
  27. package/dist/formatCardExposureDisplay.js +56 -0
  28. package/dist/formatCardExposureDisplay.test.d.ts +2 -0
  29. package/dist/formatCardExposureDisplay.test.d.ts.map +1 -0
  30. package/dist/formatCardExposureDisplay.test.js +36 -0
  31. package/dist/index.d.ts +9 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +9 -0
  34. package/dist/productMessaging.d.ts +85 -0
  35. package/dist/productMessaging.d.ts.map +1 -0
  36. package/dist/productMessaging.js +109 -0
  37. package/dist/productMessaging.test.d.ts +2 -0
  38. package/dist/productMessaging.test.d.ts.map +1 -0
  39. package/dist/productMessaging.test.js +97 -0
  40. package/dist/riskVocabulary.d.ts +8 -2
  41. package/dist/riskVocabulary.d.ts.map +1 -1
  42. package/dist/riskVocabulary.js +26 -9
  43. package/dist/scanCardSummary.d.ts +9 -0
  44. package/dist/scanCardSummary.d.ts.map +1 -1
  45. package/dist/scanCardSummary.js +19 -29
  46. package/dist/scanCardSummary.test.js +45 -8
  47. package/dist/truncateCardSummary.d.ts +5 -0
  48. package/dist/truncateCardSummary.d.ts.map +1 -0
  49. package/dist/truncateCardSummary.js +23 -0
  50. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { describe, it, expect } from "vitest";
2
+ import { isCatalogPhrase } from "./cardObservationCatalog.js";
2
3
  import { aggregateFindingCounts, deriveRiskIndexBand, deriveScanCardSummary, deriveScanCardSummaryFromDenormalized, isPipelineCardSummary, isPipelinePlaceholderCopy, resolvePipelineStatus, resolvePrScanCardSummary, staleScanSubline, } from "./scanCardSummary.js";
3
4
  const baseResult = {
4
5
  totalScore: 10,
@@ -71,15 +72,26 @@ describe("deriveScanCardSummary", () => {
71
72
  confidence: "high",
72
73
  reasoning: ["Auth boundary change"],
73
74
  },
75
+ explain: {
76
+ reasons: [
77
+ {
78
+ id: "upgrade.1",
79
+ layer: "upgradeImpact",
80
+ title: "Large upgrade blast radius",
81
+ scoreImpact: 20,
82
+ },
83
+ ],
84
+ },
74
85
  };
75
86
  const summary = deriveScanCardSummary(r, "done");
76
87
  expect(summary.mergePosture).toBe("risky");
77
88
  expect(summary.headline).toBe("Risky");
78
89
  expect(summary.riskIndex).toBe(72);
79
90
  expect(summary.riskIndexBand).toBe("high");
80
- expect(summary.summaryLine).toBe("Auth boundary change");
91
+ expect(summary.summaryLine).toBeNull();
92
+ expect(summary.operationalObservations).toContain("Large upgrade blast radius");
81
93
  });
82
- it("uses safe copy when posture is safe and no findings", () => {
94
+ it("keeps quiet safe cards posture-only without observations", () => {
83
95
  const r = {
84
96
  ...baseResult,
85
97
  totalScore: 12,
@@ -90,9 +102,10 @@ describe("deriveScanCardSummary", () => {
90
102
  },
91
103
  };
92
104
  const summary = deriveScanCardSummary(r, "done");
93
- expect(summary.summaryLine).toBe("No merge blockers detected");
105
+ expect(summary.summaryLine).toBeNull();
106
+ expect(summary.operationalObservations).toEqual([]);
94
107
  });
95
- it("caps top affected areas at 2", () => {
108
+ it("surfaces catalog observations from explain signals", () => {
96
109
  const r = {
97
110
  ...baseResult,
98
111
  decision: {
@@ -102,14 +115,28 @@ describe("deriveScanCardSummary", () => {
102
115
  },
103
116
  explain: {
104
117
  reasons: [
105
- { id: "1", layer: "security", title: "A", scoreImpact: -10 },
106
- { id: "2", layer: "security", title: "B", scoreImpact: -9 },
118
+ {
119
+ id: "1",
120
+ layer: "ecosystem",
121
+ title: "graph.transitive volume",
122
+ scoreImpact: 18,
123
+ },
124
+ {
125
+ id: "2",
126
+ layer: "ecosystem",
127
+ title: "graph.duplicates",
128
+ scoreImpact: 12,
129
+ },
107
130
  { id: "3", layer: "security", title: "C", scoreImpact: -8 },
108
131
  ],
109
132
  },
110
133
  };
111
134
  const summary = deriveScanCardSummary(r, "done");
112
135
  expect(summary.topAffectedAreas).toHaveLength(2);
136
+ expect(summary.operationalObservations.length).toBeGreaterThan(0);
137
+ for (const phrase of summary.operationalObservations) {
138
+ expect(isCatalogPhrase(phrase)).toBe(true);
139
+ }
113
140
  });
114
141
  it("exports stale subline constant", () => {
115
142
  expect(staleScanSubline()).toBe("Based on earlier commit");
@@ -149,6 +176,8 @@ describe("isPipelineCardSummary", () => {
149
176
  summaryLine: "Waiting for results…",
150
177
  findingCounts: null,
151
178
  topAffectedAreas: [],
179
+ operationalObservations: [],
180
+ supportingLine: null,
152
181
  })).toBe(true);
153
182
  });
154
183
  it("detects hybrid completed posture with pipeline summary line", () => {
@@ -160,6 +189,8 @@ describe("isPipelineCardSummary", () => {
160
189
  summaryLine: "Waiting for results…",
161
190
  findingCounts: null,
162
191
  topAffectedAreas: [],
192
+ operationalObservations: [],
193
+ supportingLine: null,
163
194
  })).toBe(true);
164
195
  });
165
196
  });
@@ -170,18 +201,24 @@ describe("isPipelinePlaceholderCopy", () => {
170
201
  });
171
202
  });
172
203
  describe("deriveScanCardSummaryFromDenormalized", () => {
173
- it("builds risky summary from denormalized columns", () => {
204
+ it("builds risky summary from denormalized columns without generic narration", () => {
174
205
  const summary = deriveScanCardSummaryFromDenormalized("risky", 72, "Auth boundary change", "done");
175
206
  expect(summary.mergePosture).toBe("risky");
176
207
  expect(summary.headline).toBe("Risky");
177
208
  expect(summary.riskIndex).toBe(72);
178
- expect(summary.summaryLine).toBe("Auth boundary change");
209
+ expect(summary.summaryLine).toBeNull();
210
+ expect(summary.operationalObservations).toEqual([]);
179
211
  });
180
212
  it("does not inject pipeline placeholder summaryText", () => {
181
213
  const summary = deriveScanCardSummaryFromDenormalized("risky", 72, "Waiting for results…", "done");
182
214
  expect(summary.mergePosture).toBe("risky");
183
215
  expect(summary.summaryLine).not.toBe("Waiting for results…");
184
216
  });
217
+ it("does not inject raw generic summaryText on quiet safe cards", () => {
218
+ const summary = deriveScanCardSummaryFromDenormalized("safe", 12, "No high-confidence merge risks from this PR dependency change", "done");
219
+ expect(summary.mergePosture).toBe("safe");
220
+ expect(summary.summaryLine).toBeNull();
221
+ });
185
222
  });
186
223
  describe("resolvePrScanCardSummary", () => {
187
224
  it("returns risk summary when wire status is running but decision exists", () => {
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Semantic truncation for dashboard card summaries (~90 chars default).
3
+ */
4
+ export declare function truncateCardSummary(text: string, maxLen?: number): string;
5
+ //# sourceMappingURL=truncateCardSummary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"truncateCardSummary.d.ts","sourceRoot":"","sources":["../src/truncateCardSummary.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,MAAoB,GAC3B,MAAM,CA+BR"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Semantic truncation for dashboard card summaries (~90 chars default).
3
+ */
4
+ const DEFAULT_MAX = 90;
5
+ export function truncateCardSummary(text, maxLen = DEFAULT_MAX) {
6
+ const trimmed = text.trim();
7
+ if (trimmed.length <= maxLen)
8
+ return trimmed;
9
+ const slice = trimmed.slice(0, maxLen + 1);
10
+ const sentenceEnd = Math.max(slice.lastIndexOf(". "), slice.lastIndexOf("! "), slice.lastIndexOf("? "));
11
+ if (sentenceEnd >= Math.floor(maxLen * 0.45)) {
12
+ return trimmed.slice(0, sentenceEnd + 1).trim();
13
+ }
14
+ const clauseEnd = Math.max(slice.lastIndexOf(", "), slice.lastIndexOf("; "), slice.lastIndexOf(" — "), slice.lastIndexOf(" - "));
15
+ if (clauseEnd >= Math.floor(maxLen * 0.45)) {
16
+ return `${trimmed.slice(0, clauseEnd).trim()}…`;
17
+ }
18
+ const lastSpace = slice.lastIndexOf(" ");
19
+ if (lastSpace >= Math.floor(maxLen * 0.6)) {
20
+ return `${trimmed.slice(0, lastSpace).trim()}…`;
21
+ }
22
+ return `${trimmed.slice(0, maxLen).trim()}…`;
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mergesignal/shared",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "MergeSignal shared scan contract, presentation policy, and trusted validation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",