@mergesignal/shared 0.4.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/dist/actionsStepSummary.d.ts +1 -22
  2. package/dist/actionsStepSummary.d.ts.map +1 -1
  3. package/dist/actionsStepSummary.js +19 -716
  4. package/dist/actionsStepSummary.test.js +34 -329
  5. package/dist/assessmentLabels.d.ts +6 -0
  6. package/dist/assessmentLabels.d.ts.map +1 -0
  7. package/dist/assessmentLabels.js +33 -0
  8. package/dist/assessmentProjection.d.ts +12 -0
  9. package/dist/assessmentProjection.d.ts.map +1 -0
  10. package/dist/assessmentProjection.js +100 -0
  11. package/dist/assessmentSchema.d.ts +155 -0
  12. package/dist/assessmentSchema.d.ts.map +1 -0
  13. package/dist/assessmentSchema.js +110 -0
  14. package/dist/cardObservationCatalog.d.ts +2 -0
  15. package/dist/cardObservationCatalog.d.ts.map +1 -1
  16. package/dist/cardObservationCatalog.js +47 -1
  17. package/dist/engineWireRegression.test.js +22 -2
  18. package/dist/fixtures/assessmentFixtures.d.ts +13 -0
  19. package/dist/fixtures/assessmentFixtures.d.ts.map +1 -0
  20. package/dist/fixtures/assessmentFixtures.js +164 -0
  21. package/dist/formatCardExposureDisplay.d.ts +0 -4
  22. package/dist/formatCardExposureDisplay.d.ts.map +1 -1
  23. package/dist/formatCardExposureDisplay.js +0 -15
  24. package/dist/formatInsight.d.ts +3 -2
  25. package/dist/formatInsight.d.ts.map +1 -1
  26. package/dist/formatInsight.js +26 -1
  27. package/dist/index.d.ts +7 -11
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +7 -11
  30. package/dist/narrativeParity.test.js +4 -34
  31. package/dist/presentGitHubPrComment.d.ts +1 -3
  32. package/dist/presentGitHubPrComment.d.ts.map +1 -1
  33. package/dist/presentGitHubPrComment.js +0 -17
  34. package/dist/presentation/buildGitHubCheckRunOutput.d.ts +20 -0
  35. package/dist/presentation/buildGitHubCheckRunOutput.d.ts.map +1 -0
  36. package/dist/presentation/buildGitHubCheckRunOutput.js +42 -0
  37. package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts +2 -0
  38. package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts.map +1 -0
  39. package/dist/presentation/buildGitHubCheckRunOutput.test.js +188 -0
  40. package/dist/presentation/compose/narrativeChannels.d.ts.map +1 -1
  41. package/dist/presentation/compose/narrativeChannels.js +13 -43
  42. package/dist/presentation/compose/narrativeCompose.d.ts.map +1 -1
  43. package/dist/presentation/compose/narrativeCompose.js +2 -5
  44. package/dist/presentation/dto/assessmentPresentationFields.d.ts +13 -0
  45. package/dist/presentation/dto/assessmentPresentationFields.d.ts.map +1 -0
  46. package/dist/presentation/dto/assessmentPresentationFields.js +1 -0
  47. package/dist/presentation/dto/dashboardCardPresentation.d.ts +2 -1
  48. package/dist/presentation/dto/dashboardCardPresentation.d.ts.map +1 -1
  49. package/dist/presentation/dto/githubAndCliPresentation.d.ts +4 -3
  50. package/dist/presentation/dto/githubAndCliPresentation.d.ts.map +1 -1
  51. package/dist/presentation/dto/scanDetailsPresentation.d.ts +2 -1
  52. package/dist/presentation/dto/scanDetailsPresentation.d.ts.map +1 -1
  53. package/dist/presentation/fixtures/dashboardCardGolden.test.js +21 -14
  54. package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts.map +1 -1
  55. package/dist/presentation/fixtures/presentationPersonaFixtures.js +31 -24
  56. package/dist/presentation/fixtures/scanResultFixtures.d.ts.map +1 -1
  57. package/dist/presentation/fixtures/scanResultFixtures.js +14 -3
  58. package/dist/presentation/index.d.ts +2 -2
  59. package/dist/presentation/index.d.ts.map +1 -1
  60. package/dist/presentation/index.js +2 -2
  61. package/dist/presentation/orchestration/buildScanCardPresentation.d.ts.map +1 -1
  62. package/dist/presentation/orchestration/buildScanCardPresentation.js +8 -3
  63. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts +1 -1
  64. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts.map +1 -1
  65. package/dist/presentation/orchestration/buildScanPresentationBundle.js +12 -10
  66. package/dist/presentation/orchestration/scanPresentationBundle.d.ts +8 -3
  67. package/dist/presentation/orchestration/scanPresentationBundle.d.ts.map +1 -1
  68. package/dist/presentation/presentationPersonas.test.js +6 -6
  69. package/dist/presentation/presenters/presentCliScanSummary.d.ts.map +1 -1
  70. package/dist/presentation/presenters/presentCliScanSummary.js +2 -0
  71. package/dist/presentation/presenters/presentDashboardCard.d.ts +1 -0
  72. package/dist/presentation/presenters/presentDashboardCard.d.ts.map +1 -1
  73. package/dist/presentation/presenters/presentDashboardCard.js +18 -0
  74. package/dist/presentation/presenters/presentGitHubCheckRun.d.ts.map +1 -1
  75. package/dist/presentation/presenters/presentGitHubCheckRun.js +2 -0
  76. package/dist/presentation/presenters/presentGitHubPrComment.d.ts.map +1 -1
  77. package/dist/presentation/presenters/presentGitHubPrComment.js +15 -21
  78. package/dist/presentation/presenters/presentScanDetails.d.ts.map +1 -1
  79. package/dist/presentation/presenters/presentScanDetails.js +52 -129
  80. package/dist/presentation/profile/buildProfileFromAssessment.d.ts +5 -0
  81. package/dist/presentation/profile/buildProfileFromAssessment.d.ts.map +1 -0
  82. package/dist/presentation/profile/buildProfileFromAssessment.js +58 -0
  83. package/dist/presentation/projectAssessmentFields.d.ts +4 -0
  84. package/dist/presentation/projectAssessmentFields.d.ts.map +1 -0
  85. package/dist/presentation/projectAssessmentFields.js +13 -0
  86. package/dist/presentation/surfaceParity.test.d.ts +2 -0
  87. package/dist/presentation/surfaceParity.test.d.ts.map +1 -0
  88. package/dist/presentation/surfaceParity.test.js +74 -0
  89. package/dist/productMessaging.d.ts +4 -4
  90. package/dist/productMessaging.d.ts.map +1 -1
  91. package/dist/productMessaging.js +4 -4
  92. package/dist/productMessaging.test.js +2 -2
  93. package/dist/repoIntelligenceSchema.d.ts +2 -2
  94. package/dist/resolvePipelineStatus.d.ts +15 -0
  95. package/dist/resolvePipelineStatus.d.ts.map +1 -0
  96. package/dist/resolvePipelineStatus.js +37 -0
  97. package/dist/scanCardPresentationState.d.ts +10 -0
  98. package/dist/scanCardPresentationState.d.ts.map +1 -0
  99. package/dist/scanCardPresentationState.js +4 -0
  100. package/dist/{scanDetailViewModel.d.ts → scanDetailViewModelTypes.d.ts} +60 -32
  101. package/dist/scanDetailViewModelTypes.d.ts.map +1 -0
  102. package/dist/scanDetailViewModelTypes.js +4 -0
  103. package/dist/scanResultSchema.d.ts +75 -2
  104. package/dist/scanResultSchema.d.ts.map +1 -1
  105. package/dist/scanResultSchema.js +4 -2
  106. package/dist/scanResultSchema.test.js +16 -0
  107. package/dist/scanSurfaceCopy.d.ts +2 -0
  108. package/dist/scanSurfaceCopy.d.ts.map +1 -1
  109. package/dist/scanSurfaceCopy.js +2 -0
  110. package/dist/trustedScanGuards.test.js +7 -0
  111. package/dist/types.d.ts +3 -0
  112. package/dist/types.d.ts.map +1 -1
  113. package/package.json +1 -1
  114. package/dist/collectNarrativeWhyBullets.d.ts +0 -12
  115. package/dist/collectNarrativeWhyBullets.d.ts.map +0 -1
  116. package/dist/collectNarrativeWhyBullets.js +0 -104
  117. package/dist/deriveCardDisplaySummary.d.ts +0 -16
  118. package/dist/deriveCardDisplaySummary.d.ts.map +0 -1
  119. package/dist/deriveCardDisplaySummary.js +0 -56
  120. package/dist/deriveCardDisplaySummary.test.d.ts +0 -2
  121. package/dist/deriveCardDisplaySummary.test.d.ts.map +0 -1
  122. package/dist/deriveCardDisplaySummary.test.js +0 -89
  123. package/dist/deriveCardOperationalObservations.d.ts +0 -19
  124. package/dist/deriveCardOperationalObservations.d.ts.map +0 -1
  125. package/dist/deriveCardOperationalObservations.js +0 -180
  126. package/dist/deriveCardOperationalObservations.test.d.ts +0 -2
  127. package/dist/deriveCardOperationalObservations.test.d.ts.map +0 -1
  128. package/dist/deriveCardOperationalObservations.test.js +0 -268
  129. package/dist/deriveRiskScoreSummary.d.ts +0 -33
  130. package/dist/deriveRiskScoreSummary.d.ts.map +0 -1
  131. package/dist/deriveRiskScoreSummary.js +0 -90
  132. package/dist/deriveRiskScoreSummary.test.d.ts +0 -2
  133. package/dist/deriveRiskScoreSummary.test.d.ts.map +0 -1
  134. package/dist/deriveRiskScoreSummary.test.js +0 -47
  135. package/dist/deriveScanDetailRecommendations.d.ts +0 -63
  136. package/dist/deriveScanDetailRecommendations.d.ts.map +0 -1
  137. package/dist/deriveScanDetailRecommendations.js +0 -725
  138. package/dist/deriveScanDetailRecommendations.test.d.ts +0 -2
  139. package/dist/deriveScanDetailRecommendations.test.d.ts.map +0 -1
  140. package/dist/deriveScanDetailRecommendations.test.js +0 -181
  141. package/dist/deriveScanSummaryText.d.ts +0 -7
  142. package/dist/deriveScanSummaryText.d.ts.map +0 -1
  143. package/dist/deriveScanSummaryText.js +0 -24
  144. package/dist/deriveScanSummaryText.test.d.ts +0 -2
  145. package/dist/deriveScanSummaryText.test.d.ts.map +0 -1
  146. package/dist/deriveScanSummaryText.test.js +0 -77
  147. package/dist/formatCardEvidenceCounts.d.ts +0 -7
  148. package/dist/formatCardEvidenceCounts.d.ts.map +0 -1
  149. package/dist/formatCardEvidenceCounts.js +0 -19
  150. package/dist/prCheckRunPresentation.d.ts +0 -68
  151. package/dist/prCheckRunPresentation.d.ts.map +0 -1
  152. package/dist/prCheckRunPresentation.js +0 -319
  153. package/dist/prCheckRunPresentation.test.d.ts +0 -2
  154. package/dist/prCheckRunPresentation.test.d.ts.map +0 -1
  155. package/dist/prCheckRunPresentation.test.js +0 -278
  156. package/dist/presentation/intent/derivePresentationInterpretation.d.ts +0 -4
  157. package/dist/presentation/intent/derivePresentationInterpretation.d.ts.map +0 -1
  158. package/dist/presentation/intent/derivePresentationInterpretation.js +0 -202
  159. package/dist/presentation/intent/formatPresentationCopy.d.ts +0 -8
  160. package/dist/presentation/intent/formatPresentationCopy.d.ts.map +0 -1
  161. package/dist/presentation/intent/formatPresentationCopy.js +0 -140
  162. package/dist/presentation/intent/presentationIntent.test.d.ts +0 -2
  163. package/dist/presentation/intent/presentationIntent.test.d.ts.map +0 -1
  164. package/dist/presentation/intent/presentationIntent.test.js +0 -29
  165. package/dist/presentation/intent/verificationFocusLabels.d.ts +0 -2
  166. package/dist/presentation/intent/verificationFocusLabels.d.ts.map +0 -1
  167. package/dist/presentation/intent/verificationFocusLabels.js +0 -45
  168. package/dist/presentation/profile/derivePresentationProfile.d.ts +0 -12
  169. package/dist/presentation/profile/derivePresentationProfile.d.ts.map +0 -1
  170. package/dist/presentation/profile/derivePresentationProfile.js +0 -85
  171. package/dist/scanCardSummary.d.ts +0 -58
  172. package/dist/scanCardSummary.d.ts.map +0 -1
  173. package/dist/scanCardSummary.js +0 -146
  174. package/dist/scanCardSummary.test.d.ts +0 -2
  175. package/dist/scanCardSummary.test.d.ts.map +0 -1
  176. package/dist/scanCardSummary.test.js +0 -234
  177. package/dist/scanDetailViewModel.d.ts.map +0 -1
  178. package/dist/scanDetailViewModel.js +0 -636
  179. package/dist/scanDetailViewModel.test.d.ts +0 -2
  180. package/dist/scanDetailViewModel.test.d.ts.map +0 -1
  181. package/dist/scanDetailViewModel.test.js +0 -530
