@mergesignal/shared 0.12.0 → 0.12.2

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 (55) hide show
  1. package/dist/deriveScanNarrative.d.ts.map +1 -1
  2. package/dist/deriveScanNarrative.js +4 -0
  3. package/dist/formatInsight.d.ts +3 -3
  4. package/dist/formatInsight.d.ts.map +1 -1
  5. package/dist/formatInsight.js +0 -9
  6. package/dist/index.d.ts +0 -3
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +0 -3
  9. package/dist/prRiskBand.d.ts +7 -0
  10. package/dist/prRiskBand.d.ts.map +1 -1
  11. package/dist/prRiskBand.js +12 -0
  12. package/dist/prRiskBand.test.js +12 -1
  13. package/dist/presentation/abi3Expression.test.js +46 -0
  14. package/dist/presentation/bundleAccessorParity.test.d.ts +9 -0
  15. package/dist/presentation/bundleAccessorParity.test.d.ts.map +1 -0
  16. package/dist/presentation/bundleAccessorParity.test.js +126 -0
  17. package/dist/presentation/cliParity.test.d.ts +2 -0
  18. package/dist/presentation/cliParity.test.d.ts.map +1 -0
  19. package/dist/presentation/cliParity.test.js +49 -0
  20. package/dist/presentation/compose/narrativeCompose.d.ts +0 -4
  21. package/dist/presentation/compose/narrativeCompose.d.ts.map +1 -1
  22. package/dist/presentation/compose/narrativeCompose.js +0 -20
  23. package/dist/presentation/decisionSurface.test.d.ts +2 -0
  24. package/dist/presentation/decisionSurface.test.d.ts.map +1 -0
  25. package/dist/presentation/decisionSurface.test.js +290 -0
  26. package/dist/presentation/dto/assessmentPresentationFields.d.ts +4 -0
  27. package/dist/presentation/dto/assessmentPresentationFields.d.ts.map +1 -1
  28. package/dist/presentation/index.d.ts +1 -1
  29. package/dist/presentation/index.d.ts.map +1 -1
  30. package/dist/presentation/index.js +1 -1
  31. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts +1 -0
  32. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts.map +1 -1
  33. package/dist/presentation/orchestration/buildScanPresentationBundle.js +4 -1
  34. package/dist/presentation/orchestration/scanPresentationBundle.d.ts +4 -4
  35. package/dist/presentation/orchestration/scanPresentationBundle.d.ts.map +1 -1
  36. package/dist/presentation/presenters/presentCliScanSummary.d.ts.map +1 -1
  37. package/dist/presentation/presenters/presentCliScanSummary.js +9 -7
  38. package/dist/presentation/presenters/presentGitHubCheckRun.d.ts.map +1 -1
  39. package/dist/presentation/presenters/presentGitHubCheckRun.js +11 -12
  40. package/dist/presentation/presenters/presentGitHubPrComment.d.ts.map +1 -1
  41. package/dist/presentation/presenters/presentGitHubPrComment.js +11 -10
  42. package/dist/presentation/presenters/presentScanDetails.d.ts.map +1 -1
  43. package/dist/presentation/presenters/presentScanDetails.js +8 -11
  44. package/dist/presentation/projectAssessmentFields.d.ts.map +1 -1
  45. package/dist/presentation/projectAssessmentFields.js +6 -0
  46. package/dist/presentation/surfaceParity.test.js +8 -0
  47. package/dist/scanResultSchema.d.ts +158 -0
  48. package/dist/scanResultSchema.d.ts.map +1 -1
  49. package/package.json +1 -1
  50. package/dist/scanDetailViewModelTypes.d.ts +0 -159
  51. package/dist/scanDetailViewModelTypes.d.ts.map +0 -1
  52. package/dist/scanDetailViewModelTypes.js +0 -4
  53. package/dist/truncateCardSummary.d.ts +0 -5
  54. package/dist/truncateCardSummary.d.ts.map +0 -1
  55. package/dist/truncateCardSummary.js +0 -23
