@usefragments/core 2.0.2 → 2.1.1

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.
@@ -13,11 +13,19 @@ import {
13
13
  } from "../facts/index.js";
14
14
  import { repositoryBindingDigestV1 } from "../repository-binding.js";
15
15
  import { contractHash } from "../contract/hash.js";
16
- import { CONTRACT_PREIMAGE_SCHEMA } from "../contract/preimage.js";
16
+ import {
17
+ CONTRACT_PREIMAGE_SCHEMA,
18
+ CONTRACT_PREIMAGE_SCHEMA_V2,
19
+ type ContractPreimage,
20
+ } from "../contract/preimage.js";
17
21
  import { g } from "../governance.js";
18
22
  import { runRules } from "../rules/index.js";
19
23
  import { evaluate } from "./evaluate.js";
20
- import { EVALUATOR_VERSION_V2, LEGACY_EVALUATOR_VERSION_V2 } from "./types.js";
24
+ import {
25
+ EVALUATOR_VERSION_V2,
26
+ SOURCE_IDENTITY_EVALUATOR_VERSION_V2,
27
+ LEGACY_EVALUATOR_VERSION_V2,
28
+ } from "./types.js";
21
29
  import type { EvaluationInput, EvaluationInputV2 } from "./types.js";
22
30
 
23
31
  const preimage = {
@@ -241,7 +249,10 @@ it.each(["error", "warn"] as const)(
241
249
  }
242
250
  );
243
251
 