@@ -1,34 +1,28 @@
1
1
  import { MERGE_POSTURE_LABEL } from "../../riskVocabulary.js";
2
- import { composeVerificationActions, evidenceContextFromProfile, } from "../compose/narrativeCompose.js";
3
- import { formatChangedPackagesShort, formatUsageSummaryLine, selectReviewerGuidance, } from "../../narrativePresentation.js";
2
+ import { buildNarrativeChannels, evidenceContextFromProfile, } from "../compose/narrativeCompose.js";
3
+ import { formatChangedPackagesShort } from "../../narrativePresentation.js";
4
+ import { projectAssessmentFields } from "../projectAssessmentFields.js";
4
5
  export function presentGitHubPrComment(bundle) {
5
6
  const { facts, profile } = bundle;
7
+ const channels = buildNarrativeChannels(bundle);
8
+ const assessmentFields = projectAssessmentFields(bundle);
6
9
  const introLines = [];
7
10
  const changed = formatChangedPackagesShort(facts, 3);
8
11
  if (changed)
9
12
  introLines.push(`Changed: ${changed}`);
10
- const usage = formatUsageSummaryLine(facts, 1);
11
- if (usage)
12
- introLines.push(usage);
13
- const verify = composeVerificationActions(bundle, 1)[0];
13
+ for (const line of assessmentFields.reasoning.slice(0, 2)) {
14
+ introLines.push(line);
15
+ }
16
+ const verify = channels.verification[0];
14
17
  if (verify)
15
18
  introLines.push(`Verify: ${verify}`);
16
- const guidance = selectReviewerGuidance(facts, { scope: "changed", max: 3 });
17
- const selected = guidance.length === 0
18
- ? selectReviewerGuidance(facts, { max: 3 })
19
- : guidance;
20
- const guidanceBlocks = selected.map((g) => {
21
- const where = g.context?.trim() ||
22
- facts.packageUsage
23
- .flatMap((u) => u.paths.slice(0, 1))
24
- .filter(Boolean)[0] ||
25
- "See scan detail for affected paths.";
26
- const action = g.remediation?.trim() ||
27
- composeVerificationActions(bundle, 1)[0] ||
28
- "Review before merge.";
29
- return { message: g.message, where, action };
30
- });
19
+ const guidanceBlocks = channels.verification.slice(0, 3).map((action) => ({
20
+ message: assessmentFields.reasoning[0] ?? action,
21
+ where: changed ?? "See scan detail for affected paths.",
22
+ action,
23
+ }));
31
24
  return {
25
+ ...assessmentFields,
32
26
  status: profile.status,
33
27
  density: profile.density,
34
28
  confidence: profile.confidence,
@@ -1 +1 @@
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
+ {"version":3,"file":"presentScanDetails.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentScanDetails.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAGzF,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;AAmDF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,yBAAyB,GAC7B,uBAAuB,CAwEzB"}
@@ -1,88 +1,9 @@
1
- import { deriveDetailReachChip } from "../../formatCardExposureDisplay.js";
1
+ import { MERGE_CONCERN_LABELS } from "../../assessmentLabels.js";
2
2
  import { MERGE_POSTURE_LABEL } from "../../riskVocabulary.js";
3
- import { deriveVerdictLineFromFacts, presentScanDetailViewModel, } from "../../scanDetailViewModel.js";
4
- import { buildNarrativeChannels, composeHeadline, composeSubheadline, composeSupportingContext, composeVerificationActions, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
3
+ import { buildNarrativeChannels, composeSubheadline, composeSupportingContext, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
5
4
  import { normalizeGeneratedText, normalizeGeneratedTextNullable, } from "../../normalizeGeneratedText.js";
6
5
  import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
7
- function mapOperationalImpact(vm) {
8
- const oi = vm.operationalImpact;
9
- if (oi.status === "hidden") {
10
- return { status: "hidden", items: [] };
11
- }
12
- if (oi.status === "rich") {
13
- return {
14
- status: "rich",
15
- items: oi.items.map((item) => ({
16
- message: item.message,
17
- where: item.where,
18
- verify: item.verify,
19
- affectedFiles: item.affectedFiles,
20
- })),
21
- };
22
- }
23
- return {
24
- status: "compact",
25
- items: oi.items,
26
- fallbackMessage: oi.fallbackMessage,
27
- };
28
- }
29
- function mapRecommendations(vm) {
30
- const items = vm.recommendedActions.items.map((item) => ({
31
- rank: item.rank,
32
- title: item.title,
33
- priority: item.priority === "high" ||
34
- item.priority === "medium" ||
35
- item.priority === "low"
36
- ? item.priority
37
- : "medium",
38
- rationale: item.detail?.why,
39
- }));
40
- return { items };
41
- }
42
- function mapEvidence(bundle, vm) {
43
- const defaultCollapsed = bundle.profile.priority === "pr_intelligence" &&
44
- bundle.profile.density === "rich";
45
- const ev = vm.evidence;
46
- if (!ev) {
47
- return {
48
- defaultCollapsed,
49
- attentionAreas: [],
50
- findings: [],
51
- findingsOverflowCount: 0,
52
- };
53
- }
54
- return {
55
- defaultCollapsed,
56
- attentionAreas: ev.attentionAreas.map((area) => ({
57
- problemLabel: area.problemLabel,
58
- problemDescription: area.problemDescription,
59
- packages: area.packages.map((p) => ({
60
- name: p.name,
61
- version: p.version,
62
- direct: p.direct,
63
- severity: p.severity,
64
- evidence: p.evidence,
65
- })),
66
- overflowCount: area.overflowCount,
67
- })),
68
- findings: ev.findings.map((f) => ({
69
- id: f.id,
70
- severity: f.severity,
71
- title: f.title,
72
- description: f.description,
73
- packageName: f.packageName,
74
- recommendation: f.recommendation,
75
- source: f.source,
76
- })),
77
- findingsOverflowCount: ev.findingsOverflowCount,
78
- topology: ev.topology
79
- ? {
80
- summaryLine: ev.topology.summaryLine,
81
- deepest: ev.topology.deepest,
82
- }
83
- : undefined,
84
- };
85
- }
6
+ import { projectAssessmentFields } from "../projectAssessmentFields.js";
86
7
  function mapUsage(bundle) {
87
8
  const { facts } = bundle;
88
9
  if (facts.packageUsage.length === 0 && facts.frameworks.length === 0) {
@@ -101,20 +22,31 @@ function mapUsage(bundle) {
101
22
  frameworks: facts.frameworks,
102
23
  };
103
24
  }
25
+ function mapEvidence(bundle) {
26
+ const defaultCollapsed = bundle.profile.priority === "pr_intelligence" &&
27
+ bundle.profile.density === "rich";
28
+ const findings = (bundle.result.findings ?? []).slice(0, 8).map((f) => ({
29
+ id: f.id,
30
+ severity: f.severity,
31
+ title: f.title,
32
+ description: f.description,
33
+ packageName: f.packageName,
34
+ recommendation: f.recommendation,
35
+ source: "dependency",
36
+ }));
37
+ return {
38
+ defaultCollapsed,
39
+ attentionAreas: [],
40
+ findings,
41
+ findingsOverflowCount: Math.max(0, (bundle.result.findings?.length ?? 0) - findings.length),
42
+ };
43
+ }
104
44
  export function presentScanDetails(bundle, ctx) {
105
- const { facts, profile, result } = bundle;
106
- const vm = presentScanDetailViewModel(facts, result, {
107
- scanId: ctx.scanId,
108
- status: "done",
109
- methodologyVersion: ctx.methodologyVersion,
110
- prNumber: ctx.prNumber,
111
- repoContext: { status: "hidden" },
112
- });
113
- const headline = composeHeadline(bundle);
114
- const subheadline = composeSubheadline(bundle);
45
+ const { assessment, facts, profile, result } = bundle;
46
+ const assessmentFields = projectAssessmentFields(bundle);
115
47
  const channels = buildNarrativeChannels(bundle);
116
48
  const keyPoints = projectCompactKeyPoints(channels, 6);
117
- const verificationTitles = composeVerificationActions(bundle, 6);
49
+ const subheadline = composeSubheadline(bundle);
118
50
  const supportingLines = composeSupportingContext(bundle);
119
51
  const supportingContext = supportingLines
120
52
  ? {
@@ -122,28 +54,39 @@ export function presentScanDetails(bundle, ctx) {
122
54
  lines: supportingLines,
123
55
  }
124
56
  : undefined;
125
- const posture = profile.status;
126
- const postureLabel = MERGE_POSTURE_LABEL[posture];
127
- const toolingIntent = profile.interpretation.intent === "tooling_patch" ||
128
- profile.interpretation.intent === "tooling_upgrade";
129
- const verdictLine = profile.priority === "pr_intelligence" && toolingIntent && keyPoints[0]
130
- ? keyPoints[0]
131
- : deriveVerdictLineFromFacts(facts, result);
57
+ const postureLabel = MERGE_POSTURE_LABEL[profile.status];
58
+ const verdictLine = assessmentFields.reasoning[0] ??
59
+ (assessment.primaryConcern
60
+ ? MERGE_CONCERN_LABELS[assessment.primaryConcern]
61
+ : postureLabel);
62
+ const verificationActions = channels.verification.map((title, index) => ({
63
+ title: normalizeGeneratedText(title),
64
+ detail: assessmentFields.reasoning[index + 1],
65
+ }));
66
+ const recommendations = verificationActions.map((action, index) => ({
67
+ rank: index + 1,
68
+ title: action.title,
69
+ priority: profile.status === "risky"
70
+ ? "high"
71
+ : profile.status === "needs_review"
72
+ ? "medium"
73
+ : "low",
74
+ rationale: action.detail,
75
+ }));
132
76
  return {
77
+ ...assessmentFields,
133
78
  evidenceContext: evidenceContextFromProfile(bundle),
134
79
  status: profile.status,
135
80
  density: profile.density,
136
81
  confidence: profile.confidence,
137
82
  presentationIntent: profile.interpretation.intent,
138
83
  hero: {
139
- headline: normalizeGeneratedText(headline),
84
+ headline: normalizeGeneratedText(channels.headline),
140
85
  subheadline: normalizeGeneratedTextNullable(subheadline) ?? undefined,
141
86
  verdictLine: normalizeGeneratedText(verdictLine),
142
- scopeChip: channels.reachLabel ??
143
- deriveDetailReachChip(result.totalScore) ??
144
- undefined,
87
+ scopeChip: channels.reachLabel,
145
88
  postureLabel,
146
- riskIndex: facts.riskIndex,
89
+ riskIndex: result.totalScore,
147
90
  },
148
91
  narrative: {
149
92
  keyPoints: keyPoints.map((k) => normalizeGeneratedText(k)),
@@ -151,30 +94,10 @@ export function presentScanDetails(bundle, ctx) {
151
94
  primaryPackage: facts.changedPackages.primary ?? undefined,
152
95
  },
153
96
  usage: mapUsage(bundle),
154
- verification: {
155
- actions: verificationTitles.map((title) => ({
156
- title: normalizeGeneratedText(title),
157
- })),
158
- },
159
- signalSummary: vm.signalSummary
160
- ? {
161
- riskIndex: vm.signalSummary.score,
162
- band: vm.signalSummary.overallBand === "moderate"
163
- ? "medium"
164
- : vm.signalSummary.overallBand === "high"
165
- ? "high"
166
- : "low",
167
- layers: vm.signalSummary.layers.map((l) => ({
168
- layer: l.layer,
169
- score: l.score,
170
- band: l.concernLabel,
171
- label: l.label,
172
- })),
173
- }
174
- : undefined,
175
- operationalImpact: mapOperationalImpact(vm),
176
- recommendations: mapRecommendations(vm),
177
- evidence: mapEvidence(bundle, vm),
97
+ verification: { actions: verificationActions },
98
+ operationalImpact: { status: "hidden", items: [] },
99
+ recommendations: { items: recommendations },
100
+ evidence: mapEvidence(bundle),
178
101
  supportingContext,
179
102
  metadata: {
180
103
  scanId: ctx.scanId,
@@ -0,0 +1,5 @@
1
+ import type { Assessment, AssessmentPresentationPublic } from "../../assessmentSchema.js";
2
+ import type { ScanResult } from "../../types.js";
3
+ import type { PresentationProfile } from "./presentationProfile.js";
4
+ export declare function buildProfileFromAssessment(assessment: Assessment, presentation: AssessmentPresentationPublic, result: ScanResult): PresentationProfile;
5
+ //# sourceMappingURL=buildProfileFromAssessment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildProfileFromAssessment.d.ts","sourceRoot":"","sources":["../../../src/presentation/profile/buildProfileFromAssessment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC7B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAwCpE,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,4BAA4B,EAC1C,MAAM,EAAE,UAAU,GACjB,mBAAmB,CAuBrB"}
@@ -0,0 +1,58 @@
1
+ import { narrativeIntensityToDensity } from "../../assessmentSchema.js";
2
+ import { safeParseRepoIntelligence } from "../../repoIntelligenceSchema.js";
3
+ import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
4
+ import { collectVerificationFocus } from "../../assessmentProjection.js";
5
+ function buildInterpretation(assessment, presentation, result) {
6
+ const verificationLabels = collectVerificationFocus(result);
7
+ const anchorPackage = result.changedPackages?.[0] ?? null;
8
+ const suppressRuntimeNarrative = presentation.reachVisibility === "hidden";
9
+ let intent = "unknown_upgrade";
10
+ if (assessment.primaryConcern === "insufficient_evidence") {
11
+ intent = "limited_context";
12
+ }
13
+ else if (assessment.changeClasses.includes("tooling_maintenance")) {
14
+ intent = "tooling_patch";
15
+ }
16
+ else if (assessment.primaryConcern === "confirmed_runtime_usage") {
17
+ if (assessment.factors.includes("queue_infrastructure")) {
18
+ intent = "queue_runtime_upgrade";
19
+ }
20
+ else if (assessment.factors.includes("auth_infrastructure")) {
21
+ intent = "auth_runtime_upgrade";
22
+ }
23
+ else {
24
+ intent = "runtime_upgrade";
25
+ }
26
+ }
27
+ return {
28
+ intent,
29
+ anchorPackage,
30
+ suppressRuntimeNarrative,
31
+ allowRuntimeNarrative: !suppressRuntimeNarrative,
32
+ showLimitedEvidence: assessment.confidence === "low",
33
+ expectedImpactKey: null,
34
+ verificationLabels,
35
+ runtimeSurfaceLabel: null,
36
+ runtimePackages: [],
37
+ };
38
+ }
39
+ export function buildProfileFromAssessment(assessment, presentation, result) {
40
+ const riParse = safeParseRepoIntelligence(result.repoIntelligence);
41
+ const priority = riParse.ok && (result.changedPackages?.length ?? 0) > 0
42
+ ? "pr_intelligence"
43
+ : "limited";
44
+ const degradedMessage = assessment.primaryConcern === "insufficient_evidence" ||
45
+ assessment.confidence === "low"
46
+ ? scanSurfaceCopy.presentation.limitedContextMessage
47
+ : priority === "limited"
48
+ ? scanSurfaceCopy.presentation.limitedContextMessage
49
+ : undefined;
50
+ return {
51
+ status: assessment.posture,
52
+ density: narrativeIntensityToDensity(presentation.narrativeIntensity),
53
+ confidence: assessment.confidence,
54
+ priority,
55
+ degradedMessage,
56
+ interpretation: buildInterpretation(assessment, presentation, result),
57
+ };
58
+ }
@@ -0,0 +1,4 @@
1
+ import type { AssessmentPresentationFields } from "./dto/assessmentPresentationFields.js";
2
+ import type { ScanPresentationBundle } from "./orchestration/scanPresentationBundle.js";
3
+ export declare function projectAssessmentFields(bundle: ScanPresentationBundle): AssessmentPresentationFields;
4
+ //# sourceMappingURL=projectAssessmentFields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectAssessmentFields.d.ts","sourceRoot":"","sources":["../../src/presentation/projectAssessmentFields.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AAC1F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAExF,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,sBAAsB,GAC7B,4BAA4B,CAW9B"}
@@ -0,0 +1,13 @@
1
+ import { collectVerificationFocus, projectReasoningLines, } from "../assessmentProjection.js";
2
+ export function projectAssessmentFields(bundle) {
3
+ const { assessment, presentation, result } = bundle;
4
+ return {
5
+ posture: assessment.posture,
6
+ primaryConcern: assessment.primaryConcern,
7
+ factors: [...assessment.factors],
8
+ reasoning: projectReasoningLines(result),
9
+ verificationFocus: collectVerificationFocus(result),
10
+ reachVisibility: presentation.reachVisibility,
11
+ narrativeIntensity: presentation.narrativeIntensity,
12
+ };
13
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=surfaceParity.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surfaceParity.test.d.ts","sourceRoot":"","sources":["../../src/presentation/surfaceParity.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildScanPresentationBundle } from "./orchestration/buildScanPresentationBundle.js";
3
+ import { presentDashboardCard } from "./presenters/presentDashboardCard.js";
4
+ import { presentScanDetails } from "./presenters/presentScanDetails.js";
5
+ import { presentGitHubCheckRun } from "./presenters/presentGitHubCheckRun.js";
6
+ import { presentGitHubPrComment } from "./presenters/presentGitHubPrComment.js";
7
+ import { renderGitHubCheckRunMarkdown } from "./render/renderGitHubCheckRunMarkdown.js";
8
+ import { renderGitHubPrCommentMarkdown } from "./render/renderGitHubPrCommentMarkdown.js";
9
+ import { scanResultBullmq, scanResultEslint, scanResultFastifyRuntime, scanResultMixedTypescriptFastify, scanResultPrettier, scanResultTypescriptPatch, scanResultVitest, } from "./fixtures/presentationPersonaFixtures.js";
10
+ import { projectAssessmentFields } from "./projectAssessmentFields.js";
11
+ const SCAN_ID = "22222222-2222-4222-8222-222222222222";
12
+ const ORIGIN = "https://app.example.com";
13
+ const validationPersonas = [
14
+ { name: "PR #26 mixed", result: scanResultMixedTypescriptFastify },
15
+ { name: "PR #27 fastify", result: scanResultFastifyRuntime },
16
+ { name: "PR #28 typescript", result: scanResultTypescriptPatch },
17
+ { name: "PR #29 prettier", result: scanResultPrettier },
18
+ { name: "PR #30 bullmq", result: scanResultBullmq },
19
+ { name: "eslint", result: scanResultEslint },
20
+ { name: "vitest", result: scanResultVitest },
21
+ ];
22
+ function surfacesFor(result) {
23
+ const bundle = buildScanPresentationBundle({
24
+ result,
25
+ pipelineStatus: "done",
26
+ });
27
+ const fields = projectAssessmentFields(bundle);
28
+ const card = presentDashboardCard(bundle);
29
+ const details = presentScanDetails(bundle, { scanId: SCAN_ID });
30
+ const check = presentGitHubCheckRun(bundle, {
31
+ scanId: SCAN_ID,
32
+ webAppOrigin: ORIGIN,
33
+ });
34
+ const comment = presentGitHubPrComment(bundle);
35
+ const checkMd = renderGitHubCheckRunMarkdown(check);
36
+ const commentMd = renderGitHubPrCommentMarkdown(comment);
37
+ return { bundle, fields, card, details, check, comment, checkMd, commentMd };
38
+ }
39
+ describe("surfaceParity guardrail", () => {
40
+ for (const persona of validationPersonas) {
41
+ it(`${persona.name}: posture and assessment fields match across surfaces`, () => {
42
+ const s = surfacesFor(persona.result);
43
+ expect(s.card.posture).toBe(s.fields.posture);
44
+ expect(s.details.posture).toBe(s.fields.posture);
45
+ expect(s.check.posture).toBe(s.fields.posture);
46
+ expect(s.comment.posture).toBe(s.fields.posture);
47
+ expect(s.card.verdict?.posture).toBe(s.fields.posture);
48
+ expect(s.card.primaryConcern).toBe(s.fields.primaryConcern);
49
+ expect(s.details.primaryConcern).toBe(s.fields.primaryConcern);
50
+ expect(s.check.primaryConcern).toBe(s.fields.primaryConcern);
51
+ expect(s.comment.primaryConcern).toBe(s.fields.primaryConcern);
52
+ expect(s.card.narrativeIntensity).toBe(s.fields.narrativeIntensity);
53
+ expect(s.details.narrativeIntensity).toBe(s.fields.narrativeIntensity);
54
+ expect(s.check.narrativeIntensity).toBe(s.fields.narrativeIntensity);
55
+ expect(s.comment.narrativeIntensity).toBe(s.fields.narrativeIntensity);
56
+ expect(s.card.reachVisibility).toBe(s.fields.reachVisibility);
57
+ expect(s.details.reachVisibility).toBe(s.fields.reachVisibility);
58
+ expect(s.check.reachVisibility).toBe(s.fields.reachVisibility);
59
+ expect(s.comment.reachVisibility).toBe(s.fields.reachVisibility);
60
+ expect(s.card.reasoning).toEqual(s.fields.reasoning);
61
+ expect(s.details.reasoning).toEqual(s.fields.reasoning);
62
+ expect(s.check.reasoning).toEqual(s.fields.reasoning);
63
+ expect(s.comment.reasoning).toEqual(s.fields.reasoning);
64
+ expect(s.card.verificationFocus).toEqual(s.fields.verificationFocus);
65
+ expect(s.details.verificationFocus).toEqual(s.fields.verificationFocus);
66
+ expect(s.check.verificationFocus).toEqual(s.fields.verificationFocus);
67
+ expect(s.comment.verificationFocus).toEqual(s.fields.verificationFocus);
68
+ });
69
+ it(`${persona.name}: headline matches across card and details`, () => {
70
+ const s = surfacesFor(persona.result);
71
+ expect(s.details.hero.headline).toBe(s.card.headline);
72
+ });
73
+ }
74
+ });
@@ -2,7 +2,7 @@
2
2
  * Canonical marketing / category copy. Operational scan strings stay in scanSurfaceCopy.
3
3
  */
4
4
  export declare const productMessaging: {
5
- readonly formalDefinition: "MergeSignal analyzes dependency upgrades, identifies affected application flows, and provides reviewer guidance before merge.";
5
+ readonly formalDefinition: "MergeSignal analyzes dependency upgrades in context, inspects relevant code paths, and provides focused reviewer guidance before merge - without requiring a full repository scan.";
6
6
  readonly hero: {
7
7
  readonly kicker: "Merge-decision intelligence for dependency upgrades";
8
8
  readonly h1: "See what a dependency upgrade can break.";
@@ -68,10 +68,10 @@ export declare const productMessaging: {
68
68
  readonly dashboardPrCard: {
69
69
  readonly intro: "The repository dashboard is a runtime-aware review surface for open upgrade pull requests: triage by merge recommendation, then read operational context on each card.";
70
70
  readonly posture: `Posture indicates the merge recommendation (${string}, ${string}, ${string}).`;
71
- readonly exposure: "Exposure indicates how broadly the upgrade touches runtime-relevant application areas.";
72
- readonly exposureCategoriesLead: "On each scanned card, exposure reads as one of:";
71
+ readonly exposure: "Reach indicates how prominently affected application paths are surfaced for this upgrade.";
72
+ readonly exposureCategoriesLead: "Risk index bands (supporting context) read as one of:";
73
73
  readonly exposureCategories: readonly string[];
74
- readonly cardBody: "Below the verdict, scanned cards lead with the changed package, runtime surface, reachability, blast radius, and affected areas when code intelligence is available; repository-wide graph signals appear only as supporting context.";
74
+ readonly cardBody: "Below the verdict, scanned cards lead with the changed package, affected areas, verification checks, and blast radius when code intelligence is available - focused on this PR, not the whole repository.";
75
75
  };
76
76
  };
77
77
  /** Illustrative scan-card example for homepage proof band and getting-started. */
@@ -1 +1 @@
1
- {"version":3,"file":"productMessaging.d.ts","sourceRoot":"","sources":["../src/productMessaging.ts"],"names":[],"mappings":"AAGA;;GAEG;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAmF3B,6FAA6F;;;;;;;;;CAYrF,CAAC;AAEX,kFAAkF;AAClF,eAAO,MAAM,gBAAgB;;;;CAOnB,CAAC;AAEX,iEAAiE;AACjE,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAOnD"}
1
+ {"version":3,"file":"productMessaging.d.ts","sourceRoot":"","sources":["../src/productMessaging.ts"],"names":[],"mappings":"AAGA;;GAEG;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAmF3B,6FAA6F;;;;;;;;;CAarF,CAAC;AAEX,kFAAkF;AAClF,eAAO,MAAM,gBAAgB;;;;CAOnB,CAAC;AAEX,iEAAiE;AACjE,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAOnD"}
@@ -4,7 +4,7 @@ import { MERGE_POSTURE_LABEL } from "./riskVocabulary.js";
4
4
  * Canonical marketing / category copy. Operational scan strings stay in scanSurfaceCopy.
5
5
  */
6
6
  export const productMessaging = {
7
- formalDefinition: "MergeSignal analyzes dependency upgrades, identifies affected application flows, and provides reviewer guidance before merge.",
7
+ formalDefinition: "MergeSignal analyzes dependency upgrades in context, inspects relevant code paths, and provides focused reviewer guidance before merge - without requiring a full repository scan.",
8
8
  hero: {
9
9
  kicker: "Merge-decision intelligence for dependency upgrades",
10
10
  h1: "See what a dependency upgrade can break.",
@@ -83,10 +83,10 @@ export const productMessaging = {
83
83
  dashboardPrCard: {
84
84
  intro: "The repository dashboard is a runtime-aware review surface for open upgrade pull requests: triage by merge recommendation, then read operational context on each card.",
85
85
  posture: `Posture indicates the merge recommendation (${MERGE_POSTURE_LABEL.safe}, ${MERGE_POSTURE_LABEL.needs_review}, ${MERGE_POSTURE_LABEL.risky}).`,
86
- exposure: "Exposure indicates how broadly the upgrade touches runtime-relevant application areas.",
87
- exposureCategoriesLead: "On each scanned card, exposure reads as one of:",
86
+ exposure: "Reach indicates how prominently affected application paths are surfaced for this upgrade.",
87
+ exposureCategoriesLead: "Risk index bands (supporting context) read as one of:",
88
88
  exposureCategories: CARD_EXPOSURE_CATEGORY_LABELS,
89
- cardBody: "Below the verdict, scanned cards lead with the changed package, runtime surface, reachability, blast radius, and affected areas when code intelligence is available; repository-wide graph signals appear only as supporting context.",
89
+ cardBody: "Below the verdict, scanned cards lead with the changed package, affected areas, verification checks, and blast radius when code intelligence is available - focused on this PR, not the whole repository.",
90
90
  },
91
91
  };
92
92
  /** Illustrative scan-card example for homepage proof band and getting-started. */
@@ -71,8 +71,8 @@ describe("productMessaging", () => {
71
71
  d.cardBody,
72
72
  ].join(" ");
73
73
  expect(text).toContain("merge recommendation");
74
- expect(text).toContain("runtime-relevant");
75
- expect(text).not.toMatch(/risk index|total score|0-100|formula|weight/i);
74
+ expect(text).toContain("affected application paths");
75
+ expect(text).not.toMatch(/total score|0-100|formula|weight/i);
76
76
  expect(d.exposureCategories).toHaveLength(5);
77
77
  expect(d.exposureCategories).toContain("Elevated exposure");
78
78
  });
@@ -35,13 +35,13 @@ export declare const repoIntelligenceWireSchema: z.ZodObject<{
35
35
  }, z.core.$strip>;
36
36
  areas: z.ZodOptional<z.ZodArray<z.ZodString>>;
37
37
  dependencyClass: z.ZodOptional<z.ZodEnum<{
38
+ format: "format";
38
39
  unknown: "unknown";
39
40
  runtime: "runtime";
40
41
  build: "build";
41
42
  test: "test";
42
43
  tooling: "tooling";
43
44
  lint: "lint";
44
- format: "format";
45
45
  ci: "ci";
46
46
  }>>;
47
47
  packageRole: z.ZodOptional<z.ZodEnum<{
@@ -64,9 +64,9 @@ export declare const repoIntelligenceWireSchema: z.ZodObject<{
64
64
  development: "development";
65
65
  }>>;
66
66
  runtimeImpact: z.ZodOptional<z.ZodEnum<{
67
+ none: "none";
67
68
  unknown: "unknown";
68
69
  confirmed: "confirmed";
69
- none: "none";
70
70
  possible: "possible";
71
71
  }>>;
72
72
  expectedImpact: z.ZodOptional<z.ZodEnum<{
@@ -0,0 +1,15 @@
1
+ export type ScanPipelineStatus = "queued" | "running" | "done" | "failed";
2
+ export type PipelineCompletionEvidence = {
3
+ scannedAt?: string | null;
4
+ decision?: string | null;
5
+ totalScore?: number | null;
6
+ hasResult?: boolean;
7
+ };
8
+ /** Scan completion signals — decision/totalScore/result are only set after analysis. */
9
+ export declare function hasScanCompletionEvidence(evidence: PipelineCompletionEvidence): boolean;
10
+ /**
11
+ * Resolve wire status using completion evidence (decision, totalScore, result).
12
+ * Handles races where denormalized columns are written before status flips to done.
13
+ */
14
+ export declare function resolvePipelineStatus(status: string | undefined, evidence: PipelineCompletionEvidence): ScanPipelineStatus;
15
+ //# sourceMappingURL=resolvePipelineStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolvePipelineStatus.d.ts","sourceRoot":"","sources":["../src/resolvePipelineStatus.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE1E,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"}
@@ -0,0 +1,37 @@
1
+ function normalizePipelineStatus(status) {
2
+ const st = String(status ?? "")
3
+ .trim()
4
+ .toLowerCase();
5
+ if (st === "queued" || st === "running" || st === "done" || st === "failed") {
6
+ return st;
7
+ }
8
+ return "failed";
9
+ }
10
+ function isMergePostureDecision(decision) {
11
+ return (decision === "safe" || decision === "needs_review" || decision === "risky");
12
+ }
13
+ /** Scan completion signals — decision/totalScore/result are only set after analysis. */
14
+ export function hasScanCompletionEvidence(evidence) {
15
+ if (isMergePostureDecision(evidence.decision))
16
+ return true;
17
+ if (evidence.hasResult === true)
18
+ return true;
19
+ if (evidence.totalScore != null && Number.isFinite(evidence.totalScore)) {
20
+ return true;
21
+ }
22
+ if (evidence.scannedAt?.trim())
23
+ return true;
24
+ return false;
25
+ }
26
+ /**
27
+ * Resolve wire status using completion evidence (decision, totalScore, result).
28
+ * Handles races where denormalized columns are written before status flips to done.
29
+ */
30
+ export function resolvePipelineStatus(status, evidence) {
31
+ const normalized = normalizePipelineStatus(status);
32
+ if (normalized === "done" || normalized === "failed")
33
+ return normalized;
34
+ if (hasScanCompletionEvidence(evidence))
35
+ return "done";
36
+ return normalized;
37
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Dashboard PR card lifecycle — distinct from pipeline status.
3
+ *
4
+ * Pipeline / infrastructure: `scanning`, `analysis_failed`, `surfaces_incomplete`
5
+ * Product / assessment: `ready`, `stale`
6
+ * Absence: `not_scanned`
7
+ */
8
+ export type ScanCardPresentationState = "not_scanned" | "scanning" | "analysis_failed" | "surfaces_incomplete" | "ready" | "stale";
9
+ export declare function staleScanSubline(): string;
10
+ //# sourceMappingURL=scanCardPresentationState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanCardPresentationState.d.ts","sourceRoot":"","sources":["../src/scanCardPresentationState.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GACjC,aAAa,GACb,UAAU,GACV,iBAAiB,GACjB,qBAAqB,GACrB,OAAO,GACP,OAAO,CAAC;AAIZ,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC"}
@@ -0,0 +1,4 @@
1
+ const STALE_SUBLINE = "Based on earlier commit";
2
+ export function staleScanSubline() {
3
+ return STALE_SUBLINE;
4
+ }