@@ -0,0 +1,290 @@
1
+ import { extractAuthoredCommunication } from "@mergesignal/contracts";
2
+ import { describe, expect, it } from "vitest";
3
+ import { minimalReviewFocalPoint, reachScopeFor, verificationScopeFor, withAssessmentScope, } from "../fixtures/assessmentScopeFixtures.js";
4
+ import { buildScanPresentationBundle } from "./orchestration/buildScanPresentationBundle.js";
5
+ // ─── Vocabulary firewall ──────────────────────────────────────────────────────
6
+ /** Engine vocabulary that must never appear in any developer-facing authored string. */
7
+ const ENGINE_VOCABULARY = [
8
+ "constraint_satisfiability",
9
+ "lockfile_integrity",
10
+ "type_surface_compatibility",
11
+ "export_resolution",
12
+ "peer_dependency",
13
+ "engine_constraint",
14
+ "no_impact_proven",
15
+ "proof_pass",
16
+ "bounded_verify",
17
+ "representative_precision_only",
18
+ "architectural_precision_only",
19
+ "clearanceEnvelope",
20
+ "coverageClass",
21
+ "precisionLevel",
22
+ "proofArtifact",
23
+ "packageOutcome",
24
+ "qualityClass",
25
+ "qualityBasis",
26
+ "proofFingerprint",
27
+ "observationId",
28
+ "dimension_verification",
29
+ ];
30
+ // ─── Fixture helpers ──────────────────────────────────────────────────────────
31
+ function scanResultWith(assessment) {
32
+ return {
33
+ totalScore: 70,
34
+ layerScores: {
35
+ security: 70,
36
+ maintainability: 70,
37
+ ecosystem: 70,
38
+ upgradeImpact: 70,
39
+ },
40
+ findings: [],
41
+ generatedAt: "2026-01-01T00:00:00Z",
42
+ changedPackages: assessment.reviewFocalPoint.anchors,
43
+ methodologyVersion: "mergesignal-engine/test",
44
+ assessment,
45
+ decision: {
46
+ recommendation: "needs_review",
47
+ confidence: "medium",
48
+ reasoning: [],
49
+ },
50
+ insights: [],
51
+ };
52
+ }
53
+ function assessmentWithTrustLine() {
54
+ return withAssessmentScope({
55
+ posture: "safe",
56
+ confidence: "high",
57
+ primaryConcern: null,
58
+ concerns: [],
59
+ factors: [],
60
+ changeClasses: [],
61
+ presentation: {
62
+ narrativeIntensity: "standard",
63
+ reachVisibility: "contextual",
64
+ verificationIntensity: "none",
65
+ insightEmissionFloor: "full",
66
+ reportMode: "high_signal_pr",
67
+ },
68
+ reasoning: [
69
+ "All peer dependency constraints verified — no runtime breakage confirmed.",
70
+ "Export compatibility proven unused in this repository.",
71
+ ],
72
+ confidenceRationale: "Confidence is high: all assessed dimensions were verified by deterministic proof.",
73
+ }, {
74
+ reviewFocalPoint: minimalReviewFocalPoint(["express"]),
75
+ reachScope: reachScopeFor(["express"]),
76
+ verificationScope: verificationScopeFor(["express"]),
77
+ });
78
+ }
79
+ function assessmentWithElectionSummary() {
80
+ return withAssessmentScope({
81
+ posture: "needs_review",
82
+ confidence: "medium",
83
+ primaryConcern: "confirmed_runtime_usage",
84
+ concerns: [
85
+ {
86
+ kind: "confirmed_runtime_usage",
87
+ rank: 1,
88
+ packages: ["fastify"],
89
+ evidenceRefs: [],
90
+ context: "Runtime usage: `fastify` — used in HTTP request handling.",
91
+ },
92
+ ],
93
+ factors: ["confirmed_runtime_usage"],
94
+ changeClasses: ["runtime_upgrade"],
95
+ presentation: {
96
+ narrativeIntensity: "standard",
97
+ reachVisibility: "contextual",
98
+ verificationIntensity: "required",
99
+ insightEmissionFloor: "full",
100
+ reportMode: "high_signal_pr",
101
+ },
102
+ reasoning: [
103
+ "fastify is used in HTTP request handling in this repository.",
104
+ ],
105
+ confidenceRationale: "Confidence is medium: runtime usage was observed but proof coverage is representative.",
106
+ notAffectedLine: null,
107
+ resolutionLine: null,
108
+ }, {
109
+ reviewFocalPoint: {
110
+ episodeShape: "single_anchor",
111
+ anchors: ["fastify"],
112
+ electionSummary: "`fastify` was selected as the review anchor over `fastify-plugin` due to higher runtime reach.",
113
+ election: {
114
+ grounding: [
115
+ {
116
+ packageName: "fastify",
117
+ reason: "highest_runtime_reach",
118
+ decidedBy: "reach",
119
+ evidenceRefs: [],
120
+ },
121
+ ],
122
+ exclusions: [
123
+ {
124
+ packageName: "fastify-plugin",
125
+ reason: "lower_runtime_reach",
126
+ lostOn: "reach",
127
+ evidenceRefs: [],
128
+ },
129
+ ],
130
+ },
131
+ },
132
+ reachScope: reachScopeFor(["fastify"]),
133
+ verificationScope: verificationScopeFor(["fastify"]),
134
+ });
135
+ }
136
+ function assessmentUncontestedElection() {
137
+ return withAssessmentScope({
138
+ posture: "needs_review",
139
+ confidence: "medium",
140
+ primaryConcern: "confirmed_runtime_usage",
141
+ concerns: [],
142
+ factors: ["confirmed_runtime_usage"],
143
+ changeClasses: ["runtime_upgrade"],
144
+ presentation: {
145
+ narrativeIntensity: "standard",
146
+ reachVisibility: "contextual",
147
+ verificationIntensity: "required",
148
+ insightEmissionFloor: "full",
149
+ reportMode: "high_signal_pr",
150
+ },
151
+ reasoning: ["express is used in HTTP request handling."],
152
+ confidenceRationale: "Confidence is high: runtime usage was directly observed.",
153
+ }, {
154
+ reviewFocalPoint: minimalReviewFocalPoint(["express"]),
155
+ reachScope: reachScopeFor(["express"]),
156
+ verificationScope: verificationScopeFor(["express"]),
157
+ });
158
+ }
159
+ function assessmentWithQ8Q9() {
160
+ return withAssessmentScope({
161
+ posture: "needs_review",
162
+ confidence: "medium",
163
+ primaryConcern: "confirmed_runtime_usage",
164
+ concerns: [],
165
+ factors: ["confirmed_runtime_usage"],
166
+ changeClasses: ["runtime_upgrade"],
167
+ outcome: "bounded_verify",
168
+ presentation: {
169
+ narrativeIntensity: "standard",
170
+ reachVisibility: "contextual",
171
+ verificationIntensity: "required",
172
+ insightEmissionFloor: "full",
173
+ reportMode: "high_signal_pr",
174
+ },
175
+ reasoning: ["Automation stopped before all dimensions were verified."],
176
+ confidenceRationale: "Confidence is medium: bounded verification is required.",
177
+ notAffectedLine: "No repository code paths are affected by export compatibility — this dimension is outside the review scope.",
178
+ resolutionLine: "A passing automated verification for this dimension would make this deterministic.",
179
+ }, {
180
+ reviewFocalPoint: minimalReviewFocalPoint(["express"]),
181
+ reachScope: reachScopeFor(["express"]),
182
+ verificationScope: {
183
+ packages: ["express"],
184
+ focus: [],
185
+ guidance: ["Verify route handler at `src/server.ts:42`."],
186
+ },
187
+ });
188
+ }
189
+ // ─── V1 bundle transport ──────────────────────────────────────────────────────
190
+ describe("decision surface contract (V1) — bundle transport", () => {
191
+ it("trustLine carries confidenceRationale verbatim from assessment", () => {
192
+ const assessment = assessmentWithTrustLine();
193
+ const bundle = buildScanPresentationBundle({
194
+ result: scanResultWith(assessment),
195
+ pipelineStatus: "done",
196
+ });
197
+ expect(bundle).not.toBeNull();
198
+ expect(bundle.trustLine).toBe(assessment.confidenceRationale);
199
+ });
200
+ it("whyThisPackageLine carries electionSummary verbatim when present", () => {
201
+ const assessment = assessmentWithElectionSummary();
202
+ const bundle = buildScanPresentationBundle({
203
+ result: scanResultWith(assessment),
204
+ pipelineStatus: "done",
205
+ });
206
+ expect(bundle.whyThisPackageLine).toBe(assessment.reviewFocalPoint.electionSummary);
207
+ });
208
+ it("whyThisPackageLine is null when electionSummary is absent", () => {
209
+ const assessment = assessmentUncontestedElection();
210
+ const bundle = buildScanPresentationBundle({
211
+ result: scanResultWith(assessment),
212
+ pipelineStatus: "done",
213
+ });
214
+ expect(bundle.whyThisPackageLine).toBeNull();
215
+ });
216
+ it("reasoningLines and guidanceLines carry assessment arrays verbatim", () => {
217
+ const assessment = assessmentWithQ8Q9();
218
+ const bundle = buildScanPresentationBundle({
219
+ result: scanResultWith(assessment),
220
+ pipelineStatus: "done",
221
+ });
222
+ expect(bundle.reasoningLines).toEqual(assessment.reasoning);
223
+ expect(bundle.guidanceLines).toEqual(assessment.verificationScope.guidance ?? []);
224
+ });
225
+ it("notAffectedLine and resolutionLine survive parse and transport (Q8/Q9)", () => {
226
+ const assessment = assessmentWithQ8Q9();
227
+ const bundle = buildScanPresentationBundle({
228
+ result: scanResultWith(assessment),
229
+ pipelineStatus: "done",
230
+ });
231
+ expect(bundle.notAffectedLine).toBe(assessment.notAffectedLine);
232
+ expect(bundle.resolutionLine).toBe(assessment.resolutionLine);
233
+ });
234
+ it("concernContextLines carries concerns[].context verbatim", () => {
235
+ const assessment = assessmentWithElectionSummary();
236
+ const bundle = buildScanPresentationBundle({
237
+ result: scanResultWith(assessment),
238
+ pipelineStatus: "done",
239
+ });
240
+ expect(bundle.concernContextLines).toEqual([
241
+ "Runtime usage: `fastify` — used in HTTP request handling.",
242
+ ]);
243
+ });
244
+ it("public bundle accessors match extractAuthoredCommunication", () => {
245
+ const assessment = assessmentWithQ8Q9();
246
+ const normalized = assessment;
247
+ const expected = extractAuthoredCommunication(normalized);
248
+ const bundle = buildScanPresentationBundle({
249
+ result: scanResultWith(assessment),
250
+ pipelineStatus: "done",
251
+ });
252
+ expect(bundle.trustLine).toBe(expected.trustLine);
253
+ expect(bundle.whyThisPackageLine).toBe(expected.whyThisPackageLine);
254
+ expect(bundle.notAffectedLine).toBe(expected.notAffectedLine);
255
+ expect(bundle.resolutionLine).toBe(expected.resolutionLine);
256
+ expect(bundle.reasoningLines).toEqual(expected.reasoningLines);
257
+ expect(bundle.guidanceLines).toEqual(expected.guidanceLines);
258
+ expect(bundle.concernContextLines).toEqual(expected.concernContextLines);
259
+ });
260
+ });
261
+ // ─── Vocabulary firewall ──────────────────────────────────────────────────────
262
+ describe("decision surface contract (V1) — vocabulary firewall on accessors", () => {
263
+ function assertNoEngineVocabulary(strings, context) {
264
+ for (const token of ENGINE_VOCABULARY) {
265
+ for (const str of strings) {
266
+ if (!str)
267
+ continue;
268
+ expect(str, `${context}: must not contain engine token "${token}"`).not.toContain(token);
269
+ }
270
+ }
271
+ }
272
+ it("bundle accessors carry no engine vocabulary", () => {
273
+ const assessment = assessmentWithQ8Q9();
274
+ const bundle = buildScanPresentationBundle({
275
+ result: scanResultWith(assessment),
276
+ pipelineStatus: "done",
277
+ });
278
+ assertNoEngineVocabulary([
279
+ bundle.trustLine,
280
+ bundle.whyThisPackageLine,
281
+ bundle.notAffectedLine,
282
+ bundle.resolutionLine,
283
+ ...bundle.reasoningLines,
284
+ ...bundle.guidanceLines,
285
+ ...bundle.concernContextLines,
286
+ ], "bundle accessors");
287
+ });
288
+ });
289
+ // V3 surface delivery (Dashboard, Scan Details) is intentionally excluded from V1 graduation.
290
+ // See presentDashboardCard / presentScanDetails adoption in V3 Surface Adoption roadmap.
@@ -10,5 +10,9 @@ export type AssessmentPresentationFields = {
10
10
  verificationChannel: VerificationChannel;
11
11
  reachVisibility: ReachVisibility;
12
12
  narrativeIntensity: NarrativeIntensity;
13
+ /** ABI 3 expression — optional confidence explanation. */
14
+ confidenceRationale?: string;
15
+ /** ABI 3 expression — optional focal-election narrative. */
16
+ electionSummary?: string;
13
17
  };
