@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
@@ -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-CWRIXP5H.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,10 +41,7 @@ import {
29
41
  profileStatusExitCode,
30
42
  resolveRiddleProofProfileTargetUrl,
31
43
  resolveRiddleProofProfileTimeoutSec
32
- } from "./chunk-GG2D3MFZ.js";
33
- import {
34
- assessRiddleProofChange
35
- } from "./chunk-6S7DZWVC.js";
44
+ } from "./chunk-FCSJZBC5.js";
36
45
  import {
37
46
  createCodexExecAgentAdapter,
38
47
  runCodexExecAgentDoctor
@@ -61,8 +70,14 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
61
70
  "balancePreflight",
62
71
  "baseUrl",
63
72
  "afterResult",
73
+ "afterObservation",
74
+ "afterPreviewReceipt",
75
+ "afterSourceRevision",
64
76
  "afterUrl",
65
77
  "beforeResult",
78
+ "beforeObservation",
79
+ "beforePreviewReceipt",
80
+ "beforeSourceRevision",
66
81
  "beforeUrl",
67
82
  "candidateJson",
68
83
  "candidatesJson",
@@ -116,6 +131,7 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
116
131
  "pack",
117
132
  "packFile",
118
133
  "profile",
134
+ "previewReceipt",
119
135
  "proofDir",
120
136
  "pr",
121
137
  "progressEveryMs",
@@ -139,6 +155,9 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
139
155
  "scriptFile",
140
156
  "selectors",
141
157
  "sourceKind",
158
+ "sourceDirty",
159
+ "sourceRepository",
160
+ "sourceRevision",
142
161
  "splitViewports",
143
162
  "stateDir",
144
163
  "statePath",
@@ -170,16 +189,16 @@ function usage() {
170
189
  " riddle-proof-loop respond --state-path <path> --response-json <file|json|->",
171
190
  " riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
172
191
  " riddle-proof-loop status --state-path <path>",
173
- " 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]",
174
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]",
175
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]",
176
- " 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]",
177
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]",
178
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>]",
179
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]",
180
199
  " riddle-proof-loop profile-body-assertions --artifact <file|url|-> --candidates-json <file|json|-> [--required-json <file|json|->] [--format json|body-contains]",
181
200
  " riddle-proof-loop profile-http-status-preflight --profile <file|json|-> --url <base-url> [--format json|summary]",
182
- " 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]",
183
202
  " riddle-proof-loop riddle-server-preview <directory> --script-file <file> [--path /route] [--wait-for-selector selector]",
184
203
  " riddle-proof-loop riddle-run-script --url <url> --script-file <file> [--viewport 1280x720] [--strict true|false]",
185
204
  " riddle-proof-loop riddle-poll <job-id> [--wait] [--attempts n] [--quiet]",
@@ -339,6 +358,7 @@ function compactRunProfileResult(result, options) {
339
358
  output_dir: outputDir,
340
359
  output_files: outputDir ? {
341
360
  profile_result: "profile-result.json",
361
+ observation_receipt: "observation-receipt.json",
342
362
  summary: "summary.md",
343
363
  proof_json: result.evidence ? "proof.json" : void 0,
344
364
  console: result.evidence?.console ? "console.json" : void 0,
@@ -967,12 +987,13 @@ function formatByteCount(bytes) {
967
987
  }
968
988
  function riddlePollProgressLine(snapshot) {
969
989
  const submittedAt = snapshot.submitted_at || "not-submitted";
970
- 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)}` : "";
971
991
  const terminalPart = snapshot.terminal ? " terminal=true" : "";
972
992
  return [
973
993
  "[riddle-poll]",
974
994
  snapshot.job_id,
975
995
  `status=${snapshot.status || "unknown"}`,
996
+ `phase=${snapshot.phase || "unknown"}`,
976
997
  `attempt=${snapshot.attempt}/${snapshot.attempts}`,
977
998
  `elapsed=${formatPollDuration(snapshot.elapsed_ms)}`,
978
999
  `submitted_at=${submittedAt}${queuePart}${terminalPart}`
@@ -3910,10 +3931,48 @@ function profileHttpStatusSummaryMarkdown(result) {
3910
3931
  }
3911
3932
  return lines;
3912
3933
  }
3913
- 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 = {}) {
3914
3970
  if (!outputDir) return;
3915
3971
  mkdirSync(outputDir, { recursive: true });
3916
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)}
3917
3976
  `);
