@riddledc/riddle-proof 0.8.76 → 0.8.78

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 (56) hide show
  1. package/README.md +74 -5
  2. package/dist/advanced/index.d.cts +1 -1
  3. package/dist/advanced/index.d.ts +1 -1
  4. package/dist/advanced/proof-run-engine.d.cts +1 -1
  5. package/dist/advanced/proof-run-engine.d.ts +1 -1
  6. package/dist/change-proof.cjs +991 -7
  7. package/dist/change-proof.d.cts +157 -1
  8. package/dist/change-proof.d.ts +157 -1
  9. package/dist/change-proof.js +25 -3
  10. package/dist/{chunk-B2DP2LET.js → chunk-5IFZSUPF.js} +52 -13
  11. package/dist/chunk-6VFS2JFR.js +886 -0
  12. package/dist/{chunk-CWRIXP5H.js → chunk-7N6X54WG.js} +181 -17
  13. package/dist/{chunk-GG2D3MFZ.js → chunk-FCSJZBC5.js} +26 -1
  14. package/dist/{chunk-AK2EPEVO.js → chunk-HSGZV2NK.js} +221 -63
  15. package/dist/chunk-MEVVL4TI.js +258 -0
  16. package/dist/{chunk-UE4I7RTI.js → chunk-RQPCKRKT.js} +1 -1
  17. package/dist/cli/index.js +7 -6
  18. package/dist/cli.cjs +1678 -361
  19. package/dist/cli.js +7 -6
  20. package/dist/index.cjs +1242 -42
  21. package/dist/index.d.cts +4 -3
  22. package/dist/index.d.ts +4 -3
  23. package/dist/index.js +46 -10
  24. package/dist/pr-comment.cjs +481 -17
  25. package/dist/pr-comment.d.cts +6 -1
  26. package/dist/pr-comment.d.ts +6 -1
  27. package/dist/pr-comment.js +6 -1
  28. package/dist/profile/index.cjs +26 -1
  29. package/dist/profile/index.js +1 -1
  30. package/dist/profile-suggestions.js +2 -2
  31. package/dist/profile.cjs +26 -1
  32. package/dist/profile.d.cts +7 -0
  33. package/dist/profile.d.ts +7 -0
  34. package/dist/profile.js +1 -1
  35. package/dist/{proof-run-engine-MiKZt9oY.d.ts → proof-run-engine-CsQshTUX.d.ts} +3 -3
  36. package/dist/{proof-run-engine-Baiv6l3A.d.cts → proof-run-engine-DmUqh7Cw.d.cts} +3 -3
  37. package/dist/proof-run-engine.d.cts +1 -1
  38. package/dist/proof-run-engine.d.ts +1 -1
  39. package/dist/receipts.cjs +286 -0
  40. package/dist/receipts.d.cts +115 -0
  41. package/dist/receipts.d.ts +115 -0
  42. package/dist/receipts.js +15 -0
  43. package/dist/riddle-client.cjs +98 -13
  44. package/dist/riddle-client.d.cts +18 -5
  45. package/dist/riddle-client.d.ts +18 -5
  46. package/dist/riddle-client.js +4 -1
  47. package/dist/runtime/index.cjs +98 -13
  48. package/dist/runtime/index.d.cts +5 -1
  49. package/dist/runtime/index.d.ts +5 -1
  50. package/dist/runtime/index.js +4 -1
  51. package/dist/runtime/riddle-client.cjs +98 -13
  52. package/dist/runtime/riddle-client.d.cts +5 -1
  53. package/dist/runtime/riddle-client.d.ts +5 -1
  54. package/dist/runtime/riddle-client.js +4 -1
  55. package/package.json +7 -2
  56. package/dist/chunk-6S7DZWVC.js +0 -167
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var pr_comment_exports = {};
22
22
  __export(pr_comment_exports, {
23
23
  RIDDLE_PROOF_PR_COMMENT_MARKER: () => RIDDLE_PROOF_PR_COMMENT_MARKER,
24
+ buildRiddleProofHandoffPrCommentMarkdown: () => buildRiddleProofHandoffPrCommentMarkdown,
24
25
  buildRiddleProofPrCommentMarkdown: () => buildRiddleProofPrCommentMarkdown,
25
26
  summarizeRiddleProofPrComment: () => summarizeRiddleProofPrComment
26
27
  });
@@ -202,6 +203,313 @@ function summarizeRiddleProofPublicState(input) {
202
203
  };
203
204
  }
204
205
 
