@mergesignal/shared 0.4.1 → 0.4.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 (96) hide show
  1. package/dist/deriveScanNarrative.d.ts.map +1 -1
  2. package/dist/deriveScanNarrative.js +41 -8
  3. package/dist/deriveScanNarrative.test.js +4 -0
  4. package/dist/engineWireRegression.test.js +2 -2
  5. package/dist/fixtures/repoIntelligenceFixtures.d.ts.map +1 -1
  6. package/dist/fixtures/repoIntelligenceFixtures.js +8 -1
  7. package/dist/narrativeParity.test.js +3 -4
  8. package/dist/presentation/compose/narrativeChannels.d.ts +17 -0
  9. package/dist/presentation/compose/narrativeChannels.d.ts.map +1 -0
  10. package/dist/presentation/compose/narrativeChannels.js +75 -0
  11. package/dist/presentation/compose/narrativeChannels.test.d.ts +2 -0
  12. package/dist/presentation/compose/narrativeChannels.test.d.ts.map +1 -0
  13. package/dist/presentation/compose/narrativeChannels.test.js +42 -0
  14. package/dist/presentation/compose/narrativeCompose.d.ts +1 -0
  15. package/dist/presentation/compose/narrativeCompose.d.ts.map +1 -1
  16. package/dist/presentation/compose/narrativeCompose.js +17 -102
  17. package/dist/presentation/dto/dashboardCardPresentation.d.ts +29 -0
  18. package/dist/presentation/dto/dashboardCardPresentation.d.ts.map +1 -0
  19. package/dist/presentation/dto/scanDetailsPresentation.d.ts +2 -0
  20. package/dist/presentation/dto/scanDetailsPresentation.d.ts.map +1 -1
  21. package/dist/presentation/dto/types.d.ts +0 -9
  22. package/dist/presentation/dto/types.d.ts.map +1 -1
  23. package/dist/presentation/dto/types.js +1 -22
  24. package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts +2 -0
  25. package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts.map +1 -0
  26. package/dist/presentation/fixtures/dashboardCardGolden.test.js +31 -0
  27. package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts +11 -0
  28. package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts.map +1 -0
  29. package/dist/presentation/fixtures/presentationPersonaFixtures.js +197 -0
  30. package/dist/presentation/index.d.ts +8 -5
  31. package/dist/presentation/index.d.ts.map +1 -1
  32. package/dist/presentation/index.js +3 -3
  33. package/dist/presentation/intent/applyPostureVocabularyGuard.d.ts +5 -0
  34. package/dist/presentation/intent/applyPostureVocabularyGuard.d.ts.map +1 -0
  35. package/dist/presentation/intent/applyPostureVocabularyGuard.js +44 -0
  36. package/dist/presentation/intent/derivePresentationInterpretation.d.ts +4 -0
  37. package/dist/presentation/intent/derivePresentationInterpretation.d.ts.map +1 -0
  38. package/dist/presentation/intent/derivePresentationInterpretation.js +202 -0
  39. package/dist/presentation/intent/formatPresentationCopy.d.ts +8 -0
  40. package/dist/presentation/intent/formatPresentationCopy.d.ts.map +1 -0
  41. package/dist/presentation/intent/formatPresentationCopy.js +140 -0
  42. package/dist/presentation/intent/packageDisplayName.d.ts +4 -0
  43. package/dist/presentation/intent/packageDisplayName.d.ts.map +1 -0
  44. package/dist/presentation/intent/packageDisplayName.js +17 -0
  45. package/dist/presentation/intent/postureVocabularyGuard.test.d.ts +2 -0
  46. package/dist/presentation/intent/postureVocabularyGuard.test.d.ts.map +1 -0
  47. package/dist/presentation/intent/postureVocabularyGuard.test.js +23 -0
  48. package/dist/presentation/intent/presentationIntent.d.ts +14 -0
  49. package/dist/presentation/intent/presentationIntent.d.ts.map +1 -0
  50. package/dist/presentation/intent/presentationIntent.js +1 -0
  51. package/dist/presentation/intent/presentationIntent.test.d.ts +2 -0
  52. package/dist/presentation/intent/presentationIntent.test.d.ts.map +1 -0
  53. package/dist/presentation/intent/presentationIntent.test.js +29 -0
  54. package/dist/presentation/intent/verificationFocusLabels.d.ts +2 -0
  55. package/dist/presentation/intent/verificationFocusLabels.d.ts.map +1 -0
  56. package/dist/presentation/intent/verificationFocusLabels.js +45 -0
  57. package/dist/presentation/orchestration/buildScanCardPresentation.d.ts +2 -2
  58. package/dist/presentation/orchestration/buildScanCardPresentation.d.ts.map +1 -1
  59. package/dist/presentation/orchestration/buildScanCardPresentation.js +5 -6
  60. package/dist/presentation/presentation.test.js +18 -17
  61. package/dist/presentation/presentationPersonas.test.d.ts +2 -0
  62. package/dist/presentation/presentationPersonas.test.d.ts.map +1 -0
  63. package/dist/presentation/presentationPersonas.test.js +167 -0
  64. package/dist/presentation/presenters/presentCliScanSummary.d.ts.map +1 -1
  65. package/dist/presentation/presenters/presentCliScanSummary.js +2 -2
  66. package/dist/presentation/presenters/presentDashboardCard.d.ts +5 -0
  67. package/dist/presentation/presenters/presentDashboardCard.d.ts.map +1 -0
  68. package/dist/presentation/presenters/presentDashboardCard.js +117 -0
  69. package/dist/presentation/presenters/presentGitHubCheckRun.d.ts.map +1 -1
  70. package/dist/presentation/presenters/presentGitHubCheckRun.js +2 -2
  71. package/dist/presentation/presenters/presentScanDetails.d.ts.map +1 -1
  72. package/dist/presentation/presenters/presentScanDetails.js +13 -4
  73. package/dist/presentation/profile/derivePresentationProfile.d.ts.map +1 -1
  74. package/dist/presentation/profile/derivePresentationProfile.js +18 -27
  75. package/dist/presentation/profile/presentationProfile.d.ts +2 -0
  76. package/dist/presentation/profile/presentationProfile.d.ts.map +1 -1
  77. package/dist/repoIntelligenceSchema.d.ts +1 -1
  78. package/dist/scanCardSummary.d.ts +3 -4
  79. package/dist/scanCardSummary.d.ts.map +1 -1
  80. package/dist/scanCardSummary.test.js +16 -16
  81. package/dist/scanNarrativeFacts.d.ts +31 -7
  82. package/dist/scanNarrativeFacts.d.ts.map +1 -1
  83. package/dist/scanNarrativeFacts.js +1 -0
  84. package/dist/scanSurfaceCopy.d.ts +8 -0
  85. package/dist/scanSurfaceCopy.d.ts.map +1 -1
  86. package/dist/scanSurfaceCopy.js +8 -0
  87. package/package.json +1 -1
  88. package/dist/presentation/dto/scanCardPresentation.d.ts +0 -25
  89. package/dist/presentation/dto/scanCardPresentation.d.ts.map +0 -1
  90. package/dist/presentation/presenters/presentPipelineScanCard.d.ts +0 -3
  91. package/dist/presentation/presenters/presentPipelineScanCard.d.ts.map +0 -1
  92. package/dist/presentation/presenters/presentPipelineScanCard.js +0 -23
  93. package/dist/presentation/presenters/presentScanCard.d.ts +0 -7
  94. package/dist/presentation/presenters/presentScanCard.d.ts.map +0 -1
  95. package/dist/presentation/presenters/presentScanCard.js +0 -43
  96. /package/dist/presentation/dto/{scanCardPresentation.js → dashboardCardPresentation.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"deriveScanNarrative.d.ts","sourceRoot":"","sources":["../src/deriveScanNarrative.ts"],"names":[],"mappings":"AAOA,OAAO,EAaL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAKV,UAAU,EACX,MAAM,YAAY,CAAC;AAsbpB;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,GACpC,kBAAkB,CAuGpB"}
