@mergesignal/shared 0.4.2 → 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 (52) hide show
  1. package/dist/engineWireRegression.test.js +2 -2
  2. package/dist/narrativeParity.test.js +3 -4
  3. package/dist/presentation/compose/narrativeChannels.d.ts +17 -0
  4. package/dist/presentation/compose/narrativeChannels.d.ts.map +1 -0
  5. package/dist/presentation/compose/narrativeChannels.js +75 -0
  6. package/dist/presentation/compose/narrativeChannels.test.d.ts +2 -0
  7. package/dist/presentation/compose/narrativeChannels.test.d.ts.map +1 -0
  8. package/dist/presentation/compose/narrativeChannels.test.js +42 -0
  9. package/dist/presentation/compose/narrativeCompose.d.ts +1 -0
  10. package/dist/presentation/compose/narrativeCompose.d.ts.map +1 -1
  11. package/dist/presentation/compose/narrativeCompose.js +4 -3
  12. package/dist/presentation/dto/dashboardCardPresentation.d.ts +29 -0
  13. package/dist/presentation/dto/dashboardCardPresentation.d.ts.map +1 -0
  14. package/dist/presentation/dto/types.d.ts +0 -9
  15. package/dist/presentation/dto/types.d.ts.map +1 -1
  16. package/dist/presentation/dto/types.js +1 -22
  17. package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts +2 -0
  18. package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts.map +1 -0
  19. package/dist/presentation/fixtures/dashboardCardGolden.test.js +31 -0
  20. package/dist/presentation/index.d.ts +6 -5
  21. package/dist/presentation/index.d.ts.map +1 -1
  22. package/dist/presentation/index.js +2 -3
  23. package/dist/presentation/intent/formatPresentationCopy.d.ts +2 -1
  24. package/dist/presentation/intent/formatPresentationCopy.d.ts.map +1 -1
  25. package/dist/presentation/intent/formatPresentationCopy.js +4 -17
  26. package/dist/presentation/orchestration/buildScanCardPresentation.d.ts +2 -2
  27. package/dist/presentation/orchestration/buildScanCardPresentation.d.ts.map +1 -1
  28. package/dist/presentation/orchestration/buildScanCardPresentation.js +5 -6
  29. package/dist/presentation/presentation.test.js +16 -18
  30. package/dist/presentation/presentationPersonas.test.js +14 -13
  31. package/dist/presentation/presenters/presentCliScanSummary.d.ts.map +1 -1
  32. package/dist/presentation/presenters/presentCliScanSummary.js +2 -2
  33. package/dist/presentation/presenters/presentDashboardCard.d.ts +5 -0
  34. package/dist/presentation/presenters/presentDashboardCard.d.ts.map +1 -0
  35. package/dist/presentation/presenters/presentDashboardCard.js +117 -0
  36. package/dist/presentation/presenters/presentGitHubCheckRun.d.ts.map +1 -1
  37. package/dist/presentation/presenters/presentGitHubCheckRun.js +2 -2
  38. package/dist/presentation/presenters/presentScanDetails.d.ts.map +1 -1
  39. package/dist/presentation/presenters/presentScanDetails.js +6 -3
  40. package/dist/scanCardSummary.d.ts +3 -4
  41. package/dist/scanCardSummary.d.ts.map +1 -1
  42. package/dist/scanCardSummary.test.js +16 -16
  43. package/package.json +1 -1
  44. package/dist/presentation/dto/scanCardPresentation.d.ts +0 -27
  45. package/dist/presentation/dto/scanCardPresentation.d.ts.map +0 -1
  46. package/dist/presentation/presenters/presentPipelineScanCard.d.ts +0 -3
  47. package/dist/presentation/presenters/presentPipelineScanCard.d.ts.map +0 -1
  48. package/dist/presentation/presenters/presentPipelineScanCard.js +0 -23
  49. package/dist/presentation/presenters/presentScanCard.d.ts +0 -7
  50. package/dist/presentation/presenters/presentScanCard.d.ts.map +0 -1
  51. package/dist/presentation/presenters/presentScanCard.js +0 -44
  52. /package/dist/presentation/dto/{scanCardPresentation.js → dashboardCardPresentation.js} +0 -0