206
+ // src/profile.ts
207
+ var RIDDLE_PROOF_PROFILE_STATUSES = [
208
+ "passed",
209
+ "product_regression",
210
+ "proof_insufficient",
211
+ "environment_blocked",
212
+ "configuration_error",
213
+ "needs_human_review"
214
+ ];
215
+
216
+ // src/receipts.ts
217
+ var RIDDLE_PREVIEW_RECEIPT_VERSION = "riddle.preview-receipt.v1";
218
+ var RIDDLE_PROOF_OBSERVATION_RECEIPT_VERSION = "riddle-proof.observation-receipt.v1";
219
+ function isRecord(value) {
220
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
221
+ }
222
+ function requiredString(record, key, context) {
223
+ const value = record[key];
224
+ if (typeof value !== "string" || !value.trim()) {
225
+ throw new Error(`${context}.${key} must be a non-empty string.`);
226
+ }
227
+ return value.trim();
228
+ }
229
+ function normalizedSourceIdentity(value) {
230
+ if (!isRecord(value)) return {};
231
+ return {
232
+ git_revision: typeof value.git_revision === "string" && value.git_revision.trim() ? value.git_revision.trim() : void 0,
233
+ repository: typeof value.repository === "string" && value.repository.trim() ? value.repository.trim() : void 0,
234
+ dirty: typeof value.dirty === "boolean" ? value.dirty : void 0,
235
+ label: typeof value.label === "string" && value.label.trim() ? value.label.trim() : void 0
236
+ };
237
+ }
238
+ function parseRiddlePreviewReceipt(value) {
239
+ if (!isRecord(value)) throw new Error("Preview receipt must be an object.");
240
+ if (value.version !== RIDDLE_PREVIEW_RECEIPT_VERSION) {
241
+ throw new Error(`Unsupported Preview receipt version ${String(value.version || "missing")}.`);
242
+ }
243
+ if (!isRecord(value.source)) throw new Error("Preview receipt source must be an object.");
244
+ const expiresAt = requiredString(value, "expires_at", "preview receipt");
245
+ const publishedAt = requiredString(value, "published_at", "preview receipt");
246
+ const contentDigest = requiredString(value, "content_digest", "preview receipt");
247
+ if (!contentDigest.startsWith("sha256:") || contentDigest.length <= "sha256:".length) {
248
+ throw new Error("preview receipt.content_digest must be a sha256 digest.");
249
+ }
250
+ if (!Number.isFinite(Date.parse(expiresAt)) || !Number.isFinite(Date.parse(publishedAt))) {
251
+ throw new Error("Preview receipt timestamps must be valid ISO date strings.");
252
+ }
253
+ return {
254
+ version: RIDDLE_PREVIEW_RECEIPT_VERSION,
255
+ preview_id: requiredString(value, "preview_id", "preview receipt"),
256
+ url: requiredString(value, "url", "preview receipt"),
257
+ expires_at: expiresAt,
258
+ content_digest: contentDigest,
259
+ source: normalizedSourceIdentity(value.source),
260
+ published_at: publishedAt
261
+ };
262
+ }
263
+ function parseRiddleProofObservationReceipt(value) {
264
+ if (!isRecord(value)) throw new Error("Observation receipt must be an object.");
265
+ if (value.version !== RIDDLE_PROOF_OBSERVATION_RECEIPT_VERSION) {
266
+ throw new Error(`Unsupported Observation receipt version ${String(value.version || "missing")}.`);
267
+ }
268
+ if (!isRecord(value.executor)) throw new Error("Observation receipt executor must be an object.");
269
+ if (!isRecord(value.target)) throw new Error("Observation receipt target must be an object.");
270
+ if (!isRecord(value.source)) throw new Error("Observation receipt source must be an object.");
271
+ requiredString(value, "observation_id", "observation receipt");
272
+ requiredString(value, "captured_at", "observation receipt");
273
+ const executorKind = requiredString(value.executor, "kind", "observation receipt executor");
274
+ if (!"local_playwright,riddle_hosted,browser_api,other".split(",").includes(executorKind)) {
275
+ throw new Error(`Unsupported observation executor kind ${executorKind}.`);
276
+ }
277
+ if (value.executor.runner !== void 0) {
278
+ requiredString(value.executor, "runner", "observation receipt executor");
279
+ }
280
+ const role = requiredString(value, "comparison_role", "observation receipt");
281
+ if (!["before", "after", "standalone"].includes(role)) {
282
+ throw new Error(`Unsupported observation comparison role ${role}.`);
283
+ }
284
+ const targetKind = requiredString(value.target, "kind", "observation receipt target");
285
+ if (targetKind !== "url" && targetKind !== "preview") {
286
+ throw new Error(`Unsupported observation target kind ${targetKind}.`);
287
+ }
288
+ requiredString(value.target, "url", "observation receipt target");
289
+ if (targetKind === "preview") {
290
+ if (value.target.preview === void 0) {
291
+ throw new Error("Preview Observation target must include its Preview receipt.");
292
+ }
293
+ parseRiddlePreviewReceipt(value.target.preview);
294
+ }
295
+ if (!Array.isArray(value.artifacts)) throw new Error("Observation receipt artifacts must be an array.");
296
+ const artifactRoles = /* @__PURE__ */ new Set(["canonical_screenshot", "setup_screenshot", "data", "diagnostic", "artifact"]);
297
+ const artifacts = value.artifacts.map((artifact, index) => {
298
+ if (!isRecord(artifact)) throw new Error(`Observation receipt artifact ${index} must be an object.`);
299
+ requiredString(artifact, "name", `observation receipt artifact ${index}`);
300
+ const artifactRole = requiredString(artifact, "role", `observation receipt artifact ${index}`);
301
+ if (!artifactRoles.has(artifactRole)) {
302
+ throw new Error(`Unsupported observation artifact role ${artifactRole}.`);
303
+ }
304
+ return artifact;
305
+ });
306
+ if (value.canonical_screenshot !== void 0) {
307
+ if (!isRecord(value.canonical_screenshot) || value.canonical_screenshot.role !== "canonical_screenshot") {
308
+ throw new Error("Observation canonical_screenshot must have the canonical_screenshot role.");
309
+ }
310
+ const canonical = value.canonical_screenshot;
311
+ const included = artifacts.some((artifact) => artifact.role === "canonical_screenshot" && artifact.name === canonical.name && artifact.url === canonical.url && artifact.path === canonical.path);
312
+ if (!included) {
313
+ throw new Error("Observation canonical_screenshot must reference an artifact in the receipt.");
314
+ }
315
+ }
316
+ return value;
317
+ }
318
+
319
+ // src/change-proof.ts
320
+ var RIDDLE_PROOF_CHANGE_RECEIPT_V1_VERSION = "riddle-proof.change-receipt.v1";
321
+ var RIDDLE_PROOF_CHANGE_RECEIPT_VERSION = "riddle-proof.change-receipt.v2";
322
+ var RIDDLE_PROOF_HANDOFF_RECEIPT_VERSION = "riddle-proof.handoff-receipt.v1";
323
+ var DEFAULT_BEFORE_STATUSES = ["passed", "product_regression"];
324
+ var DEFAULT_AFTER_STATUSES = ["passed"];
325
+ function changeReceiptVerdict(status) {
326
+ if (status === "passed") return "mergeable";
327
+ if (status === "environment_blocked") return "environment_blocked";
328
+ if (status === "configuration_error") return "configuration_error";
329
+ if (status === "needs_human_review") return "needs_human_review";
330
+ if (status === "proof_insufficient") return "proof_insufficient";
331
+ return "not_mergeable";
332
+ }
333
+ function changeRecommendation(verdict) {
334
+ if (verdict === "mergeable") {
335
+ return {
336
+ merge_recommended: true,
337
+ verdict,
338
+ label: "Merge recommended",
339
+ reason: "The declared before/after delta contract passed with sufficient bound evidence."
340
+ };
341
+ }
342
+ const reason = verdict === "not_mergeable" ? "The declared change delta did not pass." : verdict === "environment_blocked" ? "The environment blocked reliable evidence collection." : verdict === "needs_human_review" ? "The evidence requires human review." : verdict === "configuration_error" ? "The change contract is invalid." : "The change proof did not produce sufficient bound evidence.";
343
+ return { merge_recommended: false, verdict, label: "Merge not recommended", reason };
344
+ }
345
+ function noShippingAuthorization() {
346
+ return { status: "not_granted", authorized: false, source: "none" };
347
+ }
348
+ function recommendationMatches(actual, expected) {
349
+ return actual?.merge_recommended === expected.merge_recommended && actual.verdict === expected.verdict && actual.label === expected.label && actual.reason === expected.reason;
350
+ }
351
+ function assertShippingAuthorizationConsistent(authorization, receiptName) {
352
+ if (!authorization) throw new Error(`${receiptName} shipping authorization is required.`);
353
+ if (authorization.status !== "not_granted" && authorization.status !== "granted") {
354
+ throw new Error(`${receiptName} shipping authorization status is invalid.`);
355
+ }
356
+ if (authorization.source !== "none" && authorization.source !== "human" && authorization.source !== "automation") {
357
+ throw new Error(`${receiptName} shipping authorization source is invalid.`);
358
+ }
359
+ const granted = authorization.status === "granted";
360
+ if (authorization.authorized !== granted) {
361
+ throw new Error(`${receiptName} shipping authorization status and authorized flag must agree.`);
362
+ }
363
+ if (authorization.source === "none" === granted) {
364
+ throw new Error(`${receiptName} shipping authorization source must identify an authorizer only when granted.`);
365
+ }
366
+ }
367
+ function observationArtifactMatches(actual, expected) {
368
+ if (!actual || !expected) return actual === expected;
369
+ return actual.name === expected.name && actual.role === expected.role && actual.url === expected.url && actual.path === expected.path && actual.kind === expected.kind && actual.content_type === expected.content_type && actual.source === expected.source;
370
+ }
371
+ function isRecord2(value) {
372
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
373
+ }
374
+ function migratedObservationFromLegacySide(side, legacy) {
375
+ const legacyArtifacts = legacy.artifacts.map((artifact) => ({
376
+ name: artifact.name,
377
+ url: artifact.url,
378
+ path: artifact.path,
379
+ source: artifact.source,
380
+ kind: artifact.kind,
381
+ role: legacy.screenshots[0] && artifactTarget(artifact) === artifactTarget(legacy.screenshots[0]) ? "canonical_screenshot" : artifact.kind === "image" ? "diagnostic" : artifact.kind === "data" ? "data" : "artifact"
382
+ }));
383
+ const canonicalScreenshot = legacyArtifacts.find((artifact) => artifact.role === "canonical_screenshot");
384
+ return {
385
+ version: "riddle-proof.observation-receipt.v1",
386
+ observation_id: `obs_${side}_migrated`,
387
+ comparison_role: side,
388
+ executor: { kind: "other", runner: "legacy-change-receipt" },
389
+ target: { kind: "url", url: legacy.source },
390
+ source: {},
391
+ captured_at: legacy.captured_at || (/* @__PURE__ */ new Date(0)).toISOString(),
392
+ artifacts: legacyArtifacts,
393
+ canonical_screenshot: canonicalScreenshot,
394
+ profile_summary: {
395
+ profile_name: legacy.profile_name,
396
+ status: legacy.status,
397
+ summary: legacy.summary,
398
+ route: legacy.route,
399
+ checks: legacy.checks
400
+ },
401
+ metadata: { migrated_from: RIDDLE_PROOF_CHANGE_RECEIPT_V1_VERSION }
402
+ };
403
+ }
404
+ function migrateRiddleProofChangeReceipt(legacy) {
405
+ const recommendation = changeRecommendation(legacy.verdict);
406
+ return {
407
+ version: RIDDLE_PROOF_CHANGE_RECEIPT_VERSION,
408
+ contract_name: legacy.contract_name,
409
+ profile_name: legacy.profile_name,
410
+ status: legacy.status,
411
+ verdict: legacy.verdict,
412
+ summary: legacy.summary,
413
+ before: migratedObservationFromLegacySide("before", legacy.before),
414
+ after: migratedObservationFromLegacySide("after", legacy.after),
415
+ groups: {
416
+ before: {
417
+ side: "before",
418
+ label: "before",
419
+ ok: legacy.before.status === "passed" || legacy.before.status === "product_regression",
420
+ profile_name: legacy.before.profile_name,
421
+ status: legacy.before.status,
422
+ required_status: DEFAULT_BEFORE_STATUSES,
423
+ summary: legacy.before.summary
424
+ },
425
+ after: {
426
+ side: "after",
427
+ label: "after",
428
+ ok: legacy.after.status === "passed",
429
+ profile_name: legacy.after.profile_name,
430
+ status: legacy.after.status,
431
+ required_status: DEFAULT_AFTER_STATUSES,
432
+ summary: legacy.after.summary
433
+ }
434
+ },
435
+ source_bindings: {
436
+ before: { side: "before", required: false, ok: true, status: "not_required" },
437
+ after: { side: "after", required: false, ok: true, status: "not_required" }
438
+ },
439
+ deltas: legacy.deltas,
440
+ recommendation,
441
+ shipping_authorization: noShippingAuthorization(),
442
+ proves: legacy.proves,
443
+ does_not_prove: legacy.does_not_prove,
444
+ metadata: legacy.metadata
445
+ };
446
+ }
447
+ function parseRiddleProofChangeReceipt(value) {
448
+ if (!isRecord2(value)) throw new Error("Change receipt must be an object.");
449
+ if (value.version === RIDDLE_PROOF_CHANGE_RECEIPT_V1_VERSION) {
450
+ return migrateRiddleProofChangeReceipt(value);
451
+ }
452
+ if (value.version !== RIDDLE_PROOF_CHANGE_RECEIPT_VERSION) {
453
+ throw new Error(`Unsupported Change receipt version ${String(value.version || "missing")}.`);
454
+ }
455
+ const receipt = value;
456
+ if (!RIDDLE_PROOF_PROFILE_STATUSES.includes(receipt.status)) {
457
+ throw new Error(`Unsupported Change receipt status ${String(receipt.status || "missing")}.`);
458
+ }
459
+ parseRiddleProofObservationReceipt(receipt.before);
460
+ parseRiddleProofObservationReceipt(receipt.after);
461
+ if (receipt.before.comparison_role !== "before" || receipt.after.comparison_role !== "after") {
462
+ throw new Error("Change receipt observations must preserve their before and after comparison roles.");
463
+ }
464
+ const expectedVerdict = changeReceiptVerdict(receipt.status);
465
+ if (receipt.verdict !== expectedVerdict) {
466
+ throw new Error("Change receipt verdict must match its evaluated status.");
467
+ }
468
+ if (!recommendationMatches(receipt.recommendation, changeRecommendation(expectedVerdict))) {
469
+ throw new Error("Change receipt recommendation must be derived from the Change receipt verdict.");
470
+ }
471
+ assertShippingAuthorizationConsistent(receipt.shipping_authorization, "Change receipt");
472
+ return receipt;
473
+ }
474
+ function createRiddleProofHandoffReceipt(changeReceipt, options = {}) {
475
+ const change = parseRiddleProofChangeReceipt(changeReceipt);
476
+ const authorization = options.shipping_authorization || change.shipping_authorization;
477
+ assertShippingAuthorizationConsistent(authorization, "Handoff receipt");
478
+ return {
479
+ version: RIDDLE_PROOF_HANDOFF_RECEIPT_VERSION,
480
+ change,
481
+ verdict: change.verdict,
482
+ recommendation: change.recommendation,
483
+ shipping_authorization: authorization,
484
+ canonical_pair: {
485
+ before: change.before.canonical_screenshot,
486
+ after: change.after.canonical_screenshot
487
+ },
488
+ created_at: options.created_at || (/* @__PURE__ */ new Date()).toISOString()
489
+ };
490
+ }
491
+ function parseRiddleProofHandoffReceipt(value) {
492
+ if (!isRecord2(value) || value.version !== RIDDLE_PROOF_HANDOFF_RECEIPT_VERSION) {
493
+ throw new Error(`Unsupported Handoff receipt version ${String(isRecord2(value) ? value.version || "missing" : "missing")}.`);
494
+ }
495
+ const receipt = value;
496
+ if (!Number.isFinite(Date.parse(receipt.created_at))) {
497
+ throw new Error("Handoff receipt created_at must be a valid timestamp.");
498
+ }
499
+ const change = parseRiddleProofChangeReceipt(receipt.change);
500
+ if (receipt.verdict !== change.verdict || !recommendationMatches(receipt.recommendation, change.recommendation)) {
501
+ throw new Error("Handoff receipt must preserve the Change receipt verdict and recommendation.");
502
+ }
503
+ if (!observationArtifactMatches(receipt.canonical_pair?.before, change.before.canonical_screenshot) || !observationArtifactMatches(receipt.canonical_pair?.after, change.after.canonical_screenshot)) {
504
+ throw new Error("Handoff receipt canonical pair must project the Change receipt observations.");
505
+ }
506
+ assertShippingAuthorizationConsistent(receipt.shipping_authorization, "Handoff receipt");
507
+ return receipt;
508
+ }
509
+ function artifactTarget(artifact) {
510
+ return artifact.url || artifact.path;
511
+ }
512
+
205
513
  // src/pr-comment.ts
206
514
  var RIDDLE_PROOF_PR_COMMENT_MARKER = "<!-- riddle-proof:pr-comment:v1 -->";
207
515
  function asRecord2(value) {
@@ -277,6 +585,34 @@ function collectProfileArtifacts(result) {
277
585
  }
278
586
  return artifacts;
279
587
  }
588
+ function collectChangeReceiptArtifacts(result) {
589
+ const artifacts = [];
590
+ const seen = /* @__PURE__ */ new Set();
591
+ const collectSide = (sideName, side) => {
592
+ const candidates = [
593
+ side.canonical_screenshot,
594
+ ...asArray(side.screenshots),
595
+ ...asArray(side.artifacts)
596
+ ].filter(Boolean);
597
+ for (const [index, item] of candidates.entries()) {
598
+ const artifact = asRecord2(item);
599
+ const url = stringValue2(artifact.url) || stringValue2(artifact.path);
600
+ if (!url || seen.has(url)) continue;
601
+ seen.add(url);
602
+ const fallbackName = `${sideName}-artifact-${index + 1}`;
603
+ const name = `${sideName}/${artifactDisplayName(artifact.name, fallbackName)}`;
604
+ artifacts.push({
605
+ name,
606
+ url,
607
+ kind: artifactKind(name, url),
608
+ size_bytes: numberValue2(artifact.size_bytes) ?? numberValue2(artifact.size)
609
+ });
610
+ }
611
+ };
612
+ collectSide("before", asRecord2(result.before));
613
+ collectSide("after", asRecord2(result.after));
614
+ return artifacts;
615
+ }
280
616
  function mergeArtifacts(...artifactLists) {
281
617
  const artifacts = [];
282
618
  const seen = /* @__PURE__ */ new Set();
@@ -332,6 +668,33 @@ function profilePageSummaries(result, counts) {
332
668
  failed: counts.failed
333
669
  }];
334
670
  }
671
+ function changeReceiptCheckCounts(result) {
672
+ const deltas = asArray(result.deltas);
673
+ if (!deltas.length) return void 0;
674
+ let passed = 0;
675
+ let failed = 0;
676
+ for (const item of deltas) {
677
+ const status = stringValue2(asRecord2(item).status);
678
+ if (status === "passed") passed += 1;
679
+ if (status === "failed" || status === "proof_insufficient" || status === "configuration_error") failed += 1;
680
+ }
681
+ return { passed, failed };
682
+ }
683
+ function changeReceiptPageSummaries(result) {
684
+ const pages = [];
685
+ for (const sideName of ["before", "after"]) {
686
+ const side = asRecord2(result[sideName]);
687
+ if (!Object.keys(side).length) continue;
688
+ const profileSummary = asRecord2(side.profile_summary);
689
+ const checks = Object.keys(asRecord2(side.checks)).length ? asRecord2(side.checks) : asRecord2(profileSummary.checks);
690
+ pages.push({
691
+ route: firstStringValue2(asRecord2(side.target).url, side.source, profileSummary.profile_name, side.profile_name) || sideName,
692
+ passed: numberValue2(checks.passed) ?? 0,
693
+ failed: numberValue2(checks.failed) ?? 0
694
+ });
695
+ }
696
+ return pages;
697
+ }
335
698
  function summarizeExplicitChecks(value) {
336
699
  let passed = 0;
337
700
  let failed = 0;
@@ -387,11 +750,27 @@ function isProfileResult(result) {
387
750
  const version = stringValue2(result.version);
388
751
  return version === "riddle-proof.profile-result.v1" || version === "riddle-proof.profile-compact-result.v1";
389
752
  }
753
+ function isChangeReceiptResult(result) {
754
+ const version = stringValue2(result.version);
755
+ return version === RIDDLE_PROOF_CHANGE_RECEIPT_V1_VERSION || version === RIDDLE_PROOF_CHANGE_RECEIPT_VERSION;
756
+ }
757
+ function isHandoffReceiptResult(result) {
758
+ return stringValue2(result.version) === RIDDLE_PROOF_HANDOFF_RECEIPT_VERSION;
759
+ }
760
+ function isChangeResult(result) {
761
+ const version = stringValue2(result.version);
762
+ return version === "riddle-proof.change-result.v1" || version === "riddle-proof.change-compact-result.v1";
763
+ }
390
764
  function summarizeRiddleProofPrComment(input) {
391
765
  const runResponse = asRecord2(input.runResponse);
392
- const result = asRecord2(input.result);
766
+ const inputResult = asRecord2(input.result);
767
+ const handoffReceipt = isHandoffReceiptResult(inputResult) ? parseRiddleProofHandoffReceipt(inputResult) : void 0;
768
+ const parsedChangeReceipt = handoffReceipt?.change || (isChangeReceiptResult(inputResult) ? parseRiddleProofChangeReceipt(inputResult) : void 0);
769
+ const result = parsedChangeReceipt ? asRecord2(parsedChangeReceipt) : inputResult;
393
770
  const proofResult = asRecord2(runResponse.proofResult);
394
771
  const profileResult = isProfileResult(result);
772
+ const changeReceiptResult = Boolean(parsedChangeReceipt);
773
+ const changeResult = changeReceiptResult || isChangeResult(result);
395
774
  const profileRiddle = asRecord2(result.riddle);
396
775
  const preview = asRecord2(runResponse.preview);
397
776
  const resultRunCard = asRecord2(result.run_card);
@@ -400,15 +779,18 @@ function summarizeRiddleProofPrComment(input) {
400
779
  const resultRaw = asRecord2(result.raw);
401
780
  const rawDetails = asRecord2(resultRaw.details);
402
781
  const profileChecks = profileResult ? profileCheckCounts(result) : void 0;
782
+ const changeChecks = changeResult ? changeReceiptCheckCounts(result) : void 0;
403
783
  const artifacts = mergeArtifacts(
404
784
  collectArtifacts(runResponse),
405
- profileResult ? collectProfileArtifacts(result) : []
785
+ profileResult ? collectProfileArtifacts(result) : [],
786
+ changeReceiptResult ? collectChangeReceiptArtifacts(result) : []
406
787
  );
407
- const pages = profileResult ? profilePageSummaries(result, profileChecks) : pageSummaries(result);
788
+ const pages = changeReceiptResult ? changeReceiptPageSummaries(result) : profileResult ? profilePageSummaries(result, profileChecks) : pageSummaries(result);
408
789
  const checkSource = { ...result };
409
790
  delete checkSource.ok;
410
791
  const nestedChecks = summarizeExplicitChecks(checkSource);
411
- const ok = booleanValue2(result.ok) ?? booleanValue2(runResponse.ok) ?? null;
792
+ const resultStatus = firstStringValue2(result.status, stopCondition.status);
793
+ const ok = changeReceiptResult ? parsedChangeReceipt?.recommendation.merge_recommended ?? false : changeResult ? resultStatus === "passed" : booleanValue2(result.ok) ?? booleanValue2(runResponse.ok) ?? null;
412
794
  const checkpointSummary = checkpointSummaryFrom2(
413
795
  result.checkpoint_summary,
414
796
  stopCondition.checkpoint_summary,
@@ -416,19 +798,19 @@ function summarizeRiddleProofPrComment(input) {
416
798
  rawDetails.checkpoint_summary,
417
799
  proofResult.checkpoint_summary
418
800
  );
419
- const publicState = summarizeRiddleProofPublicState({
801
+ const publicState = changeReceiptResult ? void 0 : summarizeRiddleProofPublicState({
420
802
  ...result,
421
- status: firstStringValue2(result.status, stopCondition.status),
803
+ status: resultStatus,
422
804
  checkpoint_summary: checkpointSummary || result.checkpoint_summary
423
805
  });
424
- const mergeRecommendation = riddleProofPublicStateMergeRecommendation(
806
+ const mergeRecommendation = publicState ? riddleProofPublicStateMergeRecommendation(
425
807
  publicState,
426
808
  firstStringValue2(result.merge_recommendation, stopCondition.merge_recommendation, resultRaw.merge_recommendation)
427
- );
809
+ ) : void 0;
428
810
  return {
429
811
  ok,
430
812
  status: firstStringValue2(proofResult.status, profileRiddle.status),
431
- result_status: publicState.status,
813
+ result_status: changeResult ? resultStatus : publicState?.status,
432
814
  job_id: firstStringValue2(proofResult.job_id, profileRiddle.job_id),
433
815
  duration_ms: numberValue2(proofResult.duration_ms) ?? numberValue2(profileRiddle.elapsed_ms),
434
816
  proof_url: stringValue2(runResponse.proofUrl),
@@ -436,17 +818,17 @@ function summarizeRiddleProofPrComment(input) {
436
818
  preview_url: stringValue2(preview.preview_url) || stringValue2(preview.url),
437
819
  preview_publish_recovered: booleanValue2(preview.publish_recovered),
438
820
  preview_publish_error: stringValue2(preview.publish_error),
439
- ship_held: publicState.ship_held,
440
- shipping_disabled: publicState.shipping_disabled,
441
- ship_authorized: publicState.ship_authorized,
442
- merge_ready: publicState.merge_ready,
443
- sync_allowed: publicState.sync_allowed,
821
+ ship_held: publicState?.ship_held,
822
+ shipping_disabled: publicState?.shipping_disabled,
823
+ ship_authorized: changeReceiptResult ? parsedChangeReceipt?.shipping_authorization.authorized : publicState?.ship_authorized,
824
+ merge_ready: publicState?.merge_ready,
825
+ sync_allowed: publicState?.sync_allowed,
444
826
  proof_decision: firstStringValue2(result.proof_decision, stopCondition.proof_decision, resultRaw.proof_decision),
445
- merge_recommendation: mergeRecommendation,
827
+ merge_recommendation: changeReceiptResult ? parsedChangeReceipt?.recommendation.label : mergeRecommendation,
446
828
  checkpoint_summary: checkpointSummary,
447
829
  public_state: publicState,
448
- passed_checks: profileChecks?.passed ?? nestedChecks.passed,
449
- failed_checks: profileChecks?.failed ?? nestedChecks.failed,
830
+ passed_checks: changeChecks?.passed ?? profileChecks?.passed ?? nestedChecks.passed,
831
+ failed_checks: changeChecks?.failed ?? profileChecks?.failed ?? nestedChecks.failed,
450
832
  pages,
451
833
  artifacts,
452
834
  primary_image: selectPrimaryImage(artifacts)
@@ -510,7 +892,88 @@ function checkpointSummaryLine(summary) {
510
892
  summary.latest_decision ? `latest decision \`${summary.latest_decision}\`` : ""
511
893
  ].filter(Boolean).join("; ");
512
894
  }
895
+ function markdownTableValue(value) {
896
+ return String(value ?? "").replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
897
+ }
898
+ function observationArtifactTarget(artifact) {
899
+ return artifact?.url || artifact?.path;
900
+ }
901
+ function canonicalScreenshotCell(artifact, fallback) {
902
+ const target = observationArtifactTarget(artifact);
903
+ if (!artifact || !target) return fallback;
904
+ if (artifact.url) return `![${artifact.name.replace(/\]/g, "\\]")}](${artifact.url})`;
905
+ return markdownTableValue(artifact.path || artifact.name);
906
+ }
907
+ function shortRevision(value) {
908
+ return value ? value.slice(0, 12) : "not recorded";
909
+ }
910
+ function buildRiddleProofHandoffPrCommentMarkdown(handoff, input = {}) {
911
+ const receipt = parseRiddleProofHandoffReceipt(handoff);
912
+ const change = receipt.change;
913
+ const beforeStatus = change.before.profile_summary?.status || "not recorded";
914
+ const afterStatus = change.after.profile_summary?.status || "not recorded";
915
+ const beforeBaseline = change.groups.before.ok ? "matched baseline" : "baseline mismatch";
916
+ const lines = [
917
+ RIDDLE_PROOF_PR_COMMENT_MARKER,
918
+ `## ${input.title?.trim() || "Riddle Change Proof"}`,
919
+ "",
920
+ `**Result:** ${receipt.recommendation.label}`,
921
+ `**Evidence verdict:** \`${receipt.verdict}\``,
922
+ `**Shipping authorization:** ${receipt.shipping_authorization.status}`,
923
+ `**Contract:** ${change.contract_name}`
924
+ ];
925
+ if (input.goal?.trim()) lines.push(`**Goal:** ${input.goal.trim()}`);
926
+ if (input.successCriteria?.trim()) lines.push(`**Success criteria:** ${input.successCriteria.trim()}`);
927
+ lines.push("", change.summary, "", "### Canonical Before / After", "");
928
+ lines.push("| Before | After |");
929
+ lines.push("| --- | --- |");
930
+ lines.push(`| ${beforeBaseline}: \`${markdownTableValue(beforeStatus)}\` | \`${markdownTableValue(afterStatus)}\` |`);
931
+ lines.push(`| ${canonicalScreenshotCell(receipt.canonical_pair.before, "No canonical before screenshot")} | ${canonicalScreenshotCell(receipt.canonical_pair.after, "No canonical after screenshot")} |`);
932
+ lines.push("", "### Source Binding", "");
933
+ lines.push("| Side | Target | Git revision | Binding | Preview | Digest |");
934
+ lines.push("| --- | --- | --- | --- | --- | --- |");
935
+ for (const side of ["before", "after"]) {
936
+ const observation = change[side];
937
+ const binding = change.source_bindings[side];
938
+ lines.push(`| ${side} | ${markdownTableValue(observation.target.url)} | \`${shortRevision(binding.observed_git_revision || observation.source.git_revision)}\` | ${binding.status} | ${markdownTableValue(binding.preview_id || observation.target.preview?.preview_id || "not required")} | \`${markdownTableValue(binding.content_digest || observation.target.preview?.content_digest || "not required")}\` |`);
939
+ }
940
+ lines.push("", "### Declared Delta", "");
941
+ lines.push("| Measurement | Status | Before | After |");
942
+ lines.push("| --- | --- | --- | --- |");
943
+ for (const delta of change.deltas) {
944
+ lines.push(`| ${markdownTableValue(delta.label)} | ${delta.status} | ${markdownTableValue(delta.before_observed)} | ${markdownTableValue(delta.after_observed)} |`);
945
+ }
946
+ if (change.proves.length) {
947
+ lines.push("", "### What This Proves", "");
948
+ for (const claim of change.proves) lines.push(`- ${claim}`);
949
+ }
950
+ if (change.does_not_prove.length) {
951
+ lines.push("", "### Limits", "");
952
+ for (const claim of change.does_not_prove) lines.push(`- ${claim}`);
953
+ }
954
+ const linkedArtifacts = [
955
+ ...change.before.artifacts.map((artifact) => ({ side: "before", artifact })),
956
+ ...change.after.artifacts.map((artifact) => ({ side: "after", artifact }))
957
+ ].filter(({ artifact }) => Boolean(artifact.url));
958
+ if (linkedArtifacts.length) {
959
+ lines.push("", "### Artifacts", "");
960
+ for (const { side, artifact } of linkedArtifacts.slice(0, 24)) {
961
+ lines.push(`- ${side}: ${markdownLink(artifact.name, artifact.url || "")}`);
962
+ }
963
+ }
964
+ lines.push("", input.source?.trim() ? `_Source: ${input.source.trim()}_` : "_Rendered from the Handoff receipt without recomputing its verdict._");
965
+ return `${lines.join("\n").trim()}
966
+ `;
967
+ }
513
968
  function buildRiddleProofPrCommentMarkdown(input) {
969
+ const result = asRecord2(input.result);
970
+ if (isHandoffReceiptResult(result)) {
971
+ return buildRiddleProofHandoffPrCommentMarkdown(parseRiddleProofHandoffReceipt(result), input);
972
+ }
973
+ if (isChangeReceiptResult(result)) {
974
+ const handoff = createRiddleProofHandoffReceipt(parseRiddleProofChangeReceipt(result));
975
+ return buildRiddleProofHandoffPrCommentMarkdown(handoff, input);
976
+ }
514
977
  const summary = summarizeRiddleProofPrComment(input);
515
978
  const title = input.title?.trim() || "Riddle Proof Evidence";
516
979
  const lines = [
@@ -580,6 +1043,7 @@ function buildRiddleProofPrCommentMarkdown(input) {
580
1043
  // Annotate the CommonJS export names for ESM import in node:
581
1044
  0 && (module.exports = {
582
1045
  RIDDLE_PROOF_PR_COMMENT_MARKER,
1046
+ buildRiddleProofHandoffPrCommentMarkdown,
583
1047
  buildRiddleProofPrCommentMarkdown,
584
1048
  summarizeRiddleProofPrComment
585
1049
  });
@@ -1,4 +1,8 @@
1
1
  import { RiddleProofPublicStateSummary } from './public-state.cjs';
2
+ import { RiddleProofHandoffReceipt } from './change-proof.cjs';
3
+ import './profile.cjs';
4
+ import './types.cjs';
5
+ import './receipts.cjs';
2
6
 
3
7
  declare const RIDDLE_PROOF_PR_COMMENT_MARKER = "<!-- riddle-proof:pr-comment:v1 -->";
4
8
  type RiddleProofPrCommentArtifactKind = "image" | "data" | "artifact";
@@ -58,6 +62,7 @@ interface RiddleProofPrCommentInput {
58
62
  source?: string;
59
63
  }
60
64
  declare function summarizeRiddleProofPrComment(input: RiddleProofPrCommentInput): RiddleProofPrCommentSummary;
65
+ declare function buildRiddleProofHandoffPrCommentMarkdown(handoff: RiddleProofHandoffReceipt, input?: Omit<RiddleProofPrCommentInput, "result">): string;
61
66
  declare function buildRiddleProofPrCommentMarkdown(input: RiddleProofPrCommentInput): string;
62
67
 
63
- export { RIDDLE_PROOF_PR_COMMENT_MARKER, type RiddleProofPrCommentArtifact, type RiddleProofPrCommentArtifactKind, type RiddleProofPrCommentCheckpointSummary, type RiddleProofPrCommentInput, type RiddleProofPrCommentPageSummary, type RiddleProofPrCommentSummary, buildRiddleProofPrCommentMarkdown, summarizeRiddleProofPrComment };
68
+ export { RIDDLE_PROOF_PR_COMMENT_MARKER, type RiddleProofPrCommentArtifact, type RiddleProofPrCommentArtifactKind, type RiddleProofPrCommentCheckpointSummary, type RiddleProofPrCommentInput, type RiddleProofPrCommentPageSummary, type RiddleProofPrCommentSummary, buildRiddleProofHandoffPrCommentMarkdown, buildRiddleProofPrCommentMarkdown, summarizeRiddleProofPrComment };
@@ -1,4 +1,8 @@
1
1
  import { RiddleProofPublicStateSummary } from './public-state.js';
2
+ import { RiddleProofHandoffReceipt } from './change-proof.js';
3
+ import './profile.js';
4
+ import './types.js';
5
+ import './receipts.js';
2
6
 
3
7
  declare const RIDDLE_PROOF_PR_COMMENT_MARKER = "<!-- riddle-proof:pr-comment:v1 -->";
4
8
  type RiddleProofPrCommentArtifactKind = "image" | "data" | "artifact";
@@ -58,6 +62,7 @@ interface RiddleProofPrCommentInput {
58
62
  source?: string;
59
63
  }
60
64
  declare function summarizeRiddleProofPrComment(input: RiddleProofPrCommentInput): RiddleProofPrCommentSummary;
65
+ declare function buildRiddleProofHandoffPrCommentMarkdown(handoff: RiddleProofHandoffReceipt, input?: Omit<RiddleProofPrCommentInput, "result">): string;
61
66
  declare function buildRiddleProofPrCommentMarkdown(input: RiddleProofPrCommentInput): string;
62
67
 
63
- export { RIDDLE_PROOF_PR_COMMENT_MARKER, type RiddleProofPrCommentArtifact, type RiddleProofPrCommentArtifactKind, type RiddleProofPrCommentCheckpointSummary, type RiddleProofPrCommentInput, type RiddleProofPrCommentPageSummary, type RiddleProofPrCommentSummary, buildRiddleProofPrCommentMarkdown, summarizeRiddleProofPrComment };
68
+ export { RIDDLE_PROOF_PR_COMMENT_MARKER, type RiddleProofPrCommentArtifact, type RiddleProofPrCommentArtifactKind, type RiddleProofPrCommentCheckpointSummary, type RiddleProofPrCommentInput, type RiddleProofPrCommentPageSummary, type RiddleProofPrCommentSummary, buildRiddleProofHandoffPrCommentMarkdown, buildRiddleProofPrCommentMarkdown, summarizeRiddleProofPrComment };
@@ -1,12 +1,17 @@
1
1
  import {
2
2
  RIDDLE_PROOF_PR_COMMENT_MARKER,
3
+ buildRiddleProofHandoffPrCommentMarkdown,
3
4
  buildRiddleProofPrCommentMarkdown,
4
5
  summarizeRiddleProofPrComment
5
- } from "./chunk-CWRIXP5H.js";
6
+ } from "./chunk-7N6X54WG.js";
7
+ import "./chunk-6VFS2JFR.js";
8
+ import "./chunk-MEVVL4TI.js";
9
+ import "./chunk-FCSJZBC5.js";
6
10
  import "./chunk-ZAR7BWMN.js";
7
11
  import "./chunk-MLKGABMK.js";
8
12
  export {
9
13
  RIDDLE_PROOF_PR_COMMENT_MARKER,
14
+ buildRiddleProofHandoffPrCommentMarkdown,
10
15
  buildRiddleProofPrCommentMarkdown,
11
16
  summarizeRiddleProofPrComment
12
17
  };