1
+ {"version":3,"file":"deriveScanNarrative.d.ts","sourceRoot":"","sources":["../src/deriveScanNarrative.ts"],"names":[],"mappings":"AAOA,OAAO,EAeL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAKV,UAAU,EACX,MAAM,YAAY,CAAC;AA+dpB;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,GACpC,kBAAkB,CAwGpB"}
@@ -125,6 +125,41 @@ function mergeGraphHotspots(codeHotspots, graphHotspots) {
125
125
  }
126
126
  return [...byName.values()].slice(0, NARRATIVE_FACT_MAX_HOTSPOTS);
127
127
  }
128
+ function projectPackageSemanticsFromWire(pkgIntel) {
129
+ if (!pkgIntel)
130
+ return null;
131
+ return {
132
+ dependencyClass: pkgIntel.dependencyClass ?? null,
133
+ packageRole: pkgIntel.packageRole ?? null,
134
+ runtimeImpact: pkgIntel.runtimeImpact ?? null,
135
+ expectedImpact: pkgIntel.expectedImpact ?? null,
136
+ suppressRuntimeNarrative: pkgIntel.suppressRuntimeNarrative === true,
137
+ evidenceStrength: pkgIntel.evidenceStrength ?? null,
138
+ verificationFocus: pkgIntel.verificationFocus ?? [],
139
+ };
140
+ }
141
+ function projectChangedPackageSemantics(ri, changed) {
142
+ return changed.all.map((packageName) => {
143
+ const pkgIntel = ri.packages?.[packageName];
144
+ const usage = pkgIntel?.usage;
145
+ const paths = usage?.paths ?? [];
146
+ const criticalPaths = usage?.criticalPaths ?? [];
147
+ const areas = usage?.areas ?? [];
148
+ const summary = projectPackageSemanticsFromWire(pkgIntel);
149
+ return {
150
+ packageName,
151
+ dependencyClass: summary?.dependencyClass ?? null,
152
+ packageRole: summary?.packageRole ?? null,
153
+ runtimeImpact: summary?.runtimeImpact ?? null,
154
+ expectedImpact: summary?.expectedImpact ?? null,
155
+ suppressRuntimeNarrative: summary?.suppressRuntimeNarrative ?? false,
156
+ evidenceStrength: summary?.evidenceStrength ?? null,
157
+ verificationFocus: summary?.verificationFocus ?? [],
158
+ usagePathCount: paths.length + criticalPaths.length,
159
+ usageAreaCount: areas.length,
160
+ };
161
+ });
162
+ }
128
163
  function extractTier1FromRepoIntelligence(ri, changed) {
129
164
  const primary = changed.primary;
130
165
  const pkgIntel = primary && ri.packages?.[primary] ? ri.packages[primary] : undefined;
@@ -155,14 +190,8 @@ function extractTier1FromRepoIntelligence(ri, changed) {
155
190
  },
156
191
  }
