@mergesignal/shared 0.12.3 → 0.13.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 (172) hide show
  1. package/dist/assessment/authoredCommunication.d.ts +30 -0
  2. package/dist/assessment/authoredCommunication.d.ts.map +1 -0
  3. package/dist/assessment/authoredCommunication.js +18 -0
  4. package/dist/assessment/index.d.ts +6 -0
  5. package/dist/assessment/index.d.ts.map +1 -0
  6. package/dist/assessment/index.js +5 -0
  7. package/dist/assessment/literals.d.ts +44 -0
  8. package/dist/assessment/literals.d.ts.map +1 -0
  9. package/dist/assessment/literals.js +140 -0
  10. package/dist/assessment/schema.d.ts +311 -0
  11. package/dist/assessment/schema.d.ts.map +1 -0
  12. package/dist/assessment/schema.js +162 -0
  13. package/dist/assessment/types.d.ts +197 -0
  14. package/dist/assessment/types.d.ts.map +1 -0
  15. package/dist/assessment/types.js +1 -0
  16. package/dist/assessment/version.d.ts +11 -0
  17. package/dist/assessment/version.d.ts.map +1 -0
  18. package/dist/assessment/version.js +9 -0
  19. package/dist/assessmentLabels.d.ts +1 -1
  20. package/dist/assessmentLabels.d.ts.map +1 -1
  21. package/dist/assessmentPresentationUtils.d.ts +1 -1
  22. package/dist/assessmentPresentationUtils.d.ts.map +1 -1
  23. package/dist/assessmentProjection.d.ts +1 -1
  24. package/dist/assessmentProjection.d.ts.map +1 -1
  25. package/dist/deriveScanNarrative.d.ts.map +1 -1
  26. package/dist/deriveScanNarrative.js +2 -1
  27. package/dist/fixtures/assessmentFixtures.d.ts +1 -1
  28. package/dist/fixtures/assessmentScopeFixtures.d.ts +1 -1
  29. package/dist/index.d.ts +2 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +2 -0
  32. package/dist/lockfileEvidence.d.ts +22 -0
  33. package/dist/lockfileEvidence.d.ts.map +1 -0
  34. package/dist/lockfileEvidence.js +32 -0
  35. package/dist/presentation/dto/assessmentPresentationFields.d.ts +1 -1
  36. package/dist/presentation/dto/assessmentPresentationFields.d.ts.map +1 -1
  37. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts.map +1 -1
  38. package/dist/presentation/orchestration/buildScanPresentationBundle.js +2 -1
  39. package/dist/presentation/orchestration/scanPresentationBundle.d.ts +2 -1
  40. package/dist/presentation/orchestration/scanPresentationBundle.d.ts.map +1 -1
  41. package/dist/presentation/profile/buildProfileFromAssessment.d.ts +1 -1
  42. package/dist/presentation/profile/buildProfileFromAssessment.d.ts.map +1 -1
  43. package/dist/presentation/profile/buildProfileFromAssessment.js +13 -5
  44. package/dist/repoIntelligenceSchema.d.ts +5 -5
  45. package/dist/scanResultSchema.d.ts +6 -6
  46. package/dist/scanResultSchema.js +1 -1
  47. package/dist/types.d.ts +6 -2
  48. package/dist/types.d.ts.map +1 -1
  49. package/package.json +2 -3
  50. package/dist/actionsStepSummary.test.d.ts +0 -2
  51. package/dist/actionsStepSummary.test.d.ts.map +0 -1
  52. package/dist/actionsStepSummary.test.js +0 -53
  53. package/dist/assessmentProjection.verificationChannel.test.d.ts +0 -2
  54. package/dist/assessmentProjection.verificationChannel.test.d.ts.map +0 -1
  55. package/dist/assessmentProjection.verificationChannel.test.js +0 -110
  56. package/dist/deriveScanNarrative.test.d.ts +0 -2
  57. package/dist/deriveScanNarrative.test.d.ts.map +0 -1
  58. package/dist/deriveScanNarrative.test.js +0 -274
  59. package/dist/engineWireRegression.test.d.ts +0 -2
  60. package/dist/engineWireRegression.test.d.ts.map +0 -1
  61. package/dist/engineWireRegression.test.js +0 -77
  62. package/dist/fixtures/engineAbi4Fixtures.d.ts +0 -16
  63. package/dist/fixtures/engineAbi4Fixtures.d.ts.map +0 -1
  64. package/dist/fixtures/engineAbi4Fixtures.js +0 -26
  65. package/dist/fixtures/legacyEngineRepoIntelligence.d.ts +0 -28
  66. package/dist/fixtures/legacyEngineRepoIntelligence.d.ts.map +0 -1
  67. package/dist/fixtures/legacyEngineRepoIntelligence.js +0 -28
  68. package/dist/fixtures/repoIntelligenceFixtures.d.ts +0 -8
  69. package/dist/fixtures/repoIntelligenceFixtures.d.ts.map +0 -1
  70. package/dist/fixtures/repoIntelligenceFixtures.js +0 -123
  71. package/dist/fixtures/repoIntelligenceTestHelpers.d.ts +0 -5
  72. package/dist/fixtures/repoIntelligenceTestHelpers.d.ts.map +0 -1
  73. package/dist/fixtures/repoIntelligenceTestHelpers.js +0 -28
  74. package/dist/formatInsight.test.d.ts +0 -2
  75. package/dist/formatInsight.test.d.ts.map +0 -1
  76. package/dist/formatInsight.test.js +0 -102
  77. package/dist/narrativeParity.test.d.ts +0 -2
  78. package/dist/narrativeParity.test.d.ts.map +0 -1
  79. package/dist/narrativeParity.test.js +0 -35
  80. package/dist/narrativePresentation.test.d.ts +0 -2
  81. package/dist/narrativePresentation.test.d.ts.map +0 -1
  82. package/dist/narrativePresentation.test.js +0 -54
  83. package/dist/normalizeGeneratedText.test.d.ts +0 -2
  84. package/dist/normalizeGeneratedText.test.d.ts.map +0 -1
  85. package/dist/normalizeGeneratedText.test.js +0 -18
  86. package/dist/prRiskBand.test.d.ts +0 -2
  87. package/dist/prRiskBand.test.d.ts.map +0 -1
  88. package/dist/prRiskBand.test.js +0 -68
  89. package/dist/prRiskWire.test.d.ts +0 -2
  90. package/dist/prRiskWire.test.d.ts.map +0 -1
  91. package/dist/prRiskWire.test.js +0 -106
  92. package/dist/prRiskWiringRegression.test.d.ts +0 -2
  93. package/dist/prRiskWiringRegression.test.d.ts.map +0 -1
  94. package/dist/prRiskWiringRegression.test.js +0 -102
  95. package/dist/presentation/abi3Expression.test.d.ts +0 -13
  96. package/dist/presentation/abi3Expression.test.d.ts.map +0 -1
  97. package/dist/presentation/abi3Expression.test.js +0 -392
  98. package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts +0 -2
  99. package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts.map +0 -1
  100. package/dist/presentation/buildGitHubCheckRunOutput.test.js +0 -163
  101. package/dist/presentation/bundleAccessorParity.test.d.ts +0 -9
  102. package/dist/presentation/bundleAccessorParity.test.d.ts.map +0 -1
  103. package/dist/presentation/bundleAccessorParity.test.js +0 -126
  104. package/dist/presentation/cliParity.test.d.ts +0 -2
  105. package/dist/presentation/cliParity.test.d.ts.map +0 -1
  106. package/dist/presentation/cliParity.test.js +0 -49
  107. package/dist/presentation/compose/narrativeChannels.test.d.ts +0 -2
  108. package/dist/presentation/compose/narrativeChannels.test.d.ts.map +0 -1
  109. package/dist/presentation/compose/narrativeChannels.test.js +0 -42
  110. package/dist/presentation/decisionSurface.test.d.ts +0 -2
  111. package/dist/presentation/decisionSurface.test.d.ts.map +0 -1
  112. package/dist/presentation/decisionSurface.test.js +0 -290
  113. package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts +0 -2
  114. package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts.map +0 -1
  115. package/dist/presentation/fixtures/dashboardCardGolden.test.js +0 -38
  116. package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts +0 -11
  117. package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts.map +0 -1
  118. package/dist/presentation/fixtures/presentationPersonaFixtures.js +0 -208
  119. package/dist/presentation/fixtures/scanResultFixtures.d.ts +0 -5
  120. package/dist/presentation/fixtures/scanResultFixtures.d.ts.map +0 -1
  121. package/dist/presentation/fixtures/scanResultFixtures.js +0 -123
  122. package/dist/presentation/intent/postureVocabularyGuard.test.d.ts +0 -2
  123. package/dist/presentation/intent/postureVocabularyGuard.test.d.ts.map +0 -1
  124. package/dist/presentation/intent/postureVocabularyGuard.test.js +0 -23
  125. package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts +0 -2
  126. package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts.map +0 -1
  127. package/dist/presentation/orchestration/buildScanPresentationBundle.test.js +0 -99
  128. package/dist/presentation/presentation.test.d.ts +0 -2
  129. package/dist/presentation/presentation.test.d.ts.map +0 -1
  130. package/dist/presentation/presentation.test.js +0 -126
  131. package/dist/presentation/presentationPersonas.test.d.ts +0 -2
  132. package/dist/presentation/presentationPersonas.test.d.ts.map +0 -1
  133. package/dist/presentation/presentationPersonas.test.js +0 -164
  134. package/dist/presentation/surfaceParity.test.d.ts +0 -2
  135. package/dist/presentation/surfaceParity.test.d.ts.map +0 -1
  136. package/dist/presentation/surfaceParity.test.js +0 -156
  137. package/dist/presentation/verificationChannelParity.test.d.ts +0 -2
  138. package/dist/presentation/verificationChannelParity.test.d.ts.map +0 -1
  139. package/dist/presentation/verificationChannelParity.test.js +0 -57
  140. package/dist/productMessaging.test.d.ts +0 -2
  141. package/dist/productMessaging.test.d.ts.map +0 -1
  142. package/dist/productMessaging.test.js +0 -97
  143. package/dist/repoIntelligenceSchema.contract.test.d.ts +0 -2
  144. package/dist/repoIntelligenceSchema.contract.test.d.ts.map +0 -1
  145. package/dist/repoIntelligenceSchema.contract.test.js +0 -51
  146. package/dist/repoIntelligenceSemantics.test.d.ts +0 -2
  147. package/dist/repoIntelligenceSemantics.test.d.ts.map +0 -1
  148. package/dist/repoIntelligenceSemantics.test.js +0 -63
  149. package/dist/repoIntelligenceValidation.persist.test.d.ts +0 -2
  150. package/dist/repoIntelligenceValidation.persist.test.d.ts.map +0 -1
  151. package/dist/repoIntelligenceValidation.persist.test.js +0 -44
  152. package/dist/riskSignals.test.d.ts +0 -2
  153. package/dist/riskSignals.test.d.ts.map +0 -1
  154. package/dist/riskSignals.test.js +0 -32
  155. package/dist/riskVocabulary.test.d.ts +0 -2
  156. package/dist/riskVocabulary.test.d.ts.map +0 -1
  157. package/dist/riskVocabulary.test.js +0 -14
  158. package/dist/scanPresentationBundle.golden.test.d.ts +0 -2
  159. package/dist/scanPresentationBundle.golden.test.d.ts.map +0 -1
  160. package/dist/scanPresentationBundle.golden.test.js +0 -78
  161. package/dist/scanResultSchema.test.d.ts +0 -2
  162. package/dist/scanResultSchema.test.d.ts.map +0 -1
  163. package/dist/scanResultSchema.test.js +0 -162
  164. package/dist/scanSurfaceCopy.test.d.ts +0 -2
  165. package/dist/scanSurfaceCopy.test.d.ts.map +0 -1
  166. package/dist/scanSurfaceCopy.test.js +0 -14
  167. package/dist/selectTopAffectedAreas.test.d.ts +0 -2
  168. package/dist/selectTopAffectedAreas.test.d.ts.map +0 -1
  169. package/dist/selectTopAffectedAreas.test.js +0 -179
  170. package/dist/trustedScanGuards.test.d.ts +0 -2
  171. package/dist/trustedScanGuards.test.d.ts.map +0 -1
  172. package/dist/trustedScanGuards.test.js +0 -151
@@ -1,44 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { deriveScanNarrative } from "./deriveScanNarrative.js";
3
- import { legacyEngineRepoIntelligenceJwt } from "./fixtures/legacyEngineRepoIntelligence.js";
4
- import { applyRepoIntelligenceValidation } from "./repoIntelligenceValidation.js";
5
- const baseScan = {
6
- totalScore: 40,
7
- layerScores: {
8
- security: 10,
9
- maintainability: 10,
10
- ecosystem: 10,
11
- upgradeImpact: 10,
12
- },
13
- findings: [],
14
- generatedAt: "2026-01-01T00:00:00.000Z",
15
- changedPackages: ["jsonwebtoken"],
16
- analysisPreparation: {
17
- codeIntelligenceAvailable: true,
18
- warnings: [],
19
- },
20
- };
21
- describe("applyRepoIntelligenceValidation (production persist model)", () => {
22
- it("retains raw invalid repoIntelligence and marks status invalid", () => {
23
- const { result, contractFailed, logPayload } = applyRepoIntelligenceValidation({
24
- ...baseScan,
25
- repoIntelligence: legacyEngineRepoIntelligenceJwt,
26
- });
27
- expect(contractFailed).toBe(true);
28
- expect(logPayload?.issueCount).toBeGreaterThan(0);
29
- expect(result.repoIntelligence).toEqual(legacyEngineRepoIntelligenceJwt);
30
- expect(result.analysisPreparation?.repoIntelligenceValidation?.status).toBe("invalid");
31
- expect(result.analysisPreparation?.warnings.some((w) => w.code === "repo_intelligence_contract_invalid")).toBe(true);
32
- });
33
- it("deriveScanNarrative ignores invalid wire (graph_fallback, untrusted parse status)", () => {
34
- const { result } = applyRepoIntelligenceValidation({
35
- ...baseScan,
36
- repoIntelligence: legacyEngineRepoIntelligenceJwt,
37
- });
38
- const facts = deriveScanNarrative(result);
39
- expect(facts.availability.repoIntelligenceParse).toBe("untrusted");
40
- expect(facts.availability.tiersPresent.tier1).toBe(false);
41
- expect(facts.availability.mode).not.toBe("pr_intelligence");
42
- expect(facts.packageUsage).toHaveLength(0);
43
- });
44
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=riskSignals.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"riskSignals.test.d.ts","sourceRoot":"","sources":["../src/riskSignals.test.ts"],"names":[],"mappings":""}
@@ -1,32 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { deriveRiskSignals } from "./riskSignals.js";
3
- import { resolveRepositoryHealthScore } from "./prRiskWire.js";
4
- function baseResult(overrides = {}) {
5
- return {
6
- totalScore: 63,
7
- layerScores: {
8
- security: 10,
9
- maintainability: 20,
10
- ecosystem: 30,
11
- upgradeImpact: 15,
12
- },
13
- findings: [],
14
- generatedAt: "2026-01-01T00:00:00.000Z",
15
- ...overrides,
16
- };
17
- }
18
- describe("deriveRiskSignals", () => {
19
- it("prefers prRisk.score over totalScore and repositoryHealth", () => {
20
- const result = baseResult({
21
- prRisk: { score: 55 },
22
- repositoryHealth: { totalScore: 63 },
23
- totalScore: 63,
24
- });
25
- expect(deriveRiskSignals(result)?.riskIndex).toBe(55);
26
- expect(resolveRepositoryHealthScore(result)).toBe(63);
27
- });
28
- it("falls back to totalScore when prRisk is missing (historical scans)", () => {
29
- const result = baseResult({ totalScore: 48 });
30
- expect(deriveRiskSignals(result)?.riskIndex).toBe(48);
31
- });
32
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=riskVocabulary.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"riskVocabulary.test.d.ts","sourceRoot":"","sources":["../src/riskVocabulary.test.ts"],"names":[],"mappings":""}
@@ -1,14 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { mergePostureFromDecision } from "./riskVocabulary.js";
3
- describe("mergePostureFromDecision", () => {
4
- it("accepts only canonical lowercase merge posture tokens", () => {
5
- expect(mergePostureFromDecision("safe")).toBe("safe");
6
- expect(mergePostureFromDecision("needs_review")).toBe("needs_review");
7
- expect(mergePostureFromDecision("risky")).toBe("risky");
8
- });
9
- it("rejects alternate engine spellings (no silent normalization)", () => {
10
- expect(mergePostureFromDecision("SAFE")).toBeNull();
11
- expect(mergePostureFromDecision("RISKY")).toBeNull();
12
- expect(mergePostureFromDecision("BLOCK")).toBeNull();
13
- });
14
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=scanPresentationBundle.golden.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scanPresentationBundle.golden.test.d.ts","sourceRoot":"","sources":["../src/scanPresentationBundle.golden.test.ts"],"names":[],"mappings":""}
@@ -1,78 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { scoreToBand } from "./prRiskBand.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?.band).toBe(scoreToBand(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(30);
62
- expect(facts.riskSignals?.band).toBe(scoreToBand(30));
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
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=scanResultSchema.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scanResultSchema.test.d.ts","sourceRoot":"","sources":["../src/scanResultSchema.test.ts"],"names":[],"mappings":""}
@@ -1,162 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { safeParseScanResult, parseScanResultOrThrow, scanResultSchema, safeParseEngineOutputScanResult, parseEngineOutputScanResultOrThrow, } from "./scanResultSchema.js";
3
- import { emptyReachScope, emptyVerificationScope, minimalReviewFocalPoint, } from "./fixtures/assessmentScopeFixtures.js";
4
- import { withAbi4EngineScores } from "./fixtures/engineAbi4Fixtures.js";
5
- const minimalValid = {
6
- totalScore: 42,
7
- layerScores: {
8
- security: 10,
9
- maintainability: 20,
10
- ecosystem: 30,
11
- upgradeImpact: 40,
12
- },
13
- findings: [],
14
- generatedAt: "2026-01-01T00:00:00.000Z",
15
- };
16
- describe("scanResultSchema", () => {
17
- it("accepts a minimal valid payload", () => {
18
- const r = safeParseScanResult(minimalValid);
19
- expect(r.ok).toBe(true);
20
- if (r.ok)
21
- expect(r.result.totalScore).toBe(42);
22
- });
23
- it("preserves unknown top-level keys (passthrough)", () => {
24
- const r = safeParseScanResult({
25
- ...minimalValid,
26
- futureEngineField: { x: 1 },
27
- });
28
- expect(r.ok).toBe(true);
29
- if (r.ok)
30
- expect(r.result.futureEngineField).toEqual({
31
- x: 1,
32
- });
33
- });
34
- it("rejects missing layerScores", () => {
35
- const r = safeParseScanResult({
36
- totalScore: 1,
37
- findings: [],
38
- generatedAt: "2026-01-01T00:00:00.000Z",
39
- });
40
- expect(r.ok).toBe(false);
41
- });
42
- it("rejects out-of-range totalScore", () => {
43
- const r = safeParseScanResult({
44
- ...minimalValid,
45
- totalScore: 101,
46
- });
47
- expect(r.ok).toBe(false);
48
- });
49
- it("rejects invalid decision.recommendation", () => {
50
- const r = safeParseScanResult({
51
- ...minimalValid,
52
- decision: { recommendation: "maybe", reasoning: [] },
53
- });
54
- expect(r.ok).toBe(false);
55
- });
56
- it("rejects non-canonical merge posture tokens (e.g. proprietary uppercase)", () => {
57
- const r = safeParseScanResult({
58
- ...minimalValid,
59
- decision: { recommendation: "SAFE", reasoning: [] },
60
- });
61
- expect(r.ok).toBe(false);
62
- });
63
- it("rejects empty generatedAt", () => {
64
- const r = safeParseScanResult({
65
- ...minimalValid,
66
- generatedAt: "",
67
- });
68
- expect(r.ok).toBe(false);
69
- });
70
- it("parseScanResultOrThrow returns on success", () => {
71
- expect(parseScanResultOrThrow(minimalValid).totalScore).toBe(42);
72
- });
73
- it("parseScanResultOrThrow throws with validation prefix", () => {
74
- expect(() => parseScanResultOrThrow({})).toThrow(/^validation:/);
75
- });
76
- it("scanResultSchema default empty findings when omitted", () => {
77
- const { findings: _f, ...rest } = minimalValid;
78
- const parsed = scanResultSchema.parse(rest);
79
- expect(parsed.findings).toEqual([]);
80
- });
81
- });
82
- const minimalAssessment = {
83
- reviewFocalPoint: minimalReviewFocalPoint(["typescript"]),
84
- reachScope: emptyReachScope(),
85
- verificationScope: emptyVerificationScope(),
86
- posture: "safe",
87
- confidence: "high",
88
- primaryConcern: null,
89
- concerns: [],
90
- factors: ["tooling_maintenance"],
91
- changeClasses: ["tooling_maintenance"],
92
- reasoning: [],
93
- confidenceRationale: "",
94
- presentation: {
95
- narrativeIntensity: "minimal",
96
- reachVisibility: "hidden",
97
- verificationIntensity: "advisory",
98
- insightEmissionFloor: "none",
99
- reportMode: "high_signal_pr",
100
- },
101
- };
102
- describe("engineOutputScanResultSchema (strict, fresh engine only)", () => {
103
- const withMethodology = withAbi4EngineScores({
104
- ...minimalValid,
105
- methodologyVersion: "engine-test-fixture/v1",
106
- assessment: minimalAssessment,
107
- });
108
- it("rejects payload that relaxed parser accepts when methodology is missing", () => {
109
- const r = safeParseEngineOutputScanResult(minimalValid);
110
- expect(r.ok).toBe(false);
111
- });
112
- it("accepts when methodology and parseable generatedAt are present", () => {
113
- const r = safeParseEngineOutputScanResult(withMethodology);
114
- expect(r.ok).toBe(true);
115
- if (r.ok)
116
- expect(r.result.methodologyVersion).toBe("engine-test-fixture/v1");
117
- });
118
- it("synthesizes prRisk/repositoryHealth from totalScore for legacy engines", () => {
119
- const r = safeParseEngineOutputScanResult({
120
- ...minimalValid,
121
- methodologyVersion: "acme-prod/v1",
122
- assessment: minimalAssessment,
123
- });
124
- expect(r.ok).toBe(true);
125
- if (r.ok) {
126
- expect(r.result.prRisk?.score).toBe(42);
127
- expect(r.result.repositoryHealth?.totalScore).toBe(42);
128
- }
129
- });
130
- it("parseEngineOutputScanResultOrThrow throws with validation prefix", () => {
131
- expect(() => parseEngineOutputScanResultOrThrow(minimalValid)).toThrow(/^validation:/);
132
- });
133
- it("rejects unparseable generatedAt", () => {
134
- const r = safeParseEngineOutputScanResult({
135
- ...withMethodology,
136
- generatedAt: "not-a-date",
137
- });
138
- expect(r.ok).toBe(false);
139
- });
140
- it("rejects ABI-1 assessment missing focal/scope fields on fresh engine path", () => {
141
- const legacyAssessment = {
142
- posture: "safe",
143
- confidence: "high",
144
- primaryConcern: null,
145
- concerns: [],
146
- factors: ["tooling_maintenance"],
147
- changeClasses: ["tooling_maintenance"],
148
- presentation: {
149
- narrativeIntensity: "minimal",
150
- reachVisibility: "hidden",
151
- verificationIntensity: "advisory",
152
- insightEmissionFloor: "none",
153
- reportMode: "high_signal_pr",
154
- },
155
- };
156
- const r = safeParseEngineOutputScanResult({
157
- ...withMethodology,
158
- assessment: legacyAssessment,
159
- });
160
- expect(r.ok).toBe(false);
161
- });
162
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=scanSurfaceCopy.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scanSurfaceCopy.test.d.ts","sourceRoot":"","sources":["../src/scanSurfaceCopy.test.ts"],"names":[],"mappings":""}
@@ -1,14 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { readFileSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
4
- import { dirname, join } from "node:path";
5
- import { scanSurfaceCopyFlat } from "./scanSurfaceCopy.js";
6
- describe("scanSurfaceCopy", () => {
7
- it("matches generated CI JSON (no drift)", () => {
8
- const root = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
9
- const jsonPath = join(root, "scripts/ci/scan-surface-copy.generated.json");
10
- const onDisk = JSON.parse(readFileSync(jsonPath, "utf8"));
11
- const fromTs = scanSurfaceCopyFlat();
12
- expect(fromTs).toEqual(onDisk);
13
- });
14
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=selectTopAffectedAreas.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"selectTopAffectedAreas.test.d.ts","sourceRoot":"","sources":["../src/selectTopAffectedAreas.test.ts"],"names":[],"mappings":""}
@@ -1,179 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { selectTopAffectedAreas } from "./selectTopAffectedAreas.js";
3
- function makeScanResult(overrides = {}) {
4
- return {
5
- totalScore: 60,
6
- layerScores: {
7
- security: 70,
8
- maintainability: 50,
9
- ecosystem: 40,
10
- upgradeImpact: 30,
11
- },
12
- findings: [],
13
- generatedAt: "2026-01-01T00:00:00Z",
14
- ...overrides,
15
- };
16
- }
17
- describe("selectTopAffectedAreas", () => {
18
- it("returns [] for null input", () => {
19
- expect(selectTopAffectedAreas(null)).toEqual([]);
20
- });
21
- it("returns [] for undefined input", () => {
22
- expect(selectTopAffectedAreas(undefined)).toEqual([]);
23
- });
24
- it("returns at most 3 results by default", () => {
25
- const result = makeScanResult({
26
- explain: {
27
- reasons: [
28
- { id: "r1", layer: "security", title: "Auth flow", scoreImpact: -20 },
29
- {
30
- id: "r2",
31
- layer: "security",
32
- title: "State sync",
33
- scoreImpact: -15,
34
- },
35
- {
36
- id: "r3",
37
- layer: "ecosystem",
38
- title: "Middleware",
39
- scoreImpact: -10,
40
- },
41
- {
42
- id: "r4",
43
- layer: "maintainability",
44
- title: "Extra area",
45
- scoreImpact: -5,
46
- },
47
- ],
48
- },
49
- });
50
- const areas = selectTopAffectedAreas(result);
51
- expect(areas.length).toBe(3);
52
- expect(areas).toEqual(["Auth flow", "State sync", "Middleware"]);
53
- });
54
- it("respects custom max", () => {
55
- const result = makeScanResult({
56
- explain: {
57
- reasons: [
58
- { id: "r1", layer: "security", title: "Area A", scoreImpact: -20 },
59
- { id: "r2", layer: "security", title: "Area B", scoreImpact: -15 },
60
- ],
61
- },
62
- });
63
- expect(selectTopAffectedAreas(result, { max: 1 })).toEqual(["Area A"]);
64
- });
65
- it("sorts explain reasons by absolute scoreImpact descending", () => {
66
- const result = makeScanResult({
67
- explain: {
68
- reasons: [
69
- { id: "r1", layer: "security", title: "Minor", scoreImpact: -2 },
70
- { id: "r2", layer: "security", title: "Critical", scoreImpact: -30 },
71
- { id: "r3", layer: "ecosystem", title: "Medium", scoreImpact: -15 },
72
- ],
73
- },
74
- });
75
- const areas = selectTopAffectedAreas(result);
76
- expect(areas[0]).toBe("Critical");
77
- expect(areas[1]).toBe("Medium");
78
- });
79
- it("falls back to insights when explain is empty", () => {
80
- const result = makeScanResult({
81
- insights: [
82
- {
83
- type: "behavioral_change",
84
- priority: "high",
85
- confidence: "confirmed",
86
- scope: "changed",
87
- message: "Auth middleware changed. Check tokens",
88
- context: "src/auth.ts",
89
- remediation: "Review carefully",
90
- },
91
- ],
92
- });
93
- const areas = selectTopAffectedAreas(result);
94
- expect(areas).toEqual(["Auth middleware changed"]);
95
- });
96
- it("falls back to layer names when explain and insights are empty", () => {
97
- const result = makeScanResult();
98
- const areas = selectTopAffectedAreas(result);
99
- expect(areas.length).toBeGreaterThan(0);
100
- expect(areas.length).toBeLessThanOrEqual(3);
101
- expect(areas).toContain("Security");
102
- });
103
- it("deduplicates normalized labels", () => {
104
- const result = makeScanResult({
105
- explain: {
106
- reasons: [
107
- { id: "r1", layer: "security", title: "Auth flow", scoreImpact: -20 },
108
- { id: "r2", layer: "security", title: "auth flow", scoreImpact: -15 },
109
- {
110
- id: "r3",
111
- layer: "ecosystem",
112
- title: "Auth flow",
113
- scoreImpact: -10,
114
- },
115
- ],
116
- },
117
- });
118
- const areas = selectTopAffectedAreas(result);
119
- const unique = new Set(areas.map((a) => a.toLowerCase()));
120
- expect(unique.size).toBe(areas.length);
121
- expect(areas).toContain("Auth flow");
122
- expect(areas.filter((a) => a.toLowerCase() === "auth flow").length).toBe(1);
123
- });
124
- it("trims the 'Finding: ' prefix from labels", () => {
125
- const result = makeScanResult({
126
- explain: {
127
- reasons: [
128
- {
129
- id: "r1",
130
- layer: "security",
131
- title: "Finding: Outdated lodash",
132
- scoreImpact: -20,
133
- },
134
- ],
135
- },
136
- });
137
- const areas = selectTopAffectedAreas(result);
138
- expect(areas[0]).toBe("Outdated lodash");
139
- });
140
- it("truncates labels longer than maxLabelLen with ellipsis", () => {
141
- const long = "A".repeat(50);
142
- const result = makeScanResult({
143
- explain: {
144
- reasons: [
145
- { id: "r1", layer: "security", title: long, scoreImpact: -10 },
146
- ],
147
- },
148
- });
149
- const areas = selectTopAffectedAreas(result, { maxLabelLen: 10 });
150
- expect(areas[0].length).toBeLessThanOrEqual(10);
151
- expect(areas[0].endsWith("…")).toBe(true);
152
- });
153
- it("returns empty array when no useful signal at all", () => {
154
- // Completely empty scan result with empty layerScores
155
- const result = {
156
- totalScore: 0,
157
- layerScores: {
158
- security: 0,
159
- maintainability: 0,
160
- ecosystem: 0,
161
- upgradeImpact: 0,
162
- },
163
- findings: [],
164
- generatedAt: "2026-01-01T00:00:00Z",
165
- };
166
- // With layer fallback this would return layer names; only truly empty when
167
- // caller passes a result without layerScores entirely — validate max guard
168
- const areas = selectTopAffectedAreas(result, { max: 3 });
169
- expect(areas.length).toBeLessThanOrEqual(3);
170
- });
171
- it("returns [] when layerScores is missing and no explain/insights", () => {
172
- const partial = {
173
- totalScore: 10,
174
- findings: [],
175
- generatedAt: "2026-01-01T00:00:00Z",
176
- };
177
- expect(selectTopAffectedAreas(partial)).toEqual([]);
178
- });
179
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=trustedScanGuards.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"trustedScanGuards.test.d.ts","sourceRoot":"","sources":["../src/trustedScanGuards.test.ts"],"names":[],"mappings":""}