@riddledc/riddle-proof 0.8.81 → 0.8.83

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.
package/dist/index.cjs CHANGED
@@ -3442,6 +3442,7 @@ __export(index_exports, {
3442
3442
  RIDDLE_PROOF_PR_COMMENT_MARKER: () => RIDDLE_PROOF_PR_COMMENT_MARKER,
3443
3443
  RIDDLE_PROOF_RUN_CARD_VERSION: () => RIDDLE_PROOF_RUN_CARD_VERSION,
3444
3444
  RIDDLE_PROOF_RUN_STATE_VERSION: () => RIDDLE_PROOF_RUN_STATE_VERSION,
3445
+ RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION: () => RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION,
3445
3446
  RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION: () => RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION,
3446
3447
  RIDDLE_PROOF_VISUAL_SESSION_VERSION: () => RIDDLE_PROOF_VISUAL_SESSION_VERSION,
3447
3448
  RIDDLE_UNSUBMITTED_WAKE_HINT: () => RIDDLE_UNSUBMITTED_WAKE_HINT,
@@ -3483,6 +3484,7 @@ __export(index_exports, {
3483
3484
  compactBasicGameplayText: () => compactBasicGameplayText,
3484
3485
  compactRecord: () => compactRecord,
3485
3486
  compareVisualProofSessionFingerprint: () => compareVisualProofSessionFingerprint,
3487
+ composeRiddleProofSemanticCertificates: () => composeRiddleProofSemanticCertificates,
3486
3488
  createBasicGameplayCatchRecords: () => createBasicGameplayCatchRecords,
3487
3489
  createBasicGameplayCatchSummary: () => createBasicGameplayCatchSummary,
3488
3490
  createCaptureDiagnostic: () => createCaptureDiagnostic,
@@ -3500,6 +3502,7 @@ __export(index_exports, {
3500
3502
  createRiddleProofProfileEnvironmentBlockedResult: () => createRiddleProofProfileEnvironmentBlockedResult,
3501
3503
  createRiddleProofProfileInsufficientResult: () => createRiddleProofProfileInsufficientResult,
3502
3504
  createRiddleProofRunCard: () => createRiddleProofRunCard,
3505
+ createRiddleProofSemanticCertificate: () => createRiddleProofSemanticCertificate,
3503
3506
  createRunResult: () => createRunResult,
3504
3507
  createRunState: () => createRunState,
3505
3508
  createRunStatusSnapshot: () => createRunStatusSnapshot,
@@ -3517,6 +3520,7 @@ __export(index_exports, {
3517
3520
  isSuccessfulStatus: () => isSuccessfulStatus,
3518
3521
  isTerminalRiddleJobStatus: () => isTerminalRiddleJobStatus,
3519
3522
  isTerminalStatus: () => isTerminalStatus,
3523
+ matchRiddleProofSemanticCertificate: () => matchRiddleProofSemanticCertificate,
3520
3524
  migrateRiddleProofChangeReceipt: () => migrateRiddleProofChangeReceipt,
3521
3525
  nonEmptyString: () => nonEmptyString,
3522
3526
  normalizeCheckpointResponse: () => normalizeCheckpointResponse,
@@ -3529,6 +3533,7 @@ __export(index_exports, {
3529
3533
  parseRiddleProofChangeReceipt: () => parseRiddleProofChangeReceipt,
3530
3534
  parseRiddleProofHandoffReceipt: () => parseRiddleProofHandoffReceipt,
3531
3535
  parseRiddleProofObservationReceipt: () => parseRiddleProofObservationReceipt,
3536
+ parseRiddleProofSemanticCertificate: () => parseRiddleProofSemanticCertificate,
3532
3537
  parseRiddleViewport: () => parseRiddleViewport,
3533
3538
  parseVisualProofSession: () => parseVisualProofSession,
3534
3539
  pollRiddleJob: () => pollRiddleJob,
@@ -3552,6 +3557,7 @@ __export(index_exports, {
3552
3557
  riddleProofPublicStateAllowsClaim: () => riddleProofPublicStateAllowsClaim,
3553
3558
  riddleProofPublicStateAllowsMergeRecommendation: () => riddleProofPublicStateAllowsMergeRecommendation,
3554
3559
  riddleProofPublicStateMergeRecommendation: () => riddleProofPublicStateMergeRecommendation,
3560
+ riddleProofSemanticScopesEqual: () => riddleProofSemanticScopesEqual,
3555
3561
  riddleRequestJson: () => riddleRequestJson,
3556
3562
  runCodexExecAgentDoctor: () => runCodexExecAgentDoctor,
3557
3563
  runLocalAgentDoctor: () => runCodexExecAgentDoctor,
@@ -20566,6 +20572,583 @@ function parseRiddleProofObservationReceipt(value) {
20566
20572
  return value;
20567
20573
  }
20568
20574
 
20575
+ // src/semantic-certificate.ts
20576
+ var import_node_crypto5 = require("crypto");
20577
+ var RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION = "riddle-proof.semantic-certificate.v0";
20578
+ var SCOPE_FIELDS = [
20579
+ "repository",
20580
+ "revision",
20581
+ "environment",
20582
+ "target",
20583
+ "proof_attempt"
20584
+ ];
20585
+ var AUTHORITY_FIELDS = [
20586
+ "authority",
20587
+ "status",
20588
+ "verdict",
20589
+ "ready_to_ship",
20590
+ "merge_ready",
20591
+ "sync_allowed",
20592
+ "ship_authorized",
20593
+ "shipping_authorized",
20594
+ "shipping_disabled",
20595
+ "merge_recommended",
20596
+ "merge_recommendation",
20597
+ "shipping_authorization"
20598
+ ];
20599
+ var CERTIFICATE_FIELDS = /* @__PURE__ */ new Set([
20600
+ "version",
20601
+ "certificate_id",
20602
+ "scope",
20603
+ "claim",
20604
+ "evidence",
20605
+ "derivation",
20606
+ "issued_at"
20607
+ ]);
20608
+ function isRecord4(value) {
20609
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
20610
+ }
20611
+ function safeErrorMessage(error) {
20612
+ try {
20613
+ if (error instanceof Error) return String(error.message);
20614
+ } catch {
20615
+ }
20616
+ try {
20617
+ return String(error);
20618
+ } catch {
20619
+ return "unprintable thrown value";
20620
+ }
20621
+ }
20622
+ function assertOnlyKeys(record, allowed, context) {
20623
+ const allowedSet = new Set(allowed);
20624
+ for (const key of Object.keys(record)) {
20625
+ if (!allowedSet.has(key)) {
20626
+ throw new Error(`${context} contains unsupported field ${key}.`);
20627
+ }
20628
+ }
20629
+ }
20630
+ function requiredString2(record, key, context) {
20631
+ const value = record[key];
20632
+ if (typeof value !== "string" || !value.trim()) {
20633
+ throw new Error(`${context}.${key} must be a non-empty string.`);
20634
+ }
20635
+ return value.trim();
20636
+ }
20637
+ function optionalString(record, key, context) {
20638
+ if (record[key] === void 0) return void 0;
20639
+ return requiredString2(record, key, context);
20640
+ }
20641
+ function isJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
20642
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
20643
+ return true;
20644
+ }
20645
+ if (typeof value === "number") return Number.isFinite(value);
20646
+ if (Array.isArray(value)) {
20647
+ if (ancestors.has(value)) return false;
20648
+ ancestors.add(value);
20649
+ const valid2 = value.every((entry) => isJsonValue(entry, ancestors));
20650
+ ancestors.delete(value);
20651
+ return valid2;
20652
+ }
20653
+ if (!isRecord4(value)) return false;
20654
+ const prototype = Object.getPrototypeOf(value);
20655
+ if (prototype !== Object.prototype && prototype !== null) return false;
20656
+ if (ancestors.has(value)) return false;
20657
+ ancestors.add(value);
20658
+ const valid = Object.values(value).every((entry) => isJsonValue(entry, ancestors));
20659
+ ancestors.delete(value);
20660
+ return valid;
20661
+ }
20662
+ function parseJsonObject2(value, context) {
20663
+ if (value === void 0) return void 0;
20664
+ if (!isRecord4(value) || !isJsonValue(value)) {
20665
+ throw new Error(`${context} must be a JSON object.`);
20666
+ }
20667
+ const cloned = JSON.parse(JSON.stringify(value));
20668
+ if (!isRecord4(cloned) || !isJsonValue(cloned)) {
20669
+ throw new Error(`${context} must remain a JSON object when serialized.`);
20670
+ }
20671
+ return cloned;
20672
+ }
20673
+ function parseIssuedAt(value, context) {
20674
+ if (typeof value !== "string" || !value.trim() || !Number.isFinite(Date.parse(value))) {
20675
+ throw new Error(`${context} must be a valid timestamp.`);
20676
+ }
20677
+ return value.trim();
20678
+ }
20679
+ function parseScope(value, context) {
20680
+ if (!isRecord4(value)) throw new Error(`${context} must be an object.`);
20681
+ assertOnlyKeys(value, SCOPE_FIELDS, context);
20682
+ return {
20683
+ repository: requiredString2(value, "repository", context),
20684
+ revision: requiredString2(value, "revision", context),
20685
+ environment: requiredString2(value, "environment", context),
20686
+ target: requiredString2(value, "target", context),
20687
+ proof_attempt: requiredString2(value, "proof_attempt", context)
20688
+ };
20689
+ }
20690
+ function parseClaimRef(value, context, allowedExtras = []) {
20691
+ if (!isRecord4(value)) throw new Error(`${context} must be an object.`);
20692
+ assertOnlyKeys(value, ["claim_id", "claim_version", "parameters", ...allowedExtras], context);
20693
+ const parameters = parseJsonObject2(value.parameters, `${context}.parameters`);
20694
+ return {
20695
+ claim_id: requiredString2(value, "claim_id", context),
20696
+ claim_version: requiredString2(value, "claim_version", context),
20697
+ ...parameters ? { parameters } : {}
20698
+ };
20699
+ }
20700
+ function parseClaim(value, context) {
20701
+ if (!isRecord4(value)) throw new Error(`${context} must be an object.`);
20702
+ return {
20703
+ ...parseClaimRef(value, context, ["label"]),
20704
+ label: requiredString2(value, "label", context)
20705
+ };
20706
+ }
20707
+ function parseEvidenceRef(value, context) {
20708
+ if (!isRecord4(value)) throw new Error(`${context} must be an object.`);
20709
+ assertOnlyKeys(
20710
+ value,
20711
+ ["receipt_id", "artifact_digest", "role", "artifact_url", "artifact_path"],
20712
+ context
20713
+ );
20714
+ const artifactDigest = requiredString2(value, "artifact_digest", context).toLowerCase();
20715
+ if (!/^sha256:[0-9a-f]{64}$/u.test(artifactDigest)) {
20716
+ throw new Error(`${context}.artifact_digest must be a full sha256 digest.`);
20717
+ }
20718
+ const artifactUrl = optionalString(value, "artifact_url", context);
20719
+ const artifactPath = optionalString(value, "artifact_path", context);
20720
+ return {
20721
+ receipt_id: requiredString2(value, "receipt_id", context),
20722
+ artifact_digest: artifactDigest,
20723
+ role: requiredString2(value, "role", context),
20724
+ ...artifactUrl ? { artifact_url: artifactUrl } : {},
20725
+ ...artifactPath ? { artifact_path: artifactPath } : {}
20726
+ };
20727
+ }
20728
+ function parseEvidenceBundle(value, context) {
20729
+ if (!Array.isArray(value) || value.length === 0) {
20730
+ throw new Error(`${context} must contain at least one evidence reference.`);
20731
+ }
20732
+ return value.map((entry, index) => parseEvidenceRef(entry, `${context}[${index}]`));
20733
+ }
20734
+ function parseContractRef(value, context) {
20735
+ if (!isRecord4(value)) throw new Error(`${context} must be an object.`);
20736
+ return {
20737
+ contract_id: requiredString2(value, "contract_id", context),
20738
+ contract_version: requiredString2(value, "contract_version", context),
20739
+ label: requiredString2(value, "label", context)
20740
+ };
20741
+ }
20742
+ function parseContract(value, context, allowRuntimePredicate = false) {
20743
+ if (!isRecord4(value)) throw new Error(`${context} must be an object.`);
20744
+ assertOnlyKeys(
20745
+ value,
20746
+ ["contract_id", "contract_version", "label", "claim", ...allowRuntimePredicate ? ["accepts"] : []],
20747
+ context
20748
+ );
20749
+ if (allowRuntimePredicate && typeof value.accepts !== "function") {
20750
+ throw new Error(`${context}.accepts must be a function.`);
20751
+ }
20752
+ return {
20753
+ ...parseContractRef(value, context),
20754
+ claim: parseClaim(value.claim, `${context}.claim`)
20755
+ };
20756
+ }
20757
+ function parseRule(value, context, allowFullPremises = false) {
20758
+ if (!isRecord4(value)) throw new Error(`${context} must be an object.`);
20759
+ assertOnlyKeys(value, ["rule_id", "rule_version", "label", "premises", "conclusion"], context);
20760
+ if (!Array.isArray(value.premises) || value.premises.length === 0) {
20761
+ throw new Error(`${context}.premises must contain at least one claim reference.`);
20762
+ }
20763
+ return {
20764
+ rule_id: requiredString2(value, "rule_id", context),
20765
+ rule_version: requiredString2(value, "rule_version", context),
20766
+ label: requiredString2(value, "label", context),
20767
+ premises: value.premises.map((premise, index) => parseClaimRef(
20768
+ premise,
20769
+ `${context}.premises[${index}]`,
20770
+ allowFullPremises ? ["label"] : []
20771
+ )),
20772
+ conclusion: parseClaim(value.conclusion, `${context}.conclusion`)
20773
+ };
20774
+ }
20775
+ function parsePremise(value, context) {
20776
+ if (!isRecord4(value)) throw new Error(`${context} must be an object.`);
20777
+ assertOnlyKeys(
20778
+ value,
20779
+ ["certificate_id", "derivation_kind", "assurance", "scope", "claim", "evidence"],
20780
+ context
20781
+ );
20782
+ const derivationKind = requiredString2(value, "derivation_kind", context);
20783
+ const assurance = requiredString2(value, "assurance", context);
20784
+ const validAssurance = derivationKind === "contract" && assurance === "runtime_contract_accepted" || derivationKind === "composition" && assurance === "declared_runtime_rule";
20785
+ if (!validAssurance) {
20786
+ throw new Error(`${context} must preserve a valid derivation_kind and assurance pair.`);
20787
+ }
20788
+ return {
20789
+ certificate_id: requiredString2(value, "certificate_id", context),
20790
+ derivation_kind: derivationKind,
20791
+ assurance,
20792
+ scope: parseScope(value.scope, `${context}.scope`),
20793
+ claim: parseClaim(value.claim, `${context}.claim`),
20794
+ evidence: parseEvidenceBundle(value.evidence, `${context}.evidence`)
20795
+ };
20796
+ }
20797
+ function stableJson3(value) {
20798
+ if (Array.isArray(value)) return `[${value.map(stableJson3).join(",")}]`;
20799
+ if (isRecord4(value)) {
20800
+ return `{${Object.keys(value).filter((key) => value[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${stableJson3(value[key])}`).join(",")}}`;
20801
+ }
20802
+ const encoded = JSON.stringify(value);
20803
+ if (encoded === void 0) throw new Error("Semantic certificate contains a non-JSON value.");
20804
+ return encoded;
20805
+ }
20806
+ function sameClaimRef(left, right) {
20807
+ return left.claim_id === right.claim_id && left.claim_version === right.claim_version && stableJson3(left.parameters || {}) === stableJson3(right.parameters || {});
20808
+ }
20809
+ function sameEvidence(left, right) {
20810
+ return stableJson3(left) === stableJson3(right);
20811
+ }
20812
+ function certificateId(body) {
20813
+ const digest = (0, import_node_crypto5.createHash)("sha256").update(stableJson3(body)).digest("hex");
20814
+ return `rpsc_${digest}`;
20815
+ }
20816
+ function withCertificateId(body) {
20817
+ return { ...body, certificate_id: certificateId(body) };
20818
+ }
20819
+ function parseDerivation(value, context) {
20820
+ if (!isRecord4(value)) throw new Error(`${context} must be an object.`);
20821
+ const kind = requiredString2(value, "kind", context);
20822
+ if (kind === "contract") {
20823
+ assertOnlyKeys(value, ["kind", "assurance", "contract"], context);
20824
+ if (value.assurance !== "runtime_contract_accepted") {
20825
+ throw new Error(`${context}.assurance must be runtime_contract_accepted.`);
20826
+ }
20827
+ return {
20828
+ kind,
20829
+ assurance: "runtime_contract_accepted",
20830
+ contract: parseContract(value.contract, `${context}.contract`)
20831
+ };
20832
+ }
20833
+ if (kind === "composition") {
20834
+ assertOnlyKeys(value, ["kind", "assurance", "rule", "premises"], context);
20835
+ if (value.assurance !== "declared_runtime_rule") {
20836
+ throw new Error(`${context}.assurance must be declared_runtime_rule.`);
20837
+ }
20838
+ if (!Array.isArray(value.premises) || value.premises.length === 0) {
20839
+ throw new Error(`${context}.premises must contain at least one certificate premise.`);
20840
+ }
20841
+ return {
20842
+ kind,
20843
+ assurance: "declared_runtime_rule",
20844
+ rule: parseRule(value.rule, `${context}.rule`),
20845
+ premises: value.premises.map((premise, index) => parsePremise(premise, `${context}.premises[${index}]`))
20846
+ };
20847
+ }
20848
+ throw new Error(`${context}.kind must be contract or composition.`);
20849
+ }
20850
+ function riddleProofSemanticScopesEqual(left, right) {
20851
+ return SCOPE_FIELDS.every((field) => left[field] === right[field]);
20852
+ }
20853
+ function createRiddleProofSemanticCertificate(input) {
20854
+ if (!isRecord4(input)) throw new Error("Semantic certificate input must be an object.");
20855
+ assertOnlyKeys(
20856
+ input,
20857
+ ["scope", "evidence", "observation", "contract", "issued_at"],
20858
+ "semantic certificate input"
20859
+ );
20860
+ const scope = parseScope(input.scope, "semantic certificate scope");
20861
+ const evidence = parseEvidenceBundle(input.evidence, "semantic certificate evidence");
20862
+ const contract = parseContract(input.contract, "semantic certificate contract", true);
20863
+ const contractRef = {
20864
+ contract_id: contract.contract_id,
20865
+ contract_version: contract.contract_version,
20866
+ label: contract.label
20867
+ };
20868
+ const claim = contract.claim;
20869
+ let accepted;
20870
+ try {
20871
+ accepted = input.contract.accepts({ ...scope }, input.observation) === true;
20872
+ } catch (error) {
20873
+ return {
20874
+ ok: false,
20875
+ error: {
20876
+ code: "contract_error",
20877
+ contract: contractRef,
20878
+ message: `Semantic contract evaluation failed: ${error instanceof Error ? error.message : String(error)}.`
20879
+ }
20880
+ };
20881
+ }
20882
+ if (!accepted) {
20883
+ return {
20884
+ ok: false,
20885
+ error: {
20886
+ code: "contract_rejected",
20887
+ contract: contractRef,
20888
+ message: "Semantic contract rejected the supplied observation at this scope."
20889
+ }
20890
+ };
20891
+ }
20892
+ const body = {
20893
+ version: RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION,
20894
+ scope,
20895
+ claim,
20896
+ evidence,
20897
+ derivation: { kind: "contract", assurance: "runtime_contract_accepted", contract },
20898
+ issued_at: parseIssuedAt(input.issued_at || (/* @__PURE__ */ new Date()).toISOString(), "semantic certificate issued_at")
20899
+ };
20900
+ return { ok: true, certificate: withCertificateId(body) };
20901
+ }
20902
+ function parseRiddleProofSemanticCertificate(value) {
20903
+ if (!isRecord4(value)) throw new Error("Semantic certificate must be an object.");
20904
+ if (value.version !== RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION) {
20905
+ throw new Error(`Unsupported Semantic certificate version ${String(value.version || "missing")}.`);
20906
+ }
20907
+ for (const field of AUTHORITY_FIELDS) {
20908
+ if (Object.prototype.hasOwnProperty.call(value, field)) {
20909
+ throw new Error(`Semantic certificate must not contain authority field ${field}.`);
20910
+ }
20911
+ }
20912
+ for (const field of Object.keys(value)) {
20913
+ if (!CERTIFICATE_FIELDS.has(field)) {
20914
+ throw new Error(`Semantic certificate contains unsupported field ${field}.`);
20915
+ }
20916
+ }
20917
+ const scope = parseScope(value.scope, "semantic certificate scope");
20918
+ const claim = parseClaim(value.claim, "semantic certificate claim");
20919
+ const evidence = parseEvidenceBundle(value.evidence, "semantic certificate evidence");
20920
+ const derivation = parseDerivation(value.derivation, "semantic certificate derivation");
20921
+ if (derivation.kind === "contract" && !sameClaimRef(claim, derivation.contract.claim)) {
20922
+ throw new Error("Semantic contract-derived claim must match its contract claim.");
20923
+ }
20924
+ if (derivation.kind === "composition") {
20925
+ if (derivation.premises.length !== derivation.rule.premises.length) {
20926
+ throw new Error("Semantic composition premises must match the rule premise count.");
20927
+ }
20928
+ derivation.premises.forEach((premise, index) => {
20929
+ if (!riddleProofSemanticScopesEqual(scope, premise.scope)) {
20930
+ throw new Error(`Semantic composition premise ${index} must have the certificate scope.`);
20931
+ }
20932
+ if (!sameClaimRef(premise.claim, derivation.rule.premises[index])) {
20933
+ throw new Error(`Semantic composition premise ${index} must match its rule claim.`);
20934
+ }
20935
+ });
20936
+ if (!sameClaimRef(claim, derivation.rule.conclusion)) {
20937
+ throw new Error("Semantic composition claim must match its rule conclusion.");
20938
+ }
20939
+ const expectedEvidence = derivation.premises.flatMap((premise) => premise.evidence);
20940
+ if (!sameEvidence(evidence, expectedEvidence)) {
20941
+ throw new Error("Semantic composition evidence must be the ordered concatenation of premise evidence.");
20942
+ }
20943
+ }
20944
+ const body = {
20945
+ version: RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION,
20946
+ scope,
20947
+ claim,
20948
+ evidence,
20949
+ derivation,
20950
+ issued_at: parseIssuedAt(value.issued_at, "semantic certificate issued_at")
20951
+ };
20952
+ const observedId = requiredString2(value, "certificate_id", "semantic certificate");
20953
+ const expectedId = certificateId(body);
20954
+ if (observedId !== expectedId) {
20955
+ throw new Error("Semantic certificate_id must match its content.");
20956
+ }
20957
+ return { ...body, certificate_id: observedId };
20958
+ }
20959
+ function matchRiddleProofSemanticCertificate(input) {
20960
+ if (!isRecord4(input)) throw new Error("Semantic certificate match input must be an object.");
20961
+ assertOnlyKeys(
20962
+ input,
20963
+ [
20964
+ "certificate",
20965
+ "expected_certificate_id",
20966
+ "expected_scope",
20967
+ "expected_claim",
20968
+ "expected_assurance"
20969
+ ],
20970
+ "semantic certificate match input"
20971
+ );
20972
+ const expectedCertificateId = requiredString2(
20973
+ input,
20974
+ "expected_certificate_id",
20975
+ "semantic certificate match input"
20976
+ );
20977
+ if (!/^rpsc_[0-9a-f]{64}$/u.test(expectedCertificateId)) {
20978
+ throw new Error(
20979
+ "Semantic certificate match input.expected_certificate_id must be a full rpsc content ID."
20980
+ );
20981
+ }
20982
+ const expectedScope = parseScope(
20983
+ input.expected_scope,
20984
+ "semantic certificate match expected_scope"
20985
+ );
20986
+ const expectedClaim = parseClaimRef(
20987
+ input.expected_claim,
20988
+ "semantic certificate match expected_claim",
20989
+ ["label"]
20990
+ );
20991
+ const expectedAssurance = requiredString2(
20992
+ input,
20993
+ "expected_assurance",
20994
+ "semantic certificate match input"
20995
+ );
20996
+ if (expectedAssurance !== "runtime_contract_accepted" && expectedAssurance !== "declared_runtime_rule") {
20997
+ throw new Error(
20998
+ "Semantic certificate match input.expected_assurance must be runtime_contract_accepted or declared_runtime_rule."
20999
+ );
21000
+ }
21001
+ let certificate;
21002
+ try {
21003
+ certificate = parseRiddleProofSemanticCertificate(input.certificate);
21004
+ } catch (error) {
21005
+ return {
21006
+ ok: false,
21007
+ error: {
21008
+ code: "invalid_certificate",
21009
+ message: `Semantic certificate did not parse: ${safeErrorMessage(error)}`
21010
+ }
21011
+ };
21012
+ }
21013
+ if (certificate.certificate_id !== expectedCertificateId) {
21014
+ return {
21015
+ ok: false,
21016
+ error: {
21017
+ code: "certificate_id_mismatch",
21018
+ expected: expectedCertificateId,
21019
+ observed: certificate.certificate_id,
21020
+ message: "Semantic certificate does not match the trusted expected content ID."
21021
+ }
21022
+ };
21023
+ }
21024
+ const scopeMismatch = firstScopeMismatch(expectedScope, certificate.scope);
21025
+ if (scopeMismatch) {
21026
+ return {
21027
+ ok: false,
21028
+ error: {
21029
+ code: "scope_mismatch",
21030
+ ...scopeMismatch,
21031
+ message: `Semantic certificate has a different ${scopeMismatch.field} than the consumer expected.`
21032
+ }
21033
+ };
21034
+ }
21035
+ if (!sameClaimRef(expectedClaim, certificate.claim)) {
21036
+ return {
21037
+ ok: false,
21038
+ error: {
21039
+ code: "claim_mismatch",
21040
+ expected: expectedClaim,
21041
+ observed: parseClaimRef(
21042
+ certificate.claim,
21043
+ "semantic certificate match observed claim",
21044
+ ["label"]
21045
+ ),
21046
+ message: "Semantic certificate does not state the claim the consumer expected."
21047
+ }
21048
+ };
21049
+ }
21050
+ if (certificate.derivation.assurance !== expectedAssurance) {
21051
+ return {
21052
+ ok: false,
21053
+ error: {
21054
+ code: "assurance_mismatch",
21055
+ expected: expectedAssurance,
21056
+ observed: certificate.derivation.assurance,
21057
+ message: "Semantic certificate does not have the assurance the consumer expected."
21058
+ }
21059
+ };
21060
+ }
21061
+ return { ok: true, certificate };
21062
+ }
21063
+ function firstScopeMismatch(expected, observed) {
21064
+ for (const field of SCOPE_FIELDS) {
21065
+ if (expected[field] !== observed[field]) {
21066
+ return { field, expected: expected[field], observed: observed[field] };
21067
+ }
21068
+ }
21069
+ return void 0;
21070
+ }
21071
+ function premiseFromCertificate(certificate) {
21072
+ return {
21073
+ certificate_id: certificate.certificate_id,
21074
+ derivation_kind: certificate.derivation.kind,
21075
+ assurance: certificate.derivation.assurance,
21076
+ scope: { ...certificate.scope },
21077
+ claim: parseClaim(certificate.claim, "semantic composition premise claim"),
21078
+ evidence: certificate.evidence.map((entry) => ({ ...entry }))
21079
+ };
21080
+ }
21081
+ function composeRiddleProofSemanticCertificates(input) {
21082
+ if (!isRecord4(input)) throw new Error("Semantic composition input must be an object.");
21083
+ assertOnlyKeys(
21084
+ input,
21085
+ ["rule", "certificates", "issued_at"],
21086
+ "semantic composition input"
21087
+ );
21088
+ const rule = parseRule(input.rule, "semantic composition rule", true);
21089
+ if (!Array.isArray(input.certificates) || input.certificates.length === 0) {
21090
+ throw new Error("Semantic composition requires at least one certificate.");
21091
+ }
21092
+ const certificates = input.certificates.map((certificate) => parseRiddleProofSemanticCertificate(certificate));
21093
+ if (certificates.length !== rule.premises.length) {
21094
+ return {
21095
+ ok: false,
21096
+ error: {
21097
+ code: "premise_count_mismatch",
21098
+ expected: rule.premises.length,
21099
+ observed: certificates.length,
21100
+ message: `Semantic rule expected ${rule.premises.length} certificate(s), received ${certificates.length}.`
21101
+ }
21102
+ };
21103
+ }
21104
+ const expectedScope = certificates[0].scope;
21105
+ for (let index = 1; index < certificates.length; index += 1) {
21106
+ const mismatch = firstScopeMismatch(expectedScope, certificates[index].scope);
21107
+ if (mismatch) {
21108
+ return {
21109
+ ok: false,
21110
+ error: {
21111
+ code: "scope_mismatch",
21112
+ input_index: index,
21113
+ ...mismatch,
21114
+ message: `Semantic certificate ${index} has a different ${mismatch.field}.`
21115
+ }
21116
+ };
21117
+ }
21118
+ }
21119
+ for (let index = 0; index < certificates.length; index += 1) {
21120
+ const expected = rule.premises[index];
21121
+ const observed = certificates[index].claim;
21122
+ if (!sameClaimRef(expected, observed)) {
21123
+ return {
21124
+ ok: false,
21125
+ error: {
21126
+ code: "premise_mismatch",
21127
+ input_index: index,
21128
+ expected,
21129
+ observed: parseClaimRef(observed, "semantic composition observed claim", ["label"]),
21130
+ message: `Semantic certificate ${index} does not satisfy its declared rule premise.`
21131
+ }
21132
+ };
21133
+ }
21134
+ }
21135
+ const evidence = certificates.flatMap((certificate) => certificate.evidence);
21136
+ const body = {
21137
+ version: RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION,
21138
+ scope: { ...expectedScope },
21139
+ claim: { ...rule.conclusion },
21140
+ evidence,
21141
+ derivation: {
21142
+ kind: "composition",
21143
+ assurance: "declared_runtime_rule",
21144
+ rule,
21145
+ premises: certificates.map(premiseFromCertificate)
21146
+ },
21147
+ issued_at: parseIssuedAt(input.issued_at || (/* @__PURE__ */ new Date()).toISOString(), "semantic certificate issued_at")
21148
+ };
21149
+ return { ok: true, certificate: withCertificateId(body) };
21150
+ }
21151
+
20569
21152
  // src/change-proof.ts
20570
21153
  var RIDDLE_PROOF_CHANGE_CONTRACT_VERSION = "riddle-proof.change-contract.v1";
20571
21154
  var RIDDLE_PROOF_CHANGE_RESULT_VERSION = "riddle-proof.change-result.v1";
@@ -21023,7 +21606,7 @@ function createRiddleProofChangeReceipt(input) {
21023
21606
  metadata: input.result.metadata
21024
21607
  };
21025
21608
  }
21026
- function isRecord4(value) {
21609
+ function isRecord5(value) {
21027
21610
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
21028
21611
  }
21029
21612
  function migratedObservationFromLegacySide(side, legacy) {
@@ -21100,7 +21683,7 @@ function migrateRiddleProofChangeReceipt(legacy) {
21100
21683
  };
21101
21684
  }
21102
21685
  function parseRiddleProofChangeReceipt(value) {
21103
- if (!isRecord4(value)) throw new Error("Change receipt must be an object.");
21686
+ if (!isRecord5(value)) throw new Error("Change receipt must be an object.");
21104
21687
  if (value.version === RIDDLE_PROOF_CHANGE_RECEIPT_V1_VERSION) {
21105
21688
  return migrateRiddleProofChangeReceipt(value);
21106
21689
  }
@@ -21144,8 +21727,8 @@ function createRiddleProofHandoffReceipt(changeReceipt, options = {}) {
21144
21727
  };
21145
21728
  }
21146
21729
  function parseRiddleProofHandoffReceipt(value) {
21147
- if (!isRecord4(value) || value.version !== RIDDLE_PROOF_HANDOFF_RECEIPT_VERSION) {
21148
- throw new Error(`Unsupported Handoff receipt version ${String(isRecord4(value) ? value.version || "missing" : "missing")}.`);
21730
+ if (!isRecord5(value) || value.version !== RIDDLE_PROOF_HANDOFF_RECEIPT_VERSION) {
21731
+ throw new Error(`Unsupported Handoff receipt version ${String(isRecord5(value) ? value.version || "missing" : "missing")}.`);
21149
21732
  }
21150
21733
  const receipt = value;
21151
21734
  if (!Number.isFinite(Date.parse(receipt.created_at))) {
@@ -22616,6 +23199,7 @@ function buildRiddleProofPrCommentMarkdown(input) {
22616
23199
  RIDDLE_PROOF_PR_COMMENT_MARKER,
22617
23200
  RIDDLE_PROOF_RUN_CARD_VERSION,
22618
23201
  RIDDLE_PROOF_RUN_STATE_VERSION,
23202
+ RIDDLE_PROOF_SEMANTIC_CERTIFICATE_VERSION,
22619
23203
  RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION,
22620
23204
  RIDDLE_PROOF_VISUAL_SESSION_VERSION,
22621
23205
  RIDDLE_UNSUBMITTED_WAKE_HINT,
@@ -22657,6 +23241,7 @@ function buildRiddleProofPrCommentMarkdown(input) {
22657
23241
  compactBasicGameplayText,
22658
23242
  compactRecord,
22659
23243
  compareVisualProofSessionFingerprint,
23244
+ composeRiddleProofSemanticCertificates,
22660
23245
  createBasicGameplayCatchRecords,
22661
23246
  createBasicGameplayCatchSummary,
22662
23247
  createCaptureDiagnostic,
@@ -22674,6 +23259,7 @@ function buildRiddleProofPrCommentMarkdown(input) {
22674
23259
  createRiddleProofProfileEnvironmentBlockedResult,
22675
23260
  createRiddleProofProfileInsufficientResult,
22676
23261
  createRiddleProofRunCard,
23262
+ createRiddleProofSemanticCertificate,
22677
23263
  createRunResult,
22678
23264
  createRunState,
22679
23265
  createRunStatusSnapshot,
@@ -22691,6 +23277,7 @@ function buildRiddleProofPrCommentMarkdown(input) {
22691
23277
  isSuccessfulStatus,
22692
23278
  isTerminalRiddleJobStatus,
22693
23279
  isTerminalStatus,
23280
+ matchRiddleProofSemanticCertificate,
22694
23281
  migrateRiddleProofChangeReceipt,
22695
23282
  nonEmptyString,
22696
23283
  normalizeCheckpointResponse,
@@ -22703,6 +23290,7 @@ function buildRiddleProofPrCommentMarkdown(input) {
22703
23290
  parseRiddleProofChangeReceipt,
22704
23291
  parseRiddleProofHandoffReceipt,
22705
23292
  parseRiddleProofObservationReceipt,
23293
+ parseRiddleProofSemanticCertificate,
22706
23294
  parseRiddleViewport,
22707
23295
  parseVisualProofSession,
22708
23296
  pollRiddleJob,
@@ -22726,6 +23314,7 @@ function buildRiddleProofPrCommentMarkdown(input) {
22726
23314
  riddleProofPublicStateAllowsClaim,
22727
23315
  riddleProofPublicStateAllowsMergeRecommendation,
22728
23316
  riddleProofPublicStateMergeRecommendation,
23317
+ riddleProofSemanticScopesEqual,
22729
23318
  riddleRequestJson,
22730
23319
  runCodexExecAgentDoctor,
22731
23320
  runLocalAgentDoctor,