157
192
  : null;
158
- const packageSemantics = pkgIntel
159
- ? {
160
- runtimeImpact: pkgIntel.runtimeImpact ?? null,
161
- expectedImpact: pkgIntel.expectedImpact ?? null,
162
- suppressRuntimeNarrative: suppressRuntime,
163
- verificationFocus: pkgIntel.verificationFocus ?? [],
164
- }
165
- : null;
193
+ const packageSemantics = projectPackageSemanticsFromWire(pkgIntel);
194
+ const changedPackageSemantics = projectChangedPackageSemantics(ri, changed);
166
195
  const blastFromRi = ri.blastRadius;
167
196
  const blastLevel = readBlastLevel(blastFromRi?.level) ??
168
197
  (changed.all.length > 0
@@ -195,6 +224,7 @@ function extractTier1FromRepoIntelligence(ri, changed) {
195
224
  runtimeSurface,
196
225
  reachability,
197
226
  packageSemantics,
227
+ changedPackageSemantics,
198
228
  blastRadius,
199
229
  affectedAreas: affectedAreas.slice(0, NARRATIVE_FACT_MAX_AREAS),
200
230
  hotspots: codeHotspots.slice(0, NARRATIVE_FACT_MAX_HOTSPOTS),
@@ -206,6 +236,7 @@ function hasMeaningfulTier1(tier1) {
206
236
  tier1.runtimeSurface != null ||
207
237
  tier1.reachability != null ||
208
238
  tier1.packageSemantics?.expectedImpact != null ||
239
+ tier1.changedPackageSemantics.length > 0 ||
209
240
  tier1.blastRadius != null ||
210
241
  tier1.affectedAreas.length > 0 ||
211
242
  tier1.hotspots.length > 0);
@@ -287,6 +318,7 @@ function emptyTier1Blocks() {
287
318
  runtimeSurface: null,
288
319
  reachability: null,
289
320
  packageSemantics: null,
321
+ changedPackageSemantics: [],
290
322
  blastRadius: null,
291
323
  affectedAreas: [],
292
324
  hotspots: [],
@@ -386,6 +418,7 @@ export function deriveScanNarrative(result) {
386
418
  runtimeSurface: tier1Blocks.runtimeSurface,
387
419
  reachability: tier1Blocks.reachability,
388
420
  packageSemantics: tier1Blocks.packageSemantics,
421
+ changedPackageSemantics: tier1Blocks.changedPackageSemantics,
389
422
  blastRadius: tier1Blocks.blastRadius,
390
423
  affectedAreas,
391
424
  hotspots: tier1Blocks.hotspots,
@@ -126,6 +126,10 @@ describe("deriveScanNarrative", () => {
126
126
  expect(facts.packageSemantics?.runtimeImpact).toBe("none");
127
127
  expect(facts.packageSemantics?.expectedImpact).toBe("typecheck");
128
128
  expect(facts.packageSemantics?.suppressRuntimeNarrative).toBe(true);
129
+ expect(facts.packageSemantics?.dependencyClass).toBe("tooling");
130
+ expect(facts.packageSemantics?.packageRole).toBe("typechecker");
131
+ expect(facts.changedPackageSemantics).toHaveLength(1);
132
+ expect(facts.changedPackageSemantics[0]?.packageName).toBe("typescript");
129
133
  expect(facts.blastRadius?.level).toBe("narrow");
130
134
  });
131
135
  it("extracts packageUsage for all changed packages with blast factors and frameworks", () => {
@@ -37,8 +37,8 @@ describe("engine wire regression (canonical fixtures)", () => {
37
37
  expect(facts.availability.repoIntelligenceParse).toBe("ok");
38
38
  expect(facts.packageUsage[0]?.packageName).toBe("fastify");
39
39
  const card = deriveScanCardSummary(result, "done");
40
- expect(card.keyPoints.length).toBeGreaterThan(0);
41
- expect(card.verificationActions.length).toBeGreaterThan(0);
40
+ expect(card.insights.length).toBeGreaterThan(0);
41
+ expect(card.verification.length).toBeGreaterThan(0);
42
42
  });
43
43
  it("multi-package fixture: both packages in packageUsage", () => {
44
44
  validateRepoIntelligenceWire(fixtureRepoIntelligenceMultiPackage);
@@ -1 +1 @@
1
- {"version":3,"file":"repoIntelligenceFixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/repoIntelligenceFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,iEAAiE;AACjE,eAAO,MAAM,8BAA8B,EAAE,gBAyC5C,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,iCAAiC,EAAE,gBAqB/C,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,mCAAmC,EAAE,gBAgDjD,CAAC"}
1
+ {"version":3,"file":"repoIntelligenceFixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/repoIntelligenceFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,iEAAiE;AACjE,eAAO,MAAM,8BAA8B,EAAE,gBAgD5C,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,iCAAiC,EAAE,gBAqB/C,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,mCAAmC,EAAE,gBAgDjD,CAAC"}
@@ -10,6 +10,13 @@ export const fixtureRepoIntelligenceFastify = {
10
10
  suppressRuntimeNarrative: false,
11
11
  dependencyClass: "runtime",
12
12
  packageRole: "http_framework",
13
+ verificationFocus: [
14
+ "routes",
15
+ "middleware",
16
+ "auth_flow",
17
+ "serialization",
18
+ "error_handling",
19
+ ],
13
20
  usage: {
14
21
  packageName: "fastify",
15
22
  files: [
@@ -29,7 +36,7 @@ export const fixtureRepoIntelligenceFastify = {
29
36
  changedPackageCount: 1,
30
37
  },
31
38
  applicationAreas: [
32
- { id: "api", label: "API routes" },
39
+ { id: "api", label: "API request handling" },
33
40
  { id: "auth", label: "Auth middleware" },
34
41
  ],
35
42
  hotspots: [
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { buildScanPresentationBundle } from "./presentation/orchestration/buildScanPresentationBundle.js";
3
- import { presentScanCard } from "./presentation/presenters/presentScanCard.js";
3
+ import { presentDashboardCard } from "./presentation/presenters/presentDashboardCard.js";
4
4
  import { presentScanDetails } from "./presentation/presenters/presentScanDetails.js";
5
5
  import { presentGitHubPrComment } from "./presentation/presenters/presentGitHubPrComment.js";
6
6
  import { renderGitHubCheckRunMarkdown } from "./presentation/render/renderGitHubCheckRunMarkdown.js";
@@ -45,7 +45,7 @@ describe("narrative parity across consumers", () => {
45
45
  result,
46
46
  pipelineStatus: "done",
47
47
  });
48
- const card = presentScanCard(bundle);
48
+ const card = presentDashboardCard(bundle);
49
49
  const detail = presentScanDetails(bundle, {
50
50
  scanId: SCAN_ID,
51
51
  });
@@ -55,9 +55,8 @@ describe("narrative parity across consumers", () => {
55
55
  baseline: false,
56
56
  }));
57
57
  const prComment = presentGitHubPrComment(bundle);
58
- expect(card.primaryPackage).toBe("fastify");
59
58
  expect(detail.narrative.primaryPackage).toBe("fastify");
60
- expect(card.evidence.some((e) => e.label === "Reachability")).toBe(true);
59
+ expect(card.evidenceChips?.some((e) => e.label === "Reachability")).toBe(true);
61
60
  const asciiOnly = /^[\x00-\x7F]*$/;
62
61
  expect(asciiOnly.test(checkRun)).toBe(true);
63
62
  expect(checkRun.toLowerCase()).toContain("fastify");
@@ -0,0 +1,17 @@
1
+ import type { PresentationEvidenceRow } from "../dto/types.js";
2
+ import type { ScanPresentationBundle } from "../orchestration/scanPresentationBundle.js";
3
+ type NarrativeChannels = {
4
+ headline: string;
5
+ limitedContextMessage?: string;
6
+ reachLabel?: string;
7
+ insights: string[];
8
+ scopeAreas: string[];
9
+ evidence: PresentationEvidenceRow[];
10
+ verification: string[];
11
+ };
12
+ /** Canonical non-redundant narrative channels — single dedupe point for all surfaces. */
13
+ export declare function buildNarrativeChannels(bundle: ScanPresentationBundle): NarrativeChannels;
14
+ /** Merge distinct channels into one bullet list for single-column surfaces (GitHub check, CLI). */
15
+ export declare function projectCompactKeyPoints(channels: NarrativeChannels, max: number): string[];
16
+ export {};
17
+ //# sourceMappingURL=narrativeChannels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"narrativeChannels.d.ts","sourceRoot":"","sources":["../../../src/presentation/compose/narrativeChannels.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAM/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAOzF,KAAK,iBAAiB,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAgDF,yFAAyF;AACzF,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,GAC7B,iBAAiB,CA8BnB;AAED,mGAAmG;AACnG,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,iBAAiB,EAC3B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAyBV"}
@@ -0,0 +1,75 @@
1
+ import { deriveDetailReachChip } from "../../formatCardExposureDisplay.js";
2
+ import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
3
+ import { formatPresentationHeadline, formatPresentationInsightLines, formatPresentationVerification, } from "../intent/formatPresentationCopy.js";
4
+ import { formatCardAreaLabels } from "../../formatCardAreaLabels.js";
5
+ import { labelBlastRadiusLevel, labelReachabilityKind, } from "../../narrativePresentation.js";
6
+ function composeScopeAreaLabels(bundle, max) {
7
+ const labels = [];
8
+ for (const area of bundle.facts.affectedAreas) {
9
+ const formatted = formatCardAreaLabels([area.label], 1);
10
+ if (formatted[0])
11
+ labels.push(formatted[0]);
12
+ if (labels.length >= max)
13
+ break;
14
+ }
15
+ return labels;
16
+ }
17
+ function isTautologicalEvidenceRow(row) {
18
+ const label = row.label.trim().toLowerCase();
19
+ const value = row.value.trim().toLowerCase();
20
+ if (label === "runtime" && value === "runtime")
21
+ return true;
22
+ return false;
23
+ }
24
+ function composeChannelEvidence(bundle, max) {
25
+ const { facts, profile } = bundle;
26
+ const rows = [];
27
+ if (!profile.interpretation.suppressRuntimeNarrative) {
28
+ const reach = labelReachabilityKind(facts);
29
+ if (reach)
30
+ rows.push({ label: "Reachability", value: reach });
31
+ }
32
+ const blast = labelBlastRadiusLevel(facts);
33
+ if (blast)
34
+ rows.push({ label: "Blast radius", value: blast });
35
+ return rows.filter((row) => !isTautologicalEvidenceRow(row)).slice(0, max);
36
+ }
37
+ function shouldShowReachLabel(bundle) {
38
+ const { profile } = bundle;
39
+ if (profile.status === "safe" && profile.density === "minimal") {
40
+ return false;
41
+ }
42
+ return profile.status !== "safe" || profile.density !== "minimal";
43
+ }
44
+ /** Canonical non-redundant narrative channels — single dedupe point for all surfaces. */
45
+ export function buildNarrativeChannels(bundle) {
46
+ const { facts, profile, result } = bundle;
47
+ const riskIndex = facts.riskIndex ?? result.totalScore ?? null;
48
+ const reachLabel = shouldShowReachLabel(bundle) && riskIndex != null
49
+ ? (deriveDetailReachChip(riskIndex) ?? undefined)
50
+ : undefined;
51
+ return {
52
+ headline: formatPresentationHeadline(profile.interpretation, facts, profile.status),
53
+ limitedContextMessage: profile.degradedMessage ?? undefined,
54
+ reachLabel,
55
+ insights: formatPresentationInsightLines(profile.interpretation, facts, profile.status),
56
+ scopeAreas: composeScopeAreaLabels(bundle, 4),
57
+ evidence: composeChannelEvidence(bundle, 4),
58
+ verification: formatPresentationVerification(profile.interpretation, profile.status, 6),
59
+ };
60
+ }
61
+ /** Merge distinct channels into one bullet list for single-column surfaces (GitHub check, CLI). */
62
+ export function projectCompactKeyPoints(channels, max) {
63
+ const bullets = [...channels.insights];
64
+ if (channels.scopeAreas.length > 0) {
65
+ bullets.push(scanSurfaceCopy.presentation.affectedAreasKeyPoint.replace("{areas}", channels.scopeAreas.slice(0, 2).join(", ")));
66
+ }
67
+ const blastRow = channels.evidence.find((r) => r.label === "Blast radius");
68
+ if (blastRow?.value === "Wide") {
69
+ bullets.push(scanSurfaceCopy.presentation.blastRadiusWideKeyPoint);
70
+ }
71
+ else if (blastRow?.value === "Moderate") {
72
+ bullets.push(scanSurfaceCopy.presentation.blastRadiusLevelKeyPoint.replace("{level}", "Moderate"));
73
+ }
74
+ return bullets.slice(0, max);
75
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=narrativeChannels.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"narrativeChannels.test.d.ts","sourceRoot":"","sources":["../../../src/presentation/compose/narrativeChannels.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,42 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildScanPresentationBundle } from "../orchestration/buildScanPresentationBundle.js";
3
+ import { scanResultFastifyRuntime } from "../fixtures/scanResultFixtures.js";
4
+ import { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
5
+ import { presentDashboardCard } from "../presenters/presentDashboardCard.js";
6
+ describe("buildNarrativeChannels", () => {
7
+ it("fastify: areas in scopeAreas only, blast in evidence only, not in insights", () => {
8
+ const bundle = buildScanPresentationBundle({
9
+ result: scanResultFastifyRuntime,
10
+ pipelineStatus: "done",
11
+ });
12
+ const channels = buildNarrativeChannels(bundle);
13
+ const insightText = channels.insights.join(" ");
14
+ expect(insightText).not.toMatch(/Affected areas:/i);
15
+ expect(insightText).not.toMatch(/Blast radius is/i);
16
+ expect(channels.scopeAreas.length).toBeGreaterThan(0);
17
+ expect(channels.evidence.some((r) => r.label === "Blast radius")).toBe(true);
18
+ expect(channels.evidence.some((r) => r.label === "Runtime")).toBe(false);
19
+ });
20
+ it("projectCompactKeyPoints merges channels for single-column surfaces", () => {
21
+ const bundle = buildScanPresentationBundle({
22
+ result: scanResultFastifyRuntime,
23
+ pipelineStatus: "done",
24
+ });
25
+ const channels = buildNarrativeChannels(bundle);
26
+ const compact = projectCompactKeyPoints(channels, 5);
27
+ expect(compact.some((p) => /Affected areas:/i.test(p))).toBe(true);
28
+ expect(compact.some((p) => /Blast radius is/i.test(p))).toBe(true);
29
+ });
30
+ it("presentDashboardCard does not duplicate areas across insights and scopeAreas", () => {
31
+ const bundle = buildScanPresentationBundle({
32
+ result: scanResultFastifyRuntime,
33
+ pipelineStatus: "done",
34
+ });
35
+ const card = presentDashboardCard(bundle);
36
+ const insightText = card.insights.join(" ");
37
+ expect(insightText).not.toMatch(/Affected areas:/i);
38
+ expect(card.scopeAreas?.length).toBeGreaterThan(0);
39
+ expect(card.verdict?.scopeLabel).toBeTruthy();
40
+ expect(card.verification.every((v) => v.startsWith("Verify"))).toBe(true);
41
+ });
42
+ });
@@ -1,4 +1,5 @@
1
1
  import type { ScanPresentationBundle } from "../orchestration/scanPresentationBundle.js";
2
+ export { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
2
3
  export declare function composeHeadline(bundle: ScanPresentationBundle): string;
3
4
  export declare function composeSubheadline(bundle: ScanPresentationBundle): string | undefined;
4
5
  export declare function composeKeyPoints(bundle: ScanPresentationBundle, max: number): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"narrativeCompose.d.ts","sourceRoot":"","sources":["../../../src/presentation/compose/narrativeCompose.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AASzF,wBAAgB,eAAe,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAwCtE;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,GAC7B,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CA2EV;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CA0BV;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAQV;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBzC;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,sBAAsB,GAC7B,MAAM,EAAE,GAAG,SAAS,CAwBtB;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,sBAAsB,GAAG;IAC1E,QAAQ,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAKA"}
1
+ {"version":3,"file":"narrativeCompose.d.ts","sourceRoot":"","sources":["../../../src/presentation/compose/narrativeCompose.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAMzF,OAAO,EACL,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAShC,wBAAgB,eAAe,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAOtE;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,GAC7B,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAEV;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAOV;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAQV;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAmBzC;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,sBAAsB,GAC7B,MAAM,EAAE,GAAG,SAAS,CAwBtB;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,sBAAsB,GAAG;IAC1E,QAAQ,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAKA"}
@@ -1,6 +1,9 @@
1
1
  import { formatCardAreaLabels } from "../../formatCardAreaLabels.js";
2
- import { composeVerificationPrompt, formatChangedPackagesShort, labelBlastRadiusLevel, labelReachabilityKind, labelRuntimeSurface, selectReviewerGuidance, summarizeBlastRadius, summarizePackageUsage, } from "../../narrativePresentation.js";
2
+ import { formatChangedPackagesShort, labelBlastRadiusLevel, labelReachabilityKind, labelRuntimeSurface, } from "../../narrativePresentation.js";
3
3
  import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
4
+ import { formatPresentationHeadline, formatPresentationVerification, } from "../intent/formatPresentationCopy.js";
5
+ import { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
6
+ export { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
4
7
  function topAreaLabel(bundle) {
5
8
  const area = bundle.facts.affectedAreas[0];
6
9
  if (!area)
@@ -10,29 +13,7 @@ function topAreaLabel(bundle) {
10
13
  }
11
14
  export function composeHeadline(bundle) {
12
15
  const { facts, profile } = bundle;
13
- const primary = facts.changedPackages.primary;
14
- if (profile.priority === "limited") {
15
- return scanSurfaceCopy.presentation.limitedContextHeadline;
16
- }
17
- if (!primary) {
18
- return scanSurfaceCopy.presentation.noChangedPackagesHeadline;
19
- }
20
- const runtime = facts.runtimeSurface?.kind === "runtime";
21
- const area = topAreaLabel(bundle);
22
- if (facts.changedPackages.all.length > 1 && runtime) {
23
- return scanSurfaceCopy.presentation.multiRuntimeHeadline;
24
- }
25
- if (facts.runtimeSurface?.kind === "build" ||
26
- facts.reachability?.kind === "build_only") {
27
- return scanSurfaceCopy.presentation.buildOnlyHeadline.replace("{package}", primary);
28
- }
29
- if (runtime) {
30
- const surface = area ?? labelRuntimeSurface(facts) ?? "application code";
31
- return scanSurfaceCopy.presentation.runtimeHeadline
32
- .replace("{package}", primary)
33
- .replace("{surface}", surface);
34
- }
35
- return scanSurfaceCopy.presentation.defaultUpgradeHeadline.replace("{package}", primary);
16
+ return formatPresentationHeadline(profile.interpretation, facts, profile.status);
36
17
  }
37
18
  export function composeSubheadline(bundle) {
38
19
  const { profile } = bundle;
@@ -41,79 +22,11 @@ export function composeSubheadline(bundle) {
41
22
  return undefined;
42
23
  }
43
24
  export function composeKeyPoints(bundle, max) {
44
- const { facts, profile } = bundle;
45
- const points = [];
46
- if (profile.priority === "limited") {
47
- points.push(scanSurfaceCopy.presentation.limitedContextKeyPoint);
48
- if (facts.changedPackages.primary) {
49
- points.push(scanSurfaceCopy.presentation.changedPackageKeyPoint.replace("{package}", facts.changedPackages.primary));
50
- }
51
- return points.slice(0, max);
52
- }
53
- const usage = summarizePackageUsage(facts, { maxPaths: 2 });
54
- if (usage && usage.pathCount > 0) {
55
- if (usage.pathCount === 1 && usage.pathSamples[0]) {
56
- points.push(scanSurfaceCopy.presentation.usedInPathsSingle.replace("{path}", usage.pathSamples[0]));
57
- }
58
- else {
59
- points.push(scanSurfaceCopy.presentation.usedInPathsMultiple.replace("{count}", String(usage.pathCount)));
60
- }
61
- }
62
- else if (profile.density === "minimal") {
63
- points.push(scanSurfaceCopy.presentation.noRuntimePathsKeyPoint);
64
- }
65
- const blast = summarizeBlastRadius(facts, 2);
66
- if (blast.levelLabel) {
67
- if (blast.levelLabel.toLowerCase().includes("wide")) {
68
- points.push(scanSurfaceCopy.presentation.blastRadiusWideKeyPoint);
69
- }
70
- else if (profile.density === "rich") {
71
- points.push(scanSurfaceCopy.presentation.blastRadiusLevelKeyPoint.replace("{level}", blast.levelLabel));
72
- }
73
- }
74
- if (facts.affectedAreas.length === 0 && profile.density === "minimal") {
75
- points.push(scanSurfaceCopy.presentation.noAffectedAreasKeyPoint);
76
- }
77
- else if (facts.affectedAreas.length > 0) {
78
- const labels = facts.affectedAreas
79
- .slice(0, 2)
80
- .map((a) => a.label)
81
- .join(", ");
82
- points.push(scanSurfaceCopy.presentation.affectedAreasKeyPoint.replace("{areas}", labels));
83
- }
84
- const guidance = selectReviewerGuidance(facts, { scope: "changed", max: 2 });
85
- for (const g of guidance) {
86
- const msg = g.message.trim();
87
- if (msg && !points.includes(msg))
88
- points.push(msg);
89
- if (points.length >= max)
90
- break;
91
- }
92
- return points.slice(0, max);
25
+ return projectCompactKeyPoints(buildNarrativeChannels(bundle), max);
93
26
  }
94
27
  export function composeVerificationActions(bundle, max) {
95
- const { facts, profile } = bundle;
96
- const actions = [];
97
- const guidance = selectReviewerGuidance(facts, { scope: "changed", max: 4 });
98
- for (const g of guidance) {
99
- const rem = g.remediation?.trim();
100
- if (rem)
101
- actions.push(rem);
102
- }
103
- const prompt = composeVerificationPrompt(facts);
104
- if (prompt && !actions.includes(prompt))
105
- actions.push(prompt);
106
- if (actions.length === 0) {
107
- if (profile.density === "minimal" ||
108
- facts.runtimeSurface?.kind === "build") {
109
- actions.push(scanSurfaceCopy.presentation.verifyCiTypecheck);
110
- }
111
- else if (profile.density === "rich") {
112
- actions.push(scanSurfaceCopy.presentation.verifyAuthFlow);
113
- actions.push(scanSurfaceCopy.presentation.verifyErrorResponses);
114
- }
115
- }
116
- return actions.slice(0, max);
28
+ const { profile } = bundle;
29
+ return formatPresentationVerification(profile.interpretation, profile.status, max);
117
30
  }
118
31
  export function composeAffectedAreaLabels(bundle, max) {
119
32
  const labels = [];
@@ -127,14 +40,16 @@ export function composeAffectedAreaLabels(bundle, max) {
127
40
  return labels;
128
41
  }
129
42
  export function composeEvidenceRows(bundle, max) {
130
- const { facts } = bundle;
43
+ const { facts, profile } = bundle;
131
44
  const rows = [];
132
- const runtime = labelRuntimeSurface(facts);
133
- if (runtime)
134
- rows.push({ label: "Runtime", value: runtime });
135
- const reach = labelReachabilityKind(facts);
136
- if (reach)
137
- rows.push({ label: "Reachability", value: reach });
45
+ if (!profile.interpretation.suppressRuntimeNarrative) {
46
+ const runtime = labelRuntimeSurface(facts);
47
+ if (runtime)
48
+ rows.push({ label: "Runtime", value: runtime });
49
+ const reach = labelReachabilityKind(facts);
50
+ if (reach)
51
+ rows.push({ label: "Reachability", value: reach });
52
+ }
138
53
  const blast = labelBlastRadiusLevel(facts);
139
54
  if (blast)
140
55
  rows.push({ label: "Blast radius", value: blast });
@@ -0,0 +1,29 @@
1
+ import type { PresentationEvidenceRow, PresentationStatus } from "./types.js";
2
+ export type DashboardCardLayout = "quiet" | "standard" | "expanded";
3
+ export type DashboardCardPresentation = {
4
+ verdict?: {
5
+ posture: PresentationStatus;
6
+ postureLabel: string;
7
+ scopeLabel?: string;
8
+ };
9
+ headline: string;
10
+ pipeline?: {
11
+ status: "queued" | "running" | "failed";
12
+ headline: string;
13
+ subheadline?: string;
14
+ };
15
+ limitedContext?: {
16
+ message: string;
17
+ };
18
+ insights: string[];
19
+ scopeAreas?: string[];
20
+ verification: string[];
21
+ evidenceChips?: PresentationEvidenceRow[];
22
+ layout: DashboardCardLayout;
23
+ detailActionLabel: string;
24
+ sortKey: {
25
+ postureRank: number;
26
+ riskIndex: number;
27
+ };
28
+ };
29
+ //# sourceMappingURL=dashboardCardPresentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboardCardPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/dashboardCardPresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9E,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,CAAC;AAEpE,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,CAAC,EAAE;QACR,OAAO,EAAE,kBAAkB,CAAC;QAC5B,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IAEF,QAAQ,EAAE,MAAM,CAAC;IAEjB,QAAQ,CAAC,EAAE;QACT,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;QACxC,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF,cAAc,CAAC,EAAE;QACf,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IAEF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAE1C,MAAM,EAAE,mBAAmB,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAE1B,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import type { PresentationConfidence, PresentationDensity, PresentationEvidenceContext, PresentationStatus } from "./types.js";
2
+ import type { PresentationIntent } from "../intent/presentationIntent.js";
2
3
  import type { FindingSeverity, ScoreLayer } from "../../types.js";
3
4
  export type ScanDetailsPresentation = {
4
5
  evidenceContext: PresentationEvidenceContext;
@@ -107,5 +108,6 @@ export type ScanDetailsPresentation = {
107
108
  methodologyVersion?: string | null;
108
109
  changedPackagesSummary?: string;
109
110
  };
111
+ presentationIntent?: PresentationIntent;
110
112
  };
111
113
  //# sourceMappingURL=scanDetailsPresentation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scanDetailsPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/scanDetailsPresentation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scanDetailsPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/scanDetailsPresentation.ts"],"names":[],"mappings":"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"}
@@ -12,13 +12,4 @@ export type PresentationEvidenceContext = {
12
12
  priority: PresentationPriority;
13
13
  degradedMessage?: string;
14
14
  };
15
- export type FindingCountSummary = {
16
- critical: number;
17
- high: number;
18
- medium: number;
19
- low: number;
20
- };
21
- export declare function aggregateFindingCounts(findings: Array<{
22
- severity: string;
23
- }> | null | undefined): FindingCountSummary;
24
15
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,cAAc,GAAG,OAAO,CAAC;AAEnE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAElE,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/D,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAEjE,MAAM,MAAM,uBAAuB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,8DAA8D;AAC9D,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACvD,mBAAmB,CAgBrB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,cAAc,GAAG,OAAO,CAAC;AAEnE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAElE,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/D,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAEjE,MAAM,MAAM,uBAAuB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,8DAA8D;AAC9D,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC"}
@@ -1,22 +1 @@
1
- export function aggregateFindingCounts(findings) {
2
- const counts = {
3
- critical: 0,
4
- high: 0,
5
- medium: 0,
6
- low: 0,
7
- };
8
- if (!Array.isArray(findings))
9
- return counts;
10
- for (const f of findings) {
11
- const s = f.severity;
12
- if (s === "critical")
13
- counts.critical += 1;
14
- else if (s === "high")
15
- counts.high += 1;
16
- else if (s === "medium")
17
- counts.medium += 1;
18
- else if (s === "low")
19
- counts.low += 1;
20
- }
21
- return counts;
22
- }
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dashboardCardGolden.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboardCardGolden.test.d.ts","sourceRoot":"","sources":["../../../src/presentation/fixtures/dashboardCardGolden.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,31 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { describe, expect, it } from "vitest";
5
+ import { buildScanCardPresentation } from "../orchestration/buildScanCardPresentation.js";
6
+ import { scanResultFastifyRuntime, scanResultTypescriptPatch, } from "./presentationPersonaFixtures.js";
7
+ const goldenPath = join(dirname(fileURLToPath(import.meta.url)), "dashboardCardGolden.json");
8
+ const golden = JSON.parse(readFileSync(goldenPath, "utf8"));
9
+ describe("dashboardCardGolden fixtures", () => {
10
+ it("matches live typescript patch card", () => {
11
+ const live = buildScanCardPresentation({
12
+ result: scanResultTypescriptPatch,
13
+ pipelineStatus: "done",
14
+ });
15
+ expect(live).toEqual(golden.typescriptPatch);
16
+ });
17
+ it("matches live fastify runtime card", () => {
18
+ const live = buildScanCardPresentation({
19
+ result: scanResultFastifyRuntime,
20
+ pipelineStatus: "done",
21
+ });
22
+ expect(live).toEqual(golden.fastifyRuntime);
23
+ });
24
+ it("fastify golden has deduplicated channels", () => {
25
+ const card = golden.fastifyRuntime;
26
+ const insightText = card.insights.join(" ");
27
+ expect(insightText).not.toMatch(/Affected areas:/i);
28
+ expect(card.scopeAreas?.length).toBeGreaterThan(0);
29
+ expect(card.evidenceChips?.some((e) => e.label === "Blast radius")).toBe(true);
30
+ });
31
+ });