@mergesignal/shared 0.12.0 → 0.12.1

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 (45) 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/cliParity.test.d.ts +2 -0
  15. package/dist/presentation/cliParity.test.d.ts.map +1 -0
  16. package/dist/presentation/cliParity.test.js +49 -0
  17. package/dist/presentation/compose/narrativeCompose.d.ts +0 -4
  18. package/dist/presentation/compose/narrativeCompose.d.ts.map +1 -1
  19. package/dist/presentation/compose/narrativeCompose.js +0 -20
  20. package/dist/presentation/dto/assessmentPresentationFields.d.ts +4 -0
  21. package/dist/presentation/dto/assessmentPresentationFields.d.ts.map +1 -1
  22. package/dist/presentation/index.d.ts +1 -1
  23. package/dist/presentation/index.d.ts.map +1 -1
  24. package/dist/presentation/index.js +1 -1
  25. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts +1 -0
  26. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts.map +1 -1
  27. package/dist/presentation/orchestration/buildScanPresentationBundle.js +1 -0
  28. package/dist/presentation/presenters/presentCliScanSummary.d.ts.map +1 -1
  29. package/dist/presentation/presenters/presentCliScanSummary.js +9 -7
  30. package/dist/presentation/presenters/presentGitHubCheckRun.d.ts.map +1 -1
  31. package/dist/presentation/presenters/presentGitHubCheckRun.js +11 -12
  32. package/dist/presentation/presenters/presentGitHubPrComment.d.ts.map +1 -1
  33. package/dist/presentation/presenters/presentGitHubPrComment.js +11 -10
  34. package/dist/presentation/presenters/presentScanDetails.d.ts.map +1 -1
  35. package/dist/presentation/presenters/presentScanDetails.js +8 -11
  36. package/dist/presentation/projectAssessmentFields.d.ts.map +1 -1
  37. package/dist/presentation/projectAssessmentFields.js +6 -0
  38. package/dist/presentation/surfaceParity.test.js +8 -0
  39. package/package.json +1 -1
  40. package/dist/scanDetailViewModelTypes.d.ts +0 -159
  41. package/dist/scanDetailViewModelTypes.d.ts.map +0 -1
  42. package/dist/scanDetailViewModelTypes.js +0 -4
  43. package/dist/truncateCardSummary.d.ts +0 -5
  44. package/dist/truncateCardSummary.d.ts.map +0 -1
  45. package/dist/truncateCardSummary.js +0 -23
@@ -1 +1 @@
1
- {"version":3,"file":"deriveScanNarrative.d.ts","sourceRoot":"","sources":["../src/deriveScanNarrative.ts"],"names":[],"mappings":"AAYA,OAAO,EAgBL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAMV,UAAU,EACX,MAAM,YAAY,CAAC;AAoiBpB;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,GACpC,kBAAkB,CAsIpB"}
1
+ {"version":3,"file":"deriveScanNarrative.d.ts","sourceRoot":"","sources":["../src/deriveScanNarrative.ts"],"names":[],"mappings":"AAYA,OAAO,EAgBL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAMV,UAAU,EACX,MAAM,YAAY,CAAC;AAwiBpB;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,GACpC,kBAAkB,CAsIpB"}
@@ -304,6 +304,10 @@ function guidanceFromRecommendation(rec) {
304
304
  remediation: rec.impact,
305
305
  };
306
306
  }
