@riddledc/riddle-proof 0.5.32 → 0.5.34

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 (40) hide show
  1. package/dist/{chunk-R4BPJNAM.js → chunk-4YCWZVBN.js} +101 -8
  2. package/dist/{chunk-LIDYNU7Q.js → chunk-7R6ZQE3X.js} +39 -2
  3. package/dist/{chunk-TMMKRKY5.js → chunk-J2MERROF.js} +1 -0
  4. package/dist/{chunk-F6VFKS7K.js → chunk-MQ2BHHLX.js} +2 -2
  5. package/dist/{chunk-7ZJAUEUN.js → chunk-OASB3CYU.js} +6 -1
  6. package/dist/chunk-ODORKNSO.js +121 -0
  7. package/dist/engine-harness.cjs +143 -9
  8. package/dist/engine-harness.js +4 -3
  9. package/dist/index.cjs +271 -11
  10. package/dist/index.d.cts +2 -1
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.js +24 -7
  13. package/dist/openclaw.cjs +5 -0
  14. package/dist/openclaw.js +2 -2
  15. package/dist/proof-run-core.cjs +104 -8
  16. package/dist/proof-run-core.d.cts +24 -1
  17. package/dist/proof-run-core.d.ts +24 -1
  18. package/dist/proof-run-core.js +7 -1
  19. package/dist/proof-run-engine.cjs +101 -8
  20. package/dist/proof-run-engine.d.cts +24 -0
  21. package/dist/proof-run-engine.d.ts +24 -0
  22. package/dist/proof-run-engine.js +4 -1
  23. package/dist/proof-session.cjs +151 -0
  24. package/dist/proof-session.d.cts +37 -0
  25. package/dist/proof-session.d.ts +37 -0
  26. package/dist/proof-session.js +18 -0
  27. package/dist/result.cjs +1 -0
  28. package/dist/result.js +1 -1
  29. package/dist/runner.cjs +6 -0
  30. package/dist/runner.js +3 -3
  31. package/dist/state.cjs +5 -0
  32. package/dist/state.js +2 -2
  33. package/dist/types.d.cts +67 -1
  34. package/dist/types.d.ts +67 -1
  35. package/package.json +7 -2
  36. package/runtime/lib/preflight.py +42 -2
  37. package/runtime/lib/ship.py +55 -1
  38. package/runtime/lib/util.py +237 -0
  39. package/runtime/lib/verify.py +93 -6
  40. package/runtime/tests/recon_verify_smoke.py +181 -3