14
18
  //# sourceMappingURL=assessmentPresentationFields.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assessmentPresentationFields.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/assessmentPresentationFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EAEhB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,uEAAuE;AACvE,MAAM,MAAM,4BAA4B,GAAG;IACzC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,OAAO,CAAC;IAC3C,cAAc,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACxC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,EAAE,kBAAkB,CAAC;CACxC,CAAC"}
1
+ {"version":3,"file":"assessmentPresentationFields.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/assessmentPresentationFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EAEhB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,uEAAuE;AACvE,MAAM,MAAM,4BAA4B,GAAG;IACzC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,OAAO,CAAC;IAC3C,cAAc,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACxC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,eAAe,EAAE,eAAe,CAAC;IACjC,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,0DAA0D;IAC1D,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC"}
@@ -24,5 +24,5 @@ export { renderGitHubCheckRunMarkdown } from "./render/renderGitHubCheckRunMarkd
24
24
  export { buildGitHubCheckRunOutput, resolveWebAppOrigin, type GitHubCheckRunConclusion, type GitHubCheckRunOutput, type BuildGitHubCheckRunOutputOptions, } from "./buildGitHubCheckRunOutput.js";
25
25
  export { renderGitHubPrCommentMarkdown } from "./render/renderGitHubPrCommentMarkdown.js";