307
+ /**
308
+ * Legacy insight/finding/recommendation synthesis for narrative availability tiers.
309
+ * Not presentation authority — shared surfaces use assessment expression fields instead.
310
+ */
307
311
  function collectReviewerGuidance(result) {
308
312
  const guidance = [];
309
313
  const insights = Array.isArray(result.insights) ? result.insights : [];
@@ -1,11 +1,11 @@
1
- import type { PRInsight, PRDecision, PRDecisionRecommendation } from "./types.js";
2
- export type FormattedInsight = {
1
+ import type { PRInsight, PRDecision } from "./types.js";
2
+ type FormattedInsight = {
3
3
  message: string;
4
4
  where: string;
5
5
  action: string;
6
6
  };
7
- export declare function decisionTitle(recommendation: PRDecisionRecommendation): string;
8
7
  export declare function formatInsight(insight: PRInsight): FormattedInsight;
9
8
  /** Legacy markdown renderer for raw PR insights (format-only; no bundle inference). */
10
9
  export declare function renderInsightsAsMarkdown(insights: PRInsight[], decision: PRDecision): string;
10
+ export {};
11
11
  //# sourceMappingURL=formatInsight.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatInsight.d.ts","sourceRoot":"","sources":["../src/formatInsight.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EACV,wBAAwB,EACzB,MAAM,YAAY,CAAC;AAGpB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AASF,wBAAgB,aAAa,CAC3B,cAAc,EAAE,wBAAwB,GACvC,MAAM,CAER;AAgBD,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,GAAG,gBAAgB,CAMlE;AA+BD,uFAAuF;AACvF,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,SAAS,EAAE,EACrB,QAAQ,EAAE,UAAU,GACnB,MAAM,CAUR"}
1
+ {"version":3,"file":"formatInsight.d.ts","sourceRoot":"","sources":["../src/formatInsight.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGxD,KAAK,gBAAgB,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAgBF,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,GAAG,gBAAgB,CAMlE;AA+BD,uFAAuF;AACvF,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,SAAS,EAAE,EACrB,QAAQ,EAAE,UAAU,GACnB,MAAM,CAUR"}
@@ -1,13 +1,4 @@
1
1
  import { MERGE_POSTURE_LABEL } from "./riskVocabulary.js";
2
- const DECISION_TITLES = {
3
- safe: "No significant dependency risks detected",
4
- needs_review: "Elevated dependency merge risk",
5
- risky: "Merge blocked - critical dependency risk",
6
- };
7
- // Title rendered once per PR comment, not per insight
8
- export function decisionTitle(recommendation) {
9
- return DECISION_TITLES[recommendation];
10
- }
11
2
  /**
12
3
  * Normalizes typographic punctuation to ASCII equivalents so that LLM output
13
4
  * renders consistently across PR comments, the web UI, and CLI output.
package/dist/index.d.ts CHANGED
@@ -1,11 +1,9 @@
1
1
  export * from "./types.js";
2
2
  export * from "./assessmentLabels.js";
3
3
  export * from "./assessmentProjection.js";
4
- export * from "./formatInsight.js";
5
4
  export * from "./riskVocabulary.js";
6
5
  export * from "./selectTopAffectedAreas.js";
7
6
  export * from "./cardSummaryCopy.js";
8
- export * from "./truncateCardSummary.js";
9
7
  export * from "./formatCardAreaLabels.js";
10
8
  export * from "./prRiskBand.js";
11
9
  export * from "./prRiskWire.js";
@@ -20,7 +18,6 @@ export * from "./extractRepositoryContextFacts.js";
20
18
  export * from "./narrativePresentation.js";
21
19
  export * from "./normalizeGeneratedText.js";
22
20
  export { presentGitHubPrCommentMarkdownFromResult } from "./presentGitHubPrComment.js";
23
- export * from "./scanDetailViewModelTypes.js";
24
21
  export * from "./scanCardPresentationState.js";
25
22
  export * from "./scanResultSchema.js";
26
23
  export * from "./scanSurfaceCopy.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,OAAO,EAAE,wCAAwC,EAAE,MAAM,6BAA6B,CAAC;AACvF,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,aAAa,EACb,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,cAAc,EACd,yBAAyB,GAC1B,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,oCAAoC,CAAC;AACnD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,OAAO,EAAE,wCAAwC,EAAE,MAAM,6BAA6B,CAAC;AACvF,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,aAAa,EACb,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,cAAc,EACd,yBAAyB,GAC1B,MAAM,kCAAkC,CAAC"}
package/dist/index.js CHANGED
@@ -1,11 +1,9 @@
1
1
  export * from "./types.js";
2
2
  export * from "./assessmentLabels.js";
3
3
  export * from "./assessmentProjection.js";
4
- export * from "./formatInsight.js";
5
4
  export * from "./riskVocabulary.js";
6
5
  export * from "./selectTopAffectedAreas.js";
7
6
  export * from "./cardSummaryCopy.js";
8
- export * from "./truncateCardSummary.js";
9
7
  export * from "./formatCardAreaLabels.js";
10
8
  export * from "./prRiskBand.js";
11
9
  export * from "./prRiskWire.js";
@@ -20,7 +18,6 @@ export * from "./extractRepositoryContextFacts.js";
20
18
  export * from "./narrativePresentation.js";
21
19
  export * from "./normalizeGeneratedText.js";
22
20
  export { presentGitHubPrCommentMarkdownFromResult } from "./presentGitHubPrComment.js";
23
- export * from "./scanDetailViewModelTypes.js";
24
21
  export * from "./scanCardPresentationState.js";
25
22
  export * from "./scanResultSchema.js";
26
23
  export * from "./scanSurfaceCopy.js";
@@ -1,3 +1,4 @@
1
+ import type { ScanNarrativeFacts } from "./scanNarrativeFacts.js";
1
2
  /**
2
3
  * Canonical PR Risk band contract — sole authority for PR Risk band derivation.
3
4
  * Independent from legacy exposure presentation and merge verdict (`assessment.posture`).
@@ -18,6 +19,12 @@ export declare function scoreToBandLabel(score: number | null | undefined): stri
18
19
  export declare function prRiskBandLabel(band: PrRiskBand): string;
19
20
  /** Fragment for composite aria labels on PR surfaces. */
20
21
  export declare function prRiskBandAriaFragment(score: number | null | undefined): string | null;
22
+ export type PrRiskSummary = {
23
+ prRiskScore: number;
24
+ prRiskBandLabel: string;
25
+ };
26
+ /** Format PR Risk score + band from narrative facts (not assessment projection). */
27
+ export declare function formatPrRiskSummary(facts: Pick<ScanNarrativeFacts, "riskSignals" | "riskIndex">): PrRiskSummary | undefined;
21
28
  /** Project 5-band PR Risk to 3-bucket scan-detail gauge band. */
22
29
  export declare function prRiskBandToGaugeBand(band: PrRiskBand): PrRiskGaugeBand;
23
30
  //# sourceMappingURL=prRiskBand.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prRiskBand.d.ts","sourceRoot":"","sources":["../src/prRiskBand.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,gBAAgB,EAAG,GAAY,CAAC;AAE7C,iEAAiE;AACjE,eAAO,MAAM,uBAAuB,gCAAiC,CAAC;AAEtE,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAU7E,oDAAoD;AACpD,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAMhD,CAAC;AAUF,mEAAmE;AACnE,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC;AAO1D,wDAAwD;AACxD,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,UAAU,GAAG,IAAI,CAKnB;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,MAAM,GAAG,IAAI,CAGf;AAED,yCAAyC;AACzC,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAExD;AAED,yDAAyD;AACzD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,MAAM,GAAG,IAAI,CAIf;AAED,iEAAiE;AACjE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,CAIvE"}
1
+ {"version":3,"file":"prRiskBand.d.ts","sourceRoot":"","sources":["../src/prRiskBand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE;;;GAGG;AAEH,eAAO,MAAM,gBAAgB,EAAG,GAAY,CAAC;AAE7C,iEAAiE;AACjE,eAAO,MAAM,uBAAuB,gCAAiC,CAAC;AAEtE,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAU7E,oDAAoD;AACpD,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAMhD,CAAC;AAUF,mEAAmE;AACnE,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC;AAO1D,wDAAwD;AACxD,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,UAAU,GAAG,IAAI,CAKnB;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,MAAM,GAAG,IAAI,CAGf;AAED,yCAAyC;AACzC,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAExD;AAED,yDAAyD;AACzD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,MAAM,GAAG,IAAI,CAIf;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,oFAAoF;AACpF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,aAAa,GAAG,WAAW,CAAC,GAC3D,aAAa,GAAG,SAAS,CAW3B;AAED,iEAAiE;AACjE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,CAIvE"}
@@ -56,6 +56,18 @@ export function prRiskBandAriaFragment(score) {
56
56
  return null;
57
57
  return `PR Risk: ${label}`;
58
58
  }
59
+ /** Format PR Risk score + band from narrative facts (not assessment projection). */
60
+ export function formatPrRiskSummary(facts) {
61
+ const prRiskScore = facts.riskSignals?.riskIndex;
62
+ if (prRiskScore == null || !Number.isFinite(prRiskScore))
63
+ return undefined;
64
+ const prRiskBandLabelText = facts.riskSignals?.band != null
65
+ ? prRiskBandLabel(facts.riskSignals.band)
66
+ : scoreToBandLabel(prRiskScore);
67
+ if (!prRiskBandLabelText)
68
+ return undefined;
69
+ return { prRiskScore, prRiskBandLabel: prRiskBandLabelText };
70
+ }
59
71
  /** Project 5-band PR Risk to 3-bucket scan-detail gauge band. */
60
72
  export function prRiskBandToGaugeBand(band) {
61
73
  if (band === "critical" || band === "high")
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { PR_RISK_BAND_ABI, PR_RISK_BAND_THRESHOLDS, prRiskBandAriaFragment, prRiskBandLabel, prRiskBandToGaugeBand, scoreToBand, scoreToBandLabel, } from "./prRiskBand.js";
2
+ import { PR_RISK_BAND_ABI, PR_RISK_BAND_THRESHOLDS, formatPrRiskSummary, prRiskBandAriaFragment, prRiskBandLabel, prRiskBandToGaugeBand, scoreToBand, scoreToBandLabel, } from "./prRiskBand.js";
3
3
  describe("PR_RISK_BAND_ABI", () => {
4
4
  it("is stable v1", () => {
5
5
  expect(PR_RISK_BAND_ABI).toBe("1");
@@ -46,6 +46,17 @@ describe("prRiskBandAriaFragment", () => {
46
46
  expect(prRiskBandAriaFragment(55)).toBe("PR Risk: Medium");
47
47
  });
48
48
  });
49
+ describe("formatPrRiskSummary", () => {
50
+ it("returns score and band label from riskSignals", () => {
51
+ expect(formatPrRiskSummary({
52
+ riskSignals: { riskIndex: 55, band: "medium", layers: [] },
53
+ riskIndex: 55,
54
+ })).toEqual({ prRiskScore: 55, prRiskBandLabel: "Medium" });
55
+ });
56
+ it("returns undefined when risk index is missing", () => {
57
+ expect(formatPrRiskSummary({ riskSignals: null, riskIndex: null })).toBeUndefined();
58
+ });
59
+ });
49
60
  describe("prRiskBandToGaugeBand", () => {
50
61
  it("projects to three gauge buckets", () => {
51
62
  expect(prRiskBandToGaugeBand("very_low")).toBe("low");
@@ -13,6 +13,7 @@ import { describe, expect, it } from "vitest";
13
13
  import { minimalReviewFocalPoint, reachScopeFor, verificationScopeFor, withAssessmentScope, } from "../fixtures/assessmentScopeFixtures.js";
14
14
  import { projectReasoningLines, projectVerificationActions, } from "../assessmentProjection.js";
15
15
  import { toPublicPresentation } from "../assessmentPresentationUtils.js";
16
+ import { projectAssessmentFields } from "./projectAssessmentFields.js";
16
17
  import { buildScanPresentationBundle } from "./orchestration/buildScanPresentationBundle.js";
17
18
  import { presentGitHubPrComment } from "./presenters/presentGitHubPrComment.js";
18
19
  import { presentGitHubCheckRun } from "./presenters/presentGitHubCheckRun.js";
@@ -190,6 +191,51 @@ describe("projectVerificationActions — ABI 3 guidance", () => {
190
191
  });
191
192
  });
192
193
  // ---------------------------------------------------------------------------
194
+ // projectAssessmentFields — ABI 3 expression projection
195
+ // ---------------------------------------------------------------------------
196
+ describe("projectAssessmentFields — ABI 3 expression", () => {
197
+ it("projects confidenceRationale and electionSummary from assessment", () => {
198
+ const result = scanResultWithAssessment(assessmentExpressAbi3);
199
+ const bundle = buildScanPresentationBundle({
200
+ result,
201
+ pipelineStatus: "done",
202
+ });
203
+ const fields = projectAssessmentFields(bundle);
204
+ expect(fields.confidenceRationale).toBe("Confidence is high: propagation from express to application entry points was directly observed.");
205
+ expect(fields.electionSummary).toBe(expressElectionSummary);
206
+ expect(fields.reasoning).toEqual(expressReasoning);
207
+ });
208
+ it("omits empty confidenceRationale and electionSummary after trim", () => {
209
+ const assessment = {
210
+ ...assessmentExpressAbi3,
211
+ confidenceRationale: " ",
212
+ reviewFocalPoint: {
213
+ ...assessmentExpressAbi3.reviewFocalPoint,
214
+ electionSummary: "",
215
+ },
216
+ };
217
+ const result = scanResultWithAssessment(assessment);
218
+ const bundle = buildScanPresentationBundle({
219
+ result,
220
+ pipelineStatus: "done",
221
+ });
222
+ const fields = projectAssessmentFields(bundle);
223
+ expect(fields.confidenceRationale).toBeUndefined();
224
+ expect(fields.electionSummary).toBeUndefined();
225
+ });
226
+ it("projects reasoning when assessment has concerns", () => {
227
+ const result = scanResultWithAssessment(assessmentExpressAbi3);
228
+ const bundle = buildScanPresentationBundle({
229
+ result,
230
+ pipelineStatus: "done",
231
+ });
232
+ const fields = projectAssessmentFields(bundle);
233
+ expect(assessmentExpressAbi3.concerns.length).toBeGreaterThan(0);
234
+ expect(fields.reasoning.length).toBeGreaterThan(0);
235
+ expect(fields.reasoning).toEqual(expressReasoning);
236
+ });
237
+ });
238
+ // ---------------------------------------------------------------------------
193
239
  // PR comment — electionSummary and reasoning
194
240
  // ---------------------------------------------------------------------------
195
241
  describe("presentGitHubPrComment — ABI 3 expression", () => {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cliParity.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliParity.test.d.ts","sourceRoot":"","sources":["../../src/presentation/cliParity.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildScanPresentationBundle } from "./orchestration/buildScanPresentationBundle.js";
3
+ import { presentCliScanSummary } from "./presenters/presentCliScanSummary.js";
4
+ import { presentDashboardCard } from "./presenters/presentDashboardCard.js";
5
+ import { presentGitHubCheckRun } from "./presenters/presentGitHubCheckRun.js";
6
+ import { presentGitHubPrComment } from "./presenters/presentGitHubPrComment.js";
7
+ import { presentScanDetails } from "./presenters/presentScanDetails.js";
8
+ import { projectAssessmentFields } from "./projectAssessmentFields.js";
9
+ import { scanResultBullmq, scanResultEslint, scanResultFastifyRuntime, scanResultMixedTypescriptFastify, scanResultPrettier, scanResultTypescriptPatch, scanResultVitest, } from "./fixtures/presentationPersonaFixtures.js";
10
+ const SCAN_ID = "22222222-2222-4222-8222-222222222222";
11
+ const ORIGIN = "https://app.example.com";
12
+ const validationPersonas = [
13
+ { name: "mixed-ts-fastify", result: scanResultMixedTypescriptFastify },
14
+ { name: "fastify-runtime", result: scanResultFastifyRuntime },
15
+ { name: "typescript-patch", result: scanResultTypescriptPatch },
16
+ { name: "prettier", result: scanResultPrettier },
17
+ { name: "bullmq", result: scanResultBullmq },
18
+ { name: "eslint", result: scanResultEslint },
19
+ { name: "vitest", result: scanResultVitest },
20
+ ];
21
+ describe("cliParity guardrail", () => {
22
+ for (const persona of validationPersonas) {
23
+ it(`${persona.name}: CLI shares assessment fields with other surfaces`, () => {
24
+ const bundle = buildScanPresentationBundle({
25
+ result: persona.result,
26
+ pipelineStatus: "done",
27
+ });
28
+ const fields = projectAssessmentFields(bundle);
29
+ const cli = presentCliScanSummary(bundle, { repoLabel: "acme/api" });
30
+ const card = presentDashboardCard(bundle);
31
+ const details = presentScanDetails(bundle, { scanId: SCAN_ID });
32
+ const check = presentGitHubCheckRun(bundle, {
33
+ scanId: SCAN_ID,
34
+ webAppOrigin: ORIGIN,
35
+ });
36
+ const comment = presentGitHubPrComment(bundle);
37
+ expect(cli.posture).toBe(fields.posture);
38
+ expect(cli.reasoning).toEqual(fields.reasoning);
39
+ expect(cli.verificationChannel).toBe(fields.verificationChannel);
40
+ expect(cli.verificationFocus).toEqual(fields.verificationFocus);
41
+ expect(cli.confidenceRationale).toBe(fields.confidenceRationale);
42
+ expect(cli.electionSummary).toBe(fields.electionSummary);
43
+ expect(cli.reasoning).toEqual(card.reasoning);
44
+ expect(cli.reasoning).toEqual(details.reasoning);
45
+ expect(cli.reasoning).toEqual(check.reasoning);
46
+ expect(cli.reasoning).toEqual(comment.reasoning);
47
+ });
48
+ }
49
+ });
@@ -5,10 +5,6 @@ export declare function composeSubheadline(bundle: ScanPresentationBundle): stri
5
5
  export declare function composeKeyPoints(bundle: ScanPresentationBundle, max: number): string[];
6
6
  export declare function composeVerificationActions(bundle: ScanPresentationBundle, max: number): string[];
7
7
  export declare function composeAffectedAreaLabels(bundle: ScanPresentationBundle, max: number): string[];
8
- export declare function composeEvidenceRows(bundle: ScanPresentationBundle, max: number): Array<{
9
- label: string;
10
- value: string;
11
- }>;
12
8
  export declare function composeSupportingContext(bundle: ScanPresentationBundle): string[] | undefined;
13
9
  export declare function evidenceContextFromProfile(bundle: ScanPresentationBundle): {
14
10
  priority: "pr_intelligence" | "limited";
@@ -1 +1 @@
1
- {"version":3,"file":"narrativeCompose.d.ts","sourceRoot":"","sources":["../../../src/presentation/compose/narrativeCompose.ts"],"names":[],"mappings":"AAQA,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,CAEtE;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,CAEV;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":"AAEA,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,CAEtE;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,CAEV;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAQV;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,5 +1,4 @@
1
1
  import { formatCardAreaLabels } from "../../formatCardAreaLabels.js";
2
- import { formatChangedPackagesShort, labelBlastRadiusLevel, labelReachabilityKind, labelRuntimeSurface, } from "../../narrativePresentation.js";
3
2
  import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
4
3
  import { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
5
4
  export { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
@@ -36,25 +35,6 @@ export function composeAffectedAreaLabels(bundle, max) {
36
35
  }
37
36
  return labels;
38
37
  }
39
- export function composeEvidenceRows(bundle, max) {
40
- const { facts, profile } = bundle;
41
- const rows = [];
42
- if (!profile.interpretation.suppressRuntimeNarrative) {
43
- const runtime = labelRuntimeSurface(facts);
44
- if (runtime)
45
- rows.push({ label: "Runtime", value: runtime });
46
- const reach = labelReachabilityKind(facts);
47
- if (reach)
48
- rows.push({ label: "Reachability", value: reach });
49
- }
50
- const blast = labelBlastRadiusLevel(facts);
51
- if (blast)
52
- rows.push({ label: "Blast radius", value: blast });
53
- const changed = formatChangedPackagesShort(facts, 3);
54
- if (changed)
55
- rows.push({ label: "Changed", value: changed });
56
- return rows.slice(0, max);
57
- }
58
38
  export function composeSupportingContext(bundle) {
59
39
  const { facts, profile, result } = bundle;
60
40
  if (profile.density === "minimal")
@@ -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":"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;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,gCAAgC,GACtC,sBAAsB,GAAG,IAAI,CA4B/B"}
@@ -5,6 +5,7 @@ import { buildProfileFromAssessment } from "../profile/buildProfileFromAssessmen
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)
@@ -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
  }
@@ -89,6 +89,14 @@ describe("surfaceParity guardrail", () => {
89
89
  expect(s.details.verificationChannel).toBe(s.fields.verificationChannel);
90
90
  expect(s.check.verificationChannel).toBe(s.fields.verificationChannel);
91
91
  expect(s.comment.verificationChannel).toBe(s.fields.verificationChannel);
92
+ expect(s.card.confidenceRationale).toBe(s.fields.confidenceRationale);
93
+ expect(s.details.confidenceRationale).toBe(s.fields.confidenceRationale);
94
+ expect(s.check.confidenceRationale).toBe(s.fields.confidenceRationale);
95
+ expect(s.comment.confidenceRationale).toBe(s.fields.confidenceRationale);
96
+ expect(s.card.electionSummary).toBe(s.fields.electionSummary);
97
+ expect(s.details.electionSummary).toBe(s.fields.electionSummary);
98
+ expect(s.check.electionSummary).toBe(s.fields.electionSummary);
99
+ expect(s.comment.electionSummary).toBe(s.fields.electionSummary);
92
100
  });
93
101
  it(`${persona.name}: headline matches across card and details`, () => {
94
102
  const s = surfacesFor(persona.result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mergesignal/shared",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "MergeSignal shared scan contract, presentation policy, and trusted validation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,159 +0,0 @@
1
- import type { ObservationSignalFamily } from "./cardObservationCatalog.js";
2
- import type { NarrativeAvailabilityMode } from "./scanNarrativeFacts.js";
3
- import type { MergePosture } from "./riskVocabulary.js";
4
- import type { FindingSeverity, ScoreLayer } from "./types.js";
5
- /** Max operational-impact rows before collapse on scan detail. */
6
- export declare const TIER1_MAX_VISIBLE_IMPACTS = 3;
7
- /** Findings list collapse threshold on scan detail evidence panel. */
8
- export declare const ACT3_EVIDENCE_COLLAPSE_THRESHOLD = 10;
9
- export type ScanDetailOverallRiskBand = "low" | "moderate" | "high";
10
- export type ScanDetailLayerConcernLevel = "low" | "medium" | "high";
11
- export type ScanDetailSignalLayer = {
12
- layer: ScoreLayer;
13
- label: string;
14
- score: number;
15
- concernLevel: ScanDetailLayerConcernLevel;
16
- concernLabel: string;
17
- };
18
- export type RiskScoreGaugeModel = {
19
- fillPercent: number;
20
- band: ScanDetailOverallRiskBand;
21
- ariaLabel: string;
22
- };
23
- export type ScanDetailSignalSummary = {
24
- score: number;
25
- overallBand: ScanDetailOverallRiskBand;
26
- overallLabel: string;
27
- gauge: RiskScoreGaugeModel;
28
- layers: ScanDetailSignalLayer[];
29
- };
30
- export type RecommendationPriority = "high" | "medium" | "low";
31
- export type GuidanceSource = "insight" | "recommendation" | "verification";
32
- export type ScanDetailRecommendationDetail = {
33
- why: string;
34
- whyNow: string;
35
- signals: string[];
36
- affectedPackages?: {
37
- names: string[];
38
- overflowCount: number;
39
- };
40
- expectedBenefit: string;
41
- };
42
- export type ScanDetailRecommendation = {
43
- id: string;
44
- rank: number;
45
- title: string;
46
- priority: RecommendationPriority;
47
- source: GuidanceSource;
48
- signalFamily?: ObservationSignalFamily | null;
49
- detail: ScanDetailRecommendationDetail;
50
- proofRefs?: {
51
- findingIds?: string[];
52
- };
53
- };
54
- export type ScanDetailRecommendationCenter = {
55
- heading: string;
56
- defaultSelectedId: string;
57
- items: ScanDetailRecommendation[];
58
- posture: MergePosture | null;
59
- scanContext?: string;
60
- };
61
- export type ScanDetailOperationalImpactItem = {
62
- message: string;
63
- where?: string;
64
- verify?: string;
65
- affectedFiles?: string[];
66
- };
67
- export type ScanDetailOperationalImpact = {
68
- status: "rich" | "fallback" | "hidden";
69
- items: ScanDetailOperationalImpactItem[];
70
- fallbackMessage?: string;
71
- };
72
- export type ScanDetailVerdict = {
73
- posture: MergePosture | null;
74
- scopeChip: string | null;
75
- verdictLine: string;
76
- prLabel?: string;
77
- statusLabel?: string;
78
- };
79
- export type ScanDetailAttentionPackage = {
80
- name: string;
81
- version?: string;
82
- direct: boolean;
83
- severity?: FindingSeverity;
84
- evidence?: string;
85
- };
86
- export type ScanDetailAttentionArea = {
87
- problemLabel: string;
88
- problemDescription: string;
89
- packages: ScanDetailAttentionPackage[];
90
- overflowCount: number;
91
- };
92
- export type ScanDetailFindingRow = {
93
- id: string;
94
- severity: FindingSeverity;
95
- title: string;
96
- description: string;
97
- packageName: string;
98
- recommendation?: string;
99
- source: "dependency" | "code";
100
- coveredByRecommendationRank?: number;
101
- };
102
- export type ScanDetailTopology = {
103
- summaryLine: string;
104
- deepest: Array<{
105
- packageName: string;
106
- depth: number;
107
- direct: boolean;
108
- via: string[];
109
- }>;
110
- };
111
- export type ScanDetailNarrativeContext = {
112
- mode: NarrativeAvailabilityMode;
113
- codeIntelligenceAvailable: boolean;
114
- changedPackagesDisplay: string | null;
115
- runtimeSurfaceLabel: string | null;
116
- reachabilityLabel: string | null;
117
- blastRadiusLabel: string | null;
118
- affectedAreas: string[];
119
- structuralOnlyDisclaimer: string | null;
120
- usageHighlights: string[];
121
- frameworks: string[];
122
- blastRadiusFactors: string[];
123
- hotspotNames: string[];
124
- usageContextLine: string | null;
125
- upgradeContextLine: string | null;
126
- };
127
- export type ScanDetailViewModel = {
128
- verdict: ScanDetailVerdict;
129
- signalSummary: ScanDetailSignalSummary | null;
130
- followUpBridgeNote: string | null;
131
- recommendedActions: ScanDetailRecommendationCenter;
132
- operationalImpact: ScanDetailOperationalImpact;
133
- because: {
134
- themes: string[];
135
- confidenceCaveat?: string;
136
- } | null;
137
- evidence: {
138
- attentionAreas: ScanDetailAttentionArea[];
139
- findings: ScanDetailFindingRow[];
140
- findingsOverflowCount: number;
141
- topology: ScanDetailTopology | null;
142
- } | null;
143
- repoContext: {
144
- status: "hidden";
145
- } | {
146
- status: "loaded";
147
- comparisonLine: string;
148
- };
149
- narrativeContext: ScanDetailNarrativeContext;
150
- metadata: {
151
- scanId: string;
152
- generatedAt?: string;
153
- methodologyVersion?: string | null;
154
- changedPackagesSummary?: string;
155
- codeAnalysisTimedOut?: boolean;
156
- codeIntelligenceAvailable?: boolean;
157
- };
158
- };
159
- //# sourceMappingURL=scanDetailViewModelTypes.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scanDetailViewModelTypes.d.ts","sourceRoot":"","sources":["../src/scanDetailViewModelTypes.ts"],"names":[],"mappings":"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"}
@@ -1,4 +0,0 @@
1
- /** Max operational-impact rows before collapse on scan detail. */
2
- export const TIER1_MAX_VISIBLE_IMPACTS = 3;
3
- /** Findings list collapse threshold on scan detail evidence panel. */
4
- export const ACT3_EVIDENCE_COLLAPSE_THRESHOLD = 10;
@@ -1,5 +0,0 @@
1
- /**
2
- * Semantic truncation for dashboard card summaries (~90 chars default).
3
- */
4
- export declare function truncateCardSummary(text: string, maxLen?: number): string;
5
- //# sourceMappingURL=truncateCardSummary.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"truncateCardSummary.d.ts","sourceRoot":"","sources":["../src/truncateCardSummary.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,MAAoB,GAC3B,MAAM,CA+BR"}
@@ -1,23 +0,0 @@
1
- /**
2
- * Semantic truncation for dashboard card summaries (~90 chars default).
3
- */
4
- const DEFAULT_MAX = 90;
5
- export function truncateCardSummary(text, maxLen = DEFAULT_MAX) {
6
- const trimmed = text.trim();
7
- if (trimmed.length <= maxLen)
8
- return trimmed;
9
- const slice = trimmed.slice(0, maxLen + 1);
10
- const sentenceEnd = Math.max(slice.lastIndexOf(". "), slice.lastIndexOf("! "), slice.lastIndexOf("? "));
11
- if (sentenceEnd >= Math.floor(maxLen * 0.45)) {
12
- return trimmed.slice(0, sentenceEnd + 1).trim();
13
- }
14
- const clauseEnd = Math.max(slice.lastIndexOf(", "), slice.lastIndexOf("; "), slice.lastIndexOf(" — "), slice.lastIndexOf(" - "));
15
- if (clauseEnd >= Math.floor(maxLen * 0.45)) {
16
- return `${trimmed.slice(0, clauseEnd).trim()}…`;
17
- }
18
- const lastSpace = slice.lastIndexOf(" ");
19
- if (lastSpace >= Math.floor(maxLen * 0.6)) {
20
- return `${trimmed.slice(0, lastSpace).trim()}…`;
21
- }
22
- return `${trimmed.slice(0, maxLen).trim()}…`;
23
- }