3918
3977
  writeFileSync(path.join(outputDir, "summary.md"), profileResultMarkdown(result));
3919
3978
  if (result.evidence) writeFileSync(path.join(outputDir, "proof.json"), `${JSON.stringify(result, null, 2)}
@@ -3954,6 +4013,12 @@ function profileOptionsForChangeProofSide(options, url, outputDir) {
3954
4013
  }
3955
4014
  return next;
3956
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
+ }
3957
4022
  function compactChangeProofResult(run, options) {
3958
4023
  const outputDir = profileOutputDirOption(options);
3959
4024
  return {
@@ -3986,57 +4051,37 @@ function compactChangeProofResult(run, options) {
3986
4051
  output_dir: outputDir,
3987
4052
  output_files: outputDir ? {
3988
4053
  change_proof_result: "change-proof-result.json",
4054
+ change_proof_receipt: "change-proof-receipt.json",
4055
+ change_proof_receipt_markdown: "change-proof-receipt.md",
4056
+ change_proof_receipt_html: "change-proof-receipt.html",
4057
+ handoff_receipt: "handoff-receipt.json",
4058
+ handoff_receipt_markdown: "handoff-receipt.md",
3989
4059
  summary: "summary.md",
3990
4060
  before_profile_result: "before/profile-result.json",
3991
- 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"
3992
4064
  } : void 0
3993
4065
  };
3994
4066
  }
3995
4067
  function changeProofResultMarkdown(run) {
3996
- const lines = [
3997
- "# Riddle Proof Change Proof",
3998
- "",
3999
- `Contract: ${run.result.contract_name}`,
4000
- `Profile: ${run.profile.name}`,
4001
- `Status: ${run.result.status}`,
4002
- "",
4003
- run.result.summary,
4004
- "",
4005
- "## Before",
4006
- "",
4007
- `- source: ${markdownInlineCode(run.beforeSource, 160)}`,
4008
- `- profile: ${run.beforeResult.profile_name}`,
4009
- `- status: ${run.beforeResult.status}`,
4010
- `- summary: ${run.beforeResult.summary}`,
4011
- "",
4012
- "## After",
4013
- "",
4014
- `- source: ${markdownInlineCode(run.afterSource, 160)}`,
4015
- `- profile: ${run.afterResult.profile_name}`,
4016
- `- status: ${run.afterResult.status}`,
4017
- `- summary: ${run.afterResult.summary}`,
4018
- "",
4019
- "## Deltas",
4020
- ""
4021
- ];
4022
- for (const delta of run.result.deltas) {
4023
- const observed = [
4024
- delta.before_observed !== void 0 ? `before ${markdownInlineCode(String(delta.before_observed))}` : "",
4025
- delta.after_observed !== void 0 ? `after ${markdownInlineCode(String(delta.after_observed))}` : ""
4026
- ].filter(Boolean).join(" -> ");
4027
- lines.push(`- ${delta.status}: ${delta.label}${observed ? ` (${observed})` : ""}${delta.message ? ` - ${delta.message}` : ""}`);
4028
- }
4029
- return `${lines.join("\n")}
4030
- `;
4068
+ return riddleProofChangeReceiptMarkdown(run.receipt);
4031
4069
  }
4032
4070
  function writeChangeProofOutput(outputDir, run) {
4033
4071
  if (!outputDir) return;
4034
4072
  mkdirSync(outputDir, { recursive: true });
4035
4073
  writeFileSync(path.join(outputDir, "change-proof-result.json"), `${JSON.stringify(run.result, null, 2)}
4036
4074
  `);
4075
+ writeFileSync(path.join(outputDir, "change-proof-receipt.json"), `${JSON.stringify(run.receipt, null, 2)}
4076
+ `);
4077
+ writeFileSync(path.join(outputDir, "change-proof-receipt.md"), riddleProofChangeReceiptMarkdown(run.receipt));
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 }));
4037
4082
  writeFileSync(path.join(outputDir, "summary.md"), changeProofResultMarkdown(run));
4038
- writeProfileOutput(changeProofSideOutputDir(outputDir, "before"), run.beforeResult);
4039
- 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 });
4040
4085
  }
4041
4086
  function writeRunChangeProofResult(run, options) {
4042
4087
  const format = runProfileResultFormatOption(options);
@@ -4135,6 +4180,7 @@ function withRiddleMetadata(result, input) {
4135
4180
  ...result.riddle || {},
4136
4181
  job_id: input.job_id || result.riddle?.job_id,
4137
4182
  status: input.status ?? result.riddle?.status,
4183
+ phase: poll?.phase ?? result.riddle?.phase,
4138
4184
  terminal: input.terminal ?? result.riddle?.terminal,
4139
4185
  created_at: poll?.created_at ?? result.riddle?.created_at,
4140
4186
  submitted_at: poll?.submitted_at ?? result.riddle?.submitted_at,
@@ -4147,7 +4193,8 @@ function withRiddleMetadata(result, input) {
4147
4193
  timed_out: poll?.timed_out ?? result.riddle?.timed_out,
4148
4194
  retry_count: input.retryCount ?? result.riddle?.retry_count,
4149
4195
  stale_job_ids: staleJobIds?.length ? staleJobIds : result.riddle?.stale_job_ids,
4150
- artifact_recovery: input.artifactRecovery ?? result.riddle?.artifact_recovery
4196
+ artifact_recovery: input.artifactRecovery ?? result.riddle?.artifact_recovery,
4197
+ execution: poll?.execution ?? result.riddle?.execution
4151
4198
  },
4152
4199
  artifacts: {
4153
4200
  ...result.artifacts,
@@ -4215,6 +4262,7 @@ function riddleMetadataFromPoll(jobId, poll) {
4215
4262
  return {
4216
4263
  job_id: jobId,
4217
4264
  status: poll.status,
4265
+ phase: poll.poll?.phase,
4218
4266
  terminal: poll.terminal,
4219
4267
  created_at: poll.poll?.created_at,
4220
4268
  submitted_at: poll.poll?.submitted_at,
@@ -4224,7 +4272,8 @@ function riddleMetadataFromPoll(jobId, poll) {
4224
4272
  elapsed_ms: poll.poll?.elapsed_ms,
4225
4273
  attempt: poll.poll?.attempt,
4226
4274
  attempts: poll.poll?.attempts,
4227
- timed_out: poll.poll?.timed_out
4275
+ timed_out: poll.poll?.timed_out,
4276
+ execution: poll.poll?.execution
4228
4277
  };
4229
4278
  }
4230
4279
  function profileUnsubmittedRetryTimeoutMs(options) {
@@ -4770,7 +4819,7 @@ async function runSplitViewportProfileForCli(profile, options, input) {
4770
4819
  const childProfile = profileForSplitViewport(profile, viewport);
4771
4820
  const childOutputDir = outputDir ? splitViewportOutputDir(outputDir, viewport.name, seenOutputNames) : void 0;
4772
4821
  const result2 = await runSingleRiddleProfileForCli(childProfile, options, { ...input, outputDir: childOutputDir });
4773
- if (childOutputDir) writeProfileOutput(childOutputDir, result2);
4822
+ if (childOutputDir) writeProfileOutput(childOutputDir, result2, profileOutputOptionsForCli(options));
4774
4823
  childRuns.push({ viewport, profile: childProfile, result: result2 });
4775
4824
  }
4776
4825
  const artifacts = childRuns.flatMap(splitViewportArtifactRefs);
@@ -4902,18 +4951,34 @@ async function runChangeProofForCli(rawProfile, options) {
4902
4951
  const contract = readChangeContractForCli(options);
4903
4952
  const beforeResultPath = optionString(options, "beforeResult");
4904
4953
  const afterResultPath = optionString(options, "afterResult");
4954
+ const beforeObservationPath = optionString(options, "beforeObservation");
4955
+ const afterObservationPath = optionString(options, "afterObservation");
4905
4956
  const beforeUrl = optionString(options, "beforeUrl");
4906
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;
4907
4962
  const hasResultInputs = Boolean(beforeResultPath || afterResultPath);
4963
+ const hasObservationInputs = Boolean(beforeObservationPath || afterObservationPath);
4908
4964
  const hasUrlInputs = Boolean(beforeUrl || afterUrl);
4909
- if (hasResultInputs && hasUrlInputs) {
4910
- 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.");
4911
4967
  }
4912
4968
  if (hasResultInputs && (!beforeResultPath || !afterResultPath)) {
4913
4969
  throw new Error("run-change-proof requires both --before-result and --after-result.");
4914
4970
  }
4915
- if (!hasResultInputs && (!beforeUrl || !afterUrl)) {
4916
- 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.");
4917
4982
  }
4918
4983
  const outputDir = profileOutputDirOption(options);
4919
4984
  let profile;
@@ -4921,15 +4986,53 @@ async function runChangeProofForCli(rawProfile, options) {
4921
4986
  let afterResult;
4922
4987
  let beforeSource;
4923
4988
  let afterSource;
4924
- 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) {
4925
5012
  if (!beforeResultPath || !afterResultPath) {
4926
5013
  throw new Error("run-change-proof requires both --before-result and --after-result.");
4927
5014
  }
4928
5015
  profile = profileWithSelectedViewportNamesForCli(normalizeProfileRecordForCli(rawProfile, options), options);
4929
5016
  beforeResult = readProfileResultForCli(beforeResultPath, "--before-result");
4930
5017
  afterResult = readProfileResultForCli(afterResultPath, "--after-result");
4931
- beforeSource = beforeResultPath;
4932
- 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
+ });
4933
5036
  } else {
4934
5037
  if (!beforeUrl || !afterUrl) {
4935
5038
  throw new Error("run-change-proof requires both --before-url and --after-url unless saved results are provided.");
@@ -4943,12 +5046,54 @@ async function runChangeProofForCli(rawProfile, options) {
4943
5046
  afterResult = await runProfileForCli(afterProfile, afterOptions);
4944
5047
  beforeSource = beforeUrl;
4945
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
+ });
4946
5065
  }
4947
5066
  const result = assessRiddleProofChange(contract, {
4948
5067
  before_result: beforeResult,
4949
- 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
+ }
4950
5075
  });
4951
- return { profile, contract, beforeResult, afterResult, result, beforeSource, afterSource };
5076
+ const receipt = createRiddleProofChangeReceipt({
5077
+ contract,
5078
+ result,
5079
+ before_observation: beforeObservation,
5080
+ after_observation: afterObservation,
5081
+ profile_name: profile.name
5082
+ });
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
+ };
4952
5097
  }
4953
5098
  function requestForRun(options) {
4954
5099
  const statePath = optionString(options, "statePath");
@@ -5000,7 +5145,7 @@ async function main() {
5000
5145
  if (command === "run-profile") {
5001
5146
  const profile = profileWithSelectedViewportNamesForCli(normalizeProfileForCli(options), options);
5002
5147
  const result = positional[1] === "recover" ? await recoverProfileForCli(profile, options) : positional[1] === "aggregate" ? await aggregateProfileResultsForCli(profile, options) : await runProfileForCli(profile, options);
5003
- writeProfileOutput(profileOutputDirOption(options), result);
5148
+ writeProfileOutput(profileOutputDirOption(options), result, profileOutputOptionsForCli(options));
5004
5149
  const diagnosticLine = profileCliDiagnosticLine(result);
5005
5150
  if (diagnosticLine && optionBoolean(options, "quiet") !== true) {
5006
5151
  process.stderr.write(`${diagnosticLine}
@@ -5031,7 +5176,10 @@ async function main() {
5031
5176
  const explicitResultJsonPath = optionString(options, "resultJson");
5032
5177
  const runResponsePath = explicitRunResponsePath || defaultProofDirJsonPath(proofDir, "riddle-run-response.json");
5033
5178
  const resultJsonPaths = explicitResultJsonPath ? [explicitResultJsonPath] : [
5179
+ defaultProofDirJsonPath(proofDir, "handoff-receipt.json"),
5034
5180
  defaultProofDirJsonPath(proofDir, "result.json"),
5181
+ defaultProofDirJsonPath(proofDir, "change-proof-receipt.json"),
5182
+ defaultProofDirJsonPath(proofDir, "change-proof-result.json"),
5035
5183
  defaultProofDirJsonPath(proofDir, "profile-result.json")
5036
5184
  ];
5037
5185
  const runResponse = readJsonFileIfExists(runResponsePath);
@@ -5150,6 +5298,16 @@ async function main() {
5150
5298
  };
5151
5299
  }
5152
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
+ }
5153
5311
  for (const warning of result.warnings ?? []) {
5154
5312
  process.stderr.write(`Warning: ${warning}
5155
5313
  `);