@@ -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,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":"AAaA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AASzF,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,CAQV;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
+ {"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,7 +1,9 @@
1
1
  import { formatCardAreaLabels } from "../../formatCardAreaLabels.js";
2
2
  import { formatChangedPackagesShort, labelBlastRadiusLevel, labelReachabilityKind, labelRuntimeSurface, } from "../../narrativePresentation.js";
3
3
  import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
4
- import { formatPresentationHeadline, formatPresentationKeyPoints, formatPresentationVerification, } from "../intent/formatPresentationCopy.js";
4
+ import { formatPresentationHeadline, formatPresentationVerification, } from "../intent/formatPresentationCopy.js";
5
+ import { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
6
+ export { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
5
7
  function topAreaLabel(bundle) {
6
8
  const area = bundle.facts.affectedAreas[0];
7
9
  if (!area)
@@ -20,8 +22,7 @@ export function composeSubheadline(bundle) {
20
22
  return undefined;
21
23
  }
22
24
  export function composeKeyPoints(bundle, max) {
23
- const { facts, profile } = bundle;
24
- return formatPresentationKeyPoints(profile.interpretation, facts, profile.status, max);
25
+ return projectCompactKeyPoints(buildNarrativeChannels(bundle), max);
25
26
  }
26
27
  export function composeVerificationActions(bundle, max) {
27
28
  const { profile } = bundle;
@@ -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"}
@@ -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
+ });
@@ -9,12 +9,13 @@ export type { PresentationProfile } from "./profile/presentationProfile.js";
9
9
  export type { PresentationIntent, PresentationInterpretation, } from "./intent/presentationIntent.js";
10
10
  export { derivePresentationInterpretation } from "./intent/derivePresentationInterpretation.js";
11
11
  export type { PipelineStatus, PresentationStatus, PresentationDensity, PresentationConfidence, PresentationPriority, PresentationEvidenceContext, PresentationEvidenceRow, } from "./dto/types.js";
12
- export type { ScanCardPresentation } from "./dto/scanCardPresentation.js";
12
+ export type { DashboardCardPresentation } from "./dto/dashboardCardPresentation.js";
13
+ export type { DashboardCardLayout } from "./dto/dashboardCardPresentation.js";
14
+ /** @deprecated Use DashboardCardPresentation */
15
+ export type { DashboardCardPresentation as ScanCardPresentation } from "./dto/dashboardCardPresentation.js";
13
16
  export type { ScanDetailsPresentation } from "./dto/scanDetailsPresentation.js";
14
17
  export type { GitHubCheckRunPresentation, GitHubPrCommentPresentation, CliScanPresentation, } from "./dto/githubAndCliPresentation.js";
15
- export { presentScanCard } from "./presenters/presentScanCard.js";
16
- export type { PresentScanCardContext } from "./presenters/presentScanCard.js";
17
- export { presentPipelineScanCard } from "./presenters/presentPipelineScanCard.js";
18
+ export { presentDashboardCard, presentPipelineDashboardCard, } from "./presenters/presentDashboardCard.js";
18
19
  export { presentScanDetails } from "./presenters/presentScanDetails.js";
19
20
  export type { PresentScanDetailsContext } from "./presenters/presentScanDetails.js";
20
21
  export { presentGitHubCheckRun } from "./presenters/presentGitHubCheckRun.js";
@@ -23,5 +24,5 @@ export { presentCliScanSummary } from "./presenters/presentCliScanSummary.js";
23
24
  export { renderGitHubCheckRunMarkdown } from "./render/renderGitHubCheckRunMarkdown.js";
24
25
  export { renderGitHubPrCommentMarkdown } from "./render/renderGitHubPrCommentMarkdown.js";
25
26
  export { renderCliScanSummaryText } from "./render/renderCliScanSummaryText.js";
26
- export { composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeEvidenceRows, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
27
+ export { buildNarrativeChannels, projectCompactKeyPoints, composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeEvidenceRows, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
27
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/presentation/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,EAAE,2BAA2B,EAAE,MAAM,gDAAgD,CAAC;AAC7F,YAAY,EAAE,gCAAgC,EAAE,MAAM,gDAAgD,CAAC;AACvG,OAAO,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAC;AACzF,YAAY,EAAE,8BAA8B,EAAE,MAAM,8CAA8C,CAAC;AACnG,OAAO,EAAE,4BAA4B,EAAE,MAAM,iDAAiD,CAAC;AAC/F,YAAY,EAAE,iCAAiC,EAAE,MAAM,iDAAiD,CAAC;AAEzG,YAAY,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,YAAY,EACV,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,gCAAgC,EAAE,MAAM,8CAA8C,CAAC;AAChG,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,YAAY,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAChF,YAAY,EACV,0BAA0B,EAC1B,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,YAAY,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,YAAY,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACpF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,OAAO,EAAE,4BAA4B,EAAE,MAAM,0CAA0C,CAAC;AACxF,OAAO,EAAE,6BAA6B,EAAE,MAAM,2CAA2C,CAAC;AAC1F,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAEhF,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,0BAA0B,EAC1B,yBAAyB,EACzB,mBAAmB,EACnB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/presentation/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,EAAE,2BAA2B,EAAE,MAAM,gDAAgD,CAAC;AAC7F,YAAY,EAAE,gCAAgC,EAAE,MAAM,gDAAgD,CAAC;AACvG,OAAO,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAC;AACzF,YAAY,EAAE,8BAA8B,EAAE,MAAM,8CAA8C,CAAC;AACnG,OAAO,EAAE,4BAA4B,EAAE,MAAM,iDAAiD,CAAC;AAC/F,YAAY,EAAE,iCAAiC,EAAE,MAAM,iDAAiD,CAAC;AAEzG,YAAY,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,YAAY,EACV,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,gCAAgC,EAAE,MAAM,8CAA8C,CAAC;AAChG,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACpF,YAAY,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,gDAAgD;AAChD,YAAY,EAAE,yBAAyB,IAAI,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC5G,YAAY,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAChF,YAAY,EACV,0BAA0B,EAC1B,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EACL,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,YAAY,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACpF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,OAAO,EAAE,4BAA4B,EAAE,MAAM,0CAA0C,CAAC;AACxF,OAAO,EAAE,6BAA6B,EAAE,MAAM,2CAA2C,CAAC;AAC1F,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAEhF,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,0BAA0B,EAC1B,yBAAyB,EACzB,mBAAmB,EACnB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,+BAA+B,CAAC"}
@@ -2,8 +2,7 @@ export { buildScanPresentationBundle } from "./orchestration/buildScanPresentati
2
2
  export { buildScanCardPresentation } from "./orchestration/buildScanCardPresentation.js";
3
3
  export { buildScanDetailsPresentation } from "./orchestration/buildScanDetailsPresentation.js";
4
4
  export { derivePresentationInterpretation } from "./intent/derivePresentationInterpretation.js";
5
- export { presentScanCard } from "./presenters/presentScanCard.js";
6
- export { presentPipelineScanCard } from "./presenters/presentPipelineScanCard.js";
5
+ export { presentDashboardCard, presentPipelineDashboardCard, } from "./presenters/presentDashboardCard.js";
7
6
  export { presentScanDetails } from "./presenters/presentScanDetails.js";
8
7
  export { presentGitHubCheckRun } from "./presenters/presentGitHubCheckRun.js";
9
8
  export { presentGitHubPrComment } from "./presenters/presentGitHubPrComment.js";
@@ -11,4 +10,4 @@ export { presentCliScanSummary } from "./presenters/presentCliScanSummary.js";
11
10
  export { renderGitHubCheckRunMarkdown } from "./render/renderGitHubCheckRunMarkdown.js";
12
11
  export { renderGitHubPrCommentMarkdown } from "./render/renderGitHubPrCommentMarkdown.js";
13
12
  export { renderCliScanSummaryText } from "./render/renderCliScanSummaryText.js";
14
- export { composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeEvidenceRows, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
13
+ export { buildNarrativeChannels, projectCompactKeyPoints, composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeEvidenceRows, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
@@ -2,6 +2,7 @@ import type { ScanNarrativeFacts } from "../../scanNarrativeFacts.js";
2
2
  import type { PresentationStatus } from "../dto/types.js";
3
3
  import type { PresentationInterpretation } from "./presentationIntent.js";
4
4
  export declare function formatPresentationHeadline(interpretation: PresentationInterpretation, facts: ScanNarrativeFacts, status: PresentationStatus | undefined): string;
5
- export declare function formatPresentationKeyPoints(interpretation: PresentationInterpretation, facts: ScanNarrativeFacts, status: PresentationStatus | undefined, max: number): string[];
5
+ /** Atomic insight lines no embedded areas or blast radius (those live in separate channels). */
6
+ export declare function formatPresentationInsightLines(interpretation: PresentationInterpretation, facts: ScanNarrativeFacts, status: PresentationStatus | undefined): string[];
6
7
  export declare function formatPresentationVerification(interpretation: PresentationInterpretation, status: PresentationStatus | undefined, max: number): string[];
7
8
  //# sourceMappingURL=formatPresentationCopy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatPresentationCopy.d.ts","sourceRoot":"","sources":["../../../src/presentation/intent/formatPresentationCopy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAM1D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAa1E,wBAAgB,0BAA0B,CACxC,cAAc,EAAE,0BAA0B,EAC1C,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,MAAM,CAmDR;AA0BD,wBAAgB,2BAA2B,CACzC,cAAc,EAAE,0BAA0B,EAC1C,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAmEV;AAuBD,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,0BAA0B,EAC1C,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAmBV"}
1
+ {"version":3,"file":"formatPresentationCopy.d.ts","sourceRoot":"","sources":["../../../src/presentation/intent/formatPresentationCopy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAM1D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAa1E,wBAAgB,0BAA0B,CACxC,cAAc,EAAE,0BAA0B,EAC1C,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,MAAM,CAmDR;AA0BD,kGAAkG;AAClG,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,0BAA0B,EAC1C,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,MAAM,EAAE,CA0CV;AAuBD,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,0BAA0B,EAC1C,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAmBV"}
@@ -69,14 +69,15 @@ function formatExpectedImpactKeyPoint(interpretation) {
69
69
  function formatLimitedEvidenceKeyPoint() {
70
70
  return scanSurfaceCopy.presentation.limitedEvidenceKeyPoint;
71
71
  }
72
- export function formatPresentationKeyPoints(interpretation, facts, status, max) {
72
+ /** Atomic insight lines — no embedded areas or blast radius (those live in separate channels). */
73
+ export function formatPresentationInsightLines(interpretation, facts, status) {
73
74
  const points = [];
74
75
  if (interpretation.intent === "limited_context") {
75
76
  points.push(scanSurfaceCopy.presentation.limitedContextKeyPoint);
76
77
  if (facts.changedPackages.primary) {
77
78
  points.push(scanSurfaceCopy.presentation.changedPackageKeyPoint.replace("{package}", facts.changedPackages.primary));
78
79
  }
79
- return applyPostureVocabularyGuardLines(points, status).slice(0, max);
80
+ return applyPostureVocabularyGuardLines(points, status);
80
81
  }
81
82
  if (interpretation.showLimitedEvidence) {
82
83
  points.push(formatLimitedEvidenceKeyPoint());
@@ -96,22 +97,8 @@ export function formatPresentationKeyPoints(interpretation, facts, status, max)
96
97
  else {
97
98
  points.push(scanSurfaceCopy.presentation.runtimeUsageKeyPoint);
98
99
  }
99
- if (facts.affectedAreas.length > 0) {
100
- const labels = facts.affectedAreas
101
- .slice(0, 2)
102
- .map((a) => a.label)
103
- .join(", ");
104
- points.push(scanSurfaceCopy.presentation.affectedAreasKeyPoint.replace("{areas}", labels));
105
- }
106
- const blast = facts.blastRadius;
107
- if (blast?.level === "wide") {
108
- points.push(scanSurfaceCopy.presentation.blastRadiusWideKeyPoint);
109
- }
110
- else if (blast?.level === "moderate") {
111
- points.push(scanSurfaceCopy.presentation.blastRadiusLevelKeyPoint.replace("{level}", "Moderate"));
112
- }
113
100
  }
114
- return applyPostureVocabularyGuardLines(points, status).slice(0, max);
101
+ return applyPostureVocabularyGuardLines(points, status);
115
102
  }
116
103
  const AUTH_VERIFICATION = [
117
104
  "routes",
@@ -1,11 +1,11 @@
1
1
  import type { PipelineStatus } from "../dto/types.js";
2
2
  import type { ScanResult } from "../../types.js";
3
- import type { ScanCardPresentation } from "../dto/scanCardPresentation.js";
3
+ import type { DashboardCardPresentation } from "../dto/dashboardCardPresentation.js";
4
4
  export type BuildScanCardPresentationInput = {
5
5
  pipelineStatus: PipelineStatus;
6
6
  result: ScanResult | null;
7
7
  decision?: string | null;
8
8
  totalScore?: number | null;
9
9
  };
10
- export declare function buildScanCardPresentation(input: BuildScanCardPresentationInput): ScanCardPresentation;
10
+ export declare function buildScanCardPresentation(input: BuildScanCardPresentationInput): DashboardCardPresentation;
11
11
  //# sourceMappingURL=buildScanCardPresentation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildScanCardPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/orchestration/buildScanCardPresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAKjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,MAAM,MAAM,8BAA8B,GAAG;IAC3C,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AA2BF,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,8BAA8B,GACpC,oBAAoB,CA2BtB"}
1
+ {"version":3,"file":"buildScanCardPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/orchestration/buildScanCardPresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAOjD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAErF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AA2BF,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,8BAA8B,GACpC,yBAAyB,CA2B3B"}
@@ -1,7 +1,6 @@
1
1
  import { mergePostureFromDecision } from "../../riskVocabulary.js";
2
2
  import { buildScanPresentationBundle } from "./buildScanPresentationBundle.js";
3
- import { presentScanCard } from "../presenters/presentScanCard.js";
4
- import { presentPipelineScanCard } from "../presenters/presentPipelineScanCard.js";
3
+ import { presentDashboardCard, presentPipelineDashboardCard, } from "../presenters/presentDashboardCard.js";
5
4
  function minimalResultFromDenormalized(decision, totalScore) {
6
5
  const posture = mergePostureFromDecision(decision ?? undefined);
7
6
  if (!posture && (totalScore == null || !Number.isFinite(totalScore))) {
@@ -24,12 +23,12 @@ function minimalResultFromDenormalized(decision, totalScore) {
24
23
  }
25
24
  export function buildScanCardPresentation(input) {
26
25
  if (input.pipelineStatus !== "done") {
27
- return presentPipelineScanCard(input.pipelineStatus === "failed" ? "failed" : input.pipelineStatus);
26
+ return presentPipelineDashboardCard(input.pipelineStatus === "failed" ? "failed" : input.pipelineStatus);
28
27
  }
29
28
  const result = input.result ??
30
29
  minimalResultFromDenormalized(input.decision, input.totalScore);
31
30
  if (!result) {
32
- return presentPipelineScanCard("failed");
31
+ return presentPipelineDashboardCard("failed");
33
32
  }
34
33
  const bundle = buildScanPresentationBundle({
35
34
  result,
@@ -38,7 +37,7 @@ export function buildScanCardPresentation(input) {
38
37
  totalScore: input.totalScore,
39
38
  });
40
39
  if (!bundle) {
41
- return presentPipelineScanCard("failed");
40
+ return presentPipelineDashboardCard("failed");
42
41
  }
43
- return presentScanCard(bundle, { includeFindingCounts: true });
42
+ return presentDashboardCard(bundle);
44
43
  }
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { buildScanPresentationBundle } from "./orchestration/buildScanPresentationBundle.js";
3
- import { presentScanCard } from "./presenters/presentScanCard.js";
3
+ import { presentDashboardCard } from "./presenters/presentDashboardCard.js";
4
4
  import { presentScanDetails } from "./presenters/presentScanDetails.js";
5
5
  import { presentGitHubCheckRun } from "./presenters/presentGitHubCheckRun.js";
6
6
  import { presentGitHubPrComment } from "./presenters/presentGitHubPrComment.js";
@@ -43,32 +43,30 @@ describe("buildScanPresentationBundle", () => {
43
43
  expect(bundle.profile.degradedMessage).toBeTruthy();
44
44
  });
45
45
  });
46
- describe("presentScanCard personas", () => {
47
- it("fastify card is rich with affected areas and verification", () => {
46
+ describe("presentDashboardCard personas", () => {
47
+ it("fastify card has scope areas and verification", () => {
48
48
  const bundle = buildScanPresentationBundle({
49
49
  result: scanResultFastifyRuntime,
50
50
  pipelineStatus: "done",
51
51
  });
52
- const card = presentScanCard(bundle);
53
- expect(card.density).toBe("rich");
54
- expect(card.affectedAreas.length).toBeGreaterThan(0);
55
- expect(card.verificationActions.length).toBeGreaterThan(0);
52
+ const card = presentDashboardCard(bundle);
53
+ expect(card.layout).toBe("expanded");
54
+ expect(card.scopeAreas.length).toBeGreaterThan(0);
55
+ expect(card.verification.length).toBeGreaterThan(0);
56
56
  expect(card.headline.toLowerCase()).toContain("fastify");
57
- expect(card.supportingContext).toBeUndefined();
57
+ expect(card.verdict?.posture).toBe("needs_review");
58
58
  });
59
- it("typescript card is minimal safe without graph-first story", () => {
59
+ it("typescript card is quiet safe", () => {
60
60
  const bundle = buildScanPresentationBundle({
61
61
  result: scanResultTypescriptPatch,
62
62
  pipelineStatus: "done",
63
63
  });
64
- const card = presentScanCard(bundle);
65
- expect(card.density).toBe("minimal");
66
- expect(card.status).toBe("safe");
67
- expect(card.presentationIntent).toBe("tooling_patch");
64
+ const card = presentDashboardCard(bundle);
65
+ expect(["quiet", "standard"]).toContain(card.layout);
66
+ expect(card.verdict?.posture).toBe("safe");
68
67
  expect(card.headline).toMatch(/patch upgrade/i);
69
68
  expect(card.headline).not.toMatch(/needs review/i);
70
- expect(card.changedPackages).toContain("typescript");
71
- expect(card.verificationActions.length).toBeGreaterThan(0);
69
+ expect(card.verification.length).toBeGreaterThan(0);
72
70
  });
73
71
  });
74
72
  describe("presentation parity across surfaces", () => {
@@ -77,7 +75,7 @@ describe("presentation parity across surfaces", () => {
77
75
  result: scanResultFastifyRuntime,
78
76
  pipelineStatus: "done",
79
77
  });
80
- const card = presentScanCard(bundle);
78
+ const card = presentDashboardCard(bundle);
81
79
  const details = presentScanDetails(bundle, { scanId: SCAN_ID });
82
80
  const check = presentGitHubCheckRun(bundle, {
83
81
  scanId: SCAN_ID,
@@ -85,13 +83,13 @@ describe("presentation parity across surfaces", () => {
85
83
  });
86
84
  const comment = presentGitHubPrComment(bundle);
87
85
  const cli = presentCliScanSummary(bundle, { repoLabel: "acme/api" });
88
- for (const dto of [card, details, check, comment, cli]) {
86
+ for (const dto of [details, check, comment, cli]) {
89
87
  expect(dto.status).toBe(bundle.profile.status);
90
88
  expect(dto.density).toBe(bundle.profile.density);
91
89
  expect(dto.confidence).toBe(bundle.profile.confidence);
92
90
  expect(dto.evidenceContext?.priority).toBe(bundle.profile.priority);
93
91
  }
94
- expect(card.primaryPackage).toBe("fastify");
92
+ expect(card.verdict?.posture).toBe(bundle.profile.status);
95
93
  expect(details.narrative.primaryPackage).toBe("fastify");
96
94
  expect(check.title.toLowerCase()).toContain("fastify");
97
95
  });
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { buildScanPresentationBundle } from "./orchestration/buildScanPresentationBundle.js";
3
- import { presentScanCard } from "./presenters/presentScanCard.js";
3
+ import { presentDashboardCard } from "./presenters/presentDashboardCard.js";
4
4
  import { presentScanDetails } from "./presenters/presentScanDetails.js";
5
5
  import { presentCliScanSummary } from "./presenters/presentCliScanSummary.js";
6
6
  import { scanResultBullmq, scanResultEslint, scanResultFastifyRuntime, scanResultLimitedContext, scanResultMixedTypescriptFastify, scanResultNextAuth, scanResultPrettier, scanResultTypescriptPatch, scanResultUnknownSafe, scanResultVitest, } from "./fixtures/presentationPersonaFixtures.js";
@@ -99,15 +99,16 @@ function cardFor(result) {
99
99
  result,
100
100
  pipelineStatus: "done",
101
101
  });
102
- return presentScanCard(bundle);
102
+ return presentDashboardCard(bundle);
103
103
  }
104
104
  describe("presentation personas", () => {
105
105
  for (const persona of personas) {
106
106
  it(`${persona.name}: intent, posture alignment, headline`, () => {
107
107
  const card = cardFor(persona.result);
108
- expect(card.presentationIntent).toBe(persona.intent);
109
- expect(card.status).toBe(persona.status);
110
- expect(card.density).toBe(persona.density);
108
+ expect(card.verdict?.posture).toBe(persona.status);
109
+ if (persona.density === "rich") {
110
+ expect(card.layout).toBe("expanded");
111
+ }
111
112
  expect(card.headline).toMatch(persona.headlineMatch);
112
113
  if (persona.headlineMustNot) {
113
114
  expect(card.headline).not.toMatch(persona.headlineMustNot);
@@ -115,28 +116,28 @@ describe("presentation personas", () => {
115
116
  if (persona.status === "safe") {
116
117
  const combined = [
117
118
  card.headline,
118
- ...card.keyPoints,
119
- ...card.verificationActions,
119
+ ...card.insights,
120
+ ...card.verification,
120
121
  ].join(" ");
121
122
  expect(combined).not.toMatch(SAFE_FORBIDDEN);
122
123
  }
123
124
  if (persona.status === "needs_review") {
124
- const combined = card.verificationActions.join(" ");
125
+ const combined = card.verification.join(" ");
125
126
  expect(combined).not.toMatch(REVIEW_FORBIDDEN);
126
127
  }
127
128
  if (persona.verificationIncludes) {
128
129
  for (const v of persona.verificationIncludes) {
129
- expect(card.verificationActions.some((a) => a.toLowerCase().includes(v.toLowerCase()))).toBe(true);
130
+ expect(card.verification.some((a) => a.toLowerCase().includes(v.toLowerCase()))).toBe(true);
130
131
  }
131
132
  }
132
133
  if (persona.verificationMustNot) {
133
- expect(card.verificationActions.join(" ")).not.toMatch(persona.verificationMustNot);
134
+ expect(card.verification.join(" ")).not.toMatch(persona.verificationMustNot);
134
135
  }
135
136
  });
136
137
  }
137
138
  it("mixed typescript+fastify: runtime intent, fastify in headline", () => {
138
139
  const card = cardFor(scanResultMixedTypescriptFastify);
139
- expect(card.presentationIntent).toBe("runtime_upgrade");
140
+ expect(card.verdict?.posture).toBe("needs_review");
140
141
  expect(card.headline.toLowerCase()).toContain("fastify");
141
142
  expect(card.headline).not.toMatch(/patch upgrade/i);
142
143
  });
@@ -145,7 +146,7 @@ describe("presentation personas", () => {
145
146
  result: scanResultFastifyRuntime,
146
147
  pipelineStatus: "done",
147
148
  });
148
- const card = presentScanCard(bundle);
149
+ const card = presentDashboardCard(bundle);
149
150
  const details = presentScanDetails(bundle, { scanId: SCAN_ID });
150
151
  const cli = presentCliScanSummary(bundle, { repoLabel: "acme/api" });
151
152
  expect(details.hero.headline).toBe(card.headline);
@@ -156,7 +157,7 @@ describe("presentation personas", () => {
156
157
  result: scanResultTypescriptPatch,
157
158
  pipelineStatus: "done",
158
159
  });
159
- const card = presentScanCard(bundle);
160
+ const card = presentDashboardCard(bundle);
160
161
  const details = presentScanDetails(bundle, { scanId: SCAN_ID });
161
162
  const cli = presentCliScanSummary(bundle, { repoLabel: "acme/api" });
162
163
  expect(details.hero.headline).toBe(card.headline);
@@ -1 +1 @@
1
- {"version":3,"file":"presentCliScanSummary.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentCliScanSummary.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAEzF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,mBAAmB,CA6BrB"}
1
+ {"version":3,"file":"presentCliScanSummary.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentCliScanSummary.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAEzF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,mBAAmB,CA6BrB"}
@@ -1,4 +1,4 @@
1
- import { composeHeadline, composeKeyPoints, composeSubheadline, composeSupportingContext, composeVerificationActions, evidenceContextFromProfile, } from "../compose/narrativeCompose.js";
1
+ import { buildNarrativeChannels, composeHeadline, composeSubheadline, composeSupportingContext, composeVerificationActions, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
2
2
  export function presentCliScanSummary(bundle, ctx = {}) {
3
3
  const { facts, profile, result } = bundle;
4
4
  const layers = result.layerScores;
@@ -17,7 +17,7 @@ export function presentCliScanSummary(bundle, ctx = {}) {
17
17
  evidenceContext: evidenceContextFromProfile(bundle),
18
18
  headline: composeHeadline(bundle),
19
19
  subheadline: composeSubheadline(bundle),
20
- keyPoints: composeKeyPoints(bundle, 6),
20
+ keyPoints: projectCompactKeyPoints(buildNarrativeChannels(bundle), 6),
21
21
  verificationActions: composeVerificationActions(bundle, 5),
22
22
  metrics: {
23
23
  riskIndex: facts.riskIndex ?? result.totalScore ?? 0,
@@ -0,0 +1,5 @@
1
+ import type { DashboardCardPresentation } from "../dto/dashboardCardPresentation.js";
2
+ import type { ScanPresentationBundle } from "../orchestration/scanPresentationBundle.js";
3
+ export declare function presentDashboardCard(bundle: ScanPresentationBundle): DashboardCardPresentation;
4
+ export declare function presentPipelineDashboardCard(pipelineStatus: "queued" | "running" | "failed"): DashboardCardPresentation;
5
+ //# sourceMappingURL=presentDashboardCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentDashboardCard.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentDashboardCard.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAErF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAmEzF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,sBAAsB,GAC7B,yBAAyB,CA6C3B;AAED,wBAAgB,4BAA4B,CAC1C,cAAc,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAC9C,yBAAyB,CAqB3B"}
@@ -0,0 +1,117 @@
1
+ import { MERGE_POSTURE_LABEL, MERGE_POSTURE_SORT_ORDER, } from "../../riskVocabulary.js";
2
+ import { normalizeGeneratedText, normalizeGeneratedTextNullable, } from "../../normalizeGeneratedText.js";
3
+ import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
4
+ import { buildNarrativeChannels } from "../compose/narrativeChannels.js";
5
+ const INSIGHTS_CAP = 3;
6
+ const SCOPE_AREAS_CAP = 2;
7
+ const EVIDENCE_CAP = 2;
8
+ const VERIFICATION_CAP = 2;
9
+ function formatVerificationLine(action) {
10
+ const trimmed = action.trim();
11
+ if (/^verify\b/i.test(trimmed))
12
+ return trimmed;
13
+ return `Verify ${trimmed}`;
14
+ }
15
+ function deriveLayout(channels, profile) {
16
+ const bodyCount = channels.insights.length +
17
+ channels.scopeAreas.length +
18
+ channels.evidence.length +
19
+ channels.verification.length;
20
+ if (profile.status === "safe" &&
21
+ profile.density === "minimal" &&
22
+ bodyCount === 0 &&
23
+ !channels.limitedContextMessage) {
24
+ return "quiet";
25
+ }
26
+ if (profile.density === "rich") {
27
+ return "expanded";
28
+ }
29
+ return "standard";
30
+ }
31
+ function normalizeDashboardCard(card) {
32
+ return {
33
+ ...card,
34
+ headline: normalizeGeneratedText(card.headline),
35
+ limitedContext: card.limitedContext
36
+ ? {
37
+ message: normalizeGeneratedText(card.limitedContext.message),
38
+ }
39
+ : undefined,
40
+ insights: card.insights.map((i) => normalizeGeneratedText(i)),
41
+ scopeAreas: card.scopeAreas?.map((a) => normalizeGeneratedText(a)),
42
+ verification: card.verification.map((v) => normalizeGeneratedText(v)),
43
+ evidenceChips: card.evidenceChips?.map((e) => ({
44
+ label: normalizeGeneratedText(e.label),
45
+ value: normalizeGeneratedText(e.value),
46
+ })),
47
+ pipeline: card.pipeline
48
+ ? {
49
+ ...card.pipeline,
50
+ headline: normalizeGeneratedText(card.pipeline.headline),
51
+ subheadline: normalizeGeneratedTextNullable(card.pipeline.subheadline) ??
52
+ undefined,
53
+ }
54
+ : undefined,
55
+ };
56
+ }
57
+ export function presentDashboardCard(bundle) {
58
+ const { facts, profile } = bundle;
59
+ const channels = buildNarrativeChannels(bundle);
60
+ const posture = profile.status;
61
+ const riskIndex = facts.riskIndex != null && Number.isFinite(facts.riskIndex)
62
+ ? facts.riskIndex
63
+ : -1;
64
+ const insights = channels.insights.slice(0, INSIGHTS_CAP);
65
+ const scopeAreas = channels.scopeAreas.length > 0
66
+ ? channels.scopeAreas.slice(0, SCOPE_AREAS_CAP)
67
+ : undefined;
68
+ const evidenceChips = channels.evidence.length > 0
69
+ ? channels.evidence.slice(0, EVIDENCE_CAP)
70
+ : undefined;
71
+ const verification = channels.verification
72
+ .slice(0, VERIFICATION_CAP)
73
+ .map(formatVerificationLine);
74
+ const card = {
75
+ verdict: {
76
+ posture,
77
+ postureLabel: MERGE_POSTURE_LABEL[posture],
78
+ scopeLabel: channels.reachLabel,
79
+ },
80
+ headline: channels.headline,
81
+ limitedContext: channels.limitedContextMessage
82
+ ? { message: channels.limitedContextMessage }
83
+ : undefined,
84
+ insights,
85
+ scopeAreas,
86
+ verification,
87
+ evidenceChips,
88
+ layout: deriveLayout(channels, profile),
89
+ detailActionLabel: scanSurfaceCopy.presentation.actionLabelReview,
90
+ sortKey: {
91
+ postureRank: MERGE_POSTURE_SORT_ORDER[posture],
92
+ riskIndex,
93
+ },
94
+ };
95
+ return normalizeDashboardCard(card);
96
+ }
97
+ export function presentPipelineDashboardCard(pipelineStatus) {
98
+ const isRunning = pipelineStatus === "queued" || pipelineStatus === "running";
99
+ const headline = isRunning
100
+ ? scanSurfaceCopy.pipeline.scanRunning
101
+ : scanSurfaceCopy.pipeline.analysisIncomplete;
102
+ return normalizeDashboardCard({
103
+ pipeline: {
104
+ status: pipelineStatus,
105
+ headline,
106
+ subheadline: isRunning
107
+ ? scanSurfaceCopy.pipeline.scanIncomplete
108
+ : undefined,
109
+ },
110
+ headline,
111
+ insights: [],
112
+ verification: [],
113
+ layout: "standard",
114
+ detailActionLabel: scanSurfaceCopy.presentation.actionLabelReview,
115
+ sortKey: { postureRank: -1, riskIndex: -1 },
116
+ });
117
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"presentGitHubCheckRun.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentGitHubCheckRun.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAEzF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAChE,0BAA0B,CAkC5B"}
1
+ {"version":3,"file":"presentGitHubCheckRun.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentGitHubCheckRun.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAEzF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAChE,0BAA0B,CAkC5B"}
@@ -1,8 +1,8 @@
1
- import { composeHeadline, composeKeyPoints, composeSubheadline, composeVerificationActions, evidenceContextFromProfile, } from "../compose/narrativeCompose.js";
1
+ import { buildNarrativeChannels, composeHeadline, composeSubheadline, composeVerificationActions, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
2
2
  export function presentGitHubCheckRun(bundle, ctx) {
3
3
  const { profile } = bundle;
4
4
  const headline = composeHeadline(bundle);
5
- const keyPoints = composeKeyPoints(bundle, 3);
5
+ const keyPoints = projectCompactKeyPoints(buildNarrativeChannels(bundle), 3);
6
6
  const actions = composeVerificationActions(bundle, 3);
7
7
  const detailsUrl = `${ctx.webAppOrigin.replace(/\/$/, "")}/scan/${ctx.scanId}`;
8
8
  const conclusion = profile.status === "safe"
@@ -1 +1 @@
1
- {"version":3,"file":"presentScanDetails.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentScanDetails.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAEzF,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAoHF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,yBAAyB,GAC7B,uBAAuB,CAyFzB"}
1
+ {"version":3,"file":"presentScanDetails.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentScanDetails.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAEzF,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAoHF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,yBAAyB,GAC7B,uBAAuB,CA6FzB"}
@@ -1,7 +1,7 @@
1
1
  import { deriveDetailReachChip } from "../../formatCardExposureDisplay.js";
2
2
  import { MERGE_POSTURE_LABEL } from "../../riskVocabulary.js";
3
3
  import { deriveVerdictLineFromFacts, presentScanDetailViewModel, } from "../../scanDetailViewModel.js";
4
- import { composeHeadline, composeKeyPoints, composeSubheadline, composeSupportingContext, composeVerificationActions, evidenceContextFromProfile, } from "../compose/narrativeCompose.js";
4
+ import { buildNarrativeChannels, composeHeadline, composeSubheadline, composeSupportingContext, composeVerificationActions, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
5
5
  import { normalizeGeneratedText, normalizeGeneratedTextNullable, } from "../../normalizeGeneratedText.js";
6
6
  import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
7
7
  function mapOperationalImpact(vm) {
@@ -112,7 +112,8 @@ export function presentScanDetails(bundle, ctx) {
112
112
  });
113
113
  const headline = composeHeadline(bundle);
114
114
  const subheadline = composeSubheadline(bundle);
115
- const keyPoints = composeKeyPoints(bundle, 6);
115
+ const channels = buildNarrativeChannels(bundle);
116
+ const keyPoints = projectCompactKeyPoints(channels, 6);
116
117
  const verificationTitles = composeVerificationActions(bundle, 6);
117
118
  const supportingLines = composeSupportingContext(bundle);
118
119
  const supportingContext = supportingLines
@@ -138,7 +139,9 @@ export function presentScanDetails(bundle, ctx) {
138
139
  headline: normalizeGeneratedText(headline),
139
140
  subheadline: normalizeGeneratedTextNullable(subheadline) ?? undefined,
140
141
  verdictLine: normalizeGeneratedText(verdictLine),
141
- scopeChip: deriveDetailReachChip(result.totalScore) ?? undefined,
142
+ scopeChip: channels.reachLabel ??
143
+ deriveDetailReachChip(result.totalScore) ??
144
+ undefined,
142
145
  postureLabel,
143
146
  riskIndex: facts.riskIndex,
144
147
  },
@@ -1,4 +1,4 @@
1
- import type { ScanCardPresentation } from "./presentation/dto/scanCardPresentation.js";
1
+ import type { DashboardCardPresentation } from "./presentation/dto/dashboardCardPresentation.js";
2
2
  import type { Finding, ScanResult } from "./types.js";
3
3
  export type ScanPipelineStatus = "queued" | "running" | "done" | "failed";
4
4
  export type ScanCardPresentationState = "not_scanned" | "scanning" | "analysis_failed" | "ready" | "stale";
@@ -9,9 +9,8 @@ export type FindingCountSummary = {
9
9
  medium: number;
10
10
  low: number;
11
11
  };
12
- /** @deprecated Use ScanCardPresentation */
13
- export type ScanCardSummary = ScanCardPresentation;
14
- export { ScanCardPresentation };
12
+ /** @deprecated Use DashboardCardPresentation */
13
+ export type ScanCardSummary = DashboardCardPresentation;
15
14
  export declare const SCAN_CARD_SCANNING_SUMMARY = "Waiting for results...";
16
15
  export type PipelineCompletionEvidence = {
17
16
  scannedAt?: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"scanCardSummary.d.ts","sourceRoot":"","sources":["../src/scanCardSummary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAMvF,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE1E,MAAM,MAAM,yBAAyB,GACjC,aAAa,GACb,UAAU,GACV,iBAAiB,GACjB,OAAO,GACP,OAAO,CAAC;AAEZ,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD,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,2CAA2C;AAC3C,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAEnD,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAGhC,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AAEnE,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAsBF,wFAAwF;AACxF,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,0BAA0B,GACnC,OAAO,CAQT;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,QAAQ,EAAE,0BAA0B,GACnC,kBAAkB,CAKpB;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,OAAO,CAST;AAED,wEAAwE;AACxE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAEvE;AAED,mFAAmF;AACnF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,aAAa,GAAG,IAAI,CAKtB;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,SAAS,GACrC,mBAAmB,CAgBrB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,EACrC,cAAc,EAAE,kBAAkB,GACjC,eAAe,CAOjB;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,2FAA2F;AAC3F,wBAAgB,qCAAqC,CACnD,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,cAAc,EAAE,kBAAkB,GACjC,eAAe,CAuBjB;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,sBAAsB,GAC5B,eAAe,CAcjB"}
1
+ {"version":3,"file":"scanCardSummary.d.ts","sourceRoot":"","sources":["../src/scanCardSummary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,iDAAiD,CAAC;AAMjG,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE1E,MAAM,MAAM,yBAAyB,GACjC,aAAa,GACb,UAAU,GACV,iBAAiB,GACjB,OAAO,GACP,OAAO,CAAC;AAEZ,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD,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,gDAAgD;AAChD,MAAM,MAAM,eAAe,GAAG,yBAAyB,CAAC;AAGxD,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AAEnE,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAsBF,wFAAwF;AACxF,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,0BAA0B,GACnC,OAAO,CAQT;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,QAAQ,EAAE,0BAA0B,GACnC,kBAAkB,CAKpB;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,OAAO,CAST;AAED,wEAAwE;AACxE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAEvE;AAED,mFAAmF;AACnF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,aAAa,GAAG,IAAI,CAKtB;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,SAAS,GACrC,mBAAmB,CAgBrB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,EACrC,cAAc,EAAE,kBAAkB,GACjC,eAAe,CAOjB;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,2FAA2F;AAC3F,wBAAgB,qCAAqC,CACnD,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,cAAc,EAAE,kBAAkB,GACjC,eAAe,CAuBjB;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,sBAAsB,GAC5B,eAAe,CAcjB"}
@@ -20,11 +20,11 @@ function pipelineCard(overrides = {}) {
20
20
  subheadline: scanSurfaceCopy.pipeline.scanIncomplete,
21
21
  },
22
22
  headline: scanSurfaceCopy.pipeline.scanRunning,
23
- changedPackages: [],
24
- keyPoints: [],
25
- affectedAreas: [],
26
- verificationActions: [],
27
- evidence: [],
23
+ insights: [],
24
+ verification: [],
25
+ layout: "standard",
26
+ detailActionLabel: scanSurfaceCopy.presentation.actionLabelReview,
27
+ sortKey: { postureRank: -1, riskIndex: -1 },
28
28
  ...overrides,
29
29
  };
30
30
  }
@@ -70,7 +70,7 @@ describe("deriveScanCardSummary", () => {
70
70
  const queued = deriveScanCardSummary(null, "queued");
71
71
  expect(queued.headline).toBe(scanSurfaceCopy.pipeline.scanRunning);
72
72
  expect(queued.pipeline?.status).toBe("queued");
73
- expect(queued.status).toBeUndefined();
73
+ expect(queued.verdict).toBeUndefined();
74
74
  const running = deriveScanCardSummary(null, "running");
75
75
  expect(running.headline).toBe(scanSurfaceCopy.pipeline.scanRunning);
76
76
  expect(running.pipeline?.status).toBe("running");
@@ -101,9 +101,9 @@ describe("deriveScanCardSummary", () => {
101
101
  },
102
102
  };
103
103
  const summary = deriveScanCardSummary(r, "done");
104
- expect(summary.status).toBe("risky");
104
+ expect(summary.verdict?.posture).toBe("risky");
105
105
  expect(summary.headline).toBe(scanSurfaceCopy.presentation.limitedContextHeadline);
106
- expect(summary.riskIndex).toBe(72);
106
+ expect(summary.sortKey.riskIndex).toBe(72);
107
107
  });
108
108
  it("keeps quiet safe cards minimal without extra key points beyond defaults", () => {
109
109
  const r = {
@@ -116,8 +116,8 @@ describe("deriveScanCardSummary", () => {
116
116
  },
117
117
  };
118
118
  const summary = deriveScanCardSummary(r, "done");
119
- expect(summary.status).toBe("safe");
120
- expect(summary.keyPoints.length).toBeGreaterThan(0);
119
+ expect(summary.verdict?.posture).toBe("safe");
120
+ expect(summary.insights.length).toBeGreaterThan(0);
121
121
  });
122
122
  it("surfaces catalog key points from explain signals", () => {
123
123
  const r = {
@@ -146,7 +146,7 @@ describe("deriveScanCardSummary", () => {
146
146
  },
147
147
  };
148
148
  const summary = deriveScanCardSummary(r, "done");
149
- expect(summary.keyPoints.length).toBeGreaterThan(0);
149
+ expect(summary.insights.length).toBeGreaterThan(0);
150
150
  });
151
151
  it("exports stale subline constant", () => {
152
152
  expect(staleScanSubline()).toBe("Based on earlier commit");
@@ -204,18 +204,18 @@ describe("isPipelinePlaceholderCopy", () => {
204
204
  describe("deriveScanCardSummaryFromDenormalized", () => {
205
205
  it("builds risky summary from denormalized columns", () => {
206
206
  const summary = deriveScanCardSummaryFromDenormalized("risky", 72, "Auth boundary change", "done");
207
- expect(summary.status).toBe("risky");
208
- expect(summary.riskIndex).toBe(72);
207
+ expect(summary.verdict?.posture).toBe("risky");
208
+ expect(summary.sortKey.riskIndex).toBe(72);
209
209
  expect(summary.pipeline).toBeUndefined();
210
210
  });
211
211
  it("does not inject pipeline placeholder summaryText", () => {
212
212
  const summary = deriveScanCardSummaryFromDenormalized("risky", 72, "Waiting for results...", "done");
213
- expect(summary.status).toBe("risky");
213
+ expect(summary.verdict?.posture).toBe("risky");
214
214
  expect(summary.pipeline).toBeUndefined();
215
215
  });
216
216
  it("does not inject raw generic summaryText on quiet safe cards", () => {
217
217
  const summary = deriveScanCardSummaryFromDenormalized("safe", 12, "No high-confidence merge risks from this PR dependency change", "done");
218
- expect(summary.status).toBe("safe");
218
+ expect(summary.verdict?.posture).toBe("safe");
219
219
  expect(summary.pipeline).toBeUndefined();
220
220
  });
221
221
  });
@@ -228,7 +228,7 @@ describe("resolvePrScanCardSummary", () => {
228
228
  summaryText: "Waiting for results...",
229
229
  result: null,
230
230
  });
231
- expect(summary.status).toBe("risky");
231
+ expect(summary.verdict?.posture).toBe("risky");
232
232
  expect(summary.pipeline).toBeUndefined();
233
233
  });
234
234
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mergesignal/shared",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "MergeSignal shared scan contract, presentation policy, and trusted validation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,27 +0,0 @@
1
- import type { FindingCountSummary, PresentationConfidence, PresentationDensity, PresentationEvidenceContext, PresentationEvidenceRow, PresentationStatus } from "./types.js";
2
- import type { PresentationIntent } from "../intent/presentationIntent.js";
3
- export type ScanCardPresentation = {
4
- pipeline?: {
5
- status: "queued" | "running" | "failed";
6
- headline: string;
7
- subheadline?: string;
8
- };
9
- status?: PresentationStatus;
10
- density?: PresentationDensity;
11
- confidence?: PresentationConfidence;
12
- evidenceContext?: PresentationEvidenceContext;
13
- headline: string;
14
- subheadline?: string;
15
- changedPackages: string[];
16
- primaryPackage?: string;
17
- keyPoints: string[];
18
- affectedAreas: string[];
19
- verificationActions: string[];
20
- evidence: PresentationEvidenceRow[];
21
- supportingContext?: string[];
22
- riskIndex?: number | null;
23
- findingCounts?: FindingCountSummary | null;
24
- actionLabel?: string;
25
- presentationIntent?: PresentationIntent;
26
- };
27
- //# sourceMappingURL=scanCardPresentation.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scanCardPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/scanCardPresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,MAAM,MAAM,oBAAoB,GAAG;IACjC,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,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,UAAU,CAAC,EAAE,sBAAsB,CAAC;IACpC,eAAe,CAAC,EAAE,2BAA2B,CAAC;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IAEpC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC,CAAC"}
@@ -1,3 +0,0 @@
1
- import type { ScanCardPresentation } from "../dto/scanCardPresentation.js";
2
- export declare function presentPipelineScanCard(pipelineStatus: "queued" | "running" | "failed"): ScanCardPresentation;
3
- //# sourceMappingURL=presentPipelineScanCard.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"presentPipelineScanCard.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentPipelineScanCard.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,wBAAgB,uBAAuB,CACrC,cAAc,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAC9C,oBAAoB,CAqBtB"}
@@ -1,23 +0,0 @@
1
- import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
2
- import { normalizeGeneratedText, } from "../../normalizeGeneratedText.js";
3
- export function presentPipelineScanCard(pipelineStatus) {
4
- const isRunning = pipelineStatus === "queued" || pipelineStatus === "running";
5
- const headline = isRunning
6
- ? scanSurfaceCopy.pipeline.scanRunning
7
- : scanSurfaceCopy.pipeline.analysisIncomplete;
8
- return {
9
- pipeline: {
10
- status: pipelineStatus,
11
- headline: normalizeGeneratedText(headline),
12
- subheadline: isRunning
13
- ? scanSurfaceCopy.pipeline.scanIncomplete
14
- : undefined,
15
- },
16
- headline: normalizeGeneratedText(headline),
17
- changedPackages: [],
18
- keyPoints: [],
19
- affectedAreas: [],
20
- verificationActions: [],
21
- evidence: [],
22
- };
23
- }
@@ -1,7 +0,0 @@
1
- import type { ScanCardPresentation } from "../dto/scanCardPresentation.js";
2
- import type { ScanPresentationBundle } from "../orchestration/scanPresentationBundle.js";
3
- export type PresentScanCardContext = {
4
- includeFindingCounts?: boolean;
5
- };
6
- export declare function presentScanCard(bundle: ScanPresentationBundle, ctx?: PresentScanCardContext): ScanCardPresentation;
7
- //# sourceMappingURL=presentScanCard.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"presentScanCard.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentScanCard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAgB3E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAEzF,MAAM,MAAM,sBAAsB,GAAG;IACnC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAsBF,wBAAgB,eAAe,CAC7B,MAAM,EAAE,sBAAsB,EAC9B,GAAG,GAAE,sBAA2B,GAC/B,oBAAoB,CA0BtB"}
@@ -1,44 +0,0 @@
1
- import { aggregateFindingCounts } from "../dto/types.js";
2
- import { composeAffectedAreaLabels, composeEvidenceRows, composeHeadline, composeKeyPoints, composeSubheadline, composeSupportingContext, composeVerificationActions, evidenceContextFromProfile, } from "../compose/narrativeCompose.js";
3
- import { normalizeGeneratedText, normalizeGeneratedTextNullable, } from "../../normalizeGeneratedText.js";
4
- import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
5
- function normalizeCard(p) {
6
- return {
7
- ...p,
8
- headline: normalizeGeneratedText(p.headline),
9
- subheadline: normalizeGeneratedTextNullable(p.subheadline) ?? undefined,
10
- keyPoints: p.keyPoints.map((k) => normalizeGeneratedText(k)),
11
- affectedAreas: p.affectedAreas.map((a) => normalizeGeneratedText(a)),
12
- verificationActions: p.verificationActions.map((v) => normalizeGeneratedText(v)),
13
- evidence: p.evidence.map((e) => ({
14
- label: normalizeGeneratedText(e.label),
15
- value: normalizeGeneratedText(e.value),
16
- })),
17
- supportingContext: p.supportingContext?.map((s) => normalizeGeneratedText(s)),
18
- };
19
- }
20
- export function presentScanCard(bundle, ctx = {}) {
21
- const { facts, profile, result } = bundle;
22
- const presentation = {
23
- status: profile.status,
24
- density: profile.density,
25
- confidence: profile.confidence,
26
- evidenceContext: evidenceContextFromProfile(bundle),
27
- headline: composeHeadline(bundle),
28
- subheadline: composeSubheadline(bundle) ?? undefined,
29
- changedPackages: facts.changedPackages.all,
30
- primaryPackage: facts.changedPackages.primary ?? undefined,
31
- keyPoints: composeKeyPoints(bundle, 3),
32
- affectedAreas: composeAffectedAreaLabels(bundle, 2),
33
- verificationActions: composeVerificationActions(bundle, 2),
34
- evidence: composeEvidenceRows(bundle, 3),
35
- supportingContext: composeSupportingContext(bundle),
36
- riskIndex: facts.riskIndex,
37
- findingCounts: ctx.includeFindingCounts
38
- ? aggregateFindingCounts(result.findings)
39
- : null,
40
- actionLabel: scanSurfaceCopy.presentation.actionLabelReview,
41
- presentationIntent: profile.interpretation.intent,
42
- };
43
- return normalizeCard(presentation);
44
- }