@mergesignal/shared 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/affectedAreaLinkage.d.ts +14 -0
  2. package/dist/affectedAreaLinkage.d.ts.map +1 -0
  3. package/dist/affectedAreaLinkage.js +100 -0
  4. package/dist/assessmentLabels.d.ts +1 -1
  5. package/dist/assessmentLabels.d.ts.map +1 -1
  6. package/dist/assessmentPresentationUtils.d.ts +6 -0
  7. package/dist/assessmentPresentationUtils.d.ts.map +1 -0
  8. package/dist/assessmentPresentationUtils.js +17 -0
  9. package/dist/assessmentProjection.d.ts +2 -2
  10. package/dist/assessmentProjection.d.ts.map +1 -1
  11. package/dist/assessmentProjection.js +42 -13
  12. package/dist/deriveScanNarrative.d.ts.map +1 -1
  13. package/dist/deriveScanNarrative.js +92 -9
  14. package/dist/deriveScanNarrative.test.js +74 -0
  15. package/dist/engineWireRegression.test.js +1 -1
  16. package/dist/fixtures/assessmentFixtures.d.ts +2 -1
  17. package/dist/fixtures/assessmentFixtures.d.ts.map +1 -1
  18. package/dist/fixtures/assessmentFixtures.js +78 -23
  19. package/dist/fixtures/assessmentScopeFixtures.d.ts +17 -0
  20. package/dist/fixtures/assessmentScopeFixtures.d.ts.map +1 -0
  21. package/dist/fixtures/assessmentScopeFixtures.js +42 -0
  22. package/dist/formatCardExposureDisplay.d.ts +5 -3
  23. package/dist/formatCardExposureDisplay.d.ts.map +1 -1
  24. package/dist/formatCardExposureDisplay.js +12 -5
  25. package/dist/formatCardExposureDisplay.test.js +9 -1
  26. package/dist/index.d.ts +2 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +2 -1
  29. package/dist/presentation/buildGitHubCheckRunOutput.test.js +16 -41
  30. package/dist/presentation/compose/narrativeChannels.js +1 -1
  31. package/dist/presentation/dto/assessmentPresentationFields.d.ts +1 -2
  32. package/dist/presentation/dto/assessmentPresentationFields.d.ts.map +1 -1
  33. package/dist/presentation/fixtures/scanResultFixtures.d.ts.map +1 -1
  34. package/dist/presentation/fixtures/scanResultFixtures.js +19 -1
  35. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts.map +1 -1
  36. package/dist/presentation/orchestration/buildScanPresentationBundle.js +12 -5
  37. package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts +2 -0
  38. package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts.map +1 -0
  39. package/dist/presentation/orchestration/buildScanPresentationBundle.test.js +99 -0
  40. package/dist/presentation/orchestration/scanPresentationBundle.d.ts +1 -1
  41. package/dist/presentation/orchestration/scanPresentationBundle.d.ts.map +1 -1
  42. package/dist/presentation/presentation.test.js +1 -1
  43. package/dist/presentation/presentationPersonas.test.js +0 -5
  44. package/dist/presentation/profile/buildProfileFromAssessment.d.ts +1 -1
  45. package/dist/presentation/profile/buildProfileFromAssessment.d.ts.map +1 -1
  46. package/dist/presentation/profile/buildProfileFromAssessment.js +13 -2
  47. package/dist/presentation/surfaceParity.test.js +73 -5
  48. package/dist/repoIntelligenceSchema.d.ts +3 -3
  49. package/dist/riskSignals.d.ts +19 -0
  50. package/dist/riskSignals.d.ts.map +1 -0
  51. package/dist/riskSignals.js +32 -0
  52. package/dist/scanNarrativeFacts.d.ts +25 -7
  53. package/dist/scanNarrativeFacts.d.ts.map +1 -1
  54. package/dist/scanNarrativeFacts.js +4 -1
  55. package/dist/scanPresentationBundle.golden.test.d.ts +2 -0
  56. package/dist/scanPresentationBundle.golden.test.d.ts.map +1 -0
  57. package/dist/scanPresentationBundle.golden.test.js +78 -0
  58. package/dist/scanResultSchema.d.ts +53 -4
  59. package/dist/scanResultSchema.d.ts.map +1 -1
  60. package/dist/scanResultSchema.js +1 -1
  61. package/dist/scanResultSchema.test.js +26 -0
  62. package/dist/types.d.ts +1 -1
  63. package/dist/types.d.ts.map +1 -1
  64. package/package.json +4 -2
  65. package/dist/assessmentSchema.d.ts +0 -155
  66. package/dist/assessmentSchema.d.ts.map +0 -1
  67. package/dist/assessmentSchema.js +0 -110