@@ -63,6 +63,11 @@ function ensureAction(action) {
63
63
  function workflowFile(riddleProofDir, action) {
64
64
  return path.join(riddleProofDir, "pipelines", `riddle-proof-${action}.lobster`);
65
65
  }
66
+ function asJsonString(value) {
67
+ if (value === void 0 || value === null || value === "") return "";
68
+ if (typeof value === "string") return value;
69
+ return JSON.stringify(value);
70
+ }
66
71
  function buildSetupArgs(params, config) {
67
72
  if (!params.repo) throw new Error("repo is required for setup/run");
68
73
  if (!params.change_request) throw new Error("change_request is required for setup/run");
@@ -78,8 +83,13 @@ function buildSetupArgs(params, config) {
78
83
  prod_url: params.prod_url || "",
79
84
  capture_script: captureScript,
80
85
  success_criteria: params.success_criteria || "",
81
- assertions_json: params.assertions_json || "",
86
+ assertions_json: params.assertions_json || asJsonString(params.assertions),
82
87
  verification_mode: params.verification_mode || "proof",
88
+ resume_session: params.resume_session || "",
89
+ target_image_url: params.target_image_url || "",
90
+ target_image_hash: params.target_image_hash || "",
91
+ viewport_matrix_json: params.viewport_matrix_json || asJsonString(params.viewport_matrix),
92
+ deterministic_setup_json: params.deterministic_setup_json || asJsonString(params.deterministic_setup),
83
93
  reference: requestedReference,
84
94
  base_branch: params.base_branch || "main",
85
95
  before_ref: params.before_ref || "",
@@ -322,6 +332,54 @@ function normalizedProofAssessment(state = {}) {
322
332
  source: source || null
323
333
  };
324
334
  }
335
+ var VISUAL_FIRST_MODES = /* @__PURE__ */ new Set([
336
+ "visual",
337
+ "render",
338
+ "interaction",
339
+ "ui",
340
+ "layout",
341
+ "screenshot",
342
+ "canvas",
343
+ "animation"
344
+ ]);
345
+ function objectValue(value) {
346
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
347
+ }
348
+ function normalizedVerificationMode(state = {}) {
349
+ const bundle = objectValue(state?.evidence_bundle);
350
+ const bundleMode = String(bundle.verification_mode || "").trim().toLowerCase();
351
+ if (bundleMode) return bundleMode;
352
+ return String(state?.verification_mode || "proof").trim().toLowerCase() || "proof";
353
+ }
354
+ function visualDeltaRequiredForState(state = {}) {
355
+ const bundle = objectValue(state?.evidence_bundle);
356
+ const contract = objectValue(bundle.artifact_contract);
357
+ const required = objectValue(contract.required);
358
+ return required.visual_delta === true || VISUAL_FIRST_MODES.has(normalizedVerificationMode(state));
359
+ }
360
+ function visualDeltaForState(state = {}) {
361
+ const bundle = objectValue(state?.evidence_bundle);
362
+ const after = objectValue(bundle.after);
363
+ const afterDelta = objectValue(after.visual_delta);
364
+ if (Object.keys(afterDelta).length) return afterDelta;
365
+ const request = objectValue(state?.proof_assessment_request);
366
+ return objectValue(request.visual_delta);
367
+ }
368
+ function visualDeltaShipGateReason(state = {}) {
369
+ if (!visualDeltaRequiredForState(state)) return null;
370
+ const visualDelta = visualDeltaForState(state);
371
+ if (visualDelta.status === "measured" && visualDelta.passed === true) return null;
372
+ const status = String(visualDelta.status || "missing");
373
+ if (status === "unmeasured") {
374
+ return "visual_delta.status=unmeasured blocks ready_to_ship for visual/UI proof";
375
+ }
376
+ if (status === "measured" && visualDelta.passed === false) {
377
+ return "visual_delta.status=measured but visual_delta.passed=false blocks ready_to_ship for visual/UI proof";
378
+ }
379
+ const reason = String(visualDelta.reason || "").trim();
380
+ if (reason) return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof: ${reason}`;
381
+ return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof`;
382
+ }
325
383
  function requiredBaselineLabelsForState(state = {}) {
326
384
  const reference = normalizedReference(state);
327
385
  const labels = [];
@@ -337,6 +395,10 @@ function validateShipGate(state = {}) {
337
395
  const afterCdn = String(state?.after_cdn || "").trim();
338
396
  const verifyStatus = String(state?.verify_status || "").trim();
339
397
  const proofAssessment = normalizedProofAssessment(state);
398
+ const verificationMode = normalizedVerificationMode(state);
399
+ const visualDelta = visualDeltaForState(state);
400
+ const visualDeltaRequired = visualDeltaRequiredForState(state);
401
+ const visualDeltaBlocker = visualDeltaShipGateReason(state);
340
402
  const reasons = [];
341
403
  if (!["before", "prod", "both"].includes(reference)) {
342
404
  reasons.push(`reference must be before, prod, or both; got ${reference}`);
@@ -365,19 +427,26 @@ function validateShipGate(state = {}) {
365
427
  if (proofAssessment.decision !== "ready_to_ship") {
366
428
  reasons.push("proof_assessment.decision must be ready_to_ship before ship");
367
429
  }
430
+ if (visualDeltaBlocker) {
431
+ reasons.push(visualDeltaBlocker);
432
+ }
368
433
  return {
369
434
  ok: reasons.length === 0,
370
435
  reasons,
371
436
  required_baselines: requiredBaselines,
372
437
  evidence: {
373
438
  reference,
439
+ verification_mode: verificationMode || null,
374
440
  prod_url: prodUrl || null,
375
441
  before_cdn: beforeCdn || null,
376
442
  prod_cdn: prodCdn || null,
377
443
  after_cdn: afterCdn || null,
378
444
  verify_status: verifyStatus || null,
379
445
  proof_assessment_decision: proofAssessment.decision,
380
- proof_assessment_source: proofAssessment.source
446
+ proof_assessment_source: proofAssessment.source,
447
+ visual_delta_required: visualDeltaRequired,
448
+ visual_delta_status: typeof visualDelta.status === "string" ? visualDelta.status : null,
449
+ visual_delta_passed: typeof visualDelta.passed === "boolean" ? visualDelta.passed : null
381
450
  }
382
451
  };
383
452
  }
@@ -590,6 +659,11 @@ function mergeStateFromParams(statePath, params) {
590
659
  "success_criteria",
591
660
  "assertions_json",
592
661
  "verification_mode",
662
+ "resume_session",
663
+ "target_image_url",
664
+ "target_image_hash",
665
+ "viewport_matrix_json",
666
+ "deterministic_setup_json",
593
667
  "base_branch",
594
668
  "before_ref",
595
669
  "context",
@@ -691,23 +765,36 @@ function mergeStateFromParams(statePath, params) {
691
765
  state.proof_assessment_source = null;
692
766
  } else {
693
767
  const parsed = JSON.parse(raw);
694
- state.proof_assessment = {
768
+ const assessment = {
695
769
  ...parsed,
696
770
  source: (parsed?.source || "supervising_agent").toString()
697
771
  };
772
+ const readyBlocker = assessment?.decision === "ready_to_ship" ? visualDeltaShipGateReason({ ...state, proof_assessment: assessment, proof_assessment_source: assessment.source }) : null;
773
+ if (readyBlocker) {
774
+ assessment.blocked_decision = assessment.decision;
775
+ assessment.decision = "needs_richer_proof";
776
+ if (assessment.recommended_stage === "ship") assessment.recommended_stage = "verify";
777
+ if (assessment.continue_with_stage === "ship") assessment.continue_with_stage = "verify";
778
+ const blockers = Array.isArray(assessment.blockers) ? assessment.blockers : [];
779
+ assessment.blockers = [...blockers, readyBlocker];
780
+ }
781
+ state.proof_assessment = assessment;
698
782
  state.proof_assessment_source = state.proof_assessment.source;
699
- if (typeof parsed?.decision === "string") {
700
- state.proof_decision = parsed.decision;
783
+ if (typeof state.proof_assessment?.decision === "string") {
784
+ state.proof_decision = state.proof_assessment.decision;
701
785
  }
702
786
  if (typeof parsed?.summary === "string") {
703
787
  state.proof_assessment_summary = normalizeOptionalString(parsed.summary) || null;
704
788
  }
705
- if (parsed?.decision === "ready_to_ship") {
789
+ if (state.proof_assessment?.decision === "ready_to_ship") {
706
790
  state.merge_recommendation = "ready-to-ship";
707
- } else if (typeof parsed?.decision === "string" && parsed.decision.trim()) {
791
+ } else if (typeof state.proof_assessment?.decision === "string" && state.proof_assessment.decision.trim()) {
708
792
  state.merge_recommendation = "do-not-merge";
709
793
  }
710
794
  appendProofSummaryLine(state, `Supervising proof assessment: ${state.proof_assessment.decision || "unknown"}`);
795
+ if (readyBlocker) {
796
+ appendProofSummaryLine(state, `Ready-to-ship assessment blocked: ${readyBlocker}`);
797
+ }
711
798
  if (state.proof_assessment_summary) {
712
799
  appendProofSummaryLine(state, `Assessment summary: ${state.proof_assessment_summary}`);
713
800
  }
@@ -808,7 +895,10 @@ function summarizeState(state) {
808
895
  explicit_stage_gate: Boolean(state.explicit_stage_gate),
809
896
  last_requested_advance_stage: state.last_requested_advance_stage || null,
810
897
  recon_results: state.recon_results || null,
811
- verify_results: state.verify_results || null
898
+ verify_results: state.verify_results || null,
899
+ proof_session: state.proof_session || null,
900
+ parent_proof_session: state.parent_proof_session || null,
901
+ proof_session_artifact_url: state.proof_session_artifact_url || null
812
902
  };
813
903
  const parts = [
814
904
  state.workspace_ready ? "workspace ready" : "workspace not ready",
@@ -848,6 +938,9 @@ export {
848
938
  setStageDecisionRequest,
849
939
  checkpointContinueStage,
850
940
  invalidateVerifyEvidence,
941
+ visualDeltaRequiredForState,
942
+ visualDeltaForState,
943
+ visualDeltaShipGateReason,
851
944
  requiredBaselineLabelsForState,
852
945
  validateShipGate,
853
946
  buildCheckpointContract,
@@ -5,7 +5,10 @@ import {
5
5
  createRunStatusSnapshot,
6
6
  normalizeRunParams,
7
7
  setRunStatus
8
- } from "./chunk-7ZJAUEUN.js";
8
+ } from "./chunk-OASB3CYU.js";
9
+ import {
10
+ visualDeltaShipGateReason
11
+ } from "./chunk-4YCWZVBN.js";
9
12
  import {
10
13
  applyTerminalMetadata,
11
14
  compactRecord,
@@ -13,7 +16,7 @@ import {
13
16
  nonEmptyString,
14
17
  normalizeTerminalMetadata,
15
18
  recordValue
16
- } from "./chunk-TMMKRKY5.js";
19
+ } from "./chunk-J2MERROF.js";
17
20
 
18
21
  // src/engine-harness.ts
19
22
  import { execFileSync } from "child_process";
@@ -291,6 +294,26 @@ function proofAssessmentContinuation(result, payload) {
291
294
  const proof_assessment_json = jsonParam(payload);
292
295
  return { ...baseContinuation(result), proof_assessment_json };
293
296
  }
297
+ function proofAssessmentVisualBlocker(state, payload) {
298
+ if (!proofAssessmentRequestsShip(payload)) return null;
299
+ const source = nonEmptyString(payload.source) || "supervising_agent";
300
+ return visualDeltaShipGateReason({
301
+ ...state || {},
302
+ proof_assessment: { ...payload, source },
303
+ proof_assessment_source: source
304
+ });
305
+ }
306
+ function blockedProofAssessmentPayload(payload, blocker) {
307
+ const blockers = Array.isArray(payload.blockers) ? payload.blockers.filter((item) => typeof item === "string") : [];
308
+ return {
309
+ ...payload,
310
+ blocked_decision: payload.decision || "ready_to_ship",
311
+ decision: "needs_richer_proof",
312
+ recommended_stage: payload.recommended_stage === "ship" ? "verify" : payload.recommended_stage || "verify",
313
+ continue_with_stage: payload.continue_with_stage === "ship" ? "verify" : payload.continue_with_stage || "verify",
314
+ blockers: [...blockers, blocker]
315
+ };
316
+ }
294
317
  function contextFor(request, state, result) {
295
318
  return {
296
319
  request,
@@ -660,6 +683,20 @@ async function routeCheckpoint(request, state, result, agent, input) {
660
683
  summary: assessment.summary,
661
684
  details: { payload }
662
685
  });
686
+ const visualBlocker = proofAssessmentVisualBlocker({
687
+ ...context.fullRiddleState || {},
688
+ verification_mode: context.fullRiddleState?.verification_mode || request.verification_mode
689
+ }, payload);
690
+ if (visualBlocker) {
691
+ recordEvent(state, {
692
+ kind: "agent.proof_assessment.blocked",
693
+ checkpoint,
694
+ stage: "verify",
695
+ summary: visualBlocker,
696
+ details: { payload }
697
+ });
698
+ return { next: proofAssessmentContinuation(result, blockedProofAssessmentPayload(payload, visualBlocker)) };
699
+ }
663
700
  if (effectiveShipMode(request, input.config) !== "ship" && proofAssessmentRequestsShip(payload)) {
664
701
  return {
665
702
  terminal: terminalResult(
@@ -202,6 +202,7 @@ function createRunResult(input) {
202
202
  merge_recommendation: state.merge_recommendation,
203
203
  finalized: state.finalized,
204
204
  blocker: state.blocker,
205
+ proof_session: state.proof_session,
205
206
  evidence_bundle: input.evidence_bundle,
206
207
  raw: input.raw
207
208
  });
@@ -3,10 +3,10 @@ import {
3
3
  appendStageHeartbeat,
4
4
  createRunState,
5
5
  setRunStatus
6
- } from "./chunk-7ZJAUEUN.js";
6
+ } from "./chunk-OASB3CYU.js";
7
7
  import {
8
8
  createRunResult
9
- } from "./chunk-TMMKRKY5.js";
9
+ } from "./chunk-J2MERROF.js";
10
10
 
11
11
  // src/runner.ts
12
12
  function errorDetails(error) {
@@ -3,7 +3,7 @@ import {
3
3
  isTerminalStatus,
4
4
  nonEmptyString,
5
5
  recordValue
6
- } from "./chunk-TMMKRKY5.js";
6
+ } from "./chunk-J2MERROF.js";
7
7
 
8
8
  // src/state.ts
9
9
  var RIDDLE_PROOF_RUN_STATE_VERSION = "riddle-proof.run-state.v1";
@@ -77,6 +77,11 @@ function normalizeRunParams(input) {
77
77
  success_criteria: input.success_criteria,
78
78
  assertions: input.assertions,
79
79
  verification_mode: input.verification_mode,
80
+ resume_session: input.resume_session,
81
+ target_image_url: input.target_image_url,
82
+ target_image_hash: input.target_image_hash,
83
+ viewport_matrix: input.viewport_matrix,
84
+ deterministic_setup: input.deterministic_setup,
80
85
  reference: input.reference,
81
86
  base_branch: input.base_branch,
82
87
  before_ref: input.before_ref,
@@ -0,0 +1,121 @@
1
+ // src/proof-session.ts
2
+ import { createHash, randomUUID } from "crypto";
3
+ var RIDDLE_PROOF_VISUAL_SESSION_VERSION = "riddle-proof.visual-session.v1";
4
+ var RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION = "riddle-proof.visual-session.fingerprint.v1";
5
+ function trim(value) {
6
+ const text = String(value ?? "").trim();
7
+ return text || void 0;
8
+ }
9
+ function hashString(value) {
10
+ const text = trim(value);
11
+ if (!text) return void 0;
12
+ return createHash("sha256").update(text).digest("hex");
13
+ }
14
+ function stableJson(value) {
15
+ if (value === void 0) return "null";
16
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
17
+ if (Array.isArray(value)) return `[${value.map((item) => stableJson(item)).join(",")}]`;
18
+ const record = value;
19
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
20
+ }
21
+ function withoutUndefined(record) {
22
+ for (const key of Object.keys(record)) {
23
+ if (record[key] === void 0) delete record[key];
24
+ }
25
+ return record;
26
+ }
27
+ function visualSessionFingerprintBasis(input) {
28
+ return withoutUndefined({
29
+ version: RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION,
30
+ repo: trim(input.repo),
31
+ route: trim(input.route),
32
+ wait_for_selector: trim(input.wait_for_selector),
33
+ reference: trim(input.reference),
34
+ verification_mode: trim(input.verification_mode)?.toLowerCase(),
35
+ target_image_url: trim(input.target_image_url),
36
+ target_image_hash: trim(input.target_image_hash),
37
+ viewport_matrix: input.viewport_matrix,
38
+ deterministic_setup: input.deterministic_setup,
39
+ assertions: input.assertions,
40
+ capture_script_hash: hashString(input.capture_script)
41
+ });
42
+ }
43
+ function visualSessionFingerprint(input) {
44
+ const basis = input.version === RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION ? input : visualSessionFingerprintBasis(input);
45
+ return createHash("sha256").update(stableJson(basis)).digest("hex");
46
+ }
47
+ function buildVisualProofSession(input) {
48
+ const basis = visualSessionFingerprintBasis(input);
49
+ const fingerprint = visualSessionFingerprint(basis);
50
+ const session = {
51
+ version: RIDDLE_PROOF_VISUAL_SESSION_VERSION,
52
+ session_id: `rps_${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}_${randomUUID().slice(0, 8)}`,
53
+ run_id: trim(input.run_id),
54
+ parent_session_id: input.parent?.session_id || null,
55
+ parent_fingerprint: input.parent?.fingerprint || null,
56
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
57
+ fingerprint,
58
+ fingerprint_basis: basis,
59
+ repo: trim(input.repo),
60
+ branch: trim(input.branch),
61
+ route: withoutUndefined({
62
+ path: trim(input.route),
63
+ observed_after_path: trim(input.observed_after_path)
64
+ }),
65
+ reference: trim(input.reference),
66
+ verification_mode: trim(input.verification_mode),
67
+ target_image: withoutUndefined({
68
+ url: trim(input.target_image_url),
69
+ hash: trim(input.target_image_hash)
70
+ }),
71
+ viewport_matrix: input.viewport_matrix,
72
+ deterministic_setup: input.deterministic_setup,
73
+ capture: withoutUndefined({
74
+ proof_plan: trim(input.proof_plan),
75
+ capture_script: trim(input.capture_script),
76
+ wait_for_selector: trim(input.wait_for_selector)
77
+ }),
78
+ assertions: input.assertions,
79
+ artifacts: input.artifacts,
80
+ evidence: input.evidence,
81
+ status: trim(input.status)
82
+ };
83
+ return JSON.parse(JSON.stringify(session));
84
+ }
85
+ function parseVisualProofSession(value) {
86
+ const parsed = typeof value === "string" ? JSON.parse(value) : value;
87
+ if (!parsed || typeof parsed !== "object") throw new Error("proof session must be a JSON object");
88
+ const session = parsed;
89
+ if (session.version !== RIDDLE_PROOF_VISUAL_SESSION_VERSION) {
90
+ throw new Error(`unsupported proof session version: ${String(session.version || "")}`);
91
+ }
92
+ if (!session.session_id) throw new Error("proof session missing session_id");
93
+ if (!session.fingerprint) throw new Error("proof session missing fingerprint");
94
+ return session;
95
+ }
96
+ function compareVisualProofSessionFingerprint(parent, input) {
97
+ const actual = visualSessionFingerprintBasis(input);
98
+ const expected = parent.fingerprint_basis || {};
99
+ const keys = /* @__PURE__ */ new Set([...Object.keys(expected), ...Object.keys(actual)]);
100
+ const expectedRecord = expected;
101
+ const actualRecord = actual;
102
+ const mismatches = [];
103
+ for (const key of keys) {
104
+ const expectedValue = expectedRecord[key];
105
+ const actualValue = actualRecord[key];
106
+ if (stableJson(expectedValue) !== stableJson(actualValue)) {
107
+ mismatches.push({ key, expected: expectedValue, actual: actualValue });
108
+ }
109
+ }
110
+ return mismatches;
111
+ }
112
+
113
+ export {
114
+ RIDDLE_PROOF_VISUAL_SESSION_VERSION,
115
+ RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_VERSION,
116
+ visualSessionFingerprintBasis,
117
+ visualSessionFingerprint,
118
+ buildVisualProofSession,
119
+ parseVisualProofSession,
120
+ compareVisualProofSessionFingerprint
121
+ };