@usefragments/core 1.10.2 → 1.11.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 (126) hide show
  1. package/dist/{chunk-MZ4SW3TP.js → chunk-3IOWHECM.js} +8 -1
  2. package/dist/{chunk-MZ4SW3TP.js.map → chunk-3IOWHECM.js.map} +1 -1
  3. package/dist/chunk-7ULGH74M.js +66 -0
  4. package/dist/chunk-7ULGH74M.js.map +1 -0
  5. package/dist/{chunk-YF65VYRY.js → chunk-ML5S6QNU.js} +191 -169
  6. package/dist/chunk-ML5S6QNU.js.map +1 -0
  7. package/dist/chunk-PWIJMOI4.js +202 -0
  8. package/dist/chunk-PWIJMOI4.js.map +1 -0
  9. package/dist/{chunk-XN3LSDPY.js → chunk-RPSEABY3.js} +622 -344
  10. package/dist/chunk-RPSEABY3.js.map +1 -0
  11. package/dist/chunk-RYFULE43.js +578 -0
  12. package/dist/chunk-RYFULE43.js.map +1 -0
  13. package/dist/codes/index.d.ts +2 -2
  14. package/dist/codes/index.js +3 -2
  15. package/dist/compiled-types/index.d.ts +327 -2
  16. package/dist/compiled-types/index.js +1 -1
  17. package/dist/generate/index.d.ts +3 -2
  18. package/dist/{governance-CLk_wkP9.d.ts → governance-BAsy1k2H.d.ts} +472 -513
  19. package/dist/governance-telemetry.d.ts +6 -0
  20. package/dist/governance-telemetry.js +1 -1
  21. package/dist/{index-_sxhUNqx.d.ts → index-C8bcXVav.d.ts} +448 -448
  22. package/dist/index.d.ts +9165 -1297
  23. package/dist/index.js +3041 -350
  24. package/dist/index.js.map +1 -1
  25. package/dist/manifest.d.ts +228 -0
  26. package/dist/manifest.js +24 -0
  27. package/dist/manifest.js.map +1 -0
  28. package/dist/preview/index.js +45 -1
  29. package/dist/preview/index.js.map +1 -1
  30. package/dist/preview-runtime.d.ts +1 -2
  31. package/dist/preview-runtime.js +150 -14
  32. package/dist/preview-runtime.js.map +1 -1
  33. package/dist/react-types.d.ts +1 -2
  34. package/dist/registry.d.ts +1412 -203
  35. package/dist/registry.js +30 -3
  36. package/dist/schemas/index.d.ts +1 -1
  37. package/dist/schemas/index.js +3 -2
  38. package/dist/storyAdapter.d.ts +1 -2
  39. package/dist/storyAdapter.js +11 -49
  40. package/dist/storyAdapter.js.map +1 -1
  41. package/dist/test-utils.d.ts +3 -2
  42. package/package.json +8 -2
  43. package/src/__tests__/contract-parser.test.ts +318 -277
  44. package/src/__tests__/preview-runtime-hook.test.tsx +315 -0
  45. package/src/__tests__/preview-runtime.test.tsx +30 -8
  46. package/src/__tests__/schema.test.ts +191 -14
  47. package/src/analysis-plan/analysis-plan-v1.test.ts +320 -0
  48. package/src/analysis-plan/coverage.ts +181 -0
  49. package/src/analysis-plan/digest.ts +141 -0
  50. package/src/analysis-plan/index.ts +34 -0
  51. package/src/analysis-plan/types.ts +207 -0
  52. package/src/codes/__tests__/codes.test.ts +9 -0
  53. package/src/codes/codes.ts +30 -0
  54. package/src/compiled-types/index.ts +640 -39
  55. package/src/compiled-types/parse.test.ts +145 -4
  56. package/src/component-contract.ts +95 -53
  57. package/src/composition.ts +7 -13
  58. package/src/constants.ts +3 -6
  59. package/src/contract/hash.test.ts +20 -0
  60. package/src/contract/hash.ts +66 -9
  61. package/src/contract/index.ts +24 -1
  62. package/src/contract/manifest.test.ts +94 -0
  63. package/src/contract/manifest.ts +68 -0
  64. package/src/contract/preimage.test.ts +219 -1
  65. package/src/contract/preimage.ts +326 -6
  66. package/src/contract/stamp.test.ts +3 -0
  67. package/src/contract/stamp.ts +1 -1
  68. package/src/contract-parser.ts +54 -30
  69. package/src/defineFragment.test.ts +476 -91
  70. package/src/defineFragment.ts +204 -114
  71. package/src/domain-ids.test.ts +52 -0
  72. package/src/domain-ids.ts +106 -0
  73. package/src/evaluation/evaluate.test.ts +522 -0
  74. package/src/evaluation/evaluate.ts +680 -0
  75. package/src/evaluation/evaluation-v2-receipt-v1.test.ts +760 -0
  76. package/src/evaluation/index.ts +58 -0
  77. package/src/evaluation/receipt.ts +753 -0
  78. package/src/evaluation/types.ts +406 -0
  79. package/src/facts/builders.ts +2 -0
  80. package/src/facts/compile.ts +29 -6
  81. package/src/facts/fact-index.ts +13 -3
  82. package/src/facts/fact-integrity-v1.test.ts +172 -0
  83. package/src/facts/facts.test.ts +15 -0
  84. package/src/facts/ids.ts +46 -3
  85. package/src/facts/index.ts +14 -1
  86. package/src/facts/integrity.ts +134 -0
  87. package/src/facts/types.ts +36 -0
  88. package/src/feature-plan/digest.ts +217 -0
  89. package/src/feature-plan/feature-plan-v1.test.ts +529 -0
  90. package/src/feature-plan/index.ts +65 -0
  91. package/src/feature-plan/types.ts +628 -0
  92. package/src/governance-integrity.test.ts +1 -0
  93. package/src/governance-integrity.ts +5 -3
  94. package/src/governance-telemetry.ts +8 -0
  95. package/src/governance.ts +52 -8
  96. package/src/index.ts +299 -37
  97. package/src/preview/validation.test.ts +62 -0
  98. package/src/preview/validation.ts +48 -2
  99. package/src/preview-runtime.tsx +227 -20
  100. package/src/registry-install-plan.ts +200 -109
  101. package/src/registry-shards.test.ts +263 -0
  102. package/src/registry.ts +237 -0
  103. package/src/repository-binding.test.ts +50 -0
  104. package/src/repository-binding.ts +96 -0
  105. package/src/rules/families.test.ts +36 -0
  106. package/src/rules/finding.ts +7 -2
  107. package/src/rules/index.ts +17 -1
  108. package/src/rules/rule-config.test.ts +66 -0
  109. package/src/rules/rule-config.ts +73 -0
  110. package/src/rules/rules.test.ts +26 -0
  111. package/src/rules/tokens-css-vars-must-be-defined.test.ts +51 -2
  112. package/src/rules/tokens-css-vars-must-be-defined.ts +34 -1
  113. package/src/schema.ts +293 -113
  114. package/src/schemas/index.ts +1 -1
  115. package/src/storyAdapter.test.ts +68 -12
  116. package/src/storyAdapter.ts +44 -75
  117. package/src/types.ts +258 -37
  118. package/dist/chunk-RANPUC6C.js +0 -72
  119. package/dist/chunk-RANPUC6C.js.map +0 -1
  120. package/dist/chunk-XN3LSDPY.js.map +0 -1
  121. package/dist/chunk-YF65VYRY.js.map +0 -1
  122. package/src/fragment-types.ts +0 -214
  123. package/src/react-create-element.test.ts +0 -22
  124. package/src/react-create-element.ts +0 -12
  125. package/src/storyFilters.test.ts +0 -350
  126. package/src/storyFilters.ts +0 -253
@@ -0,0 +1,760 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildAnalysisPlanV1, type CoverageRegionV1 } from "../analysis-plan/index.js";
3
+ import { contractHash } from "../contract/hash.js";
4
+ import { projectContractPreimage } from "../contract/preimage.js";
5
+ import { evaluationReceiptIdFromDigest } from "../domain-ids.js";
6
+ import { FactIndex } from "../facts/fact-index.js";
7
+ import { makeTokenDefinitionFact } from "../facts/builders.js";
8
+ import { repositoryBindingDigestV1 } from "../repository-binding.js";
9
+ import {
10
+ EVALUATOR_VERSION_V2,
11
+ evaluate,
12
+ evaluationInputV2Schema,
13
+ evaluationReceiptDigestV1,
14
+ evaluationReceiptMintInputV1Schema,
15
+ evaluationReceiptV1Schema,
16
+ evaluationResultV2Schema,
17
+ parseEvaluationReceiptV1,
18
+ providerProofDigestV1,
19
+ type EvaluationInputV2,
20
+ type EvaluationReceiptMintInputV1,
21
+ type EvaluationReceiptSemanticV1,
22
+ type ProviderProofV1,
23
+ } from "./index.js";
24
+
25
+ const A = "a".repeat(64);
26
+ const B = "b".repeat(64);
27
+ const C = "c".repeat(64);
28
+
29
+ const binding = {
30
+ provider: "github",
31
+ providerInstanceId: "github.com",
32
+ repositoryExternalId: "R_1",
33
+ bindingId: "binding_1",
34
+ };
35
+
36
+ const CONTRACT_PREIMAGE = projectContractPreimage({
37
+ components: [],
38
+ tokens: [],
39
+ canonicalMappings: [],
40
+ policy: {},
41
+ });
42
+ const FCID = contractHash(CONTRACT_PREIMAGE);
43
+
44
+ function plan() {
45
+ return buildAnalysisPlanV1({
46
+ binding,
47
+ source: {
48
+ commitId: "head-sha",
49
+ defaultBranch: "main",
50
+ acquiredBy: "github_app",
51
+ },
52
+ contract: { fcid: FCID, artifactDigest: B },
53
+ config: { path: "fragments.config.ts", digest: C },
54
+ profile: { id: "react-web-v1", version: "1", capabilities: ["tsx"] },
55
+ regions: [
56
+ {
57
+ regionId: "tsx",
58
+ pathPattern: "src/**/*.tsx",
59
+ kind: "component_source",
60
+ language: "typescript",
61
+ required: true,
62
+ requiredDialects: ["tsx"],
63
+ analyzer: { id: "react", interfaceVersion: "1", implementationVersion: "1" },
64
+ },
65
+ ],
66
+ limits: {
67
+ maxSourceFiles: 1_000,
68
+ maxSourceBytes: 10_000_000,
69
+ maxFileBytes: 1_000_000,
70
+ maxFacts: 10_000,
71
+ maxRegions: 100,
72
+ maxDiagnostics: 1_000,
73
+ maxDurationMs: 60_000,
74
+ },
75
+ evaluatorVersion: "evaluation-kernel:v2",
76
+ createdAt: "2026-09-01T00:00:00.000Z",
77
+ });
78
+ }
79
+
80
+ const coverage: CoverageRegionV1[] = [
81
+ { regionId: "tsx", state: "analyzed", fileCount: 4, byteCount: 400, diagnosticIds: [] },
82
+ ];
83
+
84
+ function evaluationInput(): EvaluationInputV2 {
85
+ const analysisPlan = plan();
86
+ return {
87
+ schemaVersion: 2,
88
+ binding,
89
+ analysisPlan,
90
+ contract: { fcid: analysisPlan.contract.fcid, preimage: CONTRACT_PREIMAGE, active: true },
91
+ factSchemaVersion: "facts:v1",
92
+ facts: new FactIndex(),
93
+ factsDigest: B,
94
+ coverage,
95
+ policy: { failOnWarnings: false },
96
+ trust: { source: "verified", mappings: {} },
97
+ waivers: [],
98
+ integrity: {
99
+ governance: "healthy",
100
+ inertConfigDiagnosticCount: 0,
101
+ profile: { state: "known", profileId: "react-web-v1", profileVersion: "1" },
102
+ facts: { state: "healthy", coalescedDuplicateCount: 0 },
103
+ },
104
+ evaluatedAt: Date.UTC(2026, 8, 1),
105
+ evaluatorVersion: "evaluation-kernel:v2",
106
+ };
107
+ }
108
+
109
+ function providerProof(): ProviderProofV1 {
110
+ const semantic = {
111
+ schemaVersion: 1 as const,
112
+ adapterId: "github-app",
113
+ adapterVersion: "1",
114
+ providerInstanceId: "github.com",
115
+ bindingDigest: repositoryBindingDigestV1(binding),
116
+ repositoryExternalId: "R_1",
117
+ sourceCommitId: "head-sha",
118
+ eventExternalId: "delivery-1",
119
+ changeExternalId: "42",
120
+ publisherExternalId: "fragments-app",
121
+ };
122
+ return { ...semantic, proofDigest: providerProofDigestV1(semantic) };
123
+ }
124
+
125
+ function receiptSemantic(
126
+ overrides: Partial<EvaluationReceiptSemanticV1> = {}
127
+ ): EvaluationReceiptSemanticV1 {
128
+ return {
129
+ schemaVersion: 1,
130
+ trust: "source_verified",
131
+ authority: "enforcement_eligible",
132
+ binding,
133
+ sourceCommitId: "head-sha",
134
+ fcid: FCID,
135
+ analysisPlanDigest: plan().digest,
136
+ factsDigest: B,
137
+ inputDigest: C,
138
+ resultDigest: A,
139
+ evaluatorVersion: "evaluation-kernel:v2",
140
+ providerProof: providerProof(),
141
+ ...overrides,
142
+ };
143
+ }
144
+
145
+ describe("EvaluationInputV2", () => {
146
+ it("accepts the complete facts-only input and rejects unknown keys", () => {
147
+ expect(evaluationInputV2Schema.parse(evaluationInput())).toMatchObject({ schemaVersion: 2 });
148
+ expect(() => evaluationInputV2Schema.parse({ ...evaluationInput(), summary: {} })).toThrow();
149
+ });
150
+
151
+ it("rejects plan/binding/FCID/evaluator mismatch and missing required coverage", () => {
152
+ expect(() =>
153
+ evaluationInputV2Schema.parse({
154
+ ...evaluationInput(),
155
+ binding: { ...binding, repositoryExternalId: "R_other" },
156
+ })
157
+ ).toThrow();
158
+ expect(() =>
159
+ evaluationInputV2Schema.parse({
160
+ ...evaluationInput(),
161
+ contract: { ...evaluationInput().contract, fcid: B },
162
+ })
163
+ ).toThrow();
164
+ expect(() => evaluationInputV2Schema.parse({ ...evaluationInput(), coverage: [] })).toThrow();
165
+ expect(() =>
166
+ evaluationInputV2Schema.parse({ ...evaluationInput(), evaluatorVersion: "other" })
167
+ ).toThrow();
168
+ });
169
+
170
+ it("rejects empty/duplicate waiver matches and malformed integrity unions", () => {
171
+ const waiver = {
172
+ waiverId: "w-1",
173
+ scope: { kind: "rule" as const, ruleId: "components/prefer-library" },
174
+ matchedFindingFingerprints: ["f-1"],
175
+ reason: "Migration in progress",
176
+ };
177
+ expect(() =>
178
+ evaluationInputV2Schema.parse({
179
+ ...evaluationInput(),
180
+ waivers: [{ ...waiver, matchedFindingFingerprints: [] }],
181
+ })
182
+ ).toThrow();
183
+ expect(() =>
184
+ evaluationInputV2Schema.parse({
185
+ ...evaluationInput(),
186
+ waivers: [{ ...waiver, matchedFindingFingerprints: ["f-1", "f-1"] }],
187
+ })
188
+ ).toThrow();
189
+ expect(() =>
190
+ evaluationInputV2Schema.parse({
191
+ ...evaluationInput(),
192
+ integrity: {
193
+ ...evaluationInput().integrity,
194
+ profile: { state: "known", profileId: "react-web-v1" },
195
+ },
196
+ })
197
+ ).toThrow();
198
+ });
199
+
200
+ it("enforces the analysis-plan fact limit", () => {
201
+ const basePlan = plan();
202
+ const {
203
+ schemaVersion: _schemaVersion,
204
+ analysisPlanId: _analysisPlanId,
205
+ obligationsDigest: _obligationsDigest,
206
+ digest: _digest,
207
+ ...planInput
208
+ } = basePlan;
209
+ const limitedPlan = buildAnalysisPlanV1({
210
+ ...planInput,
211
+ limits: { ...planInput.limits, maxFacts: 1 },
212
+ });
213
+ const facts = new FactIndex();
214
+ facts.addMany([
215
+ makeTokenDefinitionFact({ name: "--one", value: "1px" }),
216
+ makeTokenDefinitionFact({ name: "--two", value: "2px" }),
217
+ ]);
218
+ expect(() =>
219
+ evaluationInputV2Schema.parse({
220
+ ...evaluationInput(),
221
+ analysisPlan: limitedPlan,
222
+ facts,
223
+ })
224
+ ).toThrow(/Fact count/u);
225
+ });
226
+ });
227
+
228
+ describe("EvaluationResultV2", () => {
229
+ it("preserves reasons, counts, derived output, and integrity explanation", () => {
230
+ const result = {
231
+ schemaVersion: 2,
232
+ verdict: "pass",
233
+ reasons: [],
234
+ findings: [],
235
+ counts: { blocked: 0, advisory: 0, pendingCloudTrust: 0 },
236
+ derived: { warnPresent: false, gatingFindingsPresent: false },
237
+ coverageSummary: {
238
+ plannedRegions: 1,
239
+ observedRegions: 1,
240
+ requiredRegions: 1,
241
+ requiredByState: {
242
+ analyzed: 1,
243
+ unsupported: 0,
244
+ excluded_by_contract: 0,
245
+ skipped_limit: 0,
246
+ failed: 0,
247
+ },
248
+ optionalByState: {
249
+ analyzed: 0,
250
+ unsupported: 0,
251
+ excluded_by_contract: 0,
252
+ skipped_limit: 0,
253
+ failed: 0,
254
+ },
255
+ eligibleFiles: 4,
256
+ analyzedFiles: 4,
257
+ analyzedBytes: 400,
258
+ },
259
+ integrity: {
260
+ fcidVerified: true,
261
+ analysisPlanVerified: true,
262
+ factIntegrity: "healthy",
263
+ requiredCoverage: "complete",
264
+ governance: "healthy",
265
+ profile: "known",
266
+ },
267
+ inputDigest: A,
268
+ resultDigest: B,
269
+ } as const;
270
+ expect(evaluationResultV2Schema.parse(result)).toMatchObject({ verdict: "pass" });
271
+ expect(() =>
272
+ evaluationResultV2Schema.parse({
273
+ ...result,
274
+ reasons: ["factConflict", "factConflict"],
275
+ })
276
+ ).toThrow(/unique/u);
277
+ });
278
+
279
+ it("holds an integrity-class reason at indeterminate even beside blocked findings", () => {
280
+ // Mirrors the kernel: `inertConfigPresent` (an INTEGRITY reason) beside
281
+ // `blockedFindings` is `indeterminate`, never `block`.
282
+ const result = {
283
+ schemaVersion: 2,
284
+ verdict: "indeterminate",
285
+ reasons: ["inertConfigPresent", "blockedFindings"],
286
+ findings: [
287
+ {
288
+ fingerprint: "f-block",
289
+ ruleId: "r-block",
290
+ severity: "error",
291
+ state: "blocked",
292
+ stateReasons: [],
293
+ },
294
+ ],
295
+ counts: { blocked: 1, advisory: 0, pendingCloudTrust: 0 },
296
+ derived: { warnPresent: false, gatingFindingsPresent: true },
297
+ coverageSummary: {
298
+ plannedRegions: 1,
299
+ observedRegions: 1,
300
+ requiredRegions: 1,
301
+ requiredByState: {
302
+ analyzed: 1,
303
+ unsupported: 0,
304
+ excluded_by_contract: 0,
305
+ skipped_limit: 0,
306
+ failed: 0,
307
+ },
308
+ optionalByState: {
309
+ analyzed: 0,
310
+ unsupported: 0,
311
+ excluded_by_contract: 0,
312
+ skipped_limit: 0,
313
+ failed: 0,
314
+ },
315
+ eligibleFiles: 1,
316
+ analyzedFiles: 1,
317
+ analyzedBytes: 100,
318
+ },
319
+ integrity: {
320
+ fcidVerified: true,
321
+ analysisPlanVerified: true,
322
+ factIntegrity: "healthy",
323
+ requiredCoverage: "complete",
324
+ governance: "healthy",
325
+ profile: "known",
326
+ },
327
+ inputDigest: A,
328
+ resultDigest: B,
329
+ } as const;
330
+ expect(evaluationResultV2Schema.parse(result)).toMatchObject({ verdict: "indeterminate" });
331
+ expect(() => evaluationResultV2Schema.parse({ ...result, verdict: "block" })).toThrow(
332
+ /verdict must be indeterminate/u
333
+ );
334
+ });
335
+
336
+ it("rejects impossible counts, derived values, findings, reasons, and coverage", () => {
337
+ const result = evaluationResultV2Schema.parse({
338
+ schemaVersion: 2,
339
+ verdict: "pass",
340
+ reasons: [],
341
+ findings: [],
342
+ counts: { blocked: 0, advisory: 0, pendingCloudTrust: 0 },
343
+ derived: { warnPresent: false, gatingFindingsPresent: false },
344
+ coverageSummary: {
345
+ plannedRegions: 1,
346
+ observedRegions: 1,
347
+ requiredRegions: 1,
348
+ requiredByState: {
349
+ analyzed: 1,
350
+ unsupported: 0,
351
+ excluded_by_contract: 0,
352
+ skipped_limit: 0,
353
+ failed: 0,
354
+ },
355
+ optionalByState: {
356
+ analyzed: 0,
357
+ unsupported: 0,
358
+ excluded_by_contract: 0,
359
+ skipped_limit: 0,
360
+ failed: 0,
361
+ },
362
+ eligibleFiles: 4,
363
+ analyzedFiles: 4,
364
+ analyzedBytes: 400,
365
+ },
366
+ integrity: {
367
+ fcidVerified: true,
368
+ analysisPlanVerified: true,
369
+ factIntegrity: "healthy",
370
+ requiredCoverage: "complete",
371
+ governance: "healthy",
372
+ profile: "known",
373
+ },
374
+ inputDigest: A,
375
+ resultDigest: B,
376
+ });
377
+
378
+ expect(() =>
379
+ evaluationResultV2Schema.parse({
380
+ ...result,
381
+ counts: { ...result.counts, blocked: 1 },
382
+ })
383
+ ).toThrow(/count must match/u);
384
+ expect(() =>
385
+ evaluationResultV2Schema.parse({
386
+ ...result,
387
+ findings: [
388
+ {
389
+ fingerprint: "f-warn",
390
+ ruleId: "r-warn",
391
+ severity: "warn",
392
+ state: "advisory",
393
+ stateReasons: [],
394
+ },
395
+ ],
396
+ counts: { blocked: 0, advisory: 1, pendingCloudTrust: 0 },
397
+ derived: { ...result.derived, warnPresent: false },
398
+ })
399
+ ).toThrow(/warnPresent/u);
400
+ expect(() =>
401
+ evaluationResultV2Schema.parse({
402
+ ...result,
403
+ findings: [
404
+ {
405
+ fingerprint: "f-1",
406
+ ruleId: "r-1",
407
+ severity: "error",
408
+ state: "advisory",
409
+ stateReasons: [],
410
+ },
411
+ {
412
+ fingerprint: "f-1",
413
+ ruleId: "r-2",
414
+ severity: "info",
415
+ state: "advisory",
416
+ stateReasons: [],
417
+ },
418
+ ],
419
+ counts: { blocked: 0, advisory: 2, pendingCloudTrust: 0 },
420
+ })
421
+ ).toThrow(/fingerprints must be unique/u);
422
+ expect(() =>
423
+ evaluationResultV2Schema.parse({
424
+ ...result,
425
+ reasons: ["factConflict"],
426
+ })
427
+ ).toThrow(/factConflict must match/u);
428
+ expect(() =>
429
+ evaluationResultV2Schema.parse({
430
+ ...result,
431
+ coverageSummary: { ...result.coverageSummary, observedRegions: 2 },
432
+ })
433
+ ).toThrow(/counts must reconcile/u);
434
+ });
435
+
436
+ it("never treats incomplete or absent required coverage as a pass", () => {
437
+ const base = {
438
+ schemaVersion: 2 as const,
439
+ reasons: ["requiredCoverageMissing" as const],
440
+ findings: [],
441
+ counts: { blocked: 0, advisory: 0, pendingCloudTrust: 0 },
442
+ derived: { warnPresent: false, gatingFindingsPresent: false },
443
+ coverageSummary: {
444
+ plannedRegions: 1,
445
+ observedRegions: 1,
446
+ requiredRegions: 0,
447
+ requiredByState: {
448
+ analyzed: 0,
449
+ unsupported: 0,
450
+ excluded_by_contract: 0,
451
+ skipped_limit: 0,
452
+ failed: 0,
453
+ },
454
+ optionalByState: {
455
+ analyzed: 1,
456
+ unsupported: 0,
457
+ excluded_by_contract: 0,
458
+ skipped_limit: 0,
459
+ failed: 0,
460
+ },
461
+ eligibleFiles: 1,
462
+ analyzedFiles: 1,
463
+ analyzedBytes: 100,
464
+ },
465
+ integrity: {
466
+ fcidVerified: true,
467
+ analysisPlanVerified: true,
468
+ factIntegrity: "healthy" as const,
469
+ requiredCoverage: "no_required_regions" as const,
470
+ governance: "healthy" as const,
471
+ profile: "known" as const,
472
+ },
473
+ inputDigest: A,
474
+ resultDigest: B,
475
+ };
476
+ expect(evaluationResultV2Schema.parse({ ...base, verdict: "indeterminate" })).toMatchObject({
477
+ verdict: "indeterminate",
478
+ });
479
+ expect(() => evaluationResultV2Schema.parse({ ...base, verdict: "pass" })).toThrow(
480
+ /verdict must be indeterminate/u
481
+ );
482
+
483
+ expect(() =>
484
+ evaluationResultV2Schema.parse({
485
+ ...base,
486
+ verdict: "pass",
487
+ reasons: ["requiredCoverageIncomplete"],
488
+ coverageSummary: {
489
+ ...base.coverageSummary,
490
+ requiredRegions: 1,
491
+ requiredByState: { ...base.coverageSummary.requiredByState, failed: 1 },
492
+ optionalByState: { ...base.coverageSummary.optionalByState, analyzed: 0 },
493
+ },
494
+ integrity: { ...base.integrity, requiredCoverage: "incomplete" },
495
+ })
496
+ ).toThrow(/verdict must be indeterminate/u);
497
+ });
498
+
499
+ it("preserves warnPresent when same-fingerprint warning evidence collapses under an error", () => {
500
+ const collapsed = {
501
+ schemaVersion: 2 as const,
502
+ verdict: "pass" as const,
503
+ reasons: [],
504
+ findings: [
505
+ {
506
+ fingerprint: "same-fingerprint",
507
+ ruleId: "harder-rule",
508
+ severity: "error" as const,
509
+ state: "advisory" as const,
510
+ stateReasons: ["harder same-fingerprint evidence retained"],
511
+ },
512
+ ],
513
+ counts: { blocked: 0, advisory: 1, pendingCloudTrust: 0 },
514
+ derived: { warnPresent: true, gatingFindingsPresent: false },
515
+ coverageSummary: {
516
+ plannedRegions: 1,
517
+ observedRegions: 1,
518
+ requiredRegions: 1,
519
+ requiredByState: {
520
+ analyzed: 1,
521
+ unsupported: 0,
522
+ excluded_by_contract: 0,
523
+ skipped_limit: 0,
524
+ failed: 0,
525
+ },
526
+ optionalByState: {
527
+ analyzed: 0,
528
+ unsupported: 0,
529
+ excluded_by_contract: 0,
530
+ skipped_limit: 0,
531
+ failed: 0,
532
+ },
533
+ eligibleFiles: 1,
534
+ analyzedFiles: 1,
535
+ analyzedBytes: 100,
536
+ },
537
+ integrity: {
538
+ fcidVerified: true,
539
+ analysisPlanVerified: true,
540
+ factIntegrity: "healthy" as const,
541
+ requiredCoverage: "complete" as const,
542
+ governance: "healthy" as const,
543
+ profile: "known" as const,
544
+ },
545
+ inputDigest: A,
546
+ resultDigest: B,
547
+ };
548
+ expect(evaluationResultV2Schema.parse(collapsed).derived.warnPresent).toBe(true);
549
+ });
550
+ });
551
+
552
+ describe("evaluate V2", () => {
553
+ it("produces the checked-in deterministic pass result for complete healthy evidence", () => {
554
+ const result = evaluate(evaluationInput());
555
+
556
+ expect(result).toMatchObject({
557
+ schemaVersion: 2,
558
+ verdict: "pass",
559
+ reasons: [],
560
+ counts: { blocked: 0, advisory: 0, pendingCloudTrust: 0 },
561
+ integrity: {
562
+ fcidVerified: true,
563
+ analysisPlanVerified: true,
564
+ factIntegrity: "healthy",
565
+ requiredCoverage: "complete",
566
+ governance: "healthy",
567
+ profile: "known",
568
+ },
569
+ });
570
+ expect(result.inputDigest).toBe(
571
+ "9c89d84e33f7569030440720c3c467622507c5aa0d1e9ca6eeb0fc4f5e045f7e"
572
+ );
573
+ expect(result.resultDigest).toBe(
574
+ "c3fe439dd196bdd7b9ca2d4d60e620282cebd9302f94af8e6ce509199471ef57"
575
+ );
576
+ });
577
+
578
+ it.each([
579
+ {
580
+ name: "unsupported required coverage",
581
+ patch: {
582
+ coverage: [
583
+ {
584
+ regionId: "tsx",
585
+ state: "unsupported" as const,
586
+ fileCount: 4,
587
+ byteCount: 400,
588
+ diagnosticIds: ["analysis.unsupported.tsx"],
589
+ },
590
+ ],
591
+ },
592
+ reason: "requiredCoverageIncomplete",
593
+ },
594
+ {
595
+ name: "canonical fact conflict",
596
+ patch: {
597
+ integrity: {
598
+ ...evaluationInput().integrity,
599
+ facts: {
600
+ state: "conflict" as const,
601
+ coalescedDuplicateCount: 0,
602
+ conflicts: [
603
+ {
604
+ canonicalKey: "token:--space-2",
605
+ factKind: "token_definition",
606
+ state: "conflict" as const,
607
+ values: [
608
+ {
609
+ valueDigest: A,
610
+ factIds: ["fact-a"],
611
+ analyzerIds: ["css"],
612
+ evidenceRefs: ["src/a.css:1"],
613
+ },
614
+ {
615
+ valueDigest: B,
616
+ factIds: ["fact-b"],
617
+ analyzerIds: ["scss"],
618
+ evidenceRefs: ["src/b.scss:1"],
619
+ },
620
+ ],
621
+ },
622
+ ],
623
+ },
624
+ },
625
+ },
626
+ reason: "factConflict",
627
+ },
628
+ {
629
+ name: "unknown profile",
630
+ patch: {
631
+ integrity: {
632
+ ...evaluationInput().integrity,
633
+ profile: { state: "unknown" as const, profileId: "react-web-v1" },
634
+ },
635
+ },
636
+ reason: "unknownAnalysisProfile",
637
+ },
638
+ ])("fails closed for $name", ({ patch, reason }) => {
639
+ const result = evaluate({ ...evaluationInput(), ...patch });
640
+ expect(result.verdict).toBe("indeterminate");
641
+ expect(result.reasons).toContain(reason);
642
+ });
643
+
644
+ it("normalizes coverage, waiver, and provenance order before digesting", () => {
645
+ const first = evaluationInput();
646
+ first.waivers = [
647
+ {
648
+ waiverId: "waiver-2",
649
+ scope: { kind: "rule", ruleId: "r-2" },
650
+ matchedFindingFingerprints: ["finding-b", "finding-a"],
651
+ reason: "Migration",
652
+ },
653
+ {
654
+ waiverId: "waiver-1",
655
+ scope: { kind: "finding", fingerprint: "finding-c" },
656
+ matchedFindingFingerprints: ["finding-c"],
657
+ reason: "Exception",
658
+ },
659
+ ];
660
+ const second = evaluationInput();
661
+ second.waivers = [
662
+ first.waivers[1]!,
663
+ {
664
+ ...first.waivers[0]!,
665
+ matchedFindingFingerprints: ["finding-a", "finding-b"],
666
+ },
667
+ ];
668
+
669
+ expect(evaluate(first).inputDigest).toBe(evaluate(second).inputDigest);
670
+ expect(evaluate(first).resultDigest).toBe(evaluate(second).resultDigest);
671
+ });
672
+
673
+ it("rejects evaluator and fact-schema versions it cannot interpret", () => {
674
+ expect(EVALUATOR_VERSION_V2).toBe("evaluation-kernel:v2");
675
+ expect(() =>
676
+ evaluate({ ...evaluationInput(), evaluatorVersion: "evaluation-kernel:v3" })
677
+ ).toThrow(/Unsupported evaluator version/u);
678
+ expect(() => evaluate({ ...evaluationInput(), factSchemaVersion: "facts:v2" })).toThrow(
679
+ /Unsupported fact schema version/u
680
+ );
681
+ });
682
+ });
683
+
684
+ describe("EvaluationReceiptV1", () => {
685
+ it("has a checked-in canonical digest and verifies all derived identity", () => {
686
+ const semantic = receiptSemantic();
687
+ const digest = evaluationReceiptDigestV1(semantic);
688
+ expect(digest).toBe("09b0294ed27a24f89d0432b19c519b4ad5c2bceffc982d2aa1fa370b5ac6b016");
689
+ const receipt = {
690
+ ...semantic,
691
+ receiptId: evaluationReceiptIdFromDigest(digest),
692
+ mintedAt: "2026-09-01T01:00:00.000Z",
693
+ digest,
694
+ };
695
+ expect(parseEvaluationReceiptV1(receipt)).toEqual(evaluationReceiptV1Schema.parse(receipt));
696
+ });
697
+
698
+ it("pins both input and result digests", () => {
699
+ const base = evaluationReceiptDigestV1(receiptSemantic());
700
+ expect(evaluationReceiptDigestV1(receiptSemantic({ inputDigest: B }))).not.toBe(base);
701
+ expect(evaluationReceiptDigestV1(receiptSemantic({ resultDigest: C }))).not.toBe(base);
702
+ });
703
+
704
+ it.each(["local", "ci_attested", "unknown"] as const)("keeps %s evidence advisory", (trust) => {
705
+ const semantic = receiptSemantic({ trust, authority: "advisory", providerProof: undefined });
706
+ expect(evaluationReceiptDigestV1(semantic)).toMatch(/^[0-9a-f]{64}$/u);
707
+ expect(() =>
708
+ evaluationReceiptDigestV1({ ...semantic, authority: "enforcement_eligible" })
709
+ ).toThrow();
710
+ });
711
+
712
+ it("requires exact provider proof for source-verified enforcement", () => {
713
+ expect(() =>
714
+ evaluationReceiptDigestV1(
715
+ receiptSemantic({ providerProof: { ...providerProof(), sourceCommitId: "other" } })
716
+ )
717
+ ).toThrow();
718
+ expect(() =>
719
+ evaluationReceiptDigestV1(receiptSemantic({ providerProof: undefined }))
720
+ ).toThrow();
721
+ expect(() =>
722
+ evaluationReceiptDigestV1(
723
+ receiptSemantic({ authority: "advisory", providerProof: undefined })
724
+ )
725
+ ).toThrow();
726
+ expect(() =>
727
+ evaluationReceiptDigestV1(receiptSemantic({ trust: "ci_attested", authority: "advisory" }))
728
+ ).toThrow();
729
+ });
730
+
731
+ it("does not let an external mint payload submit authority or proof", () => {
732
+ const external = {
733
+ schemaVersion: 1,
734
+ trust: "ci_attested",
735
+ binding,
736
+ sourceCommitId: "head-sha",
737
+ fcid: A,
738
+ analysisPlanDigest: plan().digest,
739
+ factsDigest: B,
740
+ inputDigest: C,
741
+ resultDigest: A,
742
+ evaluatorVersion: "evaluation-kernel:v2",
743
+ } satisfies EvaluationReceiptMintInputV1;
744
+ if (false) {
745
+ // @ts-expect-error Source-verified trust is derived with provider proof by the application.
746
+ const sourceVerifiedMintInput: EvaluationReceiptMintInputV1 = {
747
+ ...external,
748
+ trust: "source_verified",
749
+ };
750
+ void sourceVerifiedMintInput;
751
+ }
752
+ expect(evaluationReceiptMintInputV1Schema.parse(external)).toEqual(external);
753
+ expect(() =>
754
+ evaluationReceiptMintInputV1Schema.parse({ ...external, authority: "enforcement_eligible" })
755
+ ).toThrow();
756
+ expect(() =>
757
+ evaluationReceiptMintInputV1Schema.parse({ ...external, providerProof: providerProof() })
758
+ ).toThrow();
759
+ });
760
+ });