@riddledc/riddle-proof 0.8.77 → 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 +65 -3
  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 +744 -85
  7. package/dist/change-proof.d.cts +100 -8
  8. package/dist/change-proof.d.ts +100 -8
  9. package/dist/change-proof.js +15 -1
  10. package/dist/{chunk-B2DP2LET.js → chunk-5IFZSUPF.js} +52 -13
  11. package/dist/chunk-6VFS2JFR.js +886 -0
  12. package/dist/{chunk-IY4W6STC.js → chunk-7N6X54WG.js} +116 -17
  13. package/dist/{chunk-GG2D3MFZ.js → chunk-FCSJZBC5.js} +26 -1
  14. package/dist/{chunk-H25IDX76.js → chunk-HSGZV2NK.js} +206 -35
  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 +2009 -1058
  19. package/dist/cli.js +7 -6
  20. package/dist/index.cjs +932 -122
  21. package/dist/index.d.cts +4 -3
  22. package/dist/index.d.ts +4 -3
  23. package/dist/index.js +42 -14
  24. package/dist/pr-comment.cjs +416 -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-BILL3UC2.js +0 -488
@@ -3,15 +3,27 @@ import {
3
3
  createRiddleApiClient,
4
4
  isTerminalRiddleJobStatus,
5
5
  parseRiddleViewport
6
- } from "./chunk-B2DP2LET.js";
6
+ } from "./chunk-5IFZSUPF.js";
7
7
  import {
8
8
  RIDDLE_PROOF_PR_COMMENT_MARKER,
9
9
  buildRiddleProofPrCommentMarkdown,
10
10
  summarizeRiddleProofPrComment
11
- } from "./chunk-IY4W6STC.js";
11
+ } from "./chunk-7N6X54WG.js";
12
12
  import {
13
13
  suggestRiddleProofProfileChecks
14
- } from "./chunk-UE4I7RTI.js";
14
+ } from "./chunk-RQPCKRKT.js";
15
+ import {
16
+ assessRiddleProofChange,
17
+ createRiddleProofChangeReceipt,
18
+ createRiddleProofHandoffReceipt,
19
+ riddleProofChangeReceiptHtml,
20
+ riddleProofChangeReceiptMarkdown
21
+ } from "./chunk-6VFS2JFR.js";
22
+ import {
23
+ createRiddleProofObservationReceipt,
24
+ parseRiddlePreviewReceipt,
25
+ parseRiddleProofObservationReceipt
26
+ } from "./chunk-MEVVL4TI.js";
15
27
  import {
16
28
  RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
17
29
  applyRiddleProofProfileArtifactCompleteness,
@@ -29,13 +41,7 @@ import {
29
41
  profileStatusExitCode,
30
42
  resolveRiddleProofProfileTargetUrl,
31
43
  resolveRiddleProofProfileTimeoutSec
32
- } from "./chunk-GG2D3MFZ.js";
33
- import {
34
- assessRiddleProofChange,
35
- createRiddleProofChangeReceipt,
36
- riddleProofChangeReceiptHtml,
37
- riddleProofChangeReceiptMarkdown
38
- } from "./chunk-BILL3UC2.js";
44
+ } from "./chunk-FCSJZBC5.js";
39
45
  import {
40
46
  createCodexExecAgentAdapter,
41
47
  runCodexExecAgentDoctor
@@ -64,8 +70,14 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
64
70
  "balancePreflight",
65
71
  "baseUrl",
66
72
  "afterResult",
73
+ "afterObservation",
74
+ "afterPreviewReceipt",
75
+ "afterSourceRevision",
67
76
  "afterUrl",
68
77
  "beforeResult",
78
+ "beforeObservation",
79
+ "beforePreviewReceipt",
80
+ "beforeSourceRevision",
69
81
  "beforeUrl",
70
82
  "candidateJson",
71
83
  "candidatesJson",
@@ -119,6 +131,7 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
119
131
  "pack",
120
132
  "packFile",
121
133
  "profile",
134
+ "previewReceipt",
122
135
  "proofDir",
123
136
  "pr",
124
137
  "progressEveryMs",
@@ -142,6 +155,9 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
142
155
  "scriptFile",
143
156
  "selectors",
144
157
  "sourceKind",
158
+ "sourceDirty",
159
+ "sourceRepository",
160
+ "sourceRevision",
145
161
  "splitViewports",
146
162
  "stateDir",
147
163
  "statePath",
@@ -173,16 +189,16 @@ function usage() {
173
189
  " riddle-proof-loop respond --state-path <path> --response-json <file|json|->",
174
190
  " riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
175
191
  " riddle-proof-loop status --state-path <path>",
176
- " riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--base-url <base-url>] [--runner riddle] [--viewport-name <name[,name...]>] [--strict true|false; default false] [--split-viewports true|false; default false] [--balance-preflight true|false; default true] [--poll-attempts n] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json] [--quiet]",
192
+ " riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--preview-receipt <file|json>] [--source-revision <sha>] [--runner riddle] [--viewport-name <name[,name...]>] [--strict true|false; default false] [--split-viewports true|false; default false] [--balance-preflight true|false; default true] [--poll-attempts n] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json] [--quiet]",
177
193
  " riddle-proof-loop run-profile aggregate --profile <file|json|-> --url <base-url> [--base-url <base-url>] --input-dir <dir>|--inputs <path[,path...]> [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json]",
178
194
  " riddle-proof-loop run-profile recover --profile <file|json|-> --url <base-url> [--base-url <base-url>] --job <job-id> [--viewport-name <name[,name...]>] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json]",
179
- " riddle-proof-loop run-change-proof --profile <file|json|-> --change-contract <file|json|-> (--before-url <url> --after-url <url> | --before-result <file> --after-result <file>) [--viewport-name <name[,name...]>] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json]",
195
+ " riddle-proof-loop run-change-proof --profile <file|json|-> --change-contract <file|json|-> (--before-url <url> --after-url <url> | --before-result <file> --after-result <file> | --before-observation <file> --after-observation <file>) [--before-preview-receipt <file|json>] [--after-preview-receipt <file|json>] [--before-source-revision <sha>] [--after-source-revision <sha>] [--viewport-name <name[,name...]>] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json]",
180
196
  " riddle-proof-loop profile-suggest --route /path|--url <url> [--changed-files a,b] [--selectors .a,.b] [--changed-text-json <file|json|->] [--format json|profile]",
181
197
  " riddle-proof-loop regression-pack run [--pack oc-flow-regression|--pack-file <file>] [--local-core true|false; default true] [--hosted-riddle true|false; default false] [--format json|markdown|compact-json; default json] [--output <dir>|--output-dir <dir>]",
182
198
  " riddle-proof-loop pr-comment --proof-dir <dir>|--run-response <file> [--result-json <file>] --pr <number|url> [--repo owner/name] [--dry-run] [--body-file <file>] [--comment-mode update|append]",
183
199
  " riddle-proof-loop profile-body-assertions --artifact <file|url|-> --candidates-json <file|json|-> [--required-json <file|json|->] [--format json|body-contains]",
184
200
  " riddle-proof-loop profile-http-status-preflight --profile <file|json|-> --url <base-url> [--format json|summary]",
185
- " riddle-proof-loop riddle-preview-deploy <build-dir> <label> [--framework spa|static] [--quiet]",
201
+ " riddle-proof-loop riddle-preview-deploy <build-dir> <label> [--framework spa|static] [--output <dir>] [--quiet]",
186
202
  " riddle-proof-loop riddle-server-preview <directory> --script-file <file> [--path /route] [--wait-for-selector selector]",
187
203
  " riddle-proof-loop riddle-run-script --url <url> --script-file <file> [--viewport 1280x720] [--strict true|false]",
188
204
  " riddle-proof-loop riddle-poll <job-id> [--wait] [--attempts n] [--quiet]",
@@ -342,6 +358,7 @@ function compactRunProfileResult(result, options) {
342
358
  output_dir: outputDir,
343
359
  output_files: outputDir ? {
344
360
  profile_result: "profile-result.json",
361
+ observation_receipt: "observation-receipt.json",
345
362
  summary: "summary.md",
346
363
  proof_json: result.evidence ? "proof.json" : void 0,
347
364
  console: result.evidence?.console ? "console.json" : void 0,
@@ -970,12 +987,13 @@ function formatByteCount(bytes) {
970
987
  }
971
988
  function riddlePollProgressLine(snapshot) {
972
989
  const submittedAt = snapshot.submitted_at || "not-submitted";
973
- const queuePart = snapshot.running_without_submission ? ` waiting_for_worker_submit=${formatPollDuration(snapshot.pre_submission_elapsed_ms)} worker_wake=possible${snapshot.queue_elapsed_ms !== null ? ` queued_for=${formatPollDuration(snapshot.queue_elapsed_ms)}` : ""}` : snapshot.queue_elapsed_ms !== null ? ` queue=${formatPollDuration(snapshot.queue_elapsed_ms)}` : "";
990
+ const queuePart = snapshot.active_execution ? `${snapshot.queue_elapsed_ms !== null ? ` queue=${formatPollDuration(snapshot.queue_elapsed_ms)}` : ""} active_execution=${formatPollDuration(snapshot.elapsed_ms)}` : snapshot.running_without_submission ? ` waiting_for_worker_claim=${formatPollDuration(snapshot.pre_submission_elapsed_ms)} worker_wake=possible${snapshot.queue_elapsed_ms !== null ? ` queued_for=${formatPollDuration(snapshot.queue_elapsed_ms)}` : ""}` : snapshot.queue_elapsed_ms !== null ? ` queue=${formatPollDuration(snapshot.queue_elapsed_ms)}` : "";
974
991
  const terminalPart = snapshot.terminal ? " terminal=true" : "";
975
992
  return [
976
993
  "[riddle-poll]",
977
994
  snapshot.job_id,
978
995
  `status=${snapshot.status || "unknown"}`,
996
+ `phase=${snapshot.phase || "unknown"}`,
979
997
  `attempt=${snapshot.attempt}/${snapshot.attempts}`,
980
998
  `elapsed=${formatPollDuration(snapshot.elapsed_ms)}`,
981
999
  `submitted_at=${submittedAt}${queuePart}${terminalPart}`
@@ -3913,10 +3931,48 @@ function profileHttpStatusSummaryMarkdown(result) {
3913
3931
  }
3914
3932
  return lines;
3915
3933
  }
3916
- function writeProfileOutput(outputDir, result) {
3934
+ function profileResultTargetUrl(result) {
3935
+ const legacyRoute = result.route;
3936
+ return result.route.observed || result.route.requested || legacyRoute.url || result.evidence?.target_url || "unknown";
3937
+ }
3938
+ function profileObservationForOutput(result, outputDir, options) {
3939
+ if (options.observation) return options.observation;
3940
+ const preview = options.previewReceipt;
3941
+ const targetUrl = profileResultTargetUrl(result);
3942
+ return createRiddleProofObservationReceipt({
3943
+ comparison_role: options.comparisonRole || "standalone",
3944
+ executor: result.runner === "local-playwright" ? { kind: "local_playwright", runner: result.runner } : {
3945
+ kind: "riddle_hosted",
3946
+ runner: result.runner,
3947
+ job_id: result.riddle?.job_id,
3948
+ worker_id: typeof result.riddle?.execution?.worker_id === "string" ? result.riddle.execution.worker_id : void 0
3949
+ },
3950
+ target: preview ? { kind: "preview", url: targetUrl, preview } : { kind: "url", url: targetUrl },
3951
+ source: options.source || preview?.source,
3952
+ profile_result: result,
3953
+ publication: { kind: "local", path: outputDir },
3954
+ execution: result.riddle?.execution
3955
+ });
3956
+ }
3957
+ function profileOutputOptionsForCli(options) {
3958
+ const previewInput = optionString(options, "previewReceipt");
3959
+ const previewReceipt = previewInput ? parseRiddlePreviewReceipt(readJsonValue(previewInput, "--preview-receipt")) : void 0;
3960
+ const sourceDirty = optionBoolean(options, "sourceDirty");
3961
+ const source = {
3962
+ ...previewReceipt?.source || {},
3963
+ ...optionString(options, "sourceRevision") ? { git_revision: optionString(options, "sourceRevision") } : {},
3964
+ ...optionString(options, "sourceRepository") ? { repository: optionString(options, "sourceRepository") } : {},
3965
+ ...typeof sourceDirty === "boolean" ? { dirty: sourceDirty } : {}
3966
+ };
3967
+ return { previewReceipt, source };
3968
+ }
3969
+ function writeProfileOutput(outputDir, result, options = {}) {
3917
3970
  if (!outputDir) return;
3918
3971
  mkdirSync(outputDir, { recursive: true });
3919
3972
  writeFileSync(path.join(outputDir, "profile-result.json"), `${JSON.stringify(result, null, 2)}
3973
+ `);
3974
+ const observation = profileObservationForOutput(result, outputDir, options);
3975
+ writeFileSync(path.join(outputDir, "observation-receipt.json"), `${JSON.stringify(observation, null, 2)}
3920
3976
  `);
3921
3977
  writeFileSync(path.join(outputDir, "summary.md"), profileResultMarkdown(result));
3922
3978
  if (result.evidence) writeFileSync(path.join(outputDir, "proof.json"), `${JSON.stringify(result, null, 2)}
@@ -3957,6 +4013,12 @@ function profileOptionsForChangeProofSide(options, url, outputDir) {
3957
4013
  }
3958
4014
  return next;
3959
4015
  }
4016
+ function observationForChangeSide(observation, side) {
4017
+ if (observation.comparison_role !== "standalone" && observation.comparison_role !== side) {
4018
+ throw new Error(`${side} observation has incompatible comparison_role ${observation.comparison_role}.`);
4019
+ }
4020
+ return observation.comparison_role === side ? observation : { ...observation, comparison_role: side };
4021
+ }
3960
4022
  function compactChangeProofResult(run, options) {
3961
4023
  const outputDir = profileOutputDirOption(options);
3962
4024
  return {
@@ -3992,9 +4054,13 @@ function compactChangeProofResult(run, options) {
3992
4054
  change_proof_receipt: "change-proof-receipt.json",
3993
4055
  change_proof_receipt_markdown: "change-proof-receipt.md",
3994
4056
  change_proof_receipt_html: "change-proof-receipt.html",
4057
+ handoff_receipt: "handoff-receipt.json",
4058
+ handoff_receipt_markdown: "handoff-receipt.md",
3995
4059
  summary: "summary.md",
3996
4060
  before_profile_result: "before/profile-result.json",
3997
- after_profile_result: "after/profile-result.json"
4061
+ after_profile_result: "after/profile-result.json",
4062
+ before_observation_receipt: "before/observation-receipt.json",
4063
+ after_observation_receipt: "after/observation-receipt.json"
3998
4064
  } : void 0
3999
4065
  };
4000
4066
  }
@@ -4010,9 +4076,12 @@ function writeChangeProofOutput(outputDir, run) {
4010
4076
  `);
4011
4077
  writeFileSync(path.join(outputDir, "change-proof-receipt.md"), riddleProofChangeReceiptMarkdown(run.receipt));
4012
4078
  writeFileSync(path.join(outputDir, "change-proof-receipt.html"), riddleProofChangeReceiptHtml(run.receipt));
4079
+ writeFileSync(path.join(outputDir, "handoff-receipt.json"), `${JSON.stringify(run.handoff, null, 2)}
4080
+ `);
4081
+ writeFileSync(path.join(outputDir, "handoff-receipt.md"), buildRiddleProofPrCommentMarkdown({ result: run.handoff }));
4013
4082
  writeFileSync(path.join(outputDir, "summary.md"), changeProofResultMarkdown(run));
4014
- writeProfileOutput(changeProofSideOutputDir(outputDir, "before"), run.beforeResult);
4015
- writeProfileOutput(changeProofSideOutputDir(outputDir, "after"), run.afterResult);
4083
+ writeProfileOutput(changeProofSideOutputDir(outputDir, "before"), run.beforeResult, { observation: run.beforeObservation });
4084
+ writeProfileOutput(changeProofSideOutputDir(outputDir, "after"), run.afterResult, { observation: run.afterObservation });
4016
4085
  }
4017
4086
  function writeRunChangeProofResult(run, options) {
4018
4087
  const format = runProfileResultFormatOption(options);
@@ -4111,6 +4180,7 @@ function withRiddleMetadata(result, input) {
4111
4180
  ...result.riddle || {},
4112
4181
  job_id: input.job_id || result.riddle?.job_id,
4113
4182
  status: input.status ?? result.riddle?.status,
4183
+ phase: poll?.phase ?? result.riddle?.phase,
4114
4184
  terminal: input.terminal ?? result.riddle?.terminal,
4115
4185
  created_at: poll?.created_at ?? result.riddle?.created_at,
4116
4186
  submitted_at: poll?.submitted_at ?? result.riddle?.submitted_at,
@@ -4123,7 +4193,8 @@ function withRiddleMetadata(result, input) {
4123
4193
  timed_out: poll?.timed_out ?? result.riddle?.timed_out,
4124
4194
  retry_count: input.retryCount ?? result.riddle?.retry_count,
4125
4195
  stale_job_ids: staleJobIds?.length ? staleJobIds : result.riddle?.stale_job_ids,
4126
- artifact_recovery: input.artifactRecovery ?? result.riddle?.artifact_recovery
4196
+ artifact_recovery: input.artifactRecovery ?? result.riddle?.artifact_recovery,
4197
+ execution: poll?.execution ?? result.riddle?.execution
4127
4198
  },
4128
4199
  artifacts: {
4129
4200
  ...result.artifacts,
@@ -4191,6 +4262,7 @@ function riddleMetadataFromPoll(jobId, poll) {
4191
4262
  return {
4192
4263
  job_id: jobId,
4193
4264
  status: poll.status,
4265
+ phase: poll.poll?.phase,
4194
4266
  terminal: poll.terminal,
4195
4267
  created_at: poll.poll?.created_at,
4196
4268
  submitted_at: poll.poll?.submitted_at,
@@ -4200,7 +4272,8 @@ function riddleMetadataFromPoll(jobId, poll) {
4200
4272
  elapsed_ms: poll.poll?.elapsed_ms,
4201
4273
  attempt: poll.poll?.attempt,
4202
4274
  attempts: poll.poll?.attempts,
4203
- timed_out: poll.poll?.timed_out
4275
+ timed_out: poll.poll?.timed_out,
4276
+ execution: poll.poll?.execution
4204
4277
  };
4205
4278
  }
4206
4279
  function profileUnsubmittedRetryTimeoutMs(options) {
@@ -4746,7 +4819,7 @@ async function runSplitViewportProfileForCli(profile, options, input) {
4746
4819
  const childProfile = profileForSplitViewport(profile, viewport);
4747
4820
  const childOutputDir = outputDir ? splitViewportOutputDir(outputDir, viewport.name, seenOutputNames) : void 0;
4748
4821
  const result2 = await runSingleRiddleProfileForCli(childProfile, options, { ...input, outputDir: childOutputDir });
4749
- if (childOutputDir) writeProfileOutput(childOutputDir, result2);
4822
+ if (childOutputDir) writeProfileOutput(childOutputDir, result2, profileOutputOptionsForCli(options));
4750
4823
  childRuns.push({ viewport, profile: childProfile, result: result2 });
4751
4824
  }
4752
4825
  const artifacts = childRuns.flatMap(splitViewportArtifactRefs);
@@ -4878,18 +4951,34 @@ async function runChangeProofForCli(rawProfile, options) {
4878
4951
  const contract = readChangeContractForCli(options);
4879
4952
  const beforeResultPath = optionString(options, "beforeResult");
4880
4953
  const afterResultPath = optionString(options, "afterResult");
4954
+ const beforeObservationPath = optionString(options, "beforeObservation");
4955
+ const afterObservationPath = optionString(options, "afterObservation");
4881
4956
  const beforeUrl = optionString(options, "beforeUrl");
4882
4957
  const afterUrl = optionString(options, "afterUrl");
4958
+ const beforePreviewInput = optionString(options, "beforePreviewReceipt");
4959
+ const afterPreviewInput = optionString(options, "afterPreviewReceipt");
4960
+ const beforePreview = beforePreviewInput ? parseRiddlePreviewReceipt(readJsonValue(beforePreviewInput, "--before-preview-receipt")) : void 0;
4961
+ const afterPreview = afterPreviewInput ? parseRiddlePreviewReceipt(readJsonValue(afterPreviewInput, "--after-preview-receipt")) : void 0;
4883
4962
  const hasResultInputs = Boolean(beforeResultPath || afterResultPath);
4963
+ const hasObservationInputs = Boolean(beforeObservationPath || afterObservationPath);
4884
4964
  const hasUrlInputs = Boolean(beforeUrl || afterUrl);
4885
- if (hasResultInputs && hasUrlInputs) {
4886
- throw new Error("run-change-proof accepts either --before-result/--after-result or --before-url/--after-url, not both.");
4965
+ if ([hasResultInputs, hasObservationInputs, hasUrlInputs].filter(Boolean).length > 1) {
4966
+ throw new Error("run-change-proof accepts one before/after input pair: result, observation, or URL.");
4887
4967
  }
4888
4968
  if (hasResultInputs && (!beforeResultPath || !afterResultPath)) {
4889
4969
  throw new Error("run-change-proof requires both --before-result and --after-result.");
4890
4970
  }
4891
- if (!hasResultInputs && (!beforeUrl || !afterUrl)) {
4892
- throw new Error("run-change-proof requires both --before-url and --after-url unless saved results are provided.");
4971
+ if (hasObservationInputs && (!beforeObservationPath || !afterObservationPath)) {
4972
+ throw new Error("run-change-proof requires both --before-observation and --after-observation.");
4973
+ }
4974
+ if (hasUrlInputs && (!beforeUrl || !afterUrl)) {
4975
+ throw new Error("run-change-proof requires both --before-url and --after-url.");
4976
+ }
4977
+ if (!hasResultInputs && !hasObservationInputs && !hasUrlInputs) {
4978
+ throw new Error("run-change-proof requires a before/after result, observation, or URL pair.");
4979
+ }
4980
+ if ((beforePreview || afterPreview) && !hasUrlInputs) {
4981
+ throw new Error("--before-preview-receipt and --after-preview-receipt require the URL input mode.");
4893
4982
  }
4894
4983
  const outputDir = profileOutputDirOption(options);
4895
4984
  let profile;
@@ -4897,15 +4986,53 @@ async function runChangeProofForCli(rawProfile, options) {
4897
4986
  let afterResult;
4898
4987
  let beforeSource;
4899
4988
  let afterSource;
4900
- if (hasResultInputs) {
4989
+ let beforeObservation;
4990
+ let afterObservation;
4991
+ if (hasObservationInputs) {
4992
+ if (!beforeObservationPath || !afterObservationPath) {
4993
+ throw new Error("run-change-proof requires both --before-observation and --after-observation.");
4994
+ }
4995
+ profile = profileWithSelectedViewportNamesForCli(normalizeProfileRecordForCli(rawProfile, options), options);
4996
+ beforeObservation = observationForChangeSide(
4997
+ parseRiddleProofObservationReceipt(readJsonValue(beforeObservationPath, "--before-observation")),
4998
+ "before"
4999
+ );
5000
+ afterObservation = observationForChangeSide(
5001
+ parseRiddleProofObservationReceipt(readJsonValue(afterObservationPath, "--after-observation")),
5002
+ "after"
5003
+ );
5004
+ if (!beforeObservation.proof?.result || !afterObservation.proof?.result) {
5005
+ throw new Error("Change Proof observations must include their profile results.");
5006
+ }
5007
+ beforeResult = beforeObservation.proof.result;
5008
+ afterResult = afterObservation.proof.result;
5009
+ beforeSource = beforeObservation.target.url;
5010
+ afterSource = afterObservation.target.url;
5011
+ } else if (hasResultInputs) {
4901
5012
  if (!beforeResultPath || !afterResultPath) {
4902
5013
  throw new Error("run-change-proof requires both --before-result and --after-result.");
4903
5014
  }
4904
5015
  profile = profileWithSelectedViewportNamesForCli(normalizeProfileRecordForCli(rawProfile, options), options);
4905
5016
  beforeResult = readProfileResultForCli(beforeResultPath, "--before-result");
4906
5017
  afterResult = readProfileResultForCli(afterResultPath, "--after-result");
4907
- beforeSource = beforeResultPath;
4908
- afterSource = afterResultPath;
5018
+ beforeSource = profileResultTargetUrl(beforeResult);
5019
+ afterSource = profileResultTargetUrl(afterResult);
5020
+ beforeObservation = createRiddleProofObservationReceipt({
5021
+ comparison_role: "before",
5022
+ executor: { kind: beforeResult.runner === "local-playwright" ? "local_playwright" : "riddle_hosted", runner: beforeResult.runner, job_id: beforeResult.riddle?.job_id },
5023
+ target: { kind: "url", url: beforeSource },
5024
+ profile_result: beforeResult,
5025
+ execution: beforeResult.riddle?.execution,
5026
+ publication: { kind: "local", path: beforeResultPath }
5027
+ });
5028
+ afterObservation = createRiddleProofObservationReceipt({
5029
+ comparison_role: "after",
5030
+ executor: { kind: afterResult.runner === "local-playwright" ? "local_playwright" : "riddle_hosted", runner: afterResult.runner, job_id: afterResult.riddle?.job_id },
5031
+ target: { kind: "url", url: afterSource },
5032
+ profile_result: afterResult,
5033
+ execution: afterResult.riddle?.execution,
5034
+ publication: { kind: "local", path: afterResultPath }
5035
+ });
4909
5036
  } else {
4910
5037
  if (!beforeUrl || !afterUrl) {
4911
5038
  throw new Error("run-change-proof requires both --before-url and --after-url unless saved results are provided.");
@@ -4919,21 +5046,54 @@ async function runChangeProofForCli(rawProfile, options) {
4919
5046
  afterResult = await runProfileForCli(afterProfile, afterOptions);
4920
5047
  beforeSource = beforeUrl;
4921
5048
  afterSource = afterUrl;
5049
+ beforeObservation = createRiddleProofObservationReceipt({
5050
+ comparison_role: "before",
5051
+ executor: { kind: "riddle_hosted", runner: beforeResult.runner, job_id: beforeResult.riddle?.job_id },
5052
+ target: beforePreview ? { kind: "preview", url: beforeSource, preview: beforePreview } : { kind: "url", url: beforeSource },
5053
+ source: beforePreview?.source,
5054
+ profile_result: beforeResult,
5055
+ execution: beforeResult.riddle?.execution
5056
+ });
5057
+ afterObservation = createRiddleProofObservationReceipt({
5058
+ comparison_role: "after",
5059
+ executor: { kind: "riddle_hosted", runner: afterResult.runner, job_id: afterResult.riddle?.job_id },
5060
+ target: afterPreview ? { kind: "preview", url: afterSource, preview: afterPreview } : { kind: "url", url: afterSource },
5061
+ source: afterPreview?.source,
5062
+ profile_result: afterResult,
5063
+ execution: afterResult.riddle?.execution
5064
+ });
4922
5065
  }
4923
5066
  const result = assessRiddleProofChange(contract, {
4924
5067
  before_result: beforeResult,
4925
- after_result: afterResult
5068
+ after_result: afterResult,
5069
+ before_observation: beforeObservation,
5070
+ after_observation: afterObservation,
5071
+ expected_source_revisions: {
5072
+ before: optionString(options, "beforeSourceRevision"),
5073
+ after: optionString(options, "afterSourceRevision")
5074
+ }
4926
5075
  });
4927
5076
  const receipt = createRiddleProofChangeReceipt({
4928
5077
  contract,
4929
5078
  result,
4930
- before_result: beforeResult,
4931
- after_result: afterResult,
4932
- before_source: beforeSource,
4933
- after_source: afterSource,
5079
+ before_observation: beforeObservation,
5080
+ after_observation: afterObservation,
4934
5081
  profile_name: profile.name
4935
5082
  });
4936
- return { profile, contract, beforeResult, afterResult, result, receipt, beforeSource, afterSource };
5083
+ const handoff = createRiddleProofHandoffReceipt(receipt);
5084
+ return {
5085
+ profile,
5086
+ contract,
5087
+ beforeResult,
5088
+ afterResult,
5089
+ result,
5090
+ receipt,
5091
+ handoff,
5092
+ beforeObservation,
5093
+ afterObservation,
5094
+ beforeSource,
5095
+ afterSource
5096
+ };
4937
5097
  }
4938
5098
  function requestForRun(options) {
4939
5099
  const statePath = optionString(options, "statePath");
@@ -4985,7 +5145,7 @@ async function main() {
4985
5145
  if (command === "run-profile") {
4986
5146
  const profile = profileWithSelectedViewportNamesForCli(normalizeProfileForCli(options), options);
4987
5147
  const result = positional[1] === "recover" ? await recoverProfileForCli(profile, options) : positional[1] === "aggregate" ? await aggregateProfileResultsForCli(profile, options) : await runProfileForCli(profile, options);
4988
- writeProfileOutput(profileOutputDirOption(options), result);
5148
+ writeProfileOutput(profileOutputDirOption(options), result, profileOutputOptionsForCli(options));
4989
5149
  const diagnosticLine = profileCliDiagnosticLine(result);
4990
5150
  if (diagnosticLine && optionBoolean(options, "quiet") !== true) {
4991
5151
  process.stderr.write(`${diagnosticLine}
@@ -5016,6 +5176,7 @@ async function main() {
5016
5176
  const explicitResultJsonPath = optionString(options, "resultJson");
5017
5177
  const runResponsePath = explicitRunResponsePath || defaultProofDirJsonPath(proofDir, "riddle-run-response.json");
5018
5178
  const resultJsonPaths = explicitResultJsonPath ? [explicitResultJsonPath] : [
5179
+ defaultProofDirJsonPath(proofDir, "handoff-receipt.json"),
5019
5180
  defaultProofDirJsonPath(proofDir, "result.json"),
5020
5181
  defaultProofDirJsonPath(proofDir, "change-proof-receipt.json"),
5021
5182
  defaultProofDirJsonPath(proofDir, "change-proof-result.json"),
@@ -5137,6 +5298,16 @@ async function main() {
5137
5298
  };
5138
5299
  }
5139
5300
  const result = await createRiddleApiClient(clientConfig).deployPreview(buildDir, label, previewFrameworkOption(options));
5301
+ const outputDir = profileOutputDirOption(options);
5302
+ if (outputDir) {
5303
+ mkdirSync(outputDir, { recursive: true });
5304
+ writeFileSync(path.join(outputDir, "preview-deploy-result.json"), `${JSON.stringify(result, null, 2)}
5305
+ `);
5306
+ if (result.receipt) {
5307
+ writeFileSync(path.join(outputDir, "preview-receipt.json"), `${JSON.stringify(result.receipt, null, 2)}
5308
+ `);
5309
+ }
5310
+ }
5140
5311
  for (const warning of result.warnings ?? []) {
5141
5312
  process.stderr.write(`Warning: ${warning}
5142
5313
  `);