@@ -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,2BAA2B,CAAC;AACnC,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,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"}
@@ -66,7 +66,7 @@ describe("presentDashboardCard personas", () => {
66
66
  expect(card.verdict?.posture).toBe("safe");
67
67
  expect(card.headline).toMatch(/patch upgrade/i);
68
68
  expect(card.headline).not.toMatch(/needs review/i);
69
- expect(card.verification.length).toBeGreaterThan(0);
69
+ expect(card.verification.length).toBe(0);
70
70
  });
71
71
  });
72
72
  describe("presentation parity across surfaces", () => {
@@ -16,8 +16,6 @@ const personas = [
16
16
  density: "minimal",
17
17
  headlineMatch: /patch upgrade/i,
18
18
  headlineMustNot: /needs review/i,
19
- verificationIncludes: ["CI", "typecheck"],
20
- verificationMustNot: /auth flow/i,
21
19
  },
22
20
  {
23
21
  name: "eslint",
@@ -27,7 +25,6 @@ const personas = [
27
25
  density: "minimal",
28
26
  headlineMatch: /patch upgrade/i,
29
27
  headlineMustNot: /needs review/i,
30
- verificationIncludes: ["ci", "lint"],
31
28
  },
32
29
  {
33
30
  name: "prettier",
@@ -37,7 +34,6 @@ const personas = [
37
34
  density: "minimal",
38
35
  headlineMatch: /patch upgrade/i,
39
36
  headlineMustNot: /needs review/i,
40
- verificationIncludes: ["format"],
41
37
  },
42
38
  {
43
39
  name: "vitest",
@@ -47,7 +43,6 @@ const personas = [
47
43
  density: "minimal",
48
44
  headlineMatch: /patch upgrade|dependency upgrade/i,
49
45
  headlineMustNot: /needs review/i,
50
- verificationIncludes: ["test"],
51
46
  },
52
47
  {
53
48
  name: "fastify runtime",
@@ -1,4 +1,4 @@
1
- import type { Assessment, AssessmentPresentationPublic } from "../../assessmentSchema.js";
1
+ import type { Assessment, AssessmentPresentationPublic } from "@mergesignal/contracts";
2
2
  import type { ScanResult } from "../../types.js";
3
3
  import type { PresentationProfile } from "./presentationProfile.js";
4
4
  export declare function buildProfileFromAssessment(assessment: Assessment, presentation: AssessmentPresentationPublic, result: ScanResult): PresentationProfile;
@@ -1 +1 @@
1
- {"version":3,"file":"buildProfileFromAssessment.d.ts","sourceRoot":"","sources":["../../../src/presentation/profile/buildProfileFromAssessment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC7B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAwCpE,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,4BAA4B,EAC1C,MAAM,EAAE,UAAU,GACjB,mBAAmB,CAuBrB"}
1
+ {"version":3,"file":"buildProfileFromAssessment.d.ts","sourceRoot":"","sources":["../../../src/presentation/profile/buildProfileFromAssessment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,4BAA4B,EAC7B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAiDpE,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,4BAA4B,EAC1C,MAAM,EAAE,UAAU,GACjB,mBAAmB,CAuBrB"}
@@ -1,10 +1,21 @@
1
- import { narrativeIntensityToDensity } from "../../assessmentSchema.js";
1
+ import { narrativeIntensityToDensity } from "../../assessmentPresentationUtils.js";
2
2
  import { safeParseRepoIntelligence } from "../../repoIntelligenceSchema.js";
3
3
  import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
4
4
  import { collectVerificationFocus } from "../../assessmentProjection.js";
5
+ function focalAnchorPackage(assessment) {
6
+ const anchors = assessment.reviewFocalPoint.anchors;
7
+ if (!anchors.length)
8
+ return null;
9
+ const token = anchors[0];
10
+ if (token === "dependency_graph")
11
+ return null;
12
+ if (token.includes("+"))
13
+ return token.split("+")[0];
14
+ return token;
15
+ }
5
16
  function buildInterpretation(assessment, presentation, result) {
6
17
  const verificationLabels = collectVerificationFocus(result);
7
- const anchorPackage = result.changedPackages?.[0] ?? null;
18
+ const anchorPackage = focalAnchorPackage(assessment);
8
19
  const suppressRuntimeNarrative = presentation.reachVisibility === "hidden";
9
20
  let intent = "unknown_upgrade";
10
21
  if (assessment.primaryConcern === "insufficient_evidence") {
@@ -8,14 +8,34 @@ import { renderGitHubCheckRunMarkdown } from "./render/renderGitHubCheckRunMarkd
8
8
  import { renderGitHubPrCommentMarkdown } from "./render/renderGitHubPrCommentMarkdown.js";
9
9
  import { scanResultBullmq, scanResultEslint, scanResultFastifyRuntime, scanResultMixedTypescriptFastify, scanResultPrettier, scanResultTypescriptPatch, scanResultVitest, } from "./fixtures/presentationPersonaFixtures.js";
10
10
  import { projectAssessmentFields } from "./projectAssessmentFields.js";
11
+ import { buildNarrativeChannels } from "./compose/narrativeChannels.js";
11
12
  const SCAN_ID = "22222222-2222-4222-8222-222222222222";
12
13
  const ORIGIN = "https://app.example.com";
14
+ const validationPersonasCore = [
15
+ {
16
+ id: "validation-pr-26",
17
+ name: "PR #26 mixed",
18
+ result: scanResultMixedTypescriptFastify,
19
+ },
20
+ {
21
+ id: "validation-pr-27",
22
+ name: "PR #27 fastify",
23
+ result: scanResultFastifyRuntime,
24
+ },
25
+ {
26
+ id: "validation-pr-28",
27
+ name: "PR #28 typescript",
28
+ result: scanResultTypescriptPatch,
29
+ },
30
+ {
31
+ id: "validation-pr-29",
32
+ name: "PR #29 prettier",
33
+ result: scanResultPrettier,
34
+ },
35
+ { id: "validation-pr-30", name: "PR #30 bullmq", result: scanResultBullmq },
36
+ ];
13
37
  const validationPersonas = [
14
- { name: "PR #26 mixed", result: scanResultMixedTypescriptFastify },
15
- { name: "PR #27 fastify", result: scanResultFastifyRuntime },
16
- { name: "PR #28 typescript", result: scanResultTypescriptPatch },
17
- { name: "PR #29 prettier", result: scanResultPrettier },
18
- { name: "PR #30 bullmq", result: scanResultBullmq },
38
+ ...validationPersonasCore,
19
39
  { name: "eslint", result: scanResultEslint },
20
40
  { name: "vitest", result: scanResultVitest },
21
41
  ];
@@ -71,4 +91,52 @@ describe("surfaceParity guardrail", () => {
71
91
  expect(s.details.hero.headline).toBe(s.card.headline);
72
92
  });
73
93
  }
94
+ it("validation personas #26–#30: pairwise distinct reviewFocalPoint and headlines", () => {
95
+ const focalPoints = [];
96
+ const headlines = [];
97
+ for (const persona of validationPersonasCore) {
98
+ const s = surfacesFor(persona.result);
99
+ focalPoints.push(persona.result.assessment.reviewFocalPoint);
100
+ headlines.push(s.card.headline);
101
+ expect(s.check.title).toBe(s.card.headline);
102
+ }
103
+ for (let i = 0; i < headlines.length; i++) {
104
+ for (let j = i + 1; j < headlines.length; j++) {
105
+ expect(headlines[i]).not.toBe(headlines[j]);
106
+ }
107
+ }
108
+ for (let i = 0; i < focalPoints.length; i++) {
109
+ for (let j = i + 1; j < focalPoints.length; j++) {
110
+ expect(focalPoints[i]).not.toEqual(focalPoints[j]);
111
+ }
112
+ }
113
+ });
114
+ it("PR #26: headline uses focal fastify, not changedPackages[0] typescript", () => {
115
+ const result = scanResultMixedTypescriptFastify;
116
+ expect(result.changedPackages?.[0]).toBe("typescript");
117
+ const s = surfacesFor(result);
118
+ expect(s.card.headline.toLowerCase()).toContain("fastify");
119
+ expect(s.card.headline.toLowerCase()).not.toContain("typescript");
120
+ });
121
+ it("validation personas #28–#30: reach and verification channels differ by scope", () => {
122
+ const channelsById = new Map(validationPersonasCore.map((p) => {
123
+ const bundle = buildScanPresentationBundle({
124
+ result: p.result,
125
+ pipelineStatus: "done",
126
+ });
127
+ return [p.id, buildNarrativeChannels(bundle)];
128
+ }));
129
+ const ch28 = channelsById.get("validation-pr-28");
130
+ const ch29 = channelsById.get("validation-pr-29");
131
+ const ch27 = channelsById.get("validation-pr-27");
132
+ const ch30 = channelsById.get("validation-pr-30");
133
+ const ch26 = channelsById.get("validation-pr-26");
134
+ expect(ch28.reachLabel).toBeUndefined();
135
+ expect(ch29.reachLabel).toBeUndefined();
136
+ expect(ch27.verification.length).toBeGreaterThan(0);
137
+ expect(ch30.verification.length).toBeGreaterThan(0);
138
+ expect(ch27.verification).not.toEqual(ch30.verification);
139
+ expect(ch26.verification).not.toEqual(ch27.verification);
140
+ expect(ch26.scopeAreas.length).toBeGreaterThan(0);
141
+ });
74
142
  });
@@ -35,16 +35,17 @@ export declare const repoIntelligenceWireSchema: z.ZodObject<{
35
35
  }, z.core.$strip>;
36
36
  areas: z.ZodOptional<z.ZodArray<z.ZodString>>;
37
37
  dependencyClass: z.ZodOptional<z.ZodEnum<{
38
- format: "format";
39
38
  unknown: "unknown";
40
39
  runtime: "runtime";
41
40
  build: "build";
42
41
  test: "test";
43
42
  tooling: "tooling";
44
43
  lint: "lint";
44
+ format: "format";
45
45
  ci: "ci";
46
46
  }>>;
47
47
  packageRole: z.ZodOptional<z.ZodEnum<{
48
+ queue: "queue";
48
49
  unknown: "unknown";
49
50
  typechecker: "typechecker";
50
51
  compiler: "compiler";
@@ -54,7 +55,6 @@ export declare const repoIntelligenceWireSchema: z.ZodObject<{
54
55
  test_runner: "test_runner";
55
56
  http_framework: "http_framework";
56
57
  auth: "auth";
57
- queue: "queue";
58
58
  orm: "orm";
59
59
  }>>;
60
60
  lockfileKind: z.ZodOptional<z.ZodEnum<{
@@ -79,8 +79,8 @@ export declare const repoIntelligenceWireSchema: z.ZodObject<{
79
79
  }>>;
80
80
  evidenceStrength: z.ZodOptional<z.ZodEnum<{
81
81
  low: "low";
82
- medium: "medium";
83
82
  high: "high";
83
+ medium: "medium";
84
84
  }>>;
85
85
  confidenceReason: z.ZodOptional<z.ZodString>;
86
86
  verificationFocus: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -0,0 +1,19 @@
1
+ import { type CardExposureCategory } from "./formatCardExposureDisplay.js";
2
+ import type { ScanResult, ScoreLayer } from "./types.js";
3
+ export type RiskSignalLayer = {
4
+ layer: ScoreLayer;
5
+ score: number;
6
+ exposure: CardExposureCategory;
7
+ };
8
+ /** Canonical exposure classification for totalScore and layerScores. */
9
+ export type RiskSignals = {
10
+ riskIndex: number | null;
11
+ exposure: CardExposureCategory | null;
12
+ layers: RiskSignalLayer[];
13
+ };
14
+ /**
15
+ * Single owner for numeric risk index + exposure bucket classification.
16
+ * Surfaces must consume `facts.riskSignals` in future phases — do not re-derive bands.
17
+ */
18
+ export declare function deriveRiskSignals(result: ScanResult): RiskSignals | null;
19
+ //# sourceMappingURL=riskSignals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"riskSignals.d.ts","sourceRoot":"","sources":["../src/riskSignals.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAe,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAEF,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACtC,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B,CAAC;AASF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,CAwBxE"}
@@ -0,0 +1,32 @@
1
+ import { deriveCardExposureCategory, } from "./formatCardExposureDisplay.js";
2
+ const LAYER_ORDER = [
3
+ "security",
4
+ "maintainability",
5
+ "ecosystem",
6
+ "upgradeImpact",
7
+ ];
8
+ /**
9
+ * Single owner for numeric risk index + exposure bucket classification.
10
+ * Surfaces must consume `facts.riskSignals` in future phases — do not re-derive bands.
11
+ */
12
+ export function deriveRiskSignals(result) {
13
+ const riskIndex = typeof result.totalScore === "number" && Number.isFinite(result.totalScore)
14
+ ? result.totalScore
15
+ : null;
16
+ const layerScores = result.layerScores;
17
+ if (riskIndex == null && layerScores == null) {
18
+ return null;
19
+ }
20
+ const exposure = deriveCardExposureCategory(riskIndex);
21
+ const layers = layerScores
22
+ ? LAYER_ORDER.map((layer) => {
23
+ const score = layerScores[layer];
24
+ return {
25
+ layer,
26
+ score,
27
+ exposure: deriveCardExposureCategory(score) ?? "minimal",
28
+ };
29
+ })
30
+ : [];
31
+ return { riskIndex, exposure, layers };
32
+ }
@@ -1,5 +1,7 @@
1
1
  import type { ObservationSignalFamily } from "./cardObservationCatalog.js";
2
2
  import type { MergePosture } from "./riskVocabulary.js";
3
+ import type { RiskSignals } from "./riskSignals.js";
4
+ import type { AnalysisContextWarning } from "./types.js";
3
5
  export type NarrativeAvailabilityMode = "pr_intelligence" | "insights" | "graph_fallback" | "denormalized";
4
6
  export type RuntimeSurfaceKind = "runtime" | "build" | "test" | "unknown";
5
7
  export type ReachabilityKind = "on_runtime_paths" | "build_only" | "test_only" | "unreachable" | "unknown";
@@ -20,6 +22,23 @@ export type PackageRoleKind = "typechecker" | "compiler" | "bundler" | "linter"
20
22
  export type PackageRuntimeImpact = "none" | "possible" | "confirmed" | "unknown";
21
23
  export type PackageExpectedImpact = "runtime" | "build_time" | "typecheck" | "test_time" | "development_only" | "unknown";
22
24
  export type PackageEvidenceStrength = "high" | "medium" | "low";
25
+ export type CorpusGateReason = "ok" | "no_code_intelligence" | "repo_intelligence_invalid" | "repo_intelligence_absent";
26
+ /** Enriched affected area with proof-chain linkage (Phase 3). */
27
+ export type AffectedAreaFact = {
28
+ id: string;
29
+ label: string;
30
+ packages: string[];
31
+ findingIds: string[];
32
+ paths: string[];
33
+ evidenceStrength: PackageEvidenceStrength | null;
34
+ hotspotPackages: string[];
35
+ verificationFocus: string[];
36
+ };
37
+ export type ScanNarrativeConfidenceFacts = {
38
+ decision: "low" | "medium" | "high" | null;
39
+ assessment: "low" | "medium" | "high" | null;
40
+ limitedContext: boolean;
41
+ };
23
42
  /** Engine semantic projection for one changed package (wire copy only). */
24
43
  export type ChangedPackageSemantics = {
25
44
  packageName: string;
@@ -52,6 +71,8 @@ export type ScanNarrativeFacts = {
52
71
  tier3: boolean;
53
72
  };
54
73
  repoIntelligenceParse: RepoIntelligenceParseStatus;
74
+ preparationWarnings: AnalysisContextWarning[];
75
+ corpusGateReason: CorpusGateReason;
55
76
  };
56
77
  changedPackages: {
57
78
  primary: string | null;
@@ -83,10 +104,7 @@ export type ScanNarrativeFacts = {
83
104
  changedPackageCount?: number;
84
105
  factors: string[];
85
106
  } | null;
86
- affectedAreas: Array<{
87
- id: string;
88
- label: string;
89
- }>;
107
+ affectedAreas: AffectedAreaFact[];
90
108
  hotspots: Array<{
91
109
  packageName: string;
92
110
  source: "code" | "graph";
@@ -103,9 +121,7 @@ export type ScanNarrativeFacts = {
103
121
  remediation?: string;
104
122
  affectedFiles?: string[];
105
123
  }>;
106
- confidence: {
107
- decision: "low" | "medium" | "high" | null;
108
- };
124
+ confidence: ScanNarrativeConfidenceFacts;
109
125
  repositoryContext: Array<{
110
126
  family: ObservationSignalFamily;
111
127
  source: RepositoryContextSource;
@@ -115,7 +131,9 @@ export type ScanNarrativeFacts = {
115
131
  };
116
132
  }>;
117
133
  mergePosture: MergePosture | null;
134
+ /** @deprecated Use `riskSignals.riskIndex` — kept for frozen surface presenters. */
118
135
  riskIndex: number | null;
136
+ riskSignals: RiskSignals | null;
119
137
  };
120
138
  export declare const EMPTY_SCAN_NARRATIVE_FACTS: ScanNarrativeFacts;
121
139
  /** Derivation caps — presenters apply tighter channel limits. */
@@ -1 +1 @@
1
- {"version":3,"file":"scanNarrativeFacts.d.ts","sourceRoot":"","sources":["../src/scanNarrativeFacts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scanNarrativeFacts.d.ts","sourceRoot":"","sources":["../src/scanNarrativeFacts.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,8 @@ export const EMPTY_SCAN_NARRATIVE_FACTS = {
4
4
  codeIntelligenceAvailable: false,
5
5
  tiersPresent: { tier1: false, tier2: false, tier3: false },
6
6
  repoIntelligenceParse: "absent",
7
+ preparationWarnings: [],
8
+ corpusGateReason: "repo_intelligence_absent",
7
9
  },
8
10
  changedPackages: { primary: null, others: [], all: [] },
9
11
  packageUsage: [],
@@ -16,10 +18,11 @@ export const EMPTY_SCAN_NARRATIVE_FACTS = {
16
18
  affectedAreas: [],
17
19
  hotspots: [],
18
20
  reviewerGuidance: [],
19
- confidence: { decision: null },
21
+ confidence: { decision: null, assessment: null, limitedContext: false },
20
22
  repositoryContext: [],
21
23
  mergePosture: null,
22
24
  riskIndex: null,
25
+ riskSignals: null,
23
26
  };
24
27
  /** Derivation caps — presenters apply tighter channel limits. */
25
28
  export const NARRATIVE_FACT_MAX_PATHS = 20;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scanPresentationBundle.golden.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanPresentationBundle.golden.test.d.ts","sourceRoot":"","sources":["../src/scanPresentationBundle.golden.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,78 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { deriveCardExposureCategory } from "./formatCardExposureDisplay.js";
3
+ import { buildScanPresentationBundle } from "./presentation/orchestration/buildScanPresentationBundle.js";
4
+ import { scanResultBullmq, scanResultEslint, scanResultFastifyRuntime, scanResultLimitedContext, scanResultMixedTypescriptFastify, scanResultPrettier, scanResultTypescriptPatch, scanResultVitest, } from "./presentation/fixtures/presentationPersonaFixtures.js";
5
+ const validationPersonas = [
6
+ { id: "fastify-runtime", result: scanResultFastifyRuntime },
7
+ { id: "typescript-patch", result: scanResultTypescriptPatch },
8
+ { id: "mixed-ts-fastify", result: scanResultMixedTypescriptFastify },
9
+ { id: "prettier", result: scanResultPrettier },
10
+ { id: "bullmq", result: scanResultBullmq },
11
+ { id: "eslint", result: scanResultEslint },
12
+ { id: "vitest", result: scanResultVitest },
13
+ { id: "limited-context", result: scanResultLimitedContext },
14
+ ];
15
+ function bundleFor(result) {
16
+ return buildScanPresentationBundle({
17
+ result,
18
+ pipelineStatus: "done",
19
+ });
20
+ }
21
+ describe("scanPresentationBundle golden — proof model", () => {
22
+ it.each(validationPersonas)("builds bundle for $id", ({ result }) => {
23
+ const bundle = bundleFor(result);
24
+ expect(bundle).not.toBeNull();
25
+ expect(bundle.facts.availability.corpusGateReason).toBeDefined();
26
+ expect(bundle.facts.confidence).toMatchObject({
27
+ limitedContext: expect.any(Boolean),
28
+ });
29
+ expect(bundle.facts.riskSignals).not.toBeNull();
30
+ expect(bundle.facts.riskSignals.layers).toHaveLength(4);
31
+ });
32
+ it("fastify-runtime — linkage and diagnostics", () => {
33
+ const bundle = bundleFor(scanResultFastifyRuntime);
34
+ const { facts } = bundle;
35
+ expect(facts.availability.corpusGateReason).toBe("ok");
36
+ expect(facts.confidence.limitedContext).toBe(false);
37
+ expect(facts.confidence.assessment).toBe("medium");
38
+ const apiArea = facts.affectedAreas.find((a) => a.id === "api");
39
+ expect(apiArea?.packages).toContain("fastify");
40
+ expect(apiArea?.findingIds).toContain("finding-fastify-runtime");
41
+ expect(apiArea?.paths.length).toBeGreaterThan(0);
42
+ expect(apiArea?.hotspotPackages).toContain("fastify");
43
+ expect(apiArea?.verificationFocus.length).toBeGreaterThan(0);
44
+ expect(facts.riskSignals?.riskIndex).toBe(55);
45
+ expect(facts.riskSignals?.exposure).toBe(deriveCardExposureCategory(55));
46
+ expect(facts.riskIndex).toBe(facts.riskSignals?.riskIndex);
47
+ });
48
+ it("limited-context — diagnostics without synthetic confidence", () => {
49
+ const bundle = bundleFor(scanResultLimitedContext);
50
+ const { facts } = bundle;
51
+ expect(facts.availability.corpusGateReason).toBe("no_code_intelligence");
52
+ expect(facts.availability.preparationWarnings.some((w) => w.code === "base_lockfile_missing")).toBe(true);
53
+ expect(facts.confidence.assessment).toBe("low");
54
+ expect(facts.confidence.limitedContext).toBe(true);
55
+ });
56
+ it("typescript-patch — tooling semantics with risk signals", () => {
57
+ const bundle = bundleFor(scanResultTypescriptPatch);
58
+ const { facts } = bundle;
59
+ expect(facts.availability.corpusGateReason).toBe("ok");
60
+ expect(facts.packageSemantics?.dependencyClass).toBe("tooling");
61
+ expect(facts.riskSignals?.riskIndex).toBe(18);
62
+ expect(facts.riskSignals?.exposure).toBe(deriveCardExposureCategory(18));
63
+ });
64
+ it("affected areas always carry linkage arrays (structure)", () => {
65
+ for (const { result } of validationPersonas) {
66
+ const bundle = bundleFor(result);
67
+ if (!bundle)
68
+ continue;
69
+ for (const area of bundle.facts.affectedAreas) {
70
+ expect(Array.isArray(area.packages)).toBe(true);
71
+ expect(Array.isArray(area.findingIds)).toBe(true);
72
+ expect(Array.isArray(area.paths)).toBe(true);
73
+ expect(Array.isArray(area.hotspotPackages)).toBe(true);
74
+ expect(Array.isArray(area.verificationFocus)).toBe(true);
75
+ }
76
+ }
77
+ });
78
+ });
@@ -21,8 +21,8 @@ export declare const scanResultSchema: z.ZodObject<{
21
21
  methodologyVersion: z.ZodOptional<z.ZodString>;
22
22
  confidence: z.ZodOptional<z.ZodEnum<{
23
23
  low: "low";
24
- medium: "medium";
25
24
  high: "high";
25
+ medium: "medium";
26
26
  }>>;
27
27
  signals: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
28
28
  contributions: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
@@ -40,8 +40,8 @@ export declare const scanResultSchema: z.ZodObject<{
40
40
  }>;
41
41
  confidence: z.ZodOptional<z.ZodEnum<{
42
42
  low: "low";
43
- medium: "medium";
44
43
  high: "high";
44
+ medium: "medium";
45
45
  }>>;
46
46
  reasoning: z.ZodOptional<z.ZodArray<z.ZodString>>;
47
47
  }, z.core.$loose>>;
@@ -62,8 +62,8 @@ export declare const engineOutputScanResultSchema: z.ZodObject<{
62
62
  findings: z.ZodPipe<z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodUnknown>, z.ZodNull]>>, z.ZodTransform<unknown[], unknown[] | null | undefined>>;
63
63
  confidence: z.ZodOptional<z.ZodEnum<{
64
64
  low: "low";
65
- medium: "medium";
66
65
  high: "high";
66
+ medium: "medium";
67
67
  }>>;
68
68
  signals: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
69
69
  contributions: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
@@ -80,8 +80,8 @@ export declare const engineOutputScanResultSchema: z.ZodObject<{
80
80
  }>;
81
81
  confidence: z.ZodOptional<z.ZodEnum<{
82
82
  low: "low";
83
- medium: "medium";
84
83
  high: "high";
84
+ medium: "medium";
85
85
  }>>;
86
86
  reasoning: z.ZodOptional<z.ZodArray<z.ZodString>>;
87
87
  }, z.core.$loose>>;
@@ -89,6 +89,55 @@ export declare const engineOutputScanResultSchema: z.ZodObject<{
89
89
  methodologyVersion: z.ZodString;
90
90
  generatedAt: z.ZodString;
91
91
  assessment: z.ZodObject<{
92
+ reviewFocalPoint: z.ZodObject<{
93
+ episodeShape: z.ZodEnum<{
94
+ single_anchor: "single_anchor";
95
+ multi_anchor: "multi_anchor";
96
+ parent_supporting: "parent_supporting";
97
+ coupled_pair: "coupled_pair";
98
+ tooling_bundle: "tooling_bundle";
99
+ structural: "structural";
100
+ }>;
101
+ anchors: z.ZodArray<z.ZodString>;
102
+ supportingPackages: z.ZodOptional<z.ZodArray<z.ZodString>>;
103
+ election: z.ZodObject<{
104
+ grounding: z.ZodArray<z.ZodObject<{
105
+ packageName: z.ZodString;
106
+ reason: z.ZodString;
107
+ decidedBy: z.ZodEnum<{
108
+ concern: "concern";
109
+ reach: "reach";
110
+ changeSeverity: "changeSeverity";
111
+ role_salience_tiebreak: "role_salience_tiebreak";
112
+ }>;
113
+ evidenceRefs: z.ZodArray<z.ZodString>;
114
+ }, z.core.$strip>>;
115
+ exclusions: z.ZodArray<z.ZodObject<{
116
+ packageName: z.ZodString;
117
+ reason: z.ZodString;
118
+ lostOn: z.ZodEnum<{
119
+ concern: "concern";
120
+ reach: "reach";
121
+ changeSeverity: "changeSeverity";
122
+ role_salience_tiebreak: "role_salience_tiebreak";
123
+ }>;
124
+ evidenceRefs: z.ZodArray<z.ZodString>;
125
+ }, z.core.$strip>>;
126
+ }, z.core.$strip>;
127
+ }, z.core.$strip>;
128
+ reachScope: z.ZodObject<{
129
+ packages: z.ZodArray<z.ZodString>;
130
+ maxBucket: z.ZodEnum<{
131
+ low: "low";
132
+ high: "high";
133
+ very_low: "very_low";
134
+ moderate: "moderate";
135
+ }>;
136
+ }, z.core.$strip>;
137
+ verificationScope: z.ZodObject<{
138
+ packages: z.ZodArray<z.ZodString>;
139
+ focus: z.ZodArray<z.ZodString>;
140
+ }, z.core.$strip>;
92
141
  posture: z.ZodEnum<{
93
142
  safe: "safe";
94
143
  needs_review: "needs_review";
@@ -1 +1 @@
1
- {"version":3,"file":"scanResultSchema.d.ts","sourceRoot":"","sources":["../src/scanResultSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtE,wGAAwG;AACxG,eAAO,MAAM,eAAe,EAAG,GAAY,CAAC;AAE5C,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,EAAG,GAAY,CAAC;AASnD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4Bb,CAAC;AAUjB;;;GAGG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAIvC,CAAC;AAEH,MAAM,MAAM,kCAAkC,GAAG;IAC/C,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,uBAAuB,CAAC;CACjC,CAAC;AAEF,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,OAAO,GACZ,kCAAkC,GAAG,kCAAkC,CAazE;AAED,uFAAuF;AACvF,wBAAgB,kCAAkC,CAChD,IAAI,EAAE,OAAO,GACZ,uBAAuB,CAMzB;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,GACZ,sBAAsB,GAAG,sBAAsB,CAajD;AAED,yEAAyE;AACzE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAMhE"}
1
+ {"version":3,"file":"scanResultSchema.d.ts","sourceRoot":"","sources":["../src/scanResultSchema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtE,wGAAwG;AACxG,eAAO,MAAM,eAAe,EAAG,GAAY,CAAC;AAE5C,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,EAAG,GAAY,CAAC;AASnD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4Bb,CAAC;AAUjB;;;GAGG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAIvC,CAAC;AAEH,MAAM,MAAM,kCAAkC,GAAG;IAC/C,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,uBAAuB,CAAC;CACjC,CAAC;AAEF,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,OAAO,GACZ,kCAAkC,GAAG,kCAAkC,CAazE;AAED,uFAAuF;AACvF,wBAAgB,kCAAkC,CAChD,IAAI,EAAE,OAAO,GACZ,uBAAuB,CAMzB;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,GACZ,sBAAsB,GAAG,sBAAsB,CAajD;AAED,yEAAyE;AACzE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAMhE"}
@@ -1,5 +1,5 @@
1
+ import { assessmentSchema } from "@mergesignal/contracts";
1
2
  import { z } from "zod";
2
- import { assessmentSchema } from "./assessmentSchema.js";
3
3
  /** Bump when persisted `result` JSON validation rules change materially (relaxed / legacy-tolerant). */
4
4
  export const SCAN_RESULT_ABI = "2";
5
5
  /** Bump when strict fresh-engine-output validation rules change materially. */
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { safeParseScanResult, parseScanResultOrThrow, scanResultSchema, safeParseEngineOutputScanResult, parseEngineOutputScanResultOrThrow, } from "./scanResultSchema.js";
3
+ import { emptyReachScope, emptyVerificationScope, minimalReviewFocalPoint, } from "./fixtures/assessmentScopeFixtures.js";
3
4
  const minimalValid = {
4
5
  totalScore: 42,
5
6
  layerScores: {
@@ -78,6 +79,9 @@ describe("scanResultSchema", () => {
78
79
  });
79
80
  });
80
81
  const minimalAssessment = {
82
+ reviewFocalPoint: minimalReviewFocalPoint(["typescript"]),
83
+ reachScope: emptyReachScope(),
84
+ verificationScope: emptyVerificationScope(),
81
85
  posture: "safe",
82
86
  confidence: "high",
83
87
  primaryConcern: null,
@@ -118,4 +122,26 @@ describe("engineOutputScanResultSchema (strict, fresh engine only)", () => {
118
122
  });
119
123
  expect(r.ok).toBe(false);
120
124
  });
125
+ it("rejects ABI-1 assessment missing focal/scope fields on fresh engine path", () => {
126
+ const legacyAssessment = {
127
+ posture: "safe",
128
+ confidence: "high",
129
+ primaryConcern: null,
130
+ concerns: [],
131
+ factors: ["tooling_maintenance"],
132
+ changeClasses: ["tooling_maintenance"],
133
+ presentation: {
134
+ narrativeIntensity: "minimal",
135
+ reachVisibility: "hidden",
136
+ verificationIntensity: "advisory",
137
+ insightEmissionFloor: "none",
138
+ reportMode: "high_signal_pr",
139
+ },
140
+ };
141
+ const r = safeParseEngineOutputScanResult({
142
+ ...withMethodology,
143
+ assessment: legacyAssessment,
144
+ });
145
+ expect(r.ok).toBe(false);
146
+ });
121
147
  });
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Assessment } from "./assessmentSchema.js";
1
+ import type { Assessment } from "@mergesignal/contracts";
2
2
  export type LayerScores = {
3
3
  security: number;
4
4
  maintainability: number;