assertledger 1.0.0 → 1.1.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 (67) hide show
  1. package/README.fr.md +3 -3
  2. package/README.md +3 -3
  3. package/SECURITY.md +11 -6
  4. package/conformance/schema-extensions.json +36 -0
  5. package/dist/build-info.d.ts +16 -0
  6. package/dist/build-info.d.ts.map +1 -0
  7. package/dist/build-info.js +18 -0
  8. package/dist/build-info.js.map +1 -0
  9. package/dist/build-info.json +7 -0
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +117 -19
  12. package/dist/cli.js.map +1 -1
  13. package/dist/contracts/index.d.ts +943 -0
  14. package/dist/contracts/index.d.ts.map +1 -1
  15. package/dist/contracts/index.js +561 -21
  16. package/dist/contracts/index.js.map +1 -1
  17. package/dist/core/index.d.ts +10 -1
  18. package/dist/core/index.d.ts.map +1 -1
  19. package/dist/core/index.js +473 -4
  20. package/dist/core/index.js.map +1 -1
  21. package/dist/diagnostics.d.ts.map +1 -1
  22. package/dist/diagnostics.js +42 -2
  23. package/dist/diagnostics.js.map +1 -1
  24. package/dist/engine/adapters/node-test-runtime.d.ts +17 -0
  25. package/dist/engine/adapters/node-test-runtime.d.ts.map +1 -1
  26. package/dist/engine/adapters/node-test-runtime.js +45 -23
  27. package/dist/engine/adapters/node-test-runtime.js.map +1 -1
  28. package/dist/engine/container.d.ts +54 -0
  29. package/dist/engine/container.d.ts.map +1 -0
  30. package/dist/engine/container.js +464 -0
  31. package/dist/engine/container.js.map +1 -0
  32. package/dist/engine/git-regression.d.ts +12 -2
  33. package/dist/engine/git-regression.d.ts.map +1 -1
  34. package/dist/engine/git-regression.js +63 -16
  35. package/dist/engine/git-regression.js.map +1 -1
  36. package/dist/engine/index.d.ts +7 -1
  37. package/dist/engine/index.d.ts.map +1 -1
  38. package/dist/engine/index.js +300 -74
  39. package/dist/engine/index.js.map +1 -1
  40. package/dist/mcp/index.d.ts.map +1 -1
  41. package/dist/mcp/index.js +53 -1
  42. package/dist/mcp/index.js.map +1 -1
  43. package/dist/sdk/index.d.ts +16 -6
  44. package/dist/sdk/index.d.ts.map +1 -1
  45. package/dist/sdk/index.js +63 -5
  46. package/dist/sdk/index.js.map +1 -1
  47. package/docs/agentic-test-profile.md +5 -0
  48. package/docs/architecture.md +8 -3
  49. package/docs/ci.md +7 -1
  50. package/docs/conformance-v1.md +11 -2
  51. package/docs/container-isolation.md +156 -0
  52. package/docs/evidence-export.md +146 -0
  53. package/docs/git-regression.md +7 -3
  54. package/docs/migration-verification-v2.md +55 -0
  55. package/docs/project-intent.md +3 -2
  56. package/docs/proof-model.md +8 -4
  57. package/docs/reference.md +56 -11
  58. package/docs/roadmap.md +12 -4
  59. package/examples/agentic-profile/profile-manifest.mjs +5 -1
  60. package/examples/evidence-export/consumer.mjs +35 -0
  61. package/package.json +2 -2
  62. package/schemas/evidence-export-replay-result.v1.json +49 -0
  63. package/schemas/evidence-export-request.v1.json +681 -0
  64. package/schemas/evidence-export.v1.json +1479 -0
  65. package/schemas/evidence-manifest.v2.json +886 -0
  66. package/schemas/evidence-provider-manifest.v1.json +269 -0
  67. package/schemas/verification-request.v2.json +457 -0
@@ -71,6 +71,31 @@ export const TrustedLocalIsolationSchema = z.strictObject({
71
71
  acknowledgedUnsafeExecution: z.boolean(),
72
72
  environmentAllowlist: z.array(EnvironmentVariableSchema).max(100),
73
73
  });