26
26
  export { renderCliScanSummaryText } from "./render/renderCliScanSummaryText.js";
27
- export { buildNarrativeChannels, projectCompactKeyPoints, composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeEvidenceRows, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
27
+ export { buildNarrativeChannels, projectCompactKeyPoints, composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
28
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,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,EAC5B,sCAAsC,GACvC,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,EACL,yBAAyB,EACzB,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,gCAAgC,GACtC,MAAM,gCAAgC,CAAC;AACxC,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"}
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,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,EAC5B,sCAAsC,GACvC,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,EACL,yBAAyB,EACzB,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,gCAAgC,GACtC,MAAM,gCAAgC,CAAC;AACxC,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,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,+BAA+B,CAAC"}
@@ -10,4 +10,4 @@ export { renderGitHubCheckRunMarkdown } from "./render/renderGitHubCheckRunMarkd
10
10
  export { buildGitHubCheckRunOutput, resolveWebAppOrigin, } from "./buildGitHubCheckRunOutput.js";
11
11
  export { renderGitHubPrCommentMarkdown } from "./render/renderGitHubPrCommentMarkdown.js";
12
12
  export { renderCliScanSummaryText } from "./render/renderCliScanSummaryText.js";
13
- export { buildNarrativeChannels, projectCompactKeyPoints, composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeEvidenceRows, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
13
+ export { buildNarrativeChannels, projectCompactKeyPoints, composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
@@ -9,6 +9,7 @@ export type BuildScanPresentationBundleInput = {
9
9
  /**
10
10
  * Mandatory orchestration entrypoint for completed-scan presentation.
11
11
  * Assessment is the sole authority; profile is a thin projection.
12
+ * Consumes only the public ScanResult contract — see docs/engineering/presentation-ownership.md.
12
13
  */
13
14
  export declare function buildScanPresentationBundle(input: BuildScanPresentationBundleInput): ScanPresentationBundle | null;
14
15
  //# sourceMappingURL=buildScanPresentationBundle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildScanPresentationBundle.d.ts","sourceRoot":"","sources":["../../../src/presentation/orchestration/buildScanPresentationBundle.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE1E,MAAM,MAAM,gCAAgC,GAAG;IAC7C,MAAM,EAAE,UAAU,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,gCAAgC,GACtC,sBAAsB,GAAG,IAAI,CA4B/B"}
1
+ {"version":3,"file":"buildScanPresentationBundle.d.ts","sourceRoot":"","sources":["../../../src/presentation/orchestration/buildScanPresentationBundle.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE1E,MAAM,MAAM,gCAAgC,GAAG;IAC7C,MAAM,EAAE,UAAU,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,gCAAgC,GACtC,sBAAsB,GAAG,IAAI,CA8B/B"}
@@ -1,10 +1,11 @@
1
- import { parseAssessmentOrThrow } from "@mergesignal/contracts";
1
+ import { extractAuthoredCommunication, parseAssessmentOrThrow, } from "@mergesignal/contracts";
2
2
  import { toPublicPresentation } from "../../assessmentPresentationUtils.js";
3
3
  import { deriveScanNarrative } from "../../deriveScanNarrative.js";
4
4
  import { buildProfileFromAssessment } from "../profile/buildProfileFromAssessment.js";
5
5
  /**
6
6
  * Mandatory orchestration entrypoint for completed-scan presentation.
7
7
  * Assessment is the sole authority; profile is a thin projection.
8
+ * Consumes only the public ScanResult contract — see docs/engineering/presentation-ownership.md.
8
9
  */
9
10
  export function buildScanPresentationBundle(input) {
10
11
  if (input.pipelineStatus !== "done" || !input.result)
@@ -22,11 +23,13 @@ export function buildScanPresentationBundle(input) {
22
23
  const presentation = toPublicPresentation(normalized.presentation);
23
24
  const profile = buildProfileFromAssessment(normalized, presentation, input.result);
24
25
  const facts = deriveScanNarrative(input.result);
26
+ const authored = extractAuthoredCommunication(normalized);
25
27
  return {
26
28
  assessment: normalized,
27
29
  presentation,
28
30
  profile,
29
31
  facts,
30
32
  result: input.result,
33
+ ...authored,
31
34
  };
32
35
  }
@@ -1,19 +1,19 @@
1
- import type { Assessment, AssessmentPresentationPublic } from "@mergesignal/contracts";
1
+ import type { Assessment, AssessmentPresentationPublic, AuthoredCommunicationAccessors } from "@mergesignal/contracts";
2
2
  import type { ScanNarrativeFacts } from "../../scanNarrativeFacts.js";
3
3
  import type { ScanResult } from "../../types.js";
4
4
  import type { PresentationProfile } from "../profile/presentationProfile.js";
5
- export type ScanPresentationBundle = {
5
+ export type ScanPresentationBundle = AuthoredCommunicationAccessors & {
6
6
  assessment: Assessment;
7
7
  presentation: AssessmentPresentationPublic;
8
8
  profile: PresentationProfile;
9
9
  /**
10
10
  * Evidence layout only — not an authority for posture, concern, or intensity.
11
- * @deprecated Prefer assessment + repoIntelligence on result for new code.
11
+ * @deprecated Prefer bundle accessors for authored communication; V3 migration only.
12
12
  */
13
13
  facts: ScanNarrativeFacts;
14
14
  /**
15
15
  * Projection-only reference to the raw scan payload.
16
- * Presenters may read fields for formatting but must NOT re-derive interpretation.
16
+ * @deprecated V3 migration only surfaces must use accessors for decision meaning.
17
17
  */
18
18
  result: ScanResult;
19
19
  };
@@ -1 +1 @@
1
- {"version":3,"file":"scanPresentationBundle.d.ts","sourceRoot":"","sources":["../../../src/presentation/orchestration/scanPresentationBundle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC7B,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,MAAM,MAAM,sBAAsB,GAAG;IACnC,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,4BAA4B,CAAC;IAC3C,OAAO,EAAE,mBAAmB,CAAC;IAC7B;;;OAGG;IACH,KAAK,EAAE,kBAAkB,CAAC;IAC1B;;;OAGG;IACH,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC"}
1
+ {"version":3,"file":"scanPresentationBundle.d.ts","sourceRoot":"","sources":["../../../src/presentation/orchestration/scanPresentationBundle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC5B,8BAA8B,EAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,MAAM,MAAM,sBAAsB,GAAG,8BAA8B,GAAG;IACpE,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,4BAA4B,CAAC;IAC3C,OAAO,EAAE,mBAAmB,CAAC;IAC7B;;;OAGG;IACH,KAAK,EAAE,kBAAkB,CAAC;IAC1B;;;OAGG;IACH,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"presentCliScanSummary.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentCliScanSummary.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAGzF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,mBAAmB,CAiCrB"}
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;AAGzF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,mBAAmB,CAmCrB"}
@@ -1,14 +1,16 @@
1
- import { scoreToBandLabel } from "../../prRiskBand.js";
1
+ import { formatPrRiskSummary } from "../../prRiskBand.js";
2
2
  import { resolvePrRiskLayerScores } from "../../prRiskWire.js";
3
- import { buildNarrativeChannels, composeHeadline, composeSubheadline, composeSupportingContext, composeVerificationActions, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
3
+ import { buildNarrativeChannels, composeSubheadline, composeSupportingContext, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
4
4
  import { projectAssessmentFields } from "../projectAssessmentFields.js";
5
5
  export function presentCliScanSummary(bundle, ctx = {}) {
6
6
  const { facts, profile, result } = bundle;
7
+ const channels = buildNarrativeChannels(bundle);
7
8
  const layerScores = resolvePrRiskLayerScores(result);
8
9
  const layerLine = layerScores
9
10
  ? `security=${layerScores.security} maintainability=${layerScores.maintainability} ecosystem=${layerScores.ecosystem} upgradeImpact=${layerScores.upgradeImpact}`
10
11
  : "";
11
- const prRiskScore = facts.riskSignals?.riskIndex ?? facts.riskIndex ?? 0;
12
+ const prRisk = formatPrRiskSummary(facts);
13
+ const prRiskScore = prRisk?.prRiskScore ?? facts.riskIndex ?? 0;
12
14
  return {
13
15
  ...projectAssessmentFields(bundle),
14
16
  header: {
@@ -20,13 +22,13 @@ export function presentCliScanSummary(bundle, ctx = {}) {
20
22
  density: profile.density,
21
23
  confidence: profile.confidence,
22
24
  evidenceContext: evidenceContextFromProfile(bundle),
23
- headline: composeHeadline(bundle),
25
+ headline: channels.headline,
24
26
  subheadline: composeSubheadline(bundle),
25
- keyPoints: projectCompactKeyPoints(buildNarrativeChannels(bundle), 6),
26
- verificationActions: composeVerificationActions(bundle, 5),
27
+ keyPoints: projectCompactKeyPoints(channels, 6),
28
+ verificationActions: channels.verification.slice(0, 5),
27
29
  metrics: {
28
30
  prRiskScore,
29
- prRiskBandLabel: scoreToBandLabel(prRiskScore) ?? undefined,
31
+ prRiskBandLabel: prRisk?.prRiskBandLabel ?? undefined,
30
32
  riskIndex: prRiskScore,
31
33
  layerLine,
32
34
  findingCount: result.findings?.length ?? 0,
@@ -1 +1 @@
1
- {"version":3,"file":"presentGitHubCheckRun.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentGitHubCheckRun.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAGzF,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,CAmD5B"}
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;AAGzF,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,CAkD5B"}
@@ -1,11 +1,11 @@
1
- import { scoreToBandLabel } from "../../prRiskBand.js";
2
- import { buildNarrativeChannels, composeHeadline, composeSubheadline, composeVerificationActions, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
1
+ import { formatPrRiskSummary } from "../../prRiskBand.js";
2
+ import { buildNarrativeChannels, composeSubheadline, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
3
3
  import { projectAssessmentFields } from "../projectAssessmentFields.js";
4
4
  export function presentGitHubCheckRun(bundle, ctx) {
5
- const { profile } = bundle;
6
- const headline = composeHeadline(bundle);
5
+ const { facts, profile } = bundle;
6
+ const channels = buildNarrativeChannels(bundle);
7
7
  const assessmentFields = projectAssessmentFields(bundle);
8
- const actions = composeVerificationActions(bundle, 3);
8
+ const actions = channels.verification.slice(0, 3);
9
9
  const detailsUrl = `${ctx.webAppOrigin.replace(/\/$/, "")}/scan/${ctx.scanId}`;
10
10
  const conclusion = profile.status === "safe"
11
11
  ? "success"
@@ -13,13 +13,12 @@ export function presentGitHubCheckRun(bundle, ctx) {
13
13
  ? "failure"
14
14
  : "neutral";
15
15
  const sections = [];
16
- const prRiskScore = bundle.facts.riskSignals?.riskIndex;
17
- const prRiskBandLabelText = scoreToBandLabel(prRiskScore);
18
- if (prRiskScore != null && prRiskBandLabelText) {
16
+ const prRisk = formatPrRiskSummary(facts);
17
+ if (prRisk) {
19
18
  sections.push({
20
19
  id: "why",
21
20
  title: "PR Risk",
22
- bullets: [`${prRiskScore} (${prRiskBandLabelText})`],
21
+ bullets: [`${prRisk.prRiskScore} (${prRisk.prRiskBandLabel})`],
23
22
  });
24
23
  }
25
24
  // Use assessment.reasoning directly for the Why section (ABI 3).
@@ -27,7 +26,7 @@ export function presentGitHubCheckRun(bundle, ctx) {
27
26
  const reasoningLines = assessmentFields.reasoning.slice(0, 3);
28
27
  const whyBullets = reasoningLines.length > 0
29
28
  ? reasoningLines
30
- : projectCompactKeyPoints(buildNarrativeChannels(bundle), 3);
29
+ : projectCompactKeyPoints(channels, 3);
31
30
  if (whyBullets.length > 0) {
32
31
  sections.push({ id: "why", title: "Why", bullets: whyBullets });
33
32
  }
@@ -41,9 +40,9 @@ export function presentGitHubCheckRun(bundle, ctx) {
41
40
  density: profile.density,
42
41
  confidence: profile.confidence,
43
42
  evidenceContext: evidenceContextFromProfile(bundle),
44
- title: headline,
43
+ title: channels.headline,
45
44
  conclusion,
46
- summaryLead: composeSubheadline(bundle) ?? headline,
45
+ summaryLead: composeSubheadline(bundle) ?? channels.headline,
47
46
  sections,
48
47
  detailsUrl,
49
48
  };
@@ -1 +1 @@
1
- {"version":3,"file":"presentGitHubPrComment.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentGitHubPrComment.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAGzF,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,GAC7B,2BAA2B,CAqC7B"}
1
+ {"version":3,"file":"presentGitHubPrComment.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentGitHubPrComment.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAGzF,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,GAC7B,2BAA2B,CAyC7B"}
@@ -1,5 +1,5 @@
1
1
  import { MERGE_POSTURE_LABEL } from "../../riskVocabulary.js";
2
- import { scoreToBandLabel } from "../../prRiskBand.js";
2
+ import { formatPrRiskSummary } from "../../prRiskBand.js";
3
3
  import { buildNarrativeChannels, evidenceContextFromProfile, } from "../compose/narrativeCompose.js";
4
4
  import { formatChangedPackagesShort } from "../../narrativePresentation.js";
5
5
  import { projectAssessmentFields } from "../projectAssessmentFields.js";
@@ -11,22 +11,23 @@ export function presentGitHubPrComment(bundle) {
11
11
  const changed = formatChangedPackagesShort(facts, 3);
12
12
  if (changed)
13
13
  introLines.push(`Changed: ${changed}`);
14
- const prRiskScore = bundle.facts.riskSignals?.riskIndex;
15
- const prRiskBandLabelText = scoreToBandLabel(prRiskScore);
16
- if (prRiskScore != null && prRiskBandLabelText) {
17
- introLines.push(`PR Risk: ${prRiskScore} (${prRiskBandLabelText})`);
14
+ const prRisk = formatPrRiskSummary(facts);
15
+ if (prRisk) {
16
+ introLines.push(`PR Risk: ${prRisk.prRiskScore} (${prRisk.prRiskBandLabel})`);
17
+ }
18
+ if (assessmentFields.electionSummary) {
19
+ introLines.push(assessmentFields.electionSummary);
18
20
  }
19
- const electionSummary = bundle.assessment.reviewFocalPoint.electionSummary;
20
- if (electionSummary)
21
- introLines.push(electionSummary);
22
21
  for (const line of assessmentFields.reasoning.slice(0, 2)) {
23
22
  introLines.push(line);
24
23
  }
25
24
  const verify = channels.verification[0];
26
25
  if (verify)
27
26
  introLines.push(`Verify: ${verify}`);
28
- const guidanceBlocks = channels.verification.slice(0, 3).map((action) => ({
29
- message: assessmentFields.reasoning[0] ?? action,
27
+ const guidanceBlocks = channels.verification
28
+ .slice(0, 3)
29
+ .map((action, index) => ({
30
+ message: assessmentFields.reasoning[index] ?? action,
30
31
  where: changed ?? "See scan detail for affected paths.",
31
32
  action,
32
33
  }));
@@ -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;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;AA6FF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,yBAAyB,GAC7B,uBAAuB,CAiFzB"}
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;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;AA6FF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,yBAAyB,GAC7B,uBAAuB,CA6EzB"}
@@ -1,6 +1,6 @@
1
1
  import { MERGE_CONCERN_LABELS } from "../../assessmentLabels.js";
2
2
  import { normalizedPackageUsagePaths } from "../../narrativePresentation.js";
3
- import { prRiskBandLabel, prRiskBandToGaugeBand, scoreToBandLabel, } from "../../prRiskBand.js";
3
+ import { formatPrRiskSummary, prRiskBandToGaugeBand, } from "../../prRiskBand.js";
4
4
  import { MERGE_POSTURE_LABEL } from "../../riskVocabulary.js";
5
5
  import { buildNarrativeChannels, composeSubheadline, composeSupportingContext, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
6
6
  import { normalizeGeneratedText, normalizeGeneratedTextNullable, } from "../../normalizeGeneratedText.js";
@@ -79,7 +79,7 @@ function mapEvidence(bundle) {
79
79
  };
80
80
  }
81
81
  export function presentScanDetails(bundle, ctx) {
82
- const { assessment, facts, profile } = bundle;
82
+ const { facts, profile } = bundle;
83
83
  const assessmentFields = projectAssessmentFields(bundle);
84
84
  const channels = buildNarrativeChannels(bundle);
85
85
  const keyPoints = projectCompactKeyPoints(channels, 6);
@@ -93,8 +93,8 @@ export function presentScanDetails(bundle, ctx) {
93
93
  : undefined;
94
94
  const postureLabel = MERGE_POSTURE_LABEL[profile.status];
95
95
  const verdictLine = assessmentFields.reasoning[0] ??
96
- (assessment.primaryConcern
97
- ? MERGE_CONCERN_LABELS[assessment.primaryConcern]
96
+ (assessmentFields.primaryConcern
97
+ ? MERGE_CONCERN_LABELS[assessmentFields.primaryConcern]
98
98
  : postureLabel);
99
99
  const verificationActions = channels.verification.map((title, index) => ({
100
100
  title: normalizeGeneratedText(title),
@@ -110,10 +110,7 @@ export function presentScanDetails(bundle, ctx) {
110
110
  : "low",
111
111
  rationale: action.detail,
112
112
  }));
113
- const prRiskScore = facts.riskSignals?.riskIndex ?? null;
114
- const prRiskBandLabelText = facts.riskSignals?.band != null
115
- ? prRiskBandLabel(facts.riskSignals.band)
116
- : scoreToBandLabel(prRiskScore);
113
+ const prRisk = formatPrRiskSummary(facts);
117
114
  return {
118
115
  ...assessmentFields,
119
116
  evidenceContext: evidenceContextFromProfile(bundle),
@@ -127,9 +124,9 @@ export function presentScanDetails(bundle, ctx) {
127
124
  verdictLine: normalizeGeneratedText(verdictLine),
128
125
  scopeChip: channels.reachLabel,
129
126
  postureLabel,
130
- prRiskScore,
131
- prRiskBandLabel: prRiskBandLabelText ?? undefined,
132
- riskIndex: prRiskScore,
127
+ prRiskScore: prRisk?.prRiskScore ?? null,
128
+ prRiskBandLabel: prRisk?.prRiskBandLabel,
129
+ riskIndex: prRisk?.prRiskScore ?? null,
133
130
  },
134
131
  signalSummary: mapSignalSummary(bundle),
135
132
  narrative: {
@@ -1 +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,CAgB9B"}
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;AAOxF,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,sBAAsB,GAC7B,4BAA4B,CAoB9B"}
@@ -1,4 +1,8 @@
1
1
  import { collectVerificationFocusForPresentation, projectReasoningLines, } from "../assessmentProjection.js";
2
+ function trimOptionalExpression(value) {
3
+ const trimmed = value?.trim();
4
+ return trimmed ? trimmed : undefined;
5
+ }
2
6
  export function projectAssessmentFields(bundle) {
3
7
  const { assessment, presentation, result } = bundle;
4
8
  const { channel, focus } = collectVerificationFocusForPresentation(presentation, result);
@@ -11,5 +15,7 @@ export function projectAssessmentFields(bundle) {
11
15
  verificationChannel: channel,
12
16
  reachVisibility: presentation.reachVisibility,
13
17
  narrativeIntensity: presentation.narrativeIntensity,
18
+ confidenceRationale: trimOptionalExpression(assessment.confidenceRationale),
19
+ electionSummary: trimOptionalExpression(assessment.reviewFocalPoint.electionSummary),
14
20
  };
15
21
  }