@usefragments/core 1.10.2 → 2.0.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 (130) 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-XN3LSDPY.js → chunk-BMPYIUZE.js} +612 -344
  6. package/dist/chunk-BMPYIUZE.js.map +1 -0
  7. package/dist/{chunk-YF65VYRY.js → chunk-ML5S6QNU.js} +191 -169
  8. package/dist/chunk-ML5S6QNU.js.map +1 -0
  9. package/dist/{chunk-3LLRNCPX.js → chunk-MZ2FS7U4.js} +1 -1
  10. package/dist/chunk-MZ2FS7U4.js.map +1 -0
  11. package/dist/chunk-PWIJMOI4.js +202 -0
  12. package/dist/chunk-PWIJMOI4.js.map +1 -0
  13. package/dist/chunk-RYFULE43.js +578 -0
  14. package/dist/chunk-RYFULE43.js.map +1 -0
  15. package/dist/codes/index.d.ts +2 -2
  16. package/dist/codes/index.js +3 -2
  17. package/dist/compiled-types/index.d.ts +327 -2
  18. package/dist/compiled-types/index.js +1 -1
  19. package/dist/generate/index.d.ts +3 -2
  20. package/dist/{governance-CLk_wkP9.d.ts → governance-hOPXGbbs.d.ts} +474 -515
  21. package/dist/governance-telemetry.d.ts +6 -0
  22. package/dist/governance-telemetry.js +1 -1
  23. package/dist/{index-_sxhUNqx.d.ts → index-C8bcXVav.d.ts} +448 -448
  24. package/dist/index.d.ts +3280 -1305
  25. package/dist/index.js +2414 -350
  26. package/dist/index.js.map +1 -1
  27. package/dist/manifest.d.ts +228 -0
  28. package/dist/manifest.js +24 -0
  29. package/dist/manifest.js.map +1 -0
  30. package/dist/preview/index.js +45 -1
  31. package/dist/preview/index.js.map +1 -1
  32. package/dist/preview-runtime.d.ts +1 -2
  33. package/dist/preview-runtime.js +150 -14
  34. package/dist/preview-runtime.js.map +1 -1
  35. package/dist/react-types.d.ts +1 -2
  36. package/dist/registry.d.ts +1412 -203
  37. package/dist/registry.js +30 -3
  38. package/dist/schemas/index.d.ts +1 -1
  39. package/dist/schemas/index.js +3 -2
  40. package/dist/storyAdapter.d.ts +1 -2
  41. package/dist/storyAdapter.js +11 -49
  42. package/dist/storyAdapter.js.map +1 -1
  43. package/dist/test-utils.d.ts +3 -2
  44. package/dist/topology/index.d.ts +1 -1
  45. package/dist/topology/index.js +1 -1
  46. package/package.json +8 -2
  47. package/src/__tests__/contract-parser.test.ts +318 -277
  48. package/src/__tests__/preview-runtime-hook.test.tsx +315 -0
  49. package/src/__tests__/preview-runtime.test.tsx +30 -8
  50. package/src/__tests__/schema.test.ts +191 -14
  51. package/src/analysis-plan/analysis-plan-v1.test.ts +320 -0
  52. package/src/analysis-plan/coverage.ts +181 -0
  53. package/src/analysis-plan/digest.ts +141 -0
  54. package/src/analysis-plan/index.ts +34 -0
  55. package/src/analysis-plan/types.ts +207 -0
  56. package/src/approved-contract-tokens.test.ts +39 -0
  57. package/src/approved-contract-tokens.ts +18 -0
  58. package/src/codes/__tests__/codes.test.ts +13 -0
  59. package/src/codes/codes.ts +40 -0
  60. package/src/compiled-types/index.ts +640 -39
  61. package/src/compiled-types/parse.test.ts +145 -4
  62. package/src/component-contract.ts +95 -53
  63. package/src/composition.ts +7 -13
  64. package/src/constants.ts +3 -6
  65. package/src/contract/hash.test.ts +20 -0
  66. package/src/contract/hash.ts +66 -9
  67. package/src/contract/index.ts +24 -1
  68. package/src/contract/manifest.test.ts +94 -0
  69. package/src/contract/manifest.ts +68 -0
  70. package/src/contract/preimage.test.ts +219 -1
  71. package/src/contract/preimage.ts +326 -6
  72. package/src/contract/stamp.test.ts +3 -0
  73. package/src/contract/stamp.ts +1 -1
  74. package/src/contract-parser.ts +54 -30
  75. package/src/defineFragment.test.ts +476 -91
  76. package/src/defineFragment.ts +204 -114
  77. package/src/domain-ids.test.ts +35 -0
  78. package/src/domain-ids.ts +61 -0
  79. package/src/evaluation/evaluate.test.ts +522 -0
  80. package/src/evaluation/evaluate.ts +690 -0
  81. package/src/evaluation/evaluation-v2-receipt-v1.test.ts +772 -0
  82. package/src/evaluation/index.ts +58 -0
  83. package/src/evaluation/receipt.ts +753 -0
  84. package/src/evaluation/types.ts +406 -0
  85. package/src/facts/builders.ts +2 -0
  86. package/src/facts/compile.ts +29 -6
  87. package/src/facts/fact-index.ts +13 -3
  88. package/src/facts/fact-integrity-v1.test.ts +172 -0
  89. package/src/facts/facts.test.ts +15 -0
  90. package/src/facts/ids.ts +46 -3
  91. package/src/facts/index.ts +14 -1
  92. package/src/facts/integrity.ts +134 -0
  93. package/src/facts/types.ts +36 -0
  94. package/src/governance-integrity.test.ts +1 -0
  95. package/src/governance-integrity.ts +5 -3
  96. package/src/governance-telemetry.ts +8 -0
  97. package/src/governance.ts +70 -8
  98. package/src/index.ts +230 -37
  99. package/src/preview/validation.test.ts +62 -0
  100. package/src/preview/validation.ts +48 -2
  101. package/src/preview-runtime.tsx +227 -20
  102. package/src/registry-install-plan.ts +200 -109
  103. package/src/registry-shards.test.ts +263 -0
  104. package/src/registry.ts +237 -0
  105. package/src/repository-binding.test.ts +50 -0
  106. package/src/repository-binding.ts +96 -0
  107. package/src/rules/families.test.ts +36 -0
  108. package/src/rules/finding.ts +7 -2
  109. package/src/rules/index.ts +17 -1
  110. package/src/rules/rule-config.test.ts +66 -0
  111. package/src/rules/rule-config.ts +73 -0
  112. package/src/rules/rules.test.ts +26 -0
  113. package/src/rules/tokens-css-vars-must-be-defined.test.ts +51 -2
  114. package/src/rules/tokens-css-vars-must-be-defined.ts +34 -1
  115. package/src/schema.ts +293 -113
  116. package/src/schemas/index.ts +1 -1
  117. package/src/storyAdapter.test.ts +68 -12
  118. package/src/storyAdapter.ts +44 -75
  119. package/src/topology/resolve-area.ts +1 -1
  120. package/src/types.ts +258 -40
  121. package/dist/chunk-3LLRNCPX.js.map +0 -1
  122. package/dist/chunk-RANPUC6C.js +0 -72
  123. package/dist/chunk-RANPUC6C.js.map +0 -1
  124. package/dist/chunk-XN3LSDPY.js.map +0 -1
  125. package/dist/chunk-YF65VYRY.js.map +0 -1
  126. package/src/fragment-types.ts +0 -214
  127. package/src/react-create-element.test.ts +0 -22
  128. package/src/react-create-element.ts +0 -12
  129. package/src/storyFilters.test.ts +0 -350
  130. package/src/storyFilters.ts +0 -253