74
+ export const VERIFICATION_SCHEMA_VERSION_V2 = "2.0.0";
75
+ // Only digest-pinned references are accepted; the leading alphanumeric also keeps a reference
76
+ // from being parsed as a container runtime option.
77
+ export const ContainerImageReferenceSchema = z
78
+ .string()
79
+ .max(512)
80
+ .regex(/^(?:[A-Za-z0-9][A-Za-z0-9.-]*(?::[0-9]{1,5})?\/)?[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*(?:\/[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*)*(?::[A-Za-z0-9_][A-Za-z0-9._-]{0,127})?@sha256:[a-f0-9]{64}$/);
81
+ const ContainerEnvironmentVariableSchema = z.strictObject({
82
+ name: EnvironmentVariableSchema,
83
+ value: z.string().max(4_096),
84
+ });
85
+ export const ContainerLimitsSchema = z.strictObject({
86
+ memoryBytes: z.int().min(67_108_864).max(68_719_476_736),
87
+ cpuMillicores: z.int().min(100).max(256_000),
88
+ pids: z.int().min(16).max(65_536),
89
+ temporaryDirectoryBytes: z.int().min(1_048_576).max(17_179_869_184),
90
+ });
91
+ // The host runtime command is operator configuration, never request data: a request cannot choose
92
+ // which host executable starts the container, and it cannot forward host environment values.
93
+ export const ContainerIsolationSchema = z.strictObject({
94
+ kind: z.literal("container"),
95
+ image: ContainerImageReferenceSchema,
96
+ environment: z.array(ContainerEnvironmentVariableSchema).max(100),
97
+ limits: ContainerLimitsSchema,
98
+ });
74
99
  export const BudgetsSchema = z.strictObject({
75
100
  maximumCandidates: z.int().min(1).max(1_000),
76
101
  maximumWorlds: z.int().min(3).max(1_000),
@@ -110,6 +135,20 @@ export const VerificationRequestSchema = z
110
135
  title: "TestForge verification request",
111
136
  description: "A versioned campaign describing operator-owned worlds, candidate test overlays, budgets, and evidence policy.",
112
137
  });
138
+ export const VerificationRequestV2Schema = z
139
+ .strictObject({
140
+ ...VerificationRequestSchema.shape,
141
+ schemaVersion: z.literal(VERIFICATION_SCHEMA_VERSION_V2),
142
+ isolation: z.discriminatedUnion("kind", [
143
+ TrustedLocalIsolationSchema,
144
+ ContainerIsolationSchema,
145
+ ]),
146
+ })
147
+ .meta({
148
+ id: "https://testforge.dev/schemas/verification-request.v2.json",
149
+ title: "AssertLedger verification request v2",
150
+ description: "A versioned campaign executed either in a digest-pinned container backend or in explicitly acknowledged unsandboxed trusted-local mode.",
151
+ });
113
152
  export const RepositoryAnalysisSchema = z
114
153
  .strictObject({
115
154
  root: z.string().min(1),
@@ -388,6 +427,71 @@ export const EvidenceManifestSchema = z
388
427
  title: "TestForge evidence manifest",
389
428
  description: "The final auditable campaign artifact, including evidence, deterministic gates, decisions, and integrity digests.",
390
429
  });
430
+ const ContainerExecutionControlsSchema = z.strictObject({
431
+ network: z.literal("none"),
432
+ rootFilesystem: z.literal("read-only"),
433
+ hostMounts: z.literal("none"),
434
+ workspace: z.literal("anonymous-volume"),
435
+ user: z.literal("65534:65534"),
436
+ capabilities: z.literal("none"),
437
+ noNewPrivileges: z.literal(true),
438
+ imagePull: z.literal("never"),
439
+ logDriver: z.literal("none"),
440
+ environment: z.array(ContainerEnvironmentVariableSchema).max(100),
441
+ limits: ContainerLimitsSchema,
442
+ });
443
+ const ExecutionBackendRecordSchema = z.discriminatedUnion("kind", [
444
+ z.strictObject({ kind: z.literal("trusted-local"), level: z.literal("UNSANDBOXED") }),
445
+ z.strictObject({
446
+ kind: z.literal("container"),
447
+ level: z.literal("CONTAINER"),
448
+ image: z.strictObject({
449
+ reference: ContainerImageReferenceSchema,
450
+ id: Sha256DigestSchema,
451
+ os: z.literal("linux"),
452
+ architecture: z.string().min(1).max(64),
453
+ }),
454
+ runtime: z.strictObject({
455
+ clientVersion: z.string().min(1).max(128),
456
+ serverVersion: z.string().min(1).max(128),
457
+ serverOs: z.literal("linux"),
458
+ serverArchitecture: z.string().min(1).max(64),
459
+ cgroupVersion: z.string().max(16),
460
+ securityOptions: z.array(z.string().min(1).max(512)).max(64),
461
+ }),
462
+ controls: ContainerExecutionControlsSchema,
463
+ }),
464
+ ]);
465
+ const EvidenceContextV2Schema = z.strictObject({
466
+ ...EvidenceContextSchema.shape,
467
+ execution: z.strictObject({
468
+ ...EvidenceContextSchema.shape.execution.shape,
469
+ backend: ExecutionBackendRecordSchema,
470
+ }),
471
+ });
472
+ export const EvidenceManifestV2Schema = z
473
+ .strictObject({
474
+ ...EvidenceManifestSchema.shape,
475
+ schemaVersion: z.literal(VERIFICATION_SCHEMA_VERSION_V2),
476
+ evidenceContext: EvidenceContextV2Schema,
477
+ isolation: z.discriminatedUnion("kind", [
478
+ z.strictObject({
479
+ kind: z.literal("trusted-local"),
480
+ level: z.literal("UNSANDBOXED"),
481
+ acknowledgedUnsafeExecution: z.literal(true),
482
+ }),
483
+ z.strictObject({
484
+ kind: z.literal("container"),
485
+ level: z.literal("CONTAINER"),
486
+ runtimeCommand: z.array(z.string().min(1).max(4_096)).min(1).max(16),
487
+ }),
488
+ ]),
489
+ })
490
+ .meta({
491
+ id: "https://testforge.dev/schemas/evidence-manifest.v2.json",
492
+ title: "AssertLedger evidence manifest v2",
493
+ description: "The final auditable campaign artifact with a decision-bound record of the execution backend and its enforced controls.",
494
+ });
391
495
  export const ReplayResultSchema = z
392
496
  .strictObject({
393
497
  valid: z.boolean(),
@@ -1304,6 +1408,297 @@ export const AgenticCorpusExperimentReplayResultSchema = z.strictObject({
1304
1408
  observationSemanticsValid: z.boolean(),
1305
1409
  resultSemanticsValid: z.boolean(),
1306
1410
  });
1411
+ const GitObjectIdSchema = z.string().regex(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/);
1412
+ const EvidenceSourceRevisionSchema = z.discriminatedUnion("status", [
1413
+ z.strictObject({
1414
+ status: z.literal("RECORDED"),
1415
+ commit: GitObjectIdSchema,
1416
+ worktree: z.enum(["CLEAN", "DIRTY"]),
1417
+ }),
1418
+ z.strictObject({ status: z.literal("UNKNOWN") }),
1419
+ ]);
1420
+ const EvidenceFormatSchema = z.strictObject({
1421
+ schemaId: z.string().min(1).max(512),
1422
+ schemaVersion: z.string().min(1).max(128),
1423
+ });
1424
+ export const EvidenceProviderManifestSchema = z
1425
+ .strictObject({
1426
+ schemaVersion: z.literal("1.0.0"),
1427
+ provider: z.strictObject({
1428
+ name: z.literal("assertledger"),
1429
+ version: z.string().min(1).max(128),
1430
+ sourceRevision: EvidenceSourceRevisionSchema,
1431
+ }),
1432
+ scope: z.string().min(1).max(2_048),
1433
+ formats: z.strictObject({
1434
+ accepts: z.array(EvidenceFormatSchema).max(100),
1435
+ emits: z.array(EvidenceFormatSchema).max(100),
1436
+ }),
1437
+ capabilities: z
1438
+ .array(z.strictObject({
1439
+ id: ReasonCodeSchema,
1440
+ status: z.enum(["SUPPORTED", "SUPPORTED_WHEN_RECORDED", "UNSUPPORTED"]),
1441
+ modality: z.enum(["TEST_OBSERVED", "RECORDED_METADATA", "NONE"]),
1442
+ description: z.string().min(1).max(2_048),
1443
+ }))
1444
+ .max(100),
1445
+ adapters: z
1446
+ .array(z.strictObject({
1447
+ kind: z.enum(["node-test", "testforge-command"]),
1448
+ profileId: z.string().min(1).max(128).nullable(),
1449
+ profileVersion: z.string().min(1).max(128).nullable(),
1450
+ official: z.boolean(),
1451
+ }))
1452
+ .max(100),
1453
+ cost: z.strictObject({
1454
+ unit: z.literal("PROCESS_EXECUTIONS"),
1455
+ estimate: z.string().min(1).max(512),
1456
+ assumptions: z.array(z.string().min(1).max(2_048)).max(100),
1457
+ }),
1458
+ limits: z.array(z.string().min(1).max(2_048)).max(100),
1459
+ manifestDigest: Sha256DigestSchema,
1460
+ })
1461
+ .meta({
1462
+ id: "https://testforge.dev/schemas/evidence-provider-manifest.v1.json",
1463
+ title: "AssertLedger evidence provider manifest",
1464
+ description: "Announced provider identity, capabilities, formats, cost model, and limits. An announced capability never proves that a control ran.",
1465
+ });
1466
+ const EvidenceConsumerRequestSchema = z.strictObject({
1467
+ reference: z.string().min(1).max(256).nullable(),
1468
+ profileId: z.string().min(1).max(256).nullable(),
1469
+ obligations: z
1470
+ .array(z.strictObject({ id: IdentifierSchema, control: ReasonCodeSchema }))
1471
+ .max(100),
1472
+ });
1473
+ export const EvidenceExportRequestSchema = z
1474
+ .strictObject({
1475
+ schemaVersion: z.literal("1.0.0"),
1476
+ manifest: EmbeddedEvidenceManifestSchema,
1477
+ consumerRequest: EvidenceConsumerRequestSchema.nullable(),
1478
+ })
1479
+ .meta({
1480
+ id: "https://testforge.dev/schemas/evidence-export-request.v1.json",
1481
+ title: "AssertLedger evidence export request",
1482
+ description: "A replay-valid evidence manifest and optional consumer references and requested controls.",
1483
+ });
1484
+ const EvidenceWorldKindSchema = z.enum(["REFERENCE", "TARGET", "NEUTRAL"]);
1485
+ export const EvidenceExportSchema = z
1486
+ .strictObject({
1487
+ schemaVersion: z.literal("1.0.0"),
1488
+ sourceManifest: EmbeddedEvidenceManifestSchema,
1489
+ sourceArtifactDigest: Sha256DigestSchema,
1490
+ consumerRequest: EvidenceConsumerRequestSchema.nullable(),
1491
+ result: z.strictObject({
1492
+ detection: z.enum(["OBSERVED", "NOT_OBSERVED", "NOT_ESTABLISHED"]),
1493
+ modality: z.enum(["TEST_OBSERVED", "NONE"]),
1494
+ reasonCode: z.enum([
1495
+ "REGRESSION_ASSERTION_OBSERVED",
1496
+ "TARGET_PASSED_WITHOUT_DETECTION",
1497
+ "ENGINE_ERROR",
1498
+ "CONTROL_EVIDENCE_INVALID",
1499
+ "CANDIDATE_EVIDENCE_INCONCLUSIVE",
1500
+ "CANDIDATE_EVIDENCE_INVALID",
1501
+ "TARGET_STRENGTH_INSUFFICIENT",
1502
+ "OPERATIONAL_OUTCOME_NOT_DETECTION",
1503
+ ]),
1504
+ decision: z.strictObject({
1505
+ status: z.enum(["VERIFIED", "REJECTED", "INCONCLUSIVE", "ENGINE_ERROR"]),
1506
+ selectedCandidateIds: z.array(IdentifierSchema),
1507
+ reasonCodes: z.array(ReasonCodeSchema),
1508
+ }),
1509
+ candidates: z.array(z.strictObject({
1510
+ id: IdentifierSchema,
1511
+ digest: Sha256DigestSchema,
1512
+ status: z.enum(["ELIGIBLE", "UNSTABLE", "INCONCLUSIVE", "INVALID", "WEAK_ORACLE"]),
1513
+ selected: z.boolean(),
1514
+ reasonCodes: z.array(ReasonCodeSchema),
1515
+ worlds: z.array(z.strictObject({
1516
+ worldId: IdentifierSchema,
1517
+ kind: EvidenceWorldKindSchema,
1518
+ required: z.boolean(),
1519
+ weight: z.int().min(0).max(1_000_000),
1520
+ outcome: z.enum([
1521
+ "PASS",
1522
+ "ASSERTION_FAILURE",
1523
+ "COLLECTION_FAILURE",
1524
+ "COMPILE_FAILURE",
1525
+ "PROCESS_CRASH",
1526
+ "TIMEOUT",
1527
+ "INFRA_ERROR",
1528
+ "NO_TEST_DISCOVERED",
1529
+ "DIVERGENT",
1530
+ "MISSING",
1531
+ ]),
1532
+ attempts: z.int().min(0),
1533
+ signal: z.enum(["RED", "GREEN", "NONE"]),
1534
+ detection: z.enum(["OBSERVED", "NOT_OBSERVED", "NOT_ESTABLISHED", "NOT_APPLICABLE"]),
1535
+ })),
1536
+ })),
1537
+ }),
1538
+ integrity: z.strictObject({
1539
+ sourceReplay: z.literal("VALID"),
1540
+ verifiedRails: z.tuple([
1541
+ z.literal("SCHEMA"),
1542
+ z.literal("DECISION_DIGEST"),
1543
+ z.literal("ARTIFACT_DIGEST"),
1544
+ z.literal("DECISION_SEMANTICS"),
1545
+ ]),
1546
+ bindings: z.strictObject({
1547
+ artifactDigest: Sha256DigestSchema,
1548
+ decisionDigest: Sha256DigestSchema,
1549
+ repositoryDigest: z.union([Sha256DigestSchema, z.literal("invalid")]),
1550
+ policyDigest: Sha256DigestSchema,
1551
+ worldDigests: z.array(z.strictObject({ worldId: IdentifierSchema, digest: Sha256DigestSchema })),
1552
+ }),
1553
+ }),
1554
+ authenticity: z.strictObject({
1555
+ status: z.literal("UNAUTHENTICATED"),
1556
+ attestation: z.literal("NONE"),
1557
+ declaredProducer: z.strictObject({
1558
+ name: z.string().min(1).max(128),
1559
+ version: z.string().min(1).max(128),
1560
+ }),
1561
+ }),
1562
+ environment: z.strictObject({
1563
+ isolation: z.strictObject({
1564
+ kind: z.literal("trusted-local"),
1565
+ level: z.literal("UNSANDBOXED"),
1566
+ }),
1567
+ environmentAllowlist: z.array(z.string().min(1).max(128)).max(100),
1568
+ adapter: z.strictObject({
1569
+ kind: z.enum(["node-test", "testforge-command"]),
1570
+ name: z.string().min(1).max(128),
1571
+ version: z.string().min(1).max(128),
1572
+ framework: z.discriminatedUnion("status", [
1573
+ z.strictObject({
1574
+ status: z.literal("RECORDED"),
1575
+ profileId: z.string().min(1).max(128),
1576
+ profileVersion: z.string().min(1).max(128),
1577
+ official: z.boolean(),
1578
+ nodeVersion: z.string().min(1).max(128).nullable(),
1579
+ executableDigest: Sha256DigestSchema.nullable(),
1580
+ }),
1581
+ z.strictObject({ status: z.literal("UNKNOWN") }),
1582
+ ]),
1583
+ }),
1584
+ }),
1585
+ confidence: z.strictObject({
1586
+ level: z.literal("REPLAY_CONSISTENT_UNAUTHENTICATED"),
1587
+ established: z.array(z.enum([
1588
+ "SOURCE_SCHEMA_VALID",
1589
+ "DECISION_DIGEST_RECOMPUTED",
1590
+ "ARTIFACT_DIGEST_RECOMPUTED",
1591
+ "DECISION_RECOMPUTED_FROM_RECORDED_OBSERVATIONS",
1592
+ ])),
1593
+ notEstablished: z.array(z.enum([
1594
+ "PRODUCER_AUTHENTICITY",
1595
+ "OBSERVATION_TRUTHFULNESS",
1596
+ "EXECUTION_ISOLATION",
1597
+ "EXECUTION_FRESHNESS",
1598
+ "WORLD_SEMANTIC_RELEVANCE",
1599
+ ])),
1600
+ }),
1601
+ scope: z.strictObject({
1602
+ requiredAttempts: z.int().min(1).max(1_000),
1603
+ candidates: z.int().min(0),
1604
+ observations: z.int().min(0),
1605
+ gitRevisions: z.enum(["RECORDED", "PARTIAL", "NOT_RECORDED"]),
1606
+ worlds: z.array(z.strictObject({
1607
+ id: IdentifierSchema,
1608
+ kind: EvidenceWorldKindSchema,
1609
+ required: z.boolean(),
1610
+ weight: z.int().min(0).max(1_000_000),
1611
+ digest: Sha256DigestSchema,
1612
+ declaredProvenance: z.string().min(1).max(1_024),
1613
+ git: z
1614
+ .strictObject({
1615
+ role: z.enum(["reference", "target", "neutral"]),
1616
+ commit: GitObjectIdSchema,
1617
+ tree: GitObjectIdSchema,
1618
+ objectFormat: z.enum(["sha1", "sha256"]),
1619
+ })
1620
+ .nullable(),
1621
+ })),
1622
+ }),
1623
+ controls: z.strictObject({
1624
+ executed: z.array(z.strictObject({
1625
+ control: z.enum([
1626
+ "CONTROL_WITHOUT_CANDIDATE",
1627
+ "REFERENCE_PASS",
1628
+ "REGRESSION_DETECTION",
1629
+ "NEUTRAL_PASS",
1630
+ "STABILITY_REPETITION",
1631
+ ]),
1632
+ status: z.enum(["EXECUTED", "NOT_EXECUTED"]),
1633
+ observations: z.int().min(0),
1634
+ })),
1635
+ requested: z.union([
1636
+ z.strictObject({ status: z.literal("NOT_SUPPLIED") }),
1637
+ z.strictObject({
1638
+ status: z.enum(["NO_OBLIGATIONS", "COVERED", "PARTIAL", "NOT_COVERED"]),
1639
+ obligations: z.array(z.strictObject({
1640
+ id: IdentifierSchema,
1641
+ control: ReasonCodeSchema,
1642
+ coverage: z.enum(["EXECUTED", "NOT_EXECUTED", "UNSUPPORTED"]),
1643
+ })),
1644
+ }),
1645
+ ]),
1646
+ omittedGates: z.array(z.strictObject({
1647
+ candidateId: IdentifierSchema,
1648
+ gate: GateResultSchema.shape.name,
1649
+ reasonCodes: z.array(ReasonCodeSchema),
1650
+ })),
1651
+ }),
1652
+ profile: z.union([
1653
+ z.strictObject({ status: z.literal("NOT_REQUESTED") }),
1654
+ z.strictObject({
1655
+ status: z.literal("UNKNOWN_PROFILE"),
1656
+ requestedProfileId: z.string().min(1).max(256),
1657
+ }),
1658
+ ]),
1659
+ policy: z.strictObject({
1660
+ policyVersion: z.string().min(1).max(128),
1661
+ digest: Sha256DigestSchema,
1662
+ }),
1663
+ cost: z.strictObject({
1664
+ estimated: z.strictObject({
1665
+ unit: z.literal("PROCESS_EXECUTIONS"),
1666
+ value: z.int().min(0),
1667
+ basis: z.literal("RECORDED_CAMPAIGN_SHAPE"),
1668
+ assumptions: z.array(z.string().min(1).max(2_048)),
1669
+ }),
1670
+ observed: z.strictObject({
1671
+ unit: z.literal("PROCESS_EXECUTIONS"),
1672
+ executions: z.int().min(0),
1673
+ recordedWallTimeMs: z.number().finite().min(0).nullable(),
1674
+ wallTimeCoverage: z.enum(["COMPLETE", "PARTIAL", "NOT_RECORDED"]),
1675
+ }),
1676
+ execution: z.strictObject({
1677
+ freshness: z.literal("UNKNOWN"),
1678
+ cache: z.literal("NOT_RECORDED"),
1679
+ }),
1680
+ }),
1681
+ limitations: z.array(z.string().min(1).max(2_048)),
1682
+ exportDigest: Sha256DigestSchema,
1683
+ })
1684
+ .meta({
1685
+ id: "https://testforge.dev/schemas/evidence-export.v1.json",
1686
+ title: "AssertLedger evidence export",
1687
+ description: "A self-contained, replayable projection of replay-valid evidence that separates result, integrity, authenticity, environment, confidence, controls, and cost.",
1688
+ });
1689
+ export const EvidenceExportReplayResultSchema = z
1690
+ .strictObject({
1691
+ valid: z.boolean(),
1692
+ schemaValid: z.boolean(),
1693
+ sourceManifestValid: z.boolean(),
1694
+ exportDigestValid: z.boolean(),
1695
+ semanticsValid: z.boolean(),
1696
+ })
1697
+ .meta({
1698
+ id: "https://testforge.dev/schemas/evidence-export-replay-result.v1.json",
1699
+ title: "AssertLedger evidence export replay result",
1700
+ description: "Independent schema, source, digest, and semantic replay verdicts for an export.",
1701
+ });
1307
1702
  export class ContractError extends Error {
1308
1703
  code;
1309
1704
  constructor(code, detail) {
@@ -1389,13 +1784,20 @@ function findReservedEnvironmentVariable(value) {
1389
1784
  if (typeof value !== "object" || value === null || !("isolation" in value))
1390
1785
  return undefined;
1391
1786
  const isolation = value.isolation;
1392
- if (typeof isolation !== "object" ||
1393
- isolation === null ||
1394
- !("environmentAllowlist" in isolation) ||
1395
- !Array.isArray(isolation.environmentAllowlist)) {
1787
+ if (typeof isolation !== "object" || isolation === null)
1396
1788
  return undefined;
1789
+ const names = [];
1790
+ if ("environmentAllowlist" in isolation && Array.isArray(isolation.environmentAllowlist)) {
1791
+ names.push(...isolation.environmentAllowlist);
1792
+ }
1793
+ if ("environment" in isolation && Array.isArray(isolation.environment)) {
1794
+ for (const variable of isolation.environment) {
1795
+ if (typeof variable === "object" && variable !== null && "name" in variable) {
1796
+ names.push(variable.name);
1797
+ }
1798
+ }
1397
1799
  }
1398
- return isolation.environmentAllowlist.find((variable) => {
1800
+ return names.find((variable) => {
1399
1801
  if (typeof variable !== "string")
1400
1802
  return false;
1401
1803
  const normalized = variable.toUpperCase();
@@ -1411,6 +1813,55 @@ export function parseVerificationRequest(value) {
1411
1813
  value.schemaVersion !== SCHEMA_VERSION) {
1412
1814
  throw new ContractError("SCHEMA_VERSION_UNSUPPORTED");
1413
1815
  }
1816
+ assertVerificationRequestPreconditions(value);
1817
+ const parsed = VerificationRequestSchema.safeParse(value);
1818
+ if (!parsed.success) {
1819
+ throw new ContractError("REQUEST_SCHEMA_INVALID", z.prettifyError(parsed.error));
1820
+ }
1821
+ const request = parsed.data;
1822
+ assertUniqueIdentifiers(request.worlds, "DUPLICATE_WORLD_ID");
1823
+ assertUniqueIdentifiers(request.candidates, "DUPLICATE_CANDIDATE_ID");
1824
+ assertWorldKinds(request);
1825
+ assertBudgets(request);
1826
+ return request;
1827
+ }
1828
+ /** Parses either the frozen v1 request or the v2 request that adds container isolation. */
1829
+ export function parseVersionedVerificationRequest(value) {
1830
+ if (typeof value === "object" &&
1831
+ value !== null &&
1832
+ "schemaVersion" in value &&
1833
+ value.schemaVersion === VERIFICATION_SCHEMA_VERSION_V2) {
1834
+ return parseVerificationRequestV2(value);
1835
+ }
1836
+ return parseVerificationRequest(value);
1837
+ }
1838
+ /** Parses only the v2 request, which adds container isolation to the frozen v1 request. */
1839
+ export function parseVerificationRequestV2(value) {
1840
+ if (typeof value !== "object" ||
1841
+ value === null ||
1842
+ !("schemaVersion" in value) ||
1843
+ value.schemaVersion !== VERIFICATION_SCHEMA_VERSION_V2) {
1844
+ throw new ContractError("SCHEMA_VERSION_UNSUPPORTED");
1845
+ }
1846
+ assertVerificationRequestPreconditions(value);
1847
+ const parsed = VerificationRequestV2Schema.safeParse(value);
1848
+ if (!parsed.success) {
1849
+ throw new ContractError("REQUEST_SCHEMA_INVALID", z.prettifyError(parsed.error));
1850
+ }
1851
+ const request = parsed.data;
1852
+ assertUniqueIdentifiers(request.worlds, "DUPLICATE_WORLD_ID");
1853
+ assertUniqueIdentifiers(request.candidates, "DUPLICATE_CANDIDATE_ID");
1854
+ assertWorldKinds(request);
1855
+ assertBudgets(request);
1856
+ if (request.isolation.kind === "container") {
1857
+ const names = request.isolation.environment.map((variable) => variable.name);
1858
+ if (new Set(names).size !== names.length) {
1859
+ throw new ContractError("DUPLICATE_CONTAINER_ENVIRONMENT_VARIABLE");
1860
+ }
1861
+ }
1862
+ return request;
1863
+ }
1864
+ function assertVerificationRequestPreconditions(value) {
1414
1865
  const rawPolicy = "policy" in value ? value.policy : undefined;
1415
1866
  if (typeof rawPolicy === "object" &&
1416
1867
  rawPolicy !== null &&
@@ -1427,16 +1878,6 @@ export function parseVerificationRequest(value) {
1427
1878
  if (reservedEnvironmentVariable !== undefined) {
1428
1879
  throw new ContractError("RESERVED_ENVIRONMENT_VARIABLE", reservedEnvironmentVariable);
1429
1880
  }
1430
- const parsed = VerificationRequestSchema.safeParse(value);
1431
- if (!parsed.success) {
1432
- throw new ContractError("REQUEST_SCHEMA_INVALID", z.prettifyError(parsed.error));
1433
- }
1434
- const request = parsed.data;
1435
- assertUniqueIdentifiers(request.worlds, "DUPLICATE_WORLD_ID");
1436
- assertUniqueIdentifiers(request.candidates, "DUPLICATE_CANDIDATE_ID");
1437
- assertWorldKinds(request);
1438
- assertBudgets(request);
1439
- return request;
1440
1881
  }
1441
1882
  export function parseRepositoryAnalysis(value) {
1442
1883
  const parsed = RepositoryAnalysisSchema.safeParse(value);
@@ -1882,12 +2323,48 @@ export function parseEvidenceManifest(value) {
1882
2323
  throw new ContractError("EVIDENCE_MANIFEST_INVALID", z.prettifyError(parsed.error));
1883
2324
  }
1884
2325
  const manifest = parsed.data;
2326
+ if (!manifestSummariesAreConsistent(manifest) || !manifestReferencesAreConsistent(manifest)) {
2327
+ throw new ContractError("EVIDENCE_MANIFEST_INCONSISTENT");
2328
+ }
2329
+ return manifest;
2330
+ }
2331
+ export function parseEvidenceManifestV2(value) {
2332
+ const parsed = EvidenceManifestV2Schema.safeParse(value);
2333
+ if (!parsed.success) {
2334
+ throw new ContractError("EVIDENCE_MANIFEST_INVALID", z.prettifyError(parsed.error));
2335
+ }
2336
+ const manifest = parsed.data;
2337
+ if (!manifestSummariesAreConsistent(manifest) ||
2338
+ !manifestReferencesAreConsistent(manifest) ||
2339
+ !executionBackendIsConsistent(manifest)) {
2340
+ throw new ContractError("EVIDENCE_MANIFEST_INCONSISTENT");
2341
+ }
2342
+ return manifest;
2343
+ }
2344
+ /** Parses either a frozen v1 manifest or a v2 manifest with an execution backend record. */
2345
+ export function parseVersionedEvidenceManifest(value) {
2346
+ return isJsonObject(value) && value.schemaVersion === VERIFICATION_SCHEMA_VERSION_V2
2347
+ ? parseEvidenceManifestV2(value)
2348
+ : parseEvidenceManifest(value);
2349
+ }
2350
+ function executionBackendIsConsistent(manifest) {
2351
+ const execution = manifest.evidenceContext.execution;
2352
+ const backend = execution.backend;
2353
+ if (backend.kind !== manifest.isolation.kind || backend.level !== manifest.isolation.level) {
2354
+ return false;
2355
+ }
2356
+ if (backend.kind !== "container")
2357
+ return true;
2358
+ const names = backend.controls.environment.map((variable) => variable.name);
2359
+ return (new Set(names).size === names.length &&
2360
+ JSON.stringify([...names].sort()) === JSON.stringify(execution.environmentAllowlist));
2361
+ }
2362
+ function manifestReferencesAreConsistent(manifest) {
1885
2363
  const worldIds = new Set(manifest.worlds.map((world) => world.id));
1886
2364
  const candidateIds = new Set(manifest.candidates.map((candidate) => candidate.id));
1887
2365
  const runIds = new Set(manifest.observations.map((observation) => observation.runId));
1888
2366
  const contextWorldIds = new Set(manifest.evidenceContext.worlds.map((world) => world.id));
1889
- const inconsistent = !manifestSummariesAreConsistent(manifest) ||
1890
- worldIds.size !== manifest.worlds.length ||
2367
+ const inconsistent = worldIds.size !== manifest.worlds.length ||
1891
2368
  candidateIds.size !== manifest.candidates.length ||
1892
2369
  runIds.size !== manifest.observations.length ||
1893
2370
  contextWorldIds.size !== manifest.evidenceContext.worlds.length ||
@@ -1899,10 +2376,7 @@ export function parseEvidenceManifest(value) {
1899
2376
  (observation.candidateId !== null && !candidateIds.has(observation.candidateId))) ||
1900
2377
  manifest.candidates.some((candidate) => candidate.killedTargetIds.some((id) => !worldIds.has(id)) ||
1901
2378
  candidate.gates.some((gate) => gate.evidenceRunIds.some((id) => !runIds.has(id))));
1902
- if (inconsistent) {
1903
- throw new ContractError("EVIDENCE_MANIFEST_INCONSISTENT");
1904
- }
1905
- return manifest;
2379
+ return !inconsistent;
1906
2380
  }
1907
2381
  export function parseReplayResult(value) {
1908
2382
  const parsed = ReplayResultSchema.safeParse(value);
@@ -2453,6 +2927,54 @@ export function parseAgenticCorpusExperimentStructuredResult(value) {
2453
2927
  }
2454
2928
  return parsed.data;
2455
2929
  }
2930
+ export function parseEvidenceProviderManifest(value) {
2931
+ const parsed = EvidenceProviderManifestSchema.safeParse(value);
2932
+ if (!parsed.success) {
2933
+ throw new ContractError("EVIDENCE_PROVIDER_MANIFEST_INVALID", z.prettifyError(parsed.error));
2934
+ }
2935
+ const capabilityIds = new Set(parsed.data.capabilities.map((capability) => capability.id));
2936
+ if (capabilityIds.size !== parsed.data.capabilities.length) {
2937
+ throw new ContractError("EVIDENCE_PROVIDER_MANIFEST_INCONSISTENT");
2938
+ }
2939
+ return parsed.data;
2940
+ }
2941
+ export function parseEvidenceExportRequest(value) {
2942
+ const parsed = EvidenceExportRequestSchema.safeParse(value);
2943
+ if (!parsed.success) {
2944
+ throw new ContractError("EVIDENCE_EXPORT_REQUEST_INVALID", z.prettifyError(parsed.error));
2945
+ }
2946
+ const request = parsed.data;
2947
+ parseEvidenceManifest(request.manifest);
2948
+ assertUniqueIdentifiers(request.consumerRequest?.obligations ?? [], "EVIDENCE_EXPORT_REQUEST_INVALID");
2949
+ return request;
2950
+ }
2951
+ export function parseEvidenceExport(value) {
2952
+ const parsed = EvidenceExportSchema.safeParse(value);
2953
+ if (!parsed.success) {
2954
+ throw new ContractError("EVIDENCE_EXPORT_INVALID", z.prettifyError(parsed.error));
2955
+ }
2956
+ const evidenceExport = parsed.data;
2957
+ parseEvidenceManifest(evidenceExport.sourceManifest);
2958
+ assertUniqueIdentifiers(evidenceExport.result.candidates, "EVIDENCE_EXPORT_INCONSISTENT");
2959
+ assertUniqueIdentifiers(evidenceExport.scope.worlds, "EVIDENCE_EXPORT_INCONSISTENT");
2960
+ assertUniqueIdentifiers(evidenceExport.consumerRequest?.obligations ?? [], "EVIDENCE_EXPORT_INCONSISTENT");
2961
+ return evidenceExport;
2962
+ }
2963
+ export function parseEvidenceExportReplayResult(value) {
2964
+ const parsed = EvidenceExportReplayResultSchema.safeParse(value);
2965
+ if (!parsed.success) {
2966
+ throw new ContractError("EVIDENCE_EXPORT_REPLAY_RESULT_INVALID", z.prettifyError(parsed.error));
2967
+ }
2968
+ const result = parsed.data;
2969
+ if (result.valid !==
2970
+ (result.schemaValid &&
2971
+ result.sourceManifestValid &&
2972
+ result.exportDigestValid &&
2973
+ result.semanticsValid)) {
2974
+ throw new ContractError("EVIDENCE_EXPORT_REPLAY_RESULT_INCONSISTENT");
2975
+ }
2976
+ return result;
2977
+ }
2456
2978
  function jsonSchemaFor(schema, id) {
2457
2979
  const generated = z.toJSONSchema(schema, {
2458
2980
  target: "draft-2020-12",
@@ -2485,6 +3007,12 @@ export function repositoryInitResultJsonSchema() {
2485
3007
  export function evidenceManifestJsonSchema() {
2486
3008
  return jsonSchemaFor(EvidenceManifestSchema, "https://testforge.dev/schemas/evidence-manifest.v1.json");
2487
3009
  }
3010
+ export function verificationRequestV2JsonSchema() {
3011
+ return jsonSchemaFor(VerificationRequestV2Schema, "https://testforge.dev/schemas/verification-request.v2.json");
3012
+ }
3013
+ export function evidenceManifestV2JsonSchema() {
3014
+ return jsonSchemaFor(EvidenceManifestV2Schema, "https://testforge.dev/schemas/evidence-manifest.v2.json");
3015
+ }
2488
3016
  export function replayResultJsonSchema() {
2489
3017
  return jsonSchemaFor(ReplayResultSchema, "https://testforge.dev/schemas/replay-result.v1.json");
2490
3018
  }
@@ -2566,4 +3094,16 @@ export function agenticCorpusExperimentReplayRequestJsonSchema() {
2566
3094
  export function agenticCorpusExperimentReplayResultJsonSchema() {
2567
3095
  return jsonSchemaFor(AgenticCorpusExperimentReplayResultSchema, "https://testforge.dev/schemas/agentic-corpus-experiment-replay-result.v1.json");
2568
3096
  }
3097
+ export function evidenceProviderManifestJsonSchema() {
3098
+ return jsonSchemaFor(EvidenceProviderManifestSchema, "https://testforge.dev/schemas/evidence-provider-manifest.v1.json");
3099
+ }
3100
+ export function evidenceExportRequestJsonSchema() {
3101
+ return jsonSchemaFor(EvidenceExportRequestSchema, "https://testforge.dev/schemas/evidence-export-request.v1.json");
3102
+ }
3103
+ export function evidenceExportJsonSchema() {
3104
+ return jsonSchemaFor(EvidenceExportSchema, "https://testforge.dev/schemas/evidence-export.v1.json");
3105
+ }
3106
+ export function evidenceExportReplayResultJsonSchema() {
3107
+ return jsonSchemaFor(EvidenceExportReplayResultSchema, "https://testforge.dev/schemas/evidence-export-replay-result.v1.json");
3108
+ }
2569
3109
  //# sourceMappingURL=index.js.map