244
- function versionedInput(evaluatorVersion: string): EvaluationInputV2 {
252
+ function versionedInput(
253
+ evaluatorVersion: string,
254
+ contractPreimage: ContractPreimage = preimage
255
+ ): EvaluationInputV2 {
245
256
  const base = input(shadow());
246
257
  const binding = {
247
258
  provider: "github",
@@ -252,7 +263,7 @@ function versionedInput(evaluatorVersion: string): EvaluationInputV2 {
252
263
  const analysisPlan = buildAnalysisPlanV1({
253
264
  binding,
254
265
  source: { commitId: "proof-head", defaultBranch: "main", acquiredBy: "github_app" },
255
- contract: { fcid: contractHash(preimage), artifactDigest: "a".repeat(64) },
266
+ contract: { fcid: contractHash(contractPreimage), artifactDigest: "a".repeat(64) },
256
267
  config: { path: "fragments.config.ts", digest: "b".repeat(64) },
257
268
  profile: { id: "react-web-v1", version: "1", capabilities: ["tsx"] },
258
269
  regions: [
@@ -282,7 +293,7 @@ function versionedInput(evaluatorVersion: string): EvaluationInputV2 {
282
293
  schemaVersion: 2,
283
294
  binding,
284
295
  analysisPlan,
285
- contract: { fcid: contractHash(preimage), preimage, active: true },
296
+ contract: { fcid: contractHash(contractPreimage), preimage: contractPreimage, active: true },
286
297
  factSchemaVersion: "facts:v1",
287
298
  facts: shadow(),
288
299
  factsDigest: "c".repeat(64),
@@ -295,7 +306,7 @@ function versionedInput(evaluatorVersion: string): EvaluationInputV2 {
295
306
  identity: {
296
307
  bindingDigest: repositoryBindingDigestV1(binding),
297
308
  repositoryExternalId: binding.repositoryExternalId,
298
- fcid: contractHash(preimage),
309
+ fcid: contractHash(contractPreimage),
299
310
  sourceCommitId: "proof-base",
300
311
  generationId: "baseline-proof",
301
312
  },
@@ -335,3 +346,53 @@ it("gives corrected semantics a distinct plan, input and result identity", () =>
335
346
  expect(result.inputDigest).not.toBe(previous.inputDigest);
336
347
  expect(result.resultDigest).not.toBe(previous.resultDigest);
337
348
  });
349
+
350
+ it("rejects source-qualified contracts at the public legacy evaluator boundary", () => {
351
+ const qualified = { ...preimage, schema: CONTRACT_PREIMAGE_SCHEMA_V2 };
352
+ expect(() =>
353
+ evaluate(
354
+ input(new FactIndex(), {
355
+ contract: { state: "pinned", fcid: contractHash(qualified), preimage: qualified },
356
+ })
357
+ )
358
+ ).toThrow("Source-qualified contracts require a V2 evaluation");
359
+ });
360
+
361
+ it("holds rather than passes an enforceable raw element with ambiguous approved targets", () => {
362
+ const facts = new FactIndex();
363
+ facts.addMany(
364
+ compileGlobalGovernanceFacts({
365
+ rules: {
366
+ "components/prefer-library": {
367
+ enabled: true,
368
+ severity: "error",
369
+ options: {
370
+ sourceIdentitySchema: "component-source:v1",
371
+ canonicalMappings: ["first", "second"].map((name) => ({
372
+ name: "Button",
373
+ canonical: "Button",
374
+ htmlEquivalent: "button",
375
+ importPath: `@acme/${name}`,
376
+ source: { kind: "package", importPath: `@acme/${name}`, exportName: "Button" },
377
+ })),
378
+ },
379
+ },
380
+ },
381
+ })
382
+ );
383
+ facts.add(
384
+ makeUsageNodeFact({
385
+ file: "src/App.tsx",
386
+ nodePath: "0",
387
+ element: "button",
388
+ location: { file: "src/App.tsx", line: 1, column: 1 },
389
+ })
390
+ );
391
+ const configured = versionedInput(SOURCE_IDENTITY_EVALUATOR_VERSION_V2, {
392
+ ...preimage,
393
+ schema: CONTRACT_PREIMAGE_SCHEMA_V2,
394
+ });
395
+ const result = evaluate({ ...configured, facts });
396
+ expect(result.verdict).toBe("indeterminate");
397
+ expect(result.findings[0]?.state).toBe("pending_cloud_trust");
398
+ });
@@ -23,6 +23,7 @@
23
23
  * `pending_cloud_trust`; all affirmative → `blocked`.
24
24
  */
25
25
 
26
+ import { CONTRACT_PREIMAGE_SCHEMA_V2 } from "../contract/preimage.js";
26
27
  import { deriveCoverageSummaryV1 } from "../analysis-plan/index.js";
27
28
  import {
28
29
  contractHash,
@@ -50,7 +51,12 @@ import type {
50
51
  EvaluationVerdict,
51
52
  EvaluationWaiver,
52
53
  } from "./types.js";
53
- import { EVALUATOR_VERSION, EVALUATOR_VERSION_V2, LEGACY_EVALUATOR_VERSION_V2 } from "./types.js";
54
+ import {
55
+ EVALUATOR_VERSION,
56
+ EVALUATOR_VERSION_V2,
57
+ SOURCE_IDENTITY_EVALUATOR_VERSION_V2,
58
+ LEGACY_EVALUATOR_VERSION_V2,
59
+ } from "./types.js";
54
60
  import { evaluationInputV2Schema, evaluationResultV2Schema } from "./receipt.js";
55
61
 
56
62
  type TrustFactState = "affirmative" | "negative" | "unknown";
@@ -92,7 +98,16 @@ export function evaluate(input: EvaluationInput): EvaluationResult;
92
98
  export function evaluate(
93
99
  input: EvaluationInput | EvaluationInputV2
94
100
  ): EvaluationResult | EvaluationResultV2 {
95
- return isEvaluationInputV2(input) ? evaluateV2(input) : evaluateV1(input);
101
+ if (isEvaluationInputV2(input)) return evaluateV2(input);
102
+ if (
103
+ input.contract.state === "pinned" &&
104
+ input.contract.preimage?.schema === CONTRACT_PREIMAGE_SCHEMA_V2
105
+ ) {
106
+ throw new Error(
107
+ "Source-qualified contracts require a V2 evaluation with evaluation-kernel:v2.2"
108
+ );
109
+ }
110
+ return evaluateV1(input);
96
111
  }
97
112
 
98
113
  function isEvaluationInputV2(
@@ -251,6 +266,7 @@ const SUPPORTED_REACT_WEB_PROFILE_VERSIONS_V1 = new Set(["1"]);
251
266
  function evaluateV2(rawInput: EvaluationInputV2): EvaluationResultV2 {
252
267
  if (
253
268
  rawInput.evaluatorVersion !== EVALUATOR_VERSION_V2 &&
269
+ rawInput.evaluatorVersion !== SOURCE_IDENTITY_EVALUATOR_VERSION_V2 &&
254
270
  rawInput.evaluatorVersion !== LEGACY_EVALUATOR_VERSION_V2
255
271
  ) {
256
272
  throw new Error(`Unsupported evaluator version: ${rawInput.evaluatorVersion}`);
@@ -304,7 +320,7 @@ function evaluateV2(rawInput: EvaluationInputV2): EvaluationResultV2 {
304
320
  // New plans always select the current generation through EVALUATOR_VERSION_V2.
305
321
  const compatibilityResult = evaluateV1(
306
322
  compatibilityInput,
307
- input.evaluatorVersion === EVALUATOR_VERSION_V2
323
+ input.evaluatorVersion !== LEGACY_EVALUATOR_VERSION_V2
308
324
  );
309
325
 
310
326
  const requiredCoverage =
@@ -1,13 +1,14 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { buildAnalysisPlanV1, type CoverageRegionV1 } from "../analysis-plan/index.js";
3
3
  import { contractHash } from "../contract/hash.js";
4
- import { projectContractPreimage } from "../contract/preimage.js";
4
+ import { CONTRACT_PREIMAGE_SCHEMA_V2, projectContractPreimage } from "../contract/preimage.js";
5
5
  import { evaluationReceiptIdFromDigest } from "../domain-ids.js";
6
6
  import { FactIndex } from "../facts/fact-index.js";
7
7
  import { makeTokenDefinitionFact } from "../facts/builders.js";
8
8
  import { repositoryBindingDigestV1 } from "../repository-binding.js";
9
9
  import {
10
10
  EVALUATOR_VERSION_V2,
11
+ SOURCE_IDENTITY_EVALUATOR_VERSION_V2,
11
12
  evaluate,
12
13
  evaluationInputDigestV2,
13
14
  evaluationResultDigestV2,
@@ -770,3 +771,28 @@ describe("EvaluationReceiptV1", () => {
770
771
  ).toThrow();
771
772
  });
772
773
  });
774
+
775
+ describe("source-qualified receipt generation", () => {
776
+ it("requires the new evaluator and reproduces its input digest", () => {
777
+ const input = evaluationInput();
778
+ const preimage = { ...input.contract.preimage, schema: CONTRACT_PREIMAGE_SCHEMA_V2 };
779
+ const fcid = contractHash(preimage);
780
+ const { digest: _digest, ...oldPlan } = input.analysisPlan;
781
+ const forVersion = (version: string): EvaluationInputV2 => ({
782
+ ...input,
783
+ evaluatorVersion: version,
784
+ contract: { ...input.contract, preimage, fcid },
785
+ analysisPlan: buildAnalysisPlanV1({
786
+ ...oldPlan,
787
+ contract: { ...oldPlan.contract, fcid },
788
+ evaluatorVersion: version,
789
+ }),
790
+ });
791
+ for (const oldVersion of ["evaluation-kernel:v2", EVALUATOR_VERSION_V2])
792
+ expect(() => evaluate(forVersion(oldVersion))).toThrow(/Source-qualified/);
793
+ const result = evaluate(forVersion(SOURCE_IDENTITY_EVALUATOR_VERSION_V2));
794
+ expect(result.verdict).toBe("pass");
795
+ expect(result.integrity.fcidVerified).toBe(true);
796
+ expect(evaluate(forVersion(SOURCE_IDENTITY_EVALUATOR_VERSION_V2))).toEqual(result);
797
+ });
798
+ });
@@ -4,7 +4,13 @@
4
4
  * `apps/cloud/src/lib/govern/evaluation-adapter.ts`.
5
5
  */
6
6
  export { evaluate, evaluationInputDigestV2, evaluationResultDigestV2 } from "./evaluate.js";
7
- export { EVALUATOR_VERSION, EVALUATOR_VERSION_V2, LEGACY_EVALUATOR_VERSION_V2 } from "./types.js";
7
+ export {
8
+ EVALUATOR_VERSION,
9
+ EVALUATOR_VERSION_V2,
10
+ SOURCE_IDENTITY_EVALUATOR_VERSION_V2,
11
+ evaluatorVersionForContractSchema,
12
+ LEGACY_EVALUATOR_VERSION_V2,
13
+ } from "./types.js";
8
14
  export {
9
15
  EVALUATION_MAX_FINDINGS_V2,
10
16
  EVALUATION_MAX_MAP_ENTRIES_V2,
@@ -7,7 +7,11 @@ import {
7
7
  parseAnalysisPlanV1,
8
8
  } from "../analysis-plan/index.js";
9
9
  import { canonicalPreimage, contractHash, sha256Hex } from "../contract/hash.js";
10
- import { CONTRACT_PREIMAGE_SCHEMA, type ContractPreimage } from "../contract/preimage.js";
10
+ import {
11
+ CONTRACT_PREIMAGE_SCHEMA,
12
+ CONTRACT_PREIMAGE_SCHEMA_V2,
13
+ type ContractPreimage,
14
+ } from "../contract/preimage.js";
11
15
  import {
12
16
  digestHexStringSchema,
13
17
  evaluationReceiptIdFromDigest,
@@ -21,6 +25,7 @@ import {
21
25
  repositoryBindingDigestV1,
22
26
  repositoryBindingKeyV1Schema,
23
27
  } from "../repository-binding.js";
28
+ import { SOURCE_IDENTITY_EVALUATOR_VERSION_V2 } from "./types.js";
24
29
  import type {
25
30
  EffectiveEvaluationWaiverV2,
26
31
  EvaluationInputV2,
@@ -51,7 +56,7 @@ const unique = (values: readonly string[]): boolean => new Set(values).size ===
51
56
 
52
57
  const contractPreimageV1Schema: z.ZodType<ContractPreimage> = z
53
58
  .object({
54
- schema: z.literal(CONTRACT_PREIMAGE_SCHEMA),
59
+ schema: z.enum([CONTRACT_PREIMAGE_SCHEMA, CONTRACT_PREIMAGE_SCHEMA_V2]),
55
60
  domains: z
56
61
  .object({
57
62
  components: digestHexStringSchema,
@@ -205,6 +210,16 @@ const evaluationInputV2RawSchema = z
205
210
  })
206
211
  .strict()
207
212
  .superRefine((input, context) => {
213
+ if (
214
+ input.contract.preimage.schema === CONTRACT_PREIMAGE_SCHEMA_V2 &&
215
+ input.evaluatorVersion !== SOURCE_IDENTITY_EVALUATOR_VERSION_V2
216
+ ) {
217
+ context.addIssue({
218
+ code: z.ZodIssueCode.custom,
219
+ path: ["evaluatorVersion"],
220
+ message: "Source-qualified contracts require evaluation-kernel:v2.2",
221
+ });
222
+ }
208
223
  let plan;
209
224
  try {
210
225
  plan = parseAnalysisPlanV1(input.analysisPlan);
@@ -14,7 +14,11 @@
14
14
  * contract — every scenario's authored kernel verdict must land.
15
15
  */
16
16
 
17
- import type { ContractPreimage } from "../contract/preimage.js";
17
+ import {
18
+ CONTRACT_PREIMAGE_SCHEMA,
19
+ CONTRACT_PREIMAGE_SCHEMA_V2,
20
+ type ContractPreimage,
21
+ } from "../contract/preimage.js";
18
22
  import type {
19
23
  AnalysisPlanV1,
20
24
  CoverageRegionV1,
@@ -29,9 +33,18 @@ import type { RepositoryBindingKeyV1 } from "../repository-binding.js";
29
33
  export const EVALUATOR_VERSION = "evaluation-kernel:v1.1" as const;
30
34
  /** Current facts-only evaluator. The input schema remains V2. */
31
35
  export const EVALUATOR_VERSION_V2 = "evaluation-kernel:v2.1" as const;
36
+ /** Source-qualified contracts require this generation; older receipts retain their semantics. */
37
+ export const SOURCE_IDENTITY_EVALUATOR_VERSION_V2 = "evaluation-kernel:v2.2" as const;
32
38
  /** Replay-only generation: its captured mapping judgments remain authoritative. */
33
39
  export const LEGACY_EVALUATOR_VERSION_V2 = "evaluation-kernel:v2" as const;
34
40
 
41
+ /** Missing schema is a retained V1 row; unknown future generations fail closed. */
42
+ export function evaluatorVersionForContractSchema(schema?: string): string | null {
43
+ if (schema === undefined || schema === CONTRACT_PREIMAGE_SCHEMA) return EVALUATOR_VERSION_V2;
44
+ if (schema === CONTRACT_PREIMAGE_SCHEMA_V2) return SOURCE_IDENTITY_EVALUATOR_VERSION_V2;
45
+ return null;
46
+ }
47
+
35
48
  /**
36
49
  * Four-state per-finding model (spec §2). `candidate` is the derivation step —
37
50
  * "severity/rule says it could block" — and always resolves to one of the
package/src/index.ts CHANGED
@@ -881,6 +881,10 @@ export {
881
881
  CONTRACT_ENFORCEMENT_FIELDS,
882
882
  CONTRACT_PREIMAGE_CAPABILITY_HEADER,
883
883
  CONTRACT_PREIMAGE_SCHEMA,
884
+ CONTRACT_PREIMAGE_SCHEMA_V2,
885
+ CONTRACT_PREIMAGE_CAPABILITIES,
886
+ SUPPORTED_CONTRACT_PREIMAGE_SCHEMAS,
887
+ isSupportedContractPreimageSchema,
884
888
  ContractCatalogValidationError,
885
889
  contractComponentsFromFragments,
886
890
  contractHash,
@@ -928,6 +932,7 @@ export type {
928
932
  ContractIdentityPin,
929
933
  ContractPolicyInput,
930
934
  ContractPreimage,
935
+ ContractPreimageSchema,
931
936
  ContractPropMappingInput,
932
937
  ContractStampPin,
933
938
  ContractStampRecord,
@@ -1129,6 +1134,8 @@ export {
1129
1134
  EVALUATION_RECEIPT_STRING_MAX_BYTES_V1,
1130
1135
  EVALUATOR_VERSION,
1131
1136
  EVALUATOR_VERSION_V2,
1137
+ SOURCE_IDENTITY_EVALUATOR_VERSION_V2,
1138
+ evaluatorVersionForContractSchema,
1132
1139
  LEGACY_EVALUATOR_VERSION_V2,
1133
1140
  effectiveEvaluationWaiverV2Schema,
1134
1141
  evaluate,
@@ -1182,3 +1189,24 @@ export {
1182
1189
  APPROVED_CONTRACT_TOKEN_MAX_NAME_LENGTH,
1183
1190
  isApprovedContractTokenNames,
1184
1191
  } from "./approved-contract-tokens.js";
1192
+
1193
+ export {
1194
+ contractComponentSourceKey,
1195
+ contractComponentExportAddresses,
1196
+ approvedComponentGovernanceByteLimit,
1197
+ APPROVED_COMPONENT_GOVERNANCE_LEGACY_MAX_BYTES,
1198
+ APPROVED_COMPONENT_GOVERNANCE_QUALIFIED_MAX_BYTES,
1199
+ contractComponentReplacementImport,
1200
+ contractSourceFromFragment,
1201
+ normalizeContractComponentSource,
1202
+ normalizeContractSourcePath,
1203
+ contractComponentsFromFragmentsV2,
1204
+ projectContractPreimageV2,
1205
+ } from "./contract/source-identity.js";
1206
+ export type {
1207
+ ContractComponentSource,
1208
+ ContractComponentExport,
1209
+ ContractComponentInputV2,
1210
+ ContractCanonicalMappingInputV2,
1211
+ ContractCatalogInputV2,
1212
+ } from "./contract/source-identity.js";