@@ -0,0 +1,753 @@
1
+ import { z } from "zod";
2
+ import {
3
+ analysisPlanV1Schema,
4
+ coverageRegionV1Schema,
5
+ coverageSummaryV1Schema,
6
+ deriveCoverageSummaryV1,
7
+ parseAnalysisPlanV1,
8
+ } from "../analysis-plan/index.js";
9
+ import { canonicalPreimage, contractHash, sha256Hex } from "../contract/hash.js";
10
+ import { CONTRACT_PREIMAGE_SCHEMA, type ContractPreimage } from "../contract/preimage.js";
11
+ import {
12
+ digestHexStringSchema,
13
+ evaluationReceiptIdFromDigest,
14
+ evaluationReceiptIdStringSchema,
15
+ type DigestHex,
16
+ } from "../domain-ids.js";
17
+ import { FactIndex } from "../facts/fact-index.js";
18
+ import { canonicalFactIntegrityV1Schema } from "../facts/integrity.js";
19
+ import {
20
+ normalizeRepositoryBindingKeyV1,
21
+ repositoryBindingDigestV1,
22
+ repositoryBindingKeyV1Schema,
23
+ } from "../repository-binding.js";
24
+ import type {
25
+ EffectiveEvaluationWaiverV2,
26
+ EvaluationInputV2,
27
+ EvaluationReceiptMintInputV1,
28
+ EvaluationReasonV2,
29
+ EvaluationReceiptSemanticV1,
30
+ EvaluationReceiptV1,
31
+ EvaluationResultV2,
32
+ ProviderProofSemanticV1,
33
+ ProviderProofV1,
34
+ } from "./types.js";
35
+
36
+ export const EVALUATION_MAX_WAIVERS_V2 = 1_000;
37
+ export const EVALUATION_MAX_WAIVER_MATCHES_V2 = 10_000;
38
+ export const EVALUATION_MAX_FINDINGS_V2 = 10_000;
39
+ export const EVALUATION_MAX_REASON_CODES_V2 = 64;
40
+ export const EVALUATION_MAX_MAP_ENTRIES_V2 = 100_000;
41
+ export const EVALUATION_MAX_POLICY_RULES_V2 = 10_000;
42
+ export const EVALUATION_RECEIPT_STRING_MAX_BYTES_V1 = 4_096;
43
+
44
+ const utf8Length = (value: string): number => new TextEncoder().encode(value).byteLength;
45
+ const boundedString = (maxBytes = EVALUATION_RECEIPT_STRING_MAX_BYTES_V1) =>
46
+ z
47
+ .string()
48
+ .min(1)
49
+ .refine((value) => utf8Length(value) <= maxBytes, `Value exceeds ${maxBytes} UTF-8 bytes`);
50
+ const unique = (values: readonly string[]): boolean => new Set(values).size === values.length;
51
+
52
+ const contractPreimageV1Schema: z.ZodType<ContractPreimage> = z
53
+ .object({
54
+ schema: z.literal(CONTRACT_PREIMAGE_SCHEMA),
55
+ domains: z
56
+ .object({
57
+ components: digestHexStringSchema,
58
+ tokens: digestHexStringSchema,
59
+ canonicalMap: digestHexStringSchema,
60
+ policy: digestHexStringSchema,
61
+ })
62
+ .strict(),
63
+ })
64
+ .strict();
65
+
66
+ const effectiveRulePolicySchema = z
67
+ .object({ trust: z.enum(["canonical", "evidence", "none"]).optional() })
68
+ .strict();
69
+ const effectivePolicySchema = z
70
+ .object({
71
+ failOnWarnings: z.boolean(),
72
+ failOnInert: z.boolean().optional(),
73
+ rules: z
74
+ .record(boundedString(), effectiveRulePolicySchema)
75
+ .refine(
76
+ (rules) => Object.keys(rules).length <= EVALUATION_MAX_POLICY_RULES_V2,
77
+ "Policy rule map exceeds the V2 limit"
78
+ )
79
+ .optional(),
80
+ })
81
+ .strict();
82
+
83
+ const evaluationTrustSchema = z
84
+ .object({
85
+ source: z.enum(["verified", "unverified", "unknown"]),
86
+ sources: z
87
+ .record(boundedString(), z.enum(["verified", "unverified", "unknown"]))
88
+ .refine(
89
+ (sources) => Object.keys(sources).length <= EVALUATION_MAX_MAP_ENTRIES_V2,
90
+ "Source trust map exceeds the V2 limit"
91
+ )
92
+ .optional(),
93
+ mappings: z
94
+ .record(boundedString(), z.enum(["confirmed", "unconfirmed", "unknown"]))
95
+ .refine(
96
+ (mappings) => Object.keys(mappings).length <= EVALUATION_MAX_MAP_ENTRIES_V2,
97
+ "Mapping trust map exceeds the V2 limit"
98
+ ),
99
+ })
100
+ .strict();
101
+
102
+ const waiverScopeSchema = z.discriminatedUnion("kind", [
103
+ z.object({ kind: z.literal("finding"), fingerprint: boundedString() }).strict(),
104
+ z.object({ kind: z.literal("rule"), ruleId: boundedString() }).strict(),
105
+ z.object({ kind: z.literal("path"), pathPattern: boundedString() }).strict(),
106
+ z
107
+ .object({ kind: z.literal("rule_path"), ruleId: boundedString(), pathPattern: boundedString() })
108
+ .strict(),
109
+ ]);
110
+
111
+ export const effectiveEvaluationWaiverV2Schema: z.ZodType<
112
+ EffectiveEvaluationWaiverV2,
113
+ z.ZodTypeDef,
114
+ unknown
115
+ > = z
116
+ .object({
117
+ waiverId: boundedString(),
118
+ scope: waiverScopeSchema,
119
+ matchedFindingFingerprints: z
120
+ .array(boundedString())
121
+ .min(1)
122
+ .max(EVALUATION_MAX_WAIVER_MATCHES_V2)
123
+ .refine(unique, "matched finding fingerprints must be unique"),
124
+ reason: boundedString(),
125
+ expiresAt: z.number().finite().optional(),
126
+ })
127
+ .strict();
128
+
129
+ const evaluationIntegrityInputV2Schema = z
130
+ .object({
131
+ governance: z.enum(["healthy", "inert"]),
132
+ inertConfigDiagnosticCount: z.number().int().nonnegative(),
133
+ profile: z.discriminatedUnion("state", [
134
+ z
135
+ .object({
136
+ state: z.literal("known"),
137
+ profileId: boundedString(),
138
+ profileVersion: boundedString(),
139
+ })
140
+ .strict(),
141
+ z
142
+ .object({
143
+ state: z.literal("unknown"),
144
+ profileId: boundedString(),
145
+ profileVersion: boundedString().optional(),
146
+ })
147
+ .strict(),
148
+ ]),
149
+ facts: canonicalFactIntegrityV1Schema,
150
+ })
151
+ .strict();
152
+
153
+ const baselineSchema = z
154
+ .object({
155
+ identity: z
156
+ .object({
157
+ bindingDigest: digestHexStringSchema,
158
+ repositoryExternalId: boundedString(),
159
+ fcid: digestHexStringSchema,
160
+ sourceCommitId: boundedString(),
161
+ generationId: boundedString(),
162
+ })
163
+ .strict(),
164
+ findingStates: z
165
+ .record(boundedString(), z.enum(["introduced", "existing", "unknown"]))
166
+ .refine(
167
+ (states) => Object.keys(states).length <= EVALUATION_MAX_MAP_ENTRIES_V2,
168
+ "Baseline finding-state map exceeds the V2 limit"
169
+ ),
170
+ })
171
+ .strict();
172
+
173
+ const evaluationInputV2RawSchema = z
174
+ .object({
175
+ schemaVersion: z.literal(2),
176
+ binding: repositoryBindingKeyV1Schema,
177
+ analysisPlan: analysisPlanV1Schema,
178
+ contract: z
179
+ .object({
180
+ fcid: digestHexStringSchema,
181
+ preimage: contractPreimageV1Schema,
182
+ active: z.boolean(),
183
+ })
184
+ .strict(),
185
+ factSchemaVersion: boundedString(256),
186
+ facts: z.instanceof(FactIndex),
187
+ factsDigest: digestHexStringSchema,
188
+ coverage: z.array(coverageRegionV1Schema).max(1_000),
189
+ policy: effectivePolicySchema,
190
+ baseline: baselineSchema.optional(),
191
+ adoption: z
192
+ .union([z.literal("unavailable"), z.object({ regressed: z.boolean() }).strict()])
193
+ .optional(),
194
+ trust: evaluationTrustSchema.optional(),
195
+ waivers: z
196
+ .array(effectiveEvaluationWaiverV2Schema)
197
+ .max(EVALUATION_MAX_WAIVERS_V2)
198
+ .refine(
199
+ (waivers) => new Set(waivers.map((waiver) => waiver.waiverId)).size === waivers.length,
200
+ "waiver IDs must be unique"
201
+ ),
202
+ integrity: evaluationIntegrityInputV2Schema,
203
+ evaluatedAt: z.number().finite(),
204
+ evaluatorVersion: boundedString(256),
205
+ })
206
+ .strict()
207
+ .superRefine((input, context) => {
208
+ let plan;
209
+ try {
210
+ plan = parseAnalysisPlanV1(input.analysisPlan);
211
+ } catch (error) {
212
+ context.addIssue({
213
+ code: z.ZodIssueCode.custom,
214
+ path: ["analysisPlan"],
215
+ message: error instanceof Error ? error.message : "Invalid analysis plan identity",
216
+ });
217
+ return;
218
+ }
219
+ if (repositoryBindingDigestV1(plan.binding) !== repositoryBindingDigestV1(input.binding)) {
220
+ context.addIssue({
221
+ code: z.ZodIssueCode.custom,
222
+ path: ["binding"],
223
+ message: "Evaluation binding does not match the analysis plan binding",
224
+ });
225
+ }
226
+ if (plan.contract.fcid !== input.contract.fcid) {
227
+ context.addIssue({
228
+ code: z.ZodIssueCode.custom,
229
+ path: ["contract", "fcid"],
230
+ message: "Evaluation FCID does not match the analysis plan",
231
+ });
232
+ }
233
+ if (contractHash(input.contract.preimage) !== input.contract.fcid) {
234
+ context.addIssue({
235
+ code: z.ZodIssueCode.custom,
236
+ path: ["contract", "preimage"],
237
+ message: "Contract preimage does not reproduce the pinned FCID",
238
+ });
239
+ }
240
+ if (plan.evaluatorVersion !== input.evaluatorVersion) {
241
+ context.addIssue({
242
+ code: z.ZodIssueCode.custom,
243
+ path: ["evaluatorVersion"],
244
+ message: "Evaluator version does not match the analysis plan",
245
+ });
246
+ }
247
+ if (
248
+ input.integrity.profile.state === "known" &&
249
+ (input.integrity.profile.profileId !== plan.profile.id ||
250
+ input.integrity.profile.profileVersion !== plan.profile.version)
251
+ ) {
252
+ context.addIssue({
253
+ code: z.ZodIssueCode.custom,
254
+ path: ["integrity", "profile"],
255
+ message: "Known profile identity does not match the analysis plan",
256
+ });
257
+ }
258
+ if (input.baseline) {
259
+ const expectedBindingDigest = repositoryBindingDigestV1(input.binding);
260
+ if (
261
+ input.baseline.identity.bindingDigest !== expectedBindingDigest ||
262
+ input.baseline.identity.repositoryExternalId !== input.binding.repositoryExternalId ||
263
+ input.baseline.identity.fcid !== input.contract.fcid
264
+ ) {
265
+ context.addIssue({
266
+ code: z.ZodIssueCode.custom,
267
+ path: ["baseline", "identity"],
268
+ message: "Baseline identity does not match the evaluation binding and FCID",
269
+ });
270
+ }
271
+ }
272
+ if (input.facts.size() > plan.limits.maxFacts) {
273
+ context.addIssue({
274
+ code: z.ZodIssueCode.custom,
275
+ path: ["facts"],
276
+ message: "Fact count exceeds the analysis-plan limit",
277
+ });
278
+ }
279
+ try {
280
+ deriveCoverageSummaryV1(plan, input.coverage);
281
+ } catch (error) {
282
+ context.addIssue({
283
+ code: z.ZodIssueCode.custom,
284
+ path: ["coverage"],
285
+ message: error instanceof Error ? error.message : "Coverage does not match the plan",
286
+ });
287
+ }
288
+ });
289
+
290
+ export const evaluationInputV2Schema: z.ZodType<EvaluationInputV2, z.ZodTypeDef, unknown> =
291
+ evaluationInputV2RawSchema.transform(
292
+ (input) =>
293
+ ({
294
+ ...input,
295
+ binding: normalizeRepositoryBindingKeyV1(input.binding),
296
+ analysisPlan: parseAnalysisPlanV1(input.analysisPlan),
297
+ }) as EvaluationInputV2
298
+ );
299
+
300
+ const evaluationReasonV2Schema = z.enum([
301
+ "zeroFileScan",
302
+ "governanceInert",
303
+ "contractUnpinned",
304
+ "contractStale",
305
+ "contractIdentityMismatch",
306
+ "inertConfigPresent",
307
+ "unknownPresetPresent",
308
+ "blockedFindings",
309
+ "adoptionRegression",
310
+ "expiredSuppression",
311
+ "pendingCloudTrust",
312
+ "adoptionBaselineUnavailable",
313
+ "analysisPlanIdentityMismatch",
314
+ "requiredCoverageMissing",
315
+ "requiredCoverageIncomplete",
316
+ "factConflict",
317
+ "unknownAnalysisProfile",
318
+ ]);
319
+
320
+ export const evaluationResultV2Schema: z.ZodType<EvaluationResultV2, z.ZodTypeDef, unknown> = z
321
+ .object({
322
+ schemaVersion: z.literal(2),
323
+ verdict: z.enum(["pass", "block", "indeterminate"]),
324
+ reasons: z
325
+ .array(evaluationReasonV2Schema)
326
+ .max(EVALUATION_MAX_REASON_CODES_V2)
327
+ .refine(unique, "Evaluation reason codes must be unique"),
328
+ findings: z
329
+ .array(
330
+ z
331
+ .object({
332
+ fingerprint: boundedString(),
333
+ ruleId: boundedString(),
334
+ severity: z.enum(["error", "warn", "info"]),
335
+ state: z.enum(["blocked", "advisory", "pending_cloud_trust"]),
336
+ stateReasons: z.array(boundedString()).max(256),
337
+ })
338
+ .strict()
339
+ )
340
+ .max(EVALUATION_MAX_FINDINGS_V2),
341
+ counts: z
342
+ .object({
343
+ blocked: z.number().int().nonnegative().max(EVALUATION_MAX_FINDINGS_V2),
344
+ advisory: z.number().int().nonnegative().max(EVALUATION_MAX_FINDINGS_V2),
345
+ pendingCloudTrust: z.number().int().nonnegative().max(EVALUATION_MAX_FINDINGS_V2),
346
+ })
347
+ .strict(),
348
+ derived: z.object({ warnPresent: z.boolean(), gatingFindingsPresent: z.boolean() }).strict(),
349
+ coverageSummary: coverageSummaryV1Schema,
350
+ integrity: z
351
+ .object({
352
+ fcidVerified: z.boolean(),
353
+ analysisPlanVerified: z.boolean(),
354
+ factIntegrity: z.enum(["healthy", "conflict"]),
355
+ requiredCoverage: z.enum(["complete", "incomplete", "no_required_regions"]),
356
+ governance: z.enum(["healthy", "inert"]),
357
+ profile: z.enum(["known", "unknown"]),
358
+ })
359
+ .strict(),
360
+ inputDigest: digestHexStringSchema,
361
+ resultDigest: digestHexStringSchema,
362
+ })
363
+ .strict()
364
+ .superRefine((result, context) => {
365
+ const expectedCounts = { blocked: 0, advisory: 0, pendingCloudTrust: 0 };
366
+ let emittedWarnPresent = false;
367
+ for (const [index, finding] of result.findings.entries()) {
368
+ if (finding.state === "blocked") expectedCounts.blocked += 1;
369
+ else if (finding.state === "pending_cloud_trust") expectedCounts.pendingCloudTrust += 1;
370
+ else expectedCounts.advisory += 1;
371
+ if (finding.severity === "warn") emittedWarnPresent = true;
372
+ if (new Set(finding.stateReasons).size !== finding.stateReasons.length) {
373
+ context.addIssue({
374
+ code: z.ZodIssueCode.custom,
375
+ path: ["findings", index, "stateReasons"],
376
+ message: "Finding state reasons must be unique",
377
+ });
378
+ }
379
+ }
380
+ if (
381
+ new Set(result.findings.map((finding) => finding.fingerprint)).size !== result.findings.length
382
+ ) {
383
+ context.addIssue({
384
+ code: z.ZodIssueCode.custom,
385
+ path: ["findings"],
386
+ message: "Evaluation finding fingerprints must be unique",
387
+ });
388
+ }
389
+ for (const key of ["blocked", "advisory", "pendingCloudTrust"] as const) {
390
+ if (result.counts[key] !== expectedCounts[key]) {
391
+ context.addIssue({
392
+ code: z.ZodIssueCode.custom,
393
+ path: ["counts", key],
394
+ message: `Evaluation ${key} count must match findings`,
395
+ });
396
+ }
397
+ }
398
+ if (emittedWarnPresent && !result.derived.warnPresent) {
399
+ context.addIssue({
400
+ code: z.ZodIssueCode.custom,
401
+ path: ["derived", "warnPresent"],
402
+ message: "An emitted warning finding requires warnPresent",
403
+ });
404
+ }
405
+ if (result.derived.gatingFindingsPresent !== expectedCounts.blocked > 0) {
406
+ context.addIssue({
407
+ code: z.ZodIssueCode.custom,
408
+ path: ["derived", "gatingFindingsPresent"],
409
+ message: "gatingFindingsPresent must match blocked findings",
410
+ });
411
+ }
412
+
413
+ const coverageStates = [
414
+ "analyzed",
415
+ "unsupported",
416
+ "excluded_by_contract",
417
+ "skipped_limit",
418
+ "failed",
419
+ ] as const;
420
+ const requiredCount = coverageStates.reduce(
421
+ (total, state) => total + result.coverageSummary.requiredByState[state],
422
+ 0
423
+ );
424
+ const optionalCount = coverageStates.reduce(
425
+ (total, state) => total + result.coverageSummary.optionalByState[state],
426
+ 0
427
+ );
428
+ if (
429
+ requiredCount !== result.coverageSummary.requiredRegions ||
430
+ requiredCount + optionalCount !== result.coverageSummary.observedRegions ||
431
+ result.coverageSummary.observedRegions !== result.coverageSummary.plannedRegions
432
+ ) {
433
+ context.addIssue({
434
+ code: z.ZodIssueCode.custom,
435
+ path: ["coverageSummary"],
436
+ message: "Coverage summary counts must reconcile with planned and observed regions",
437
+ });
438
+ }
439
+ if (result.coverageSummary.analyzedFiles > result.coverageSummary.eligibleFiles) {
440
+ context.addIssue({
441
+ code: z.ZodIssueCode.custom,
442
+ path: ["coverageSummary", "analyzedFiles"],
443
+ message: "Analyzed files cannot exceed eligible files",
444
+ });
445
+ }
446
+
447
+ const expectedRequiredCoverage =
448
+ result.coverageSummary.requiredRegions === 0
449
+ ? "no_required_regions"
450
+ : result.coverageSummary.requiredByState.analyzed === result.coverageSummary.requiredRegions
451
+ ? "complete"
452
+ : "incomplete";
453
+ if (result.integrity.requiredCoverage !== expectedRequiredCoverage) {
454
+ context.addIssue({
455
+ code: z.ZodIssueCode.custom,
456
+ path: ["integrity", "requiredCoverage"],
457
+ message: "Required coverage integrity must match the derived coverage summary",
458
+ });
459
+ }
460
+
461
+ const reasons = new Set(result.reasons);
462
+ const requireReason = (reason: EvaluationReasonV2, required: boolean, path: string[]) => {
463
+ if (reasons.has(reason) !== required) {
464
+ context.addIssue({
465
+ code: z.ZodIssueCode.custom,
466
+ path,
467
+ message: `${reason} must match the evaluation result`,
468
+ });
469
+ }
470
+ };
471
+ requireReason("blockedFindings", expectedCounts.blocked > 0, ["reasons"]);
472
+ requireReason("pendingCloudTrust", expectedCounts.pendingCloudTrust > 0, ["reasons"]);
473
+ requireReason("contractIdentityMismatch", !result.integrity.fcidVerified, ["reasons"]);
474
+ requireReason("analysisPlanIdentityMismatch", !result.integrity.analysisPlanVerified, [
475
+ "reasons",
476
+ ]);
477
+ requireReason("factConflict", result.integrity.factIntegrity === "conflict", ["reasons"]);
478
+ requireReason("governanceInert", result.integrity.governance === "inert", ["reasons"]);
479
+ requireReason("unknownAnalysisProfile", result.integrity.profile === "unknown", ["reasons"]);
480
+ requireReason(
481
+ "requiredCoverageIncomplete",
482
+ result.integrity.requiredCoverage === "incomplete",
483
+ ["reasons"]
484
+ );
485
+ requireReason(
486
+ "requiredCoverageMissing",
487
+ result.integrity.requiredCoverage === "no_required_regions",
488
+ ["reasons"]
489
+ );
490
+
491
+ const integrityHealthy =
492
+ result.integrity.fcidVerified &&
493
+ result.integrity.analysisPlanVerified &&
494
+ result.integrity.factIntegrity === "healthy" &&
495
+ result.integrity.requiredCoverage === "complete" &&
496
+ result.integrity.governance === "healthy" &&
497
+ result.integrity.profile === "known";
498
+ const blockingReasons = new Set<EvaluationReasonV2>([
499
+ "blockedFindings",
500
+ "adoptionRegression",
501
+ "expiredSuppression",
502
+ ]);
503
+ // Mirrors the kernel: any integrity-class reason holds the verdict at
504
+ // `indeterminate` even when blocking findings are present (nothing can
505
+ // be proven on doubtful coverage or a doubtful contract).
506
+ const integrityReasons = new Set<EvaluationReasonV2>([
507
+ "zeroFileScan",
508
+ "governanceInert",
509
+ "contractUnpinned",
510
+ "contractStale",
511
+ "contractIdentityMismatch",
512
+ "inertConfigPresent",
513
+ "unknownPresetPresent",
514
+ "analysisPlanIdentityMismatch",
515
+ "requiredCoverageMissing",
516
+ "requiredCoverageIncomplete",
517
+ "factConflict",
518
+ "unknownAnalysisProfile",
519
+ ]);
520
+ const expectedVerdict =
521
+ !integrityHealthy || result.reasons.some((reason) => integrityReasons.has(reason))
522
+ ? "indeterminate"
523
+ : result.reasons.some((reason) => blockingReasons.has(reason))
524
+ ? "block"
525
+ : result.reasons.length > 0
526
+ ? "indeterminate"
527
+ : "pass";
528
+ if (result.verdict !== expectedVerdict) {
529
+ context.addIssue({
530
+ code: z.ZodIssueCode.custom,
531
+ path: ["verdict"],
532
+ message: `Evaluation verdict must be ${expectedVerdict} for its reasons and integrity`,
533
+ });
534
+ }
535
+ }) as unknown as z.ZodType<EvaluationResultV2, z.ZodTypeDef, unknown>;
536
+
537
+ const providerProofSemanticV1Schema: z.ZodType<ProviderProofSemanticV1, z.ZodTypeDef, unknown> = z
538
+ .object({
539
+ schemaVersion: z.literal(1),
540
+ adapterId: boundedString(),
541
+ adapterVersion: boundedString(),
542
+ providerInstanceId: boundedString(),
543
+ bindingDigest: digestHexStringSchema,
544
+ repositoryExternalId: boundedString(),
545
+ sourceCommitId: boundedString(),
546
+ eventExternalId: boundedString(),
547
+ changeExternalId: boundedString().optional(),
548
+ publisherExternalId: boundedString(),
549
+ })
550
+ .strict() as unknown as z.ZodType<ProviderProofSemanticV1, z.ZodTypeDef, unknown>;
551
+
552
+ export function providerProofDigestV1(value: ProviderProofSemanticV1): DigestHex {
553
+ const proof = providerProofSemanticV1Schema.parse(value);
554
+ return sha256Hex(canonicalPreimage({ schema: "provider-proof.v1", ...proof })) as DigestHex;
555
+ }
556
+
557
+ export const providerProofV1Schema: z.ZodType<ProviderProofV1, z.ZodTypeDef, unknown> = z
558
+ .object({
559
+ schemaVersion: z.literal(1),
560
+ adapterId: boundedString(),
561
+ adapterVersion: boundedString(),
562
+ providerInstanceId: boundedString(),
563
+ bindingDigest: digestHexStringSchema,
564
+ repositoryExternalId: boundedString(),
565
+ sourceCommitId: boundedString(),
566
+ eventExternalId: boundedString(),
567
+ changeExternalId: boundedString().optional(),
568
+ publisherExternalId: boundedString(),
569
+ proofDigest: digestHexStringSchema,
570
+ })
571
+ .strict()
572
+ .superRefine((proof, context) => {
573
+ const { proofDigest, ...semantic } = proof;
574
+ if (providerProofDigestV1(semantic) !== proofDigest) {
575
+ context.addIssue({
576
+ code: z.ZodIssueCode.custom,
577
+ path: ["proofDigest"],
578
+ message: "Provider proof digest mismatch",
579
+ });
580
+ }
581
+ }) as unknown as z.ZodType<ProviderProofV1, z.ZodTypeDef, unknown>;
582
+
583
+ const receiptSemanticShape = {
584
+ schemaVersion: z.literal(1),
585
+ trust: z.enum(["source_verified", "ci_attested", "local", "unknown"]),
586
+ authority: z.enum(["advisory", "enforcement_eligible"]),
587
+ binding: repositoryBindingKeyV1Schema,
588
+ sourceCommitId: boundedString(),
589
+ fcid: digestHexStringSchema,
590
+ analysisPlanDigest: digestHexStringSchema,
591
+ factsDigest: digestHexStringSchema,
592
+ inputDigest: digestHexStringSchema,
593
+ resultDigest: digestHexStringSchema,
594
+ evaluatorVersion: boundedString(),
595
+ providerProof: providerProofV1Schema.optional(),
596
+ };
597
+
598
+ function validateReceiptSemantics(
599
+ receipt: z.infer<z.ZodObject<typeof receiptSemanticShape>>,
600
+ context: z.RefinementCtx
601
+ ): void {
602
+ if (receipt.authority === "enforcement_eligible") {
603
+ if (receipt.trust !== "source_verified") {
604
+ context.addIssue({
605
+ code: z.ZodIssueCode.custom,
606
+ path: ["authority"],
607
+ message: "Only source-verified evidence can enforce",
608
+ });
609
+ }
610
+ if (!receipt.providerProof) {
611
+ context.addIssue({
612
+ code: z.ZodIssueCode.custom,
613
+ path: ["providerProof"],
614
+ message: "Source-verified enforcement requires provider proof",
615
+ });
616
+ }
617
+ }
618
+ if (receipt.trust === "source_verified" && !receipt.providerProof) {
619
+ context.addIssue({
620
+ code: z.ZodIssueCode.custom,
621
+ path: ["providerProof"],
622
+ message: "Source-verified trust requires provider proof",
623
+ });
624
+ }
625
+ if (receipt.trust !== "source_verified" && receipt.providerProof) {
626
+ context.addIssue({
627
+ code: z.ZodIssueCode.custom,
628
+ path: ["providerProof"],
629
+ message: "Provider proof is reserved for source-verified trust",
630
+ });
631
+ }
632
+ if (!receipt.providerProof) return;
633
+ const proof = receipt.providerProof;
634
+ const binding = normalizeRepositoryBindingKeyV1(receipt.binding);
635
+ if (proof.bindingDigest !== repositoryBindingDigestV1(binding)) {
636
+ context.addIssue({
637
+ code: z.ZodIssueCode.custom,
638
+ path: ["providerProof", "bindingDigest"],
639
+ message: "Provider proof binding mismatch",
640
+ });
641
+ }
642
+ if (proof.providerInstanceId !== binding.providerInstanceId) {
643
+ context.addIssue({
644
+ code: z.ZodIssueCode.custom,
645
+ path: ["providerProof", "providerInstanceId"],
646
+ message: "Provider proof instance mismatch",
647
+ });
648
+ }
649
+ if (proof.repositoryExternalId !== binding.repositoryExternalId) {
650
+ context.addIssue({
651
+ code: z.ZodIssueCode.custom,
652
+ path: ["providerProof", "repositoryExternalId"],
653
+ message: "Provider proof repository mismatch",
654
+ });
655
+ }
656
+ if (proof.sourceCommitId !== receipt.sourceCommitId) {
657
+ context.addIssue({
658
+ code: z.ZodIssueCode.custom,
659
+ path: ["providerProof", "sourceCommitId"],
660
+ message: "Provider proof source mismatch",
661
+ });
662
+ }
663
+ }
664
+
665
+ const evaluationReceiptSemanticV1Schema: z.ZodType<
666
+ EvaluationReceiptSemanticV1,
667
+ z.ZodTypeDef,
668
+ unknown
669
+ > = z.object(receiptSemanticShape).strict().superRefine(validateReceiptSemantics) as z.ZodType<
670
+ EvaluationReceiptSemanticV1,
671
+ z.ZodTypeDef,
672
+ unknown
673
+ >;
674
+
675
+ export function evaluationReceiptDigestV1(value: EvaluationReceiptSemanticV1): DigestHex {
676
+ const receipt = evaluationReceiptSemanticV1Schema.parse(value);
677
+ return sha256Hex(
678
+ canonicalPreimage({
679
+ schema: "evaluation-receipt.v1",
680
+ trust: receipt.trust,
681
+ authority: receipt.authority,
682
+ bindingDigest: repositoryBindingDigestV1(receipt.binding),
683
+ sourceCommitId: receipt.sourceCommitId,
684
+ fcid: receipt.fcid,
685
+ analysisPlanDigest: receipt.analysisPlanDigest,
686
+ factsDigest: receipt.factsDigest,
687
+ inputDigest: receipt.inputDigest,
688
+ resultDigest: receipt.resultDigest,
689
+ evaluatorVersion: receipt.evaluatorVersion,
690
+ providerProofDigest: receipt.providerProof?.proofDigest,
691
+ })
692
+ ) as DigestHex;
693
+ }
694
+
695
+ export const evaluationReceiptV1Schema: z.ZodType<EvaluationReceiptV1, z.ZodTypeDef, unknown> = z
696
+ .object({
697
+ receiptId: evaluationReceiptIdStringSchema,
698
+ ...receiptSemanticShape,
699
+ mintedAt: z.string().datetime({ offset: true }),
700
+ digest: digestHexStringSchema,
701
+ })
702
+ .strict()
703
+ .superRefine((receipt, context) => {
704
+ validateReceiptSemantics(receipt, context);
705
+ let digest: DigestHex;
706
+ try {
707
+ const { receiptId: _receiptId, mintedAt: _mintedAt, digest: _digest, ...semantic } = receipt;
708
+ digest = evaluationReceiptDigestV1(semantic as EvaluationReceiptSemanticV1);
709
+ } catch (error) {
710
+ context.addIssue({
711
+ code: z.ZodIssueCode.custom,
712
+ message: error instanceof Error ? error.message : "Invalid receipt semantics",
713
+ });
714
+ return;
715
+ }
716
+ if (digest !== receipt.digest) {
717
+ context.addIssue({
718
+ code: z.ZodIssueCode.custom,
719
+ path: ["digest"],
720
+ message: "Receipt digest mismatch",
721
+ });
722
+ }
723
+ if (evaluationReceiptIdFromDigest(digest) !== receipt.receiptId) {
724
+ context.addIssue({
725
+ code: z.ZodIssueCode.custom,
726
+ path: ["receiptId"],
727
+ message: "Receipt ID mismatch",
728
+ });
729
+ }
730
+ }) as unknown as z.ZodType<EvaluationReceiptV1, z.ZodTypeDef, unknown>;
731
+
732
+ export function parseEvaluationReceiptV1(value: unknown): EvaluationReceiptV1 {
733
+ return evaluationReceiptV1Schema.parse(value);
734
+ }
735
+
736
+ export const evaluationReceiptMintInputV1Schema: z.ZodType<
737
+ EvaluationReceiptMintInputV1,
738
+ z.ZodTypeDef,
739
+ unknown
740
+ > = z
741
+ .object({
742
+ schemaVersion: z.literal(1),
743
+ trust: z.enum(["ci_attested", "local", "unknown"]),
744
+ binding: repositoryBindingKeyV1Schema,
745
+ sourceCommitId: boundedString(),
746
+ fcid: digestHexStringSchema,
747
+ analysisPlanDigest: digestHexStringSchema,
748
+ factsDigest: digestHexStringSchema,
749
+ inputDigest: digestHexStringSchema,
750
+ resultDigest: digestHexStringSchema,
751
+ evaluatorVersion: boundedString(),
752
+ })
753
+ .strict();