@mergesignal/shared 0.2.8 → 0.3.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 (43) hide show
  1. package/dist/deriveScanNarrative.d.ts +0 -3
  2. package/dist/deriveScanNarrative.d.ts.map +1 -1
  3. package/dist/deriveScanNarrative.js +29 -18
  4. package/dist/deriveScanNarrative.test.js +37 -8
  5. package/dist/engineWireRegression.test.d.ts +2 -0
  6. package/dist/engineWireRegression.test.d.ts.map +1 -0
  7. package/dist/engineWireRegression.test.js +57 -0
  8. package/dist/fixtures/legacyEngineRepoIntelligence.d.ts +28 -0
  9. package/dist/fixtures/legacyEngineRepoIntelligence.d.ts.map +1 -0
  10. package/dist/fixtures/legacyEngineRepoIntelligence.js +28 -0
  11. package/dist/fixtures/repoIntelligenceFixtures.d.ts +0 -2
  12. package/dist/fixtures/repoIntelligenceFixtures.d.ts.map +1 -1
  13. package/dist/fixtures/repoIntelligenceFixtures.js +14 -4
  14. package/dist/fixtures/repoIntelligenceTestHelpers.d.ts +5 -0
  15. package/dist/fixtures/repoIntelligenceTestHelpers.d.ts.map +1 -0
  16. package/dist/fixtures/repoIntelligenceTestHelpers.js +28 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -0
  20. package/dist/narrativeParity.test.js +2 -1
  21. package/dist/narrativePresentation.test.js +2 -1
  22. package/dist/repoIntelligenceMappers.d.ts +20 -0
  23. package/dist/repoIntelligenceMappers.d.ts.map +1 -0
  24. package/dist/repoIntelligenceMappers.js +60 -0
  25. package/dist/repoIntelligenceSchema.contract.test.d.ts +2 -0
  26. package/dist/repoIntelligenceSchema.contract.test.d.ts.map +1 -0
  27. package/dist/repoIntelligenceSchema.contract.test.js +19 -0
  28. package/dist/repoIntelligenceSchema.d.ts +52 -83
  29. package/dist/repoIntelligenceSchema.d.ts.map +1 -1
  30. package/dist/repoIntelligenceSchema.js +99 -141
  31. package/dist/repoIntelligenceValidation.d.ts +18 -0
  32. package/dist/repoIntelligenceValidation.d.ts.map +1 -0
  33. package/dist/repoIntelligenceValidation.js +76 -0
  34. package/dist/repoIntelligenceValidation.persist.test.d.ts +2 -0
  35. package/dist/repoIntelligenceValidation.persist.test.d.ts.map +1 -0
  36. package/dist/repoIntelligenceValidation.persist.test.js +44 -0
  37. package/dist/scanDetailViewModel.test.js +2 -1
  38. package/dist/scanNarrativeFacts.d.ts +2 -0
  39. package/dist/scanNarrativeFacts.d.ts.map +1 -1
  40. package/dist/scanNarrativeFacts.js +1 -0
  41. package/dist/types.d.ts +15 -3
  42. package/dist/types.d.ts.map +1 -1
  43. package/package.json +1 -1
@@ -1,10 +1,7 @@
1
- import { normalizeReachabilityKind, normalizeRuntimeSurfaceKind } from "./repoIntelligenceSchema.js";
2
1
  import { type ScanNarrativeFacts } from "./scanNarrativeFacts.js";
3
2
  import type { ScanResult } from "./types.js";
4
3
  /**
5
4
  * Consumer-agnostic scan intelligence — structured facts only (no UI copy).
6
5
  */
7
6
  export declare function deriveScanNarrative(result: ScanResult | null | undefined): ScanNarrativeFacts;
8
- /** @internal Test helper — normalize loose engine strings. */
9
- export { normalizeReachabilityKind, normalizeRuntimeSurfaceKind };
10
7
  //# sourceMappingURL=deriveScanNarrative.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deriveScanNarrative.d.ts","sourceRoot":"","sources":["../src/deriveScanNarrative.ts"],"names":[],"mappings":"AACA,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAM5B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAYL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAKV,UAAU,EACX,MAAM,YAAY,CAAC;AAkZpB;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,GACpC,kBAAkB,CAiGpB;AAED,8DAA8D;AAC9D,OAAO,EAAE,yBAAyB,EAAE,2BAA2B,EAAE,CAAC"}
1
+ {"version":3,"file":"deriveScanNarrative.d.ts","sourceRoot":"","sources":["../src/deriveScanNarrative.ts"],"names":[],"mappings":"AAMA,OAAO,EAaL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAKV,UAAU,EACX,MAAM,YAAY,CAAC;AAgapB;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,GACpC,kBAAkB,CAsGpB"}
@@ -1,5 +1,5 @@
1
1
  import { extractRepositoryContextFacts } from "./extractRepositoryContextFacts.js";
2
- import { normalizeReachabilityKind, normalizeRuntimeSurfaceKind, parseRepoIntelligence, readBlastLevel, readReachabilityFromLoose, readSurfaceFromLoose, } from "./repoIntelligenceSchema.js";
2
+ import { readBlastLevel, safeParseRepoIntelligence, } from "./repoIntelligenceSchema.js";
3
3
  import { EMPTY_SCAN_NARRATIVE_FACTS, NARRATIVE_FACT_MAX_AREAS, NARRATIVE_FACT_MAX_HOTSPOTS, NARRATIVE_FACT_MAX_PATHS, } from "./scanNarrativeFacts.js";
4
4
  import { mergePostureFromDecision } from "./riskVocabulary.js";
5
5
  import { selectTopAffectedAreas } from "./selectTopAffectedAreas.js";
@@ -32,10 +32,6 @@ function uniqueStrings(values) {
32
32
  }
33
33
  return out;
34
34
  }
35
- function usageEntryPackageName(entry) {
36
- const name = (entry.packageName ?? entry.package ?? "").trim();
37
- return name || null;
38
- }
39
35
  function collectPackageUsageForChanged(ri, changed) {
40
36
  const byPackage = new Map();
41
37
  const ensure = (packageName) => {
@@ -53,8 +49,8 @@ function collectPackageUsageForChanged(ri, changed) {
53
49
  return row;
54
50
  };
55
51
  const mergeEntry = (entry, packageNameOverride) => {
56
- const name = packageNameOverride ?? usageEntryPackageName(entry);
57
- if (!name || !changed.all.includes(name))
52
+ const name = packageNameOverride ?? entry.packageName.trim();
53
+ if (!changed.all.includes(name))
58
54
  return;
59
55
  const row = ensure(name);
60
56
  row.paths = uniqueStrings([...row.paths, ...(entry.paths ?? [])]);
@@ -69,8 +65,6 @@ function collectPackageUsageForChanged(ri, changed) {
69
65
  const pkgIntel = ri.packages?.[name];
70
66
  if (pkgIntel?.usage)
71
67
  mergeEntry(pkgIntel.usage, name);
72
- if (pkgIntel?.packageUsage)
73
- mergeEntry(pkgIntel.packageUsage, name);
74
68
  if (pkgIntel?.areas?.length) {
75
69
  const row = ensure(name);
76
70
  row.areas = uniqueStrings([...row.areas, ...pkgIntel.areas]);
@@ -98,7 +92,7 @@ function collectFrameworks(ri) {
98
92
  function collectCodeHotspots(ri, primary) {
99
93
  const hotspots = [];
100
94
  const pkgIntel = primary && ri.packages?.[primary] ? ri.packages[primary] : undefined;
101
- const riHotspots = ri.hotspots ?? pkgIntel?.hotspots ?? [];
95
+ const riHotspots = ri.hotspots ?? [];
102
96
  for (const h of riHotspots) {
103
97
  const name = (h.packageName ?? primary ?? "").trim();
104
98
  if (!name)
@@ -136,10 +130,8 @@ function extractTier1FromRepoIntelligence(ri, changed) {
136
130
  const packageUsage = collectPackageUsageForChanged(ri, changed);
137
131
  const frameworks = collectFrameworks(ri);
138
132
  const paths = allUsagePaths(packageUsage);
139
- const runtimeRaw = pkgIntel?.runtimeSurface ?? ri.runtimeSurface ?? undefined;
140
- const reachRaw = pkgIntel?.reachability ?? ri.reachability ?? undefined;
141
- const runtimeKind = readSurfaceFromLoose(runtimeRaw) ?? (primary ? "unknown" : null);
142
- const reachKind = readReachabilityFromLoose(reachRaw);
133
+ const runtimeKind = pkgIntel?.runtimeSurface ?? null;
134
+ const reachKind = pkgIntel?.reachability ?? null;
143
135
  const runtimeSurface = runtimeKind != null
144
136
  ? {
145
137
  kind: runtimeKind,
@@ -291,6 +283,24 @@ function blastRadiusFromChangedCount(count) {
291
283
  factors: [],
292
284
  };
293
285
  }
286
+ function resolveTrustedRepoIntelligence(result) {
287
+ const raw = result.repoIntelligence;
288
+ const validationStatus = result.analysisPreparation?.repoIntelligenceValidation?.status;
289
+ if (raw == null) {
290
+ return { parsed: null, parseStatus: "absent" };
291
+ }
292
+ if (validationStatus === "invalid") {
293
+ return { parsed: null, parseStatus: "untrusted" };
294
+ }
295
+ if (validationStatus !== "valid") {
296
+ return { parsed: null, parseStatus: "absent" };
297
+ }
298
+ const parsed = safeParseRepoIntelligence(raw);
299
+ if (parsed.ok) {
300
+ return { parsed: parsed.value, parseStatus: "ok" };
301
+ }
302
+ return { parsed: null, parseStatus: "invalid" };
303
+ }
294
304
  /**
295
305
  * Consumer-agnostic scan intelligence — structured facts only (no UI copy).
296
306
  */
@@ -304,8 +314,10 @@ export function deriveScanNarrative(result) {
304
314
  ? result.totalScore
305
315
  : null;
306
316
  const codeIntelligenceAvailable = result.analysisPreparation?.codeIntelligenceAvailable !== false;
307
- const parsedRi = parseRepoIntelligence(result.repoIntelligence);
308
- const corpusGate = codeIntelligenceAvailable && parsedRi != null;
317
+ const { parsed: parsedRi, parseStatus: repoIntelligenceParse } = resolveTrustedRepoIntelligence(result);
318
+ const corpusGate = codeIntelligenceAvailable &&
319
+ repoIntelligenceParse === "ok" &&
320
+ parsedRi != null;
309
321
  let tier1Blocks = emptyTier1Blocks();
310
322
  if (corpusGate && parsedRi) {
311
323
  tier1Blocks = extractTier1FromRepoIntelligence(parsedRi, changedPackages);
@@ -350,6 +362,7 @@ export function deriveScanNarrative(result) {
350
362
  tier2: reviewerGuidance.length > 0,
351
363
  tier3,
352
364
  },
365
+ repoIntelligenceParse,
353
366
  },
354
367
  changedPackages,
355
368
  packageUsage: tier1Blocks.packageUsage,
@@ -366,5 +379,3 @@ export function deriveScanNarrative(result) {
366
379
  riskIndex,
367
380
  };
368
381
  }
369
- /** @internal Test helper — normalize loose engine strings. */
370
- export { normalizeReachabilityKind, normalizeRuntimeSurfaceKind };
@@ -1,6 +1,8 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { deriveScanNarrative } from "./deriveScanNarrative.js";
3
- import { fixtureRepoIntelligenceEmpty, fixtureRepoIntelligenceFastify, fixtureRepoIntelligenceMultiPackage, fixtureRepoIntelligenceTypescript, } from "./fixtures/repoIntelligenceFixtures.js";
3
+ import { legacyEngineRepoIntelligenceJwt } from "./fixtures/legacyEngineRepoIntelligence.js";
4
+ import { fixtureRepoIntelligenceFastify, fixtureRepoIntelligenceMultiPackage, fixtureRepoIntelligenceTypescript, } from "./fixtures/repoIntelligenceFixtures.js";
5
+ import { analysisPreparationWithInvalidRepoIntel, analysisPreparationWithValidRepoIntel, } from "./fixtures/repoIntelligenceTestHelpers.js";
4
6
  const baseResult = {
5
7
  totalScore: 10,
6
8
  layerScores: {
@@ -18,15 +20,42 @@ describe("deriveScanNarrative", () => {
18
20
  expect(facts.availability.mode).toBe("denormalized");
19
21
  expect(facts.changedPackages.all).toEqual([]);
20
22
  });
23
+ it("ignores repoIntelligence when validation status is not valid", () => {
24
+ const result = {
25
+ ...baseResult,
26
+ changedPackages: ["fastify"],
27
+ analysisPreparation: {
28
+ codeIntelligenceAvailable: true,
29
+ warnings: [],
30
+ },
31
+ repoIntelligence: fixtureRepoIntelligenceFastify,
32
+ };
33
+ const facts = deriveScanNarrative(result);
34
+ expect(facts.availability.repoIntelligenceParse).toBe("absent");
35
+ expect(facts.availability.tiersPresent.tier1).toBe(false);
36
+ });
37
+ it("treats worker-invalid repoIntelligence as untrusted (raw retained, not consumed)", () => {
38
+ const result = {
39
+ ...baseResult,
40
+ changedPackages: ["jsonwebtoken"],
41
+ analysisPreparation: analysisPreparationWithInvalidRepoIntel(),
42
+ repoIntelligence: legacyEngineRepoIntelligenceJwt,
43
+ };
44
+ const facts = deriveScanNarrative(result);
45
+ expect(facts.availability.repoIntelligenceParse).toBe("untrusted");
46
+ expect(facts.availability.tiersPresent.tier1).toBe(false);
47
+ expect(facts.packageUsage).toHaveLength(0);
48
+ });
21
49
  it("populates tier1 from repoIntelligence when corpus available", () => {
22
50
  const result = {
23
51
  ...baseResult,
24
52
  changedPackages: ["fastify"],
25
- analysisPreparation: { codeIntelligenceAvailable: true, warnings: [] },
53
+ analysisPreparation: analysisPreparationWithValidRepoIntel(),
26
54
  repoIntelligence: fixtureRepoIntelligenceFastify,
27
55
  };
28
56
  const facts = deriveScanNarrative(result);
29
57
  expect(facts.availability.mode).toBe("pr_intelligence");
58
+ expect(facts.availability.repoIntelligenceParse).toBe("ok");
30
59
  expect(facts.availability.codeIntelligenceAvailable).toBe(true);
31
60
  expect(facts.changedPackages.primary).toBe("fastify");
32
61
  expect(facts.runtimeSurface?.kind).toBe("runtime");
@@ -87,7 +116,7 @@ describe("deriveScanNarrative", () => {
87
116
  const result = {
88
117
  ...baseResult,
89
118
  changedPackages: ["typescript"],
90
- analysisPreparation: { codeIntelligenceAvailable: true, warnings: [] },
119
+ analysisPreparation: analysisPreparationWithValidRepoIntel(),
91
120
  repoIntelligence: fixtureRepoIntelligenceTypescript,
92
121
  };
93
122
  const facts = deriveScanNarrative(result);
@@ -100,7 +129,7 @@ describe("deriveScanNarrative", () => {
100
129
  const result = {
101
130
  ...baseResult,
102
131
  changedPackages: ["lodash", "axios"],
103
- analysisPreparation: { codeIntelligenceAvailable: true, warnings: [] },
132
+ analysisPreparation: analysisPreparationWithValidRepoIntel(),
104
133
  repoIntelligence: fixtureRepoIntelligenceMultiPackage,
105
134
  };
106
135
  const facts = deriveScanNarrative(result);
@@ -124,7 +153,7 @@ describe("deriveScanNarrative", () => {
124
153
  const result = {
125
154
  ...baseResult,
126
155
  changedPackages: ["fastify"],
127
- analysisPreparation: { codeIntelligenceAvailable: true, warnings: [] },
156
+ analysisPreparation: analysisPreparationWithValidRepoIntel(),
128
157
  repoIntelligence: fixtureRepoIntelligenceFastify,
129
158
  graphInsights: {
130
159
  maxDepth: 3,
@@ -151,14 +180,14 @@ describe("deriveScanNarrative", () => {
151
180
  expect(facts.hotspots.map((h) => h.packageName)).toContain("transitive-only-pkg");
152
181
  expect(facts.hotspots.filter((h) => h.packageName === "fastify")).toHaveLength(1);
153
182
  });
154
- it("treats empty packages-only repoIntelligence as absent", () => {
183
+ it("does not tier1 when repoIntelligence block is omitted", () => {
155
184
  const result = {
156
185
  ...baseResult,
157
186
  changedPackages: ["react"],
158
- analysisPreparation: { codeIntelligenceAvailable: true, warnings: [] },
159
- repoIntelligence: fixtureRepoIntelligenceEmpty,
187
+ analysisPreparation: analysisPreparationWithValidRepoIntel(),
160
188
  };
161
189
  const facts = deriveScanNarrative(result);
190
+ expect(facts.availability.repoIntelligenceParse).toBe("absent");
162
191
  expect(facts.availability.tiersPresent.tier1).toBe(false);
163
192
  });
164
193
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=engineWireRegression.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engineWireRegression.test.d.ts","sourceRoot":"","sources":["../src/engineWireRegression.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { deriveScanCardSummary } from "./scanCardSummary.js";
3
+ import { deriveScanNarrative } from "./deriveScanNarrative.js";
4
+ import { fixtureRepoIntelligenceFastify, fixtureRepoIntelligenceMultiPackage, } from "./fixtures/repoIntelligenceFixtures.js";
5
+ import { validateRepoIntelligenceWire } from "./repoIntelligenceSchema.js";
6
+ const baseScan = {
7
+ totalScore: 55,
8
+ layerScores: {
9
+ security: 10,
10
+ maintainability: 20,
11
+ ecosystem: 30,
12
+ upgradeImpact: 15,
13
+ },
14
+ findings: [],
15
+ generatedAt: "2026-01-01T00:00:00.000Z",
16
+ analysisPreparation: {
17
+ codeIntelligenceAvailable: true,
18
+ warnings: [],
19
+ repoIntelligenceValidation: {
20
+ status: "valid",
21
+ abi: "1",
22
+ validatedAt: "2026-01-01T00:00:00.000Z",
23
+ },
24
+ },
25
+ };
26
+ describe("engine wire regression (canonical fixtures)", () => {
27
+ it("fastify fixture: parse → tier1 pr_intelligence → card usage lines", () => {
28
+ validateRepoIntelligenceWire(fixtureRepoIntelligenceFastify);
29
+ const result = {
30
+ ...baseScan,
31
+ changedPackages: ["fastify"],
32
+ repoIntelligence: fixtureRepoIntelligenceFastify,
33
+ };
34
+ const facts = deriveScanNarrative(result);
35
+ expect(facts.availability.tiersPresent.tier1).toBe(true);
36
+ expect(facts.availability.mode).toBe("pr_intelligence");
37
+ expect(facts.availability.repoIntelligenceParse).toBe("ok");
38
+ expect(facts.packageUsage[0]?.packageName).toBe("fastify");
39
+ const card = deriveScanCardSummary(result, "done");
40
+ expect(card.usageSummary).toBeTruthy();
41
+ expect(card.verificationLine).toBeTruthy();
42
+ });
43
+ it("multi-package fixture: both packages in packageUsage", () => {
44
+ validateRepoIntelligenceWire(fixtureRepoIntelligenceMultiPackage);
45
+ const result = {
46
+ ...baseScan,
47
+ changedPackages: ["lodash", "axios"],
48
+ repoIntelligence: fixtureRepoIntelligenceMultiPackage,
49
+ };
50
+ const facts = deriveScanNarrative(result);
51
+ expect(facts.availability.mode).toBe("pr_intelligence");
52
+ expect(facts.packageUsage.map((u) => u.packageName)).toEqual([
53
+ "lodash",
54
+ "axios",
55
+ ]);
56
+ });
57
+ });
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Pre-contract engine wire shape (Wave 1/2 drift). Used to assert strict schema rejection
3
+ * and narrative fallback while preserving raw payload for forensics.
4
+ */
5
+ export declare const legacyEngineRepoIntelligenceJwt: {
6
+ readonly packageUsage: readonly [{
7
+ readonly name: "jsonwebtoken";
8
+ readonly files: readonly ["src/auth/auth.service.ts", "src/auth/login.controller.ts"];
9
+ readonly reachability: "moderate";
10
+ readonly runtimeSurface: "runtime";
11
+ readonly areas: readonly ["authentication"];
12
+ readonly blastRadius: "moderate";
13
+ }];
14
+ readonly hotspots: readonly [{
15
+ readonly file: "src/auth/auth.service.ts";
16
+ readonly fanIn: 2;
17
+ readonly areas: readonly ["authentication"];
18
+ }];
19
+ readonly frameworks: readonly [{
20
+ readonly framework: "express";
21
+ readonly roles: readonly ["middleware"];
22
+ }];
23
+ readonly blastRadius: {
24
+ readonly level: "large";
25
+ readonly factors: readonly ["reaches runtime application code"];
26
+ };
27
+ };
28
+ //# sourceMappingURL=legacyEngineRepoIntelligence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacyEngineRepoIntelligence.d.ts","sourceRoot":"","sources":["../../src/fixtures/legacyEngineRepoIntelligence.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;CAuBlC,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Pre-contract engine wire shape (Wave 1/2 drift). Used to assert strict schema rejection
3
+ * and narrative fallback while preserving raw payload for forensics.
4
+ */
5
+ export const legacyEngineRepoIntelligenceJwt = {
6
+ packageUsage: [
7
+ {
8
+ name: "jsonwebtoken",
9
+ files: ["src/auth/auth.service.ts", "src/auth/login.controller.ts"],
10
+ reachability: "moderate",
11
+ runtimeSurface: "runtime",
12
+ areas: ["authentication"],
13
+ blastRadius: "moderate",
14
+ },
15
+ ],
16
+ hotspots: [
17
+ {
18
+ file: "src/auth/auth.service.ts",
19
+ fanIn: 2,
20
+ areas: ["authentication"],
21
+ },
22
+ ],
23
+ frameworks: [{ framework: "express", roles: ["middleware"] }],
24
+ blastRadius: {
25
+ level: "large",
26
+ factors: ["reaches runtime application code"],
27
+ },
28
+ };
@@ -3,8 +3,6 @@ import type { RepoIntelligence } from "../repoIntelligenceSchema.js";
3
3
  export declare const fixtureRepoIntelligenceFastify: RepoIntelligence;
4
4
  /** Build-only toolchain change — no runtime paths. */
5
5
  export declare const fixtureRepoIntelligenceTypescript: RepoIntelligence;
6
- /** Empty corpus stub (engine-test-fixture default). */
7
- export declare const fixtureRepoIntelligenceEmpty: RepoIntelligence;
8
6
  /** Multi-package PR with distinct usage per package. */
9
7
  export declare const fixtureRepoIntelligenceMultiPackage: RepoIntelligence;
10
8
  //# sourceMappingURL=repoIntelligenceFixtures.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repoIntelligenceFixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/repoIntelligenceFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,iEAAiE;AACjE,eAAO,MAAM,8BAA8B,EAAE,gBA8B5C,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,iCAAiC,EAAE,gBAS/C,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,4BAA4B,EAAE,gBAE1C,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,mCAAmC,EAAE,gBA4CjD,CAAC"}
1
+ {"version":3,"file":"repoIntelligenceFixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/repoIntelligenceFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,iEAAiE;AACjE,eAAO,MAAM,8BAA8B,EAAE,gBAmC5C,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,iCAAiC,EAAE,gBAc/C,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,mCAAmC,EAAE,gBAgDjD,CAAC"}
@@ -6,6 +6,11 @@ export const fixtureRepoIntelligenceFastify = {
6
6
  reachability: "on_runtime_paths",
7
7
  usage: {
8
8
  packageName: "fastify",
9
+ files: [
10
+ "apps/api/src/server.ts",
11
+ "apps/api/src/routes/account.ts",
12
+ "apps/api/src/middleware/auth.ts",
13
+ ],
9
14
  paths: ["apps/api/src/server.ts", "apps/api/src/routes/account.ts"],
10
15
  criticalPaths: ["apps/api/src/middleware/auth.ts"],
11
16
  areas: ["API", "Auth middleware"],
@@ -36,15 +41,16 @@ export const fixtureRepoIntelligenceTypescript = {
36
41
  typescript: {
37
42
  runtimeSurface: "build",
38
43
  reachability: "build_only",
44
+ usage: {
45
+ packageName: "typescript",
46
+ files: ["tsconfig.json"],
47
+ paths: [],
48
+ },
39
49
  },
40
50
  },
41
51
  blastRadius: { level: "narrow", changedPackageCount: 1 },
42
52
  applicationAreas: [],
43
53
  };
44
- /** Empty corpus stub (engine-test-fixture default). */
45
- export const fixtureRepoIntelligenceEmpty = {
46
- packages: {},
47
- };
48
54
  /** Multi-package PR with distinct usage per package. */
49
55
  export const fixtureRepoIntelligenceMultiPackage = {
50
56
  packages: {
@@ -53,6 +59,7 @@ export const fixtureRepoIntelligenceMultiPackage = {
53
59
  reachability: "on_runtime_paths",
54
60
  usage: {
55
61
  packageName: "lodash",
62
+ files: ["apps/billing/export.ts"],
56
63
  paths: ["apps/billing/export.ts"],
57
64
  areas: ["Billing"],
58
65
  },
@@ -62,6 +69,7 @@ export const fixtureRepoIntelligenceMultiPackage = {
62
69
  reachability: "on_runtime_paths",
63
70
  usage: {
64
71
  packageName: "axios",
72
+ files: ["apps/api/client.ts"],
65
73
  paths: ["apps/api/client.ts"],
66
74
  areas: ["API"],
67
75
  },
@@ -70,10 +78,12 @@ export const fixtureRepoIntelligenceMultiPackage = {
70
78
  packageUsage: [
71
79
  {
72
80
  packageName: "lodash",
81
+ files: ["apps/billing/export.ts"],
73
82
  paths: ["apps/billing/export.ts"],
74
83
  },
75
84
  {
76
85
  packageName: "axios",
86
+ files: ["apps/api/client.ts"],
77
87
  paths: ["apps/api/client.ts"],
78
88
  },
79
89
  ],
@@ -0,0 +1,5 @@
1
+ import type { AnalysisPreparation } from "../types.js";
2
+ /** Worker-shaped preparation for tests that consume trusted repoIntelligence. */
3
+ export declare function analysisPreparationWithValidRepoIntel(over?: Partial<AnalysisPreparation>): AnalysisPreparation;
4
+ export declare function analysisPreparationWithInvalidRepoIntel(over?: Partial<AnalysisPreparation>): AnalysisPreparation;
5
+ //# sourceMappingURL=repoIntelligenceTestHelpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repoIntelligenceTestHelpers.d.ts","sourceRoot":"","sources":["../../src/fixtures/repoIntelligenceTestHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvD,iFAAiF;AACjF,wBAAgB,qCAAqC,CACnD,IAAI,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAClC,mBAAmB,CAWrB;AAED,wBAAgB,uCAAuC,CACrD,IAAI,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAClC,mBAAmB,CAarB"}
@@ -0,0 +1,28 @@
1
+ import { REPO_INTELLIGENCE_ABI } from "../repoIntelligenceSchema.js";
2
+ /** Worker-shaped preparation for tests that consume trusted repoIntelligence. */
3
+ export function analysisPreparationWithValidRepoIntel(over) {
4
+ return {
5
+ codeIntelligenceAvailable: true,
6
+ warnings: [],
7
+ repoIntelligenceValidation: {
8
+ status: "valid",
9
+ abi: REPO_INTELLIGENCE_ABI,
10
+ validatedAt: "2026-01-01T00:00:00.000Z",
11
+ },
12
+ ...over,
13
+ };
14
+ }
15
+ export function analysisPreparationWithInvalidRepoIntel(over) {
16
+ return {
17
+ codeIntelligenceAvailable: true,
18
+ warnings: [],
19
+ repoIntelligenceValidation: {
20
+ status: "invalid",
21
+ abi: REPO_INTELLIGENCE_ABI,
22
+ issueCount: 1,
23
+ representativeIssues: ["packages: at least one package required"],
24
+ validatedAt: "2026-01-01T00:00:00.000Z",
25
+ },
26
+ ...over,
27
+ };
28
+ }
package/dist/index.d.ts CHANGED
@@ -13,6 +13,8 @@ export * from "./deriveCardOperationalObservations.js";
13
13
  export * from "./deriveCardDisplaySummary.js";
14
14
  export * from "./scanNarrativeFacts.js";
15
15
  export * from "./repoIntelligenceSchema.js";
16
+ export * from "./repoIntelligenceMappers.js";
17
+ export * from "./repoIntelligenceValidation.js";
16
18
  export * from "./deriveScanNarrative.js";
17
19
  export * from "./extractRepositoryContextFacts.js";
18
20
  export * from "./presentScanCardSummary.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,oCAAoC,CAAC;AACnD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,sCAAsC,CAAC;AACrD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,0BAA0B,CAAC;AACzC,cAAc,oCAAoC,CAAC;AACnD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,sCAAsC,CAAC;AACrD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -13,6 +13,8 @@ export * from "./deriveCardOperationalObservations.js";
13
13
  export * from "./deriveCardDisplaySummary.js";
14
14
  export * from "./scanNarrativeFacts.js";
15
15
  export * from "./repoIntelligenceSchema.js";
16
+ export * from "./repoIntelligenceMappers.js";
17
+ export * from "./repoIntelligenceValidation.js";
16
18
  export * from "./deriveScanNarrative.js";
17
19
  export * from "./extractRepositoryContextFacts.js";
18
20
  export * from "./presentScanCardSummary.js";
@@ -5,6 +5,7 @@ import { deriveScanCardSummary } from "./scanCardSummary.js";
5
5
  import { deriveScanDetailViewModel } from "./scanDetailViewModel.js";
6
6
  import { presentGitHubPrComment } from "./presentGitHubPrComment.js";
7
7
  import { fixtureRepoIntelligenceFastify } from "./fixtures/repoIntelligenceFixtures.js";
8
+ import { analysisPreparationWithValidRepoIntel } from "./fixtures/repoIntelligenceTestHelpers.js";
8
9
  const ORIGIN = "https://app.example.com";
9
10
  const SCAN_ID = "22222222-2222-4222-8222-222222222222";
10
11
  describe("narrative parity across consumers", () => {
@@ -20,7 +21,7 @@ describe("narrative parity across consumers", () => {
20
21
  findings: [],
21
22
  generatedAt: "2026-01-01T00:00:00.000Z",
22
23
  changedPackages: ["fastify"],
23
- analysisPreparation: { codeIntelligenceAvailable: true, warnings: [] },
24
+ analysisPreparation: analysisPreparationWithValidRepoIntel(),
24
25
  repoIntelligence: fixtureRepoIntelligenceFastify,
25
26
  decision: {
26
27
  recommendation: "needs_review",
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { deriveScanNarrative } from "./deriveScanNarrative.js";
3
3
  import { fixtureRepoIntelligenceFastify } from "./fixtures/repoIntelligenceFixtures.js";
4
+ import { analysisPreparationWithValidRepoIntel } from "./fixtures/repoIntelligenceTestHelpers.js";
4
5
  import { composeVerificationPrompt, formatChangedPackagesShort, formatUsageSummaryLine, summarizePackageUsage, } from "./narrativePresentation.js";
5
6
  const baseResult = {
6
7
  totalScore: 10,
@@ -25,7 +26,7 @@ describe("narrativePresentation", () => {
25
26
  const facts = deriveScanNarrative({
26
27
  ...baseResult,
27
28
  changedPackages: ["fastify"],
28
- analysisPreparation: { codeIntelligenceAvailable: true, warnings: [] },
29
+ analysisPreparation: analysisPreparationWithValidRepoIntel(),
29
30
  repoIntelligence: fixtureRepoIntelligenceFastify,
30
31
  });
31
32
  const usage = summarizePackageUsage(facts);
@@ -0,0 +1,20 @@
1
+ import type { BlastRadiusLevel, ReachabilityKind, RuntimeSurfaceKind } from "./scanNarrativeFacts.js";
2
+ /** Engine-internal blast radius vocabulary → canonical wire. */
3
+ export type EngineBlastRadiusLevel = "small" | "moderate" | "large";
4
+ /** Engine-internal reachability spread buckets. */
5
+ export type EngineReachabilityBucket = "very_low" | "low" | "moderate" | "high";
6
+ /** Engine-internal runtime surface labels. */
7
+ export type EngineRuntimeSurface = "runtime" | "test" | "build" | "dev" | "ci";
8
+ export declare function mapBlastRadiusLevel(level: EngineBlastRadiusLevel): BlastRadiusLevel;
9
+ export declare function mapRuntimeSurfaceKind(surface: EngineRuntimeSurface): RuntimeSurfaceKind;
10
+ /**
11
+ * Maps engine reachability spread + runtime classification to narrative reachability kind.
12
+ * Documented in repoIntelligenceSchema.ts header.
13
+ */
14
+ export declare function mapReachabilityKind(input: {
15
+ bucket: EngineReachabilityBucket;
16
+ reachesRuntime: boolean;
17
+ dominantSurface: EngineRuntimeSurface;
18
+ }): ReachabilityKind;
19
+ export declare function areaSlugToLabel(slug: string): string;
20
+ //# sourceMappingURL=repoIntelligenceMappers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repoIntelligenceMappers.d.ts","sourceRoot":"","sources":["../src/repoIntelligenceMappers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAEjC,gEAAgE;AAChE,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;AAEpE,mDAAmD;AACnD,MAAM,MAAM,wBAAwB,GAAG,UAAU,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC;AAEhF,8CAA8C;AAC9C,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,IAAI,CAAC;AAE/E,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,sBAAsB,GAC5B,gBAAgB,CAalB;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,GAC5B,kBAAkB,CAapB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,MAAM,EAAE,wBAAwB,CAAC;IACjC,cAAc,EAAE,OAAO,CAAC;IACxB,eAAe,EAAE,oBAAoB,CAAC;CACvC,GAAG,gBAAgB,CAwBnB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKpD"}
@@ -0,0 +1,60 @@
1
+ export function mapBlastRadiusLevel(level) {
2
+ switch (level) {
3
+ case "small":
4
+ return "narrow";
5
+ case "moderate":
6
+ return "moderate";
7
+ case "large":
8
+ return "wide";
9
+ default: {
10
+ const x = level;
11
+ return x;
12
+ }
13
+ }
14
+ }
15
+ export function mapRuntimeSurfaceKind(surface) {
16
+ switch (surface) {
17
+ case "runtime":
18
+ return "runtime";
19
+ case "test":
20
+ return "test";
21
+ case "build":
22
+ case "dev":
23
+ case "ci":
24
+ return "build";
25
+ default:
26
+ return "unknown";
27
+ }
28
+ }
29
+ /**
30
+ * Maps engine reachability spread + runtime classification to narrative reachability kind.
31
+ * Documented in repoIntelligenceSchema.ts header.
32
+ */
33
+ export function mapReachabilityKind(input) {
34
+ const { bucket, reachesRuntime, dominantSurface } = input;
35
+ if (dominantSurface === "test" && !reachesRuntime) {
36
+ return "test_only";
37
+ }
38
+ if ((dominantSurface === "build" ||
39
+ dominantSurface === "dev" ||
40
+ dominantSurface === "ci") &&
41
+ !reachesRuntime) {
42
+ return "build_only";
43
+ }
44
+ if (reachesRuntime) {
45
+ return "on_runtime_paths";
46
+ }
47
+ if (bucket === "very_low" || bucket === "low") {
48
+ return "unreachable";
49
+ }
50
+ if (bucket === "moderate" || bucket === "high") {
51
+ return "on_runtime_paths";
52
+ }
53
+ return "unknown";
54
+ }
55
+ export function areaSlugToLabel(slug) {
56
+ return slug
57
+ .split("_")
58
+ .map((w) => (w ? w[0].toUpperCase() + w.slice(1) : w))
59
+ .join(" ");
60
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=repoIntelligenceSchema.contract.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repoIntelligenceSchema.contract.test.d.ts","sourceRoot":"","sources":["../src/repoIntelligenceSchema.contract.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { fixtureRepoIntelligenceFastify, fixtureRepoIntelligenceMultiPackage, } from "./fixtures/repoIntelligenceFixtures.js";
3
+ import { legacyEngineRepoIntelligenceJwt } from "./fixtures/legacyEngineRepoIntelligence.js";
4
+ import { safeParseRepoIntelligence, validateRepoIntelligenceWire, } from "./repoIntelligenceSchema.js";
5
+ describe("repoIntelligenceWireSchema contract", () => {
6
+ it("accepts canonical consumer fixtures", () => {
7
+ expect(() => validateRepoIntelligenceWire(fixtureRepoIntelligenceFastify)).not.toThrow();
8
+ expect(() => validateRepoIntelligenceWire(fixtureRepoIntelligenceMultiPackage)).not.toThrow();
9
+ });
10
+ it("rejects legacy engine wire (name, large blast, object frameworks, file hotspots)", () => {
11
+ const r = safeParseRepoIntelligence(legacyEngineRepoIntelligenceJwt);
12
+ expect(r.ok).toBe(false);
13
+ if (r.ok)
14
+ return;
15
+ expect(r.issueCount).toBeGreaterThan(0);
16
+ const joined = r.issues.join(" ");
17
+ expect(joined.length).toBeGreaterThan(0);
18
+ });
19
+ });