@riddledc/riddle-proof 0.5.32 → 0.5.33

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.
@@ -322,6 +322,54 @@ function normalizedProofAssessment(state = {}) {
322
322
  source: source || null
323
323
  };
324
324
  }
325
+ var VISUAL_FIRST_MODES = /* @__PURE__ */ new Set([
326
+ "visual",
327
+ "render",
328
+ "interaction",
329
+ "ui",
330
+ "layout",
331
+ "screenshot",
332
+ "canvas",
333
+ "animation"
334
+ ]);
335
+ function objectValue(value) {
336
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
337
+ }
338
+ function normalizedVerificationMode(state = {}) {
339
+ const bundle = objectValue(state?.evidence_bundle);
340
+ const bundleMode = String(bundle.verification_mode || "").trim().toLowerCase();
341
+ if (bundleMode) return bundleMode;
342
+ return String(state?.verification_mode || "proof").trim().toLowerCase() || "proof";
343
+ }
344
+ function visualDeltaRequiredForState(state = {}) {
345
+ const bundle = objectValue(state?.evidence_bundle);
346
+ const contract = objectValue(bundle.artifact_contract);
347
+ const required = objectValue(contract.required);
348
+ return required.visual_delta === true || VISUAL_FIRST_MODES.has(normalizedVerificationMode(state));
349
+ }
350
+ function visualDeltaForState(state = {}) {
351
+ const bundle = objectValue(state?.evidence_bundle);
352
+ const after = objectValue(bundle.after);
353
+ const afterDelta = objectValue(after.visual_delta);
354
+ if (Object.keys(afterDelta).length) return afterDelta;
355
+ const request = objectValue(state?.proof_assessment_request);
356
+ return objectValue(request.visual_delta);
357
+ }
358
+ function visualDeltaShipGateReason(state = {}) {
359
+ if (!visualDeltaRequiredForState(state)) return null;
360
+ const visualDelta = visualDeltaForState(state);
361
+ if (visualDelta.status === "measured" && visualDelta.passed === true) return null;
362
+ const status = String(visualDelta.status || "missing");
363
+ if (status === "unmeasured") {
364
+ return "visual_delta.status=unmeasured blocks ready_to_ship for visual/UI proof";
365
+ }
366
+ if (status === "measured" && visualDelta.passed === false) {
367
+ return "visual_delta.status=measured but visual_delta.passed=false blocks ready_to_ship for visual/UI proof";
368
+ }
369
+ const reason = String(visualDelta.reason || "").trim();
370
+ if (reason) return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof: ${reason}`;
371
+ return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof`;
372
+ }
325
373
  function requiredBaselineLabelsForState(state = {}) {
326
374
  const reference = normalizedReference(state);
327
375
  const labels = [];
@@ -337,6 +385,10 @@ function validateShipGate(state = {}) {
337
385
  const afterCdn = String(state?.after_cdn || "").trim();
338
386
  const verifyStatus = String(state?.verify_status || "").trim();
339
387
  const proofAssessment = normalizedProofAssessment(state);
388
+ const verificationMode = normalizedVerificationMode(state);
389
+ const visualDelta = visualDeltaForState(state);
390
+ const visualDeltaRequired = visualDeltaRequiredForState(state);
391
+ const visualDeltaBlocker = visualDeltaShipGateReason(state);
340
392
  const reasons = [];
341
393
  if (!["before", "prod", "both"].includes(reference)) {
342
394
  reasons.push(`reference must be before, prod, or both; got ${reference}`);
@@ -365,19 +417,26 @@ function validateShipGate(state = {}) {
365
417
  if (proofAssessment.decision !== "ready_to_ship") {
366
418
  reasons.push("proof_assessment.decision must be ready_to_ship before ship");
367
419
  }
420
+ if (visualDeltaBlocker) {
421
+ reasons.push(visualDeltaBlocker);
422
+ }
368
423
  return {
369
424
  ok: reasons.length === 0,
370
425
  reasons,
371
426
  required_baselines: requiredBaselines,
372
427
  evidence: {
373
428
  reference,
429
+ verification_mode: verificationMode || null,
374
430
  prod_url: prodUrl || null,
375
431
  before_cdn: beforeCdn || null,
376
432
  prod_cdn: prodCdn || null,
377
433
  after_cdn: afterCdn || null,
378
434
  verify_status: verifyStatus || null,
379
435
  proof_assessment_decision: proofAssessment.decision,
380
- proof_assessment_source: proofAssessment.source
436
+ proof_assessment_source: proofAssessment.source,
437
+ visual_delta_required: visualDeltaRequired,
438
+ visual_delta_status: typeof visualDelta.status === "string" ? visualDelta.status : null,
439
+ visual_delta_passed: typeof visualDelta.passed === "boolean" ? visualDelta.passed : null
381
440
  }
382
441
  };
383
442
  }
@@ -691,23 +750,36 @@ function mergeStateFromParams(statePath, params) {
691
750
  state.proof_assessment_source = null;
692
751
  } else {
693
752
  const parsed = JSON.parse(raw);
694
- state.proof_assessment = {
753
+ const assessment = {
695
754
  ...parsed,
696
755
  source: (parsed?.source || "supervising_agent").toString()
697
756
  };
757
+ const readyBlocker = assessment?.decision === "ready_to_ship" ? visualDeltaShipGateReason({ ...state, proof_assessment: assessment, proof_assessment_source: assessment.source }) : null;
758
+ if (readyBlocker) {
759
+ assessment.blocked_decision = assessment.decision;
760
+ assessment.decision = "needs_richer_proof";
761
+ if (assessment.recommended_stage === "ship") assessment.recommended_stage = "verify";
762
+ if (assessment.continue_with_stage === "ship") assessment.continue_with_stage = "verify";
763
+ const blockers = Array.isArray(assessment.blockers) ? assessment.blockers : [];
764
+ assessment.blockers = [...blockers, readyBlocker];
765
+ }
766
+ state.proof_assessment = assessment;
698
767
  state.proof_assessment_source = state.proof_assessment.source;
699
- if (typeof parsed?.decision === "string") {
700
- state.proof_decision = parsed.decision;
768
+ if (typeof state.proof_assessment?.decision === "string") {
769
+ state.proof_decision = state.proof_assessment.decision;
701
770
  }
702
771
  if (typeof parsed?.summary === "string") {
703
772
  state.proof_assessment_summary = normalizeOptionalString(parsed.summary) || null;
704
773
  }
705
- if (parsed?.decision === "ready_to_ship") {
774
+ if (state.proof_assessment?.decision === "ready_to_ship") {
706
775
  state.merge_recommendation = "ready-to-ship";
707
- } else if (typeof parsed?.decision === "string" && parsed.decision.trim()) {
776
+ } else if (typeof state.proof_assessment?.decision === "string" && state.proof_assessment.decision.trim()) {
708
777
  state.merge_recommendation = "do-not-merge";
709
778
  }
710
779
  appendProofSummaryLine(state, `Supervising proof assessment: ${state.proof_assessment.decision || "unknown"}`);
780
+ if (readyBlocker) {
781
+ appendProofSummaryLine(state, `Ready-to-ship assessment blocked: ${readyBlocker}`);
782
+ }
711
783
  if (state.proof_assessment_summary) {
712
784
  appendProofSummaryLine(state, `Assessment summary: ${state.proof_assessment_summary}`);
713
785
  }
@@ -848,6 +920,9 @@ export {
848
920
  setStageDecisionRequest,
849
921
  checkpointContinueStage,
850
922
  invalidateVerifyEvidence,
923
+ visualDeltaRequiredForState,
924
+ visualDeltaForState,
925
+ visualDeltaShipGateReason,
851
926
  requiredBaselineLabelsForState,
852
927
  validateShipGate,
853
928
  buildCheckpointContract,
@@ -1,3 +1,6 @@
1
+ import {
2
+ visualDeltaShipGateReason
3
+ } from "./chunk-A5AWVY5A.js";
1
4
  import {
2
5
  appendRunEvent,
3
6
  appendStageHeartbeat,
@@ -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(
@@ -340,6 +340,44 @@ function normalizedProofAssessment(state = {}) {
340
340
  source: source || null
341
341
  };
342
342
  }
343
+ function objectValue(value) {
344
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
345
+ }
346
+ function normalizedVerificationMode(state = {}) {
347
+ const bundle = objectValue(state?.evidence_bundle);
348
+ const bundleMode = String(bundle.verification_mode || "").trim().toLowerCase();
349
+ if (bundleMode) return bundleMode;
350
+ return String(state?.verification_mode || "proof").trim().toLowerCase() || "proof";
351
+ }
352
+ function visualDeltaRequiredForState(state = {}) {
353
+ const bundle = objectValue(state?.evidence_bundle);
354
+ const contract = objectValue(bundle.artifact_contract);
355
+ const required = objectValue(contract.required);
356
+ return required.visual_delta === true || VISUAL_FIRST_MODES.has(normalizedVerificationMode(state));
357
+ }
358
+ function visualDeltaForState(state = {}) {
359
+ const bundle = objectValue(state?.evidence_bundle);
360
+ const after = objectValue(bundle.after);
361
+ const afterDelta = objectValue(after.visual_delta);
362
+ if (Object.keys(afterDelta).length) return afterDelta;
363
+ const request = objectValue(state?.proof_assessment_request);
364
+ return objectValue(request.visual_delta);
365
+ }
366
+ function visualDeltaShipGateReason(state = {}) {
367
+ if (!visualDeltaRequiredForState(state)) return null;
368
+ const visualDelta = visualDeltaForState(state);
369
+ if (visualDelta.status === "measured" && visualDelta.passed === true) return null;
370
+ const status = String(visualDelta.status || "missing");
371
+ if (status === "unmeasured") {
372
+ return "visual_delta.status=unmeasured blocks ready_to_ship for visual/UI proof";
373
+ }
374
+ if (status === "measured" && visualDelta.passed === false) {
375
+ return "visual_delta.status=measured but visual_delta.passed=false blocks ready_to_ship for visual/UI proof";
376
+ }
377
+ const reason = String(visualDelta.reason || "").trim();
378
+ if (reason) return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof: ${reason}`;
379
+ return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof`;
380
+ }
343
381
  function requiredBaselineLabelsForState(state = {}) {
344
382
  const reference = normalizedReference(state);
345
383
  const labels = [];
@@ -355,6 +393,10 @@ function validateShipGate(state = {}) {
355
393
  const afterCdn = String(state?.after_cdn || "").trim();
356
394
  const verifyStatus = String(state?.verify_status || "").trim();
357
395
  const proofAssessment = normalizedProofAssessment(state);
396
+ const verificationMode = normalizedVerificationMode(state);
397
+ const visualDelta = visualDeltaForState(state);
398
+ const visualDeltaRequired = visualDeltaRequiredForState(state);
399
+ const visualDeltaBlocker = visualDeltaShipGateReason(state);
358
400
  const reasons = [];
359
401
  if (!["before", "prod", "both"].includes(reference)) {
360
402
  reasons.push(`reference must be before, prod, or both; got ${reference}`);
@@ -383,19 +425,26 @@ function validateShipGate(state = {}) {
383
425
  if (proofAssessment.decision !== "ready_to_ship") {
384
426
  reasons.push("proof_assessment.decision must be ready_to_ship before ship");
385
427
  }
428
+ if (visualDeltaBlocker) {
429
+ reasons.push(visualDeltaBlocker);
430
+ }
386
431
  return {
387
432
  ok: reasons.length === 0,
388
433
  reasons,
389
434
  required_baselines: requiredBaselines,
390
435
  evidence: {
391
436
  reference,
437
+ verification_mode: verificationMode || null,
392
438
  prod_url: prodUrl || null,
393
439
  before_cdn: beforeCdn || null,
394
440
  prod_cdn: prodCdn || null,
395
441
  after_cdn: afterCdn || null,
396
442
  verify_status: verifyStatus || null,
397
443
  proof_assessment_decision: proofAssessment.decision,
398
- proof_assessment_source: proofAssessment.source
444
+ proof_assessment_source: proofAssessment.source,
445
+ visual_delta_required: visualDeltaRequired,
446
+ visual_delta_status: typeof visualDelta.status === "string" ? visualDelta.status : null,
447
+ visual_delta_passed: typeof visualDelta.passed === "boolean" ? visualDelta.passed : null
399
448
  }
400
449
  };
401
450
  }
@@ -541,23 +590,36 @@ function mergeStateFromParams(statePath, params) {
541
590
  state.proof_assessment_source = null;
542
591
  } else {
543
592
  const parsed = JSON.parse(raw);
544
- state.proof_assessment = {
593
+ const assessment = {
545
594
  ...parsed,
546
595
  source: (parsed?.source || "supervising_agent").toString()
547
596
  };
597
+ const readyBlocker = assessment?.decision === "ready_to_ship" ? visualDeltaShipGateReason({ ...state, proof_assessment: assessment, proof_assessment_source: assessment.source }) : null;
598
+ if (readyBlocker) {
599
+ assessment.blocked_decision = assessment.decision;
600
+ assessment.decision = "needs_richer_proof";
601
+ if (assessment.recommended_stage === "ship") assessment.recommended_stage = "verify";
602
+ if (assessment.continue_with_stage === "ship") assessment.continue_with_stage = "verify";
603
+ const blockers = Array.isArray(assessment.blockers) ? assessment.blockers : [];
604
+ assessment.blockers = [...blockers, readyBlocker];
605
+ }
606
+ state.proof_assessment = assessment;
548
607
  state.proof_assessment_source = state.proof_assessment.source;
549
- if (typeof parsed?.decision === "string") {
550
- state.proof_decision = parsed.decision;
608
+ if (typeof state.proof_assessment?.decision === "string") {
609
+ state.proof_decision = state.proof_assessment.decision;
551
610
  }
552
611
  if (typeof parsed?.summary === "string") {
553
612
  state.proof_assessment_summary = normalizeOptionalString(parsed.summary) || null;
554
613
  }
555
- if (parsed?.decision === "ready_to_ship") {
614
+ if (state.proof_assessment?.decision === "ready_to_ship") {
556
615
  state.merge_recommendation = "ready-to-ship";
557
- } else if (typeof parsed?.decision === "string" && parsed.decision.trim()) {
616
+ } else if (typeof state.proof_assessment?.decision === "string" && state.proof_assessment.decision.trim()) {
558
617
  state.merge_recommendation = "do-not-merge";
559
618
  }
560
619
  appendProofSummaryLine(state, `Supervising proof assessment: ${state.proof_assessment.decision || "unknown"}`);
620
+ if (readyBlocker) {
621
+ appendProofSummaryLine(state, `Ready-to-ship assessment blocked: ${readyBlocker}`);
622
+ }
561
623
  if (state.proof_assessment_summary) {
562
624
  appendProofSummaryLine(state, `Assessment summary: ${state.proof_assessment_summary}`);
563
625
  }
@@ -679,7 +741,7 @@ function summarizeState(state) {
679
741
  state: selected
680
742
  };
681
743
  }
682
- var import_node_fs, import_node_crypto, import_node_path, import_node_url, import_meta, WORKFLOW_STAGE_ORDER, CHECKPOINT_CONTRACT_VERSION, BUNDLED_RIDDLE_PROOF_DIR, RIDDLE_PROOF_DIR_CANDIDATES, CHECKPOINT_CONTRACT_SPECS;
744
+ var import_node_fs, import_node_crypto, import_node_path, import_node_url, import_meta, WORKFLOW_STAGE_ORDER, CHECKPOINT_CONTRACT_VERSION, BUNDLED_RIDDLE_PROOF_DIR, RIDDLE_PROOF_DIR_CANDIDATES, VISUAL_FIRST_MODES, CHECKPOINT_CONTRACT_SPECS;
683
745
  var init_proof_run_core = __esm({
684
746
  "src/proof-run-core.ts"() {
685
747
  "use strict";
@@ -698,6 +760,16 @@ var init_proof_run_core = __esm({
698
760
  RIDDLE_PROOF_DIR_CANDIDATES = [
699
761
  BUNDLED_RIDDLE_PROOF_DIR
700
762
  ];
763
+ VISUAL_FIRST_MODES = /* @__PURE__ */ new Set([
764
+ "visual",
765
+ "render",
766
+ "interaction",
767
+ "ui",
768
+ "layout",
769
+ "screenshot",
770
+ "canvas",
771
+ "animation"
772
+ ]);
701
773
  CHECKPOINT_CONTRACT_SPECS = {
702
774
  setup_review: {
703
775
  purpose: "Inspect prepared workspace state before moving into recon."
@@ -1624,6 +1696,9 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1624
1696
  if (reasons.some((reason) => reason.includes("proof_assessment"))) {
1625
1697
  return "resume with riddle_proof_review using decision=ready_to_ship only after the screenshots and semantic evidence visibly prove the request; otherwise choose needs_implementation or needs_richer_proof";
1626
1698
  }
1699
+ if (reasons.some((reason) => reason.includes("visual_delta"))) {
1700
+ return "rerun verify with a measured before/after visual delta, or choose needs_richer_proof instead of ready_to_ship for this visual proof";
1701
+ }
1627
1702
  if (reasons.some((reason) => reason.includes("after_cdn") || reason.includes("verify_status"))) {
1628
1703
  return "rerun verify with stronger proof framing so after evidence is captured before shipping";
1629
1704
  }
@@ -3028,6 +3103,7 @@ function setRunStatus(state, status, at = timestamp()) {
3028
3103
  }
3029
3104
 
3030
3105
  // src/engine-harness.ts
3106
+ init_proof_run_core();
3031
3107
  var DEFAULT_MAX_ITERATIONS = 12;
3032
3108
  var DEFAULT_STAGE_ITERATION_LIMITS = {
3033
3109
  setup: 2,
@@ -3292,6 +3368,26 @@ function proofAssessmentContinuation(result, payload) {
3292
3368
  const proof_assessment_json = jsonParam(payload);
3293
3369
  return { ...baseContinuation(result), proof_assessment_json };
3294
3370
  }
3371
+ function proofAssessmentVisualBlocker(state, payload) {
3372
+ if (!proofAssessmentRequestsShip(payload)) return null;
3373
+ const source = nonEmptyString(payload.source) || "supervising_agent";
3374
+ return visualDeltaShipGateReason({
3375
+ ...state || {},
3376
+ proof_assessment: { ...payload, source },
3377
+ proof_assessment_source: source
3378
+ });
3379
+ }
3380
+ function blockedProofAssessmentPayload(payload, blocker) {
3381
+ const blockers = Array.isArray(payload.blockers) ? payload.blockers.filter((item) => typeof item === "string") : [];
3382
+ return {
3383
+ ...payload,
3384
+ blocked_decision: payload.decision || "ready_to_ship",
3385
+ decision: "needs_richer_proof",
3386
+ recommended_stage: payload.recommended_stage === "ship" ? "verify" : payload.recommended_stage || "verify",
3387
+ continue_with_stage: payload.continue_with_stage === "ship" ? "verify" : payload.continue_with_stage || "verify",
3388
+ blockers: [...blockers, blocker]
3389
+ };
3390
+ }
3295
3391
  function contextFor(request, state, result) {
3296
3392
  return {
3297
3393
  request,
@@ -3661,6 +3757,20 @@ async function routeCheckpoint(request, state, result, agent, input) {
3661
3757
  summary: assessment.summary,
3662
3758
  details: { payload }
3663
3759
  });
3760
+ const visualBlocker = proofAssessmentVisualBlocker({
3761
+ ...context.fullRiddleState || {},
3762
+ verification_mode: context.fullRiddleState?.verification_mode || request.verification_mode
3763
+ }, payload);
3764
+ if (visualBlocker) {
3765
+ recordEvent(state, {
3766
+ kind: "agent.proof_assessment.blocked",
3767
+ checkpoint,
3768
+ stage: "verify",
3769
+ summary: visualBlocker,
3770
+ details: { payload }
3771
+ });
3772
+ return { next: proofAssessmentContinuation(result, blockedProofAssessmentPayload(payload, visualBlocker)) };
3773
+ }
3664
3774
  if (effectiveShipMode(request, input.config) !== "ship" && proofAssessmentRequestsShip(payload)) {
3665
3775
  return {
3666
3776
  terminal: terminalResult(
@@ -2,7 +2,8 @@ import {
2
2
  createDisabledRiddleProofAgentAdapter,
3
3
  readRiddleProofRunStatus,
4
4
  runRiddleProofEngineHarness
5
- } from "./chunk-LIDYNU7Q.js";
5
+ } from "./chunk-CHKYSZLU.js";
6
+ import "./chunk-A5AWVY5A.js";
6
7
  import "./chunk-7ZJAUEUN.js";
7
8
  import "./chunk-TMMKRKY5.js";
8
9
  export {
package/dist/index.cjs CHANGED
@@ -340,6 +340,44 @@ function normalizedProofAssessment(state = {}) {
340
340
  source: source || null
341
341
  };
342
342
  }
343
+ function objectValue(value) {
344
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
345
+ }
346
+ function normalizedVerificationMode(state = {}) {
347
+ const bundle = objectValue(state?.evidence_bundle);
348
+ const bundleMode = String(bundle.verification_mode || "").trim().toLowerCase();
349
+ if (bundleMode) return bundleMode;
350
+ return String(state?.verification_mode || "proof").trim().toLowerCase() || "proof";
351
+ }
352
+ function visualDeltaRequiredForState(state = {}) {
353
+ const bundle = objectValue(state?.evidence_bundle);
354
+ const contract = objectValue(bundle.artifact_contract);
355
+ const required = objectValue(contract.required);
356
+ return required.visual_delta === true || VISUAL_FIRST_MODES.has(normalizedVerificationMode(state));
357
+ }
358
+ function visualDeltaForState(state = {}) {
359
+ const bundle = objectValue(state?.evidence_bundle);
360
+ const after = objectValue(bundle.after);
361
+ const afterDelta = objectValue(after.visual_delta);
362
+ if (Object.keys(afterDelta).length) return afterDelta;
363
+ const request = objectValue(state?.proof_assessment_request);
364
+ return objectValue(request.visual_delta);
365
+ }
366
+ function visualDeltaShipGateReason(state = {}) {
367
+ if (!visualDeltaRequiredForState(state)) return null;
368
+ const visualDelta = visualDeltaForState(state);
369
+ if (visualDelta.status === "measured" && visualDelta.passed === true) return null;
370
+ const status = String(visualDelta.status || "missing");
371
+ if (status === "unmeasured") {
372
+ return "visual_delta.status=unmeasured blocks ready_to_ship for visual/UI proof";
373
+ }
374
+ if (status === "measured" && visualDelta.passed === false) {
375
+ return "visual_delta.status=measured but visual_delta.passed=false blocks ready_to_ship for visual/UI proof";
376
+ }
377
+ const reason = String(visualDelta.reason || "").trim();
378
+ if (reason) return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof: ${reason}`;
379
+ return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof`;
380
+ }
343
381
  function requiredBaselineLabelsForState(state = {}) {
344
382
  const reference = normalizedReference(state);
345
383
  const labels = [];
@@ -355,6 +393,10 @@ function validateShipGate(state = {}) {
355
393
  const afterCdn = String(state?.after_cdn || "").trim();
356
394
  const verifyStatus = String(state?.verify_status || "").trim();
357
395
  const proofAssessment = normalizedProofAssessment(state);
396
+ const verificationMode = normalizedVerificationMode(state);
397
+ const visualDelta = visualDeltaForState(state);
398
+ const visualDeltaRequired = visualDeltaRequiredForState(state);
399
+ const visualDeltaBlocker = visualDeltaShipGateReason(state);
358
400
  const reasons = [];
359
401
  if (!["before", "prod", "both"].includes(reference)) {
360
402
  reasons.push(`reference must be before, prod, or both; got ${reference}`);
@@ -383,19 +425,26 @@ function validateShipGate(state = {}) {
383
425
  if (proofAssessment.decision !== "ready_to_ship") {
384
426
  reasons.push("proof_assessment.decision must be ready_to_ship before ship");
385
427
  }
428
+ if (visualDeltaBlocker) {
429
+ reasons.push(visualDeltaBlocker);
430
+ }
386
431
  return {
387
432
  ok: reasons.length === 0,
388
433
  reasons,
389
434
  required_baselines: requiredBaselines,
390
435
  evidence: {
391
436
  reference,
437
+ verification_mode: verificationMode || null,
392
438
  prod_url: prodUrl || null,
393
439
  before_cdn: beforeCdn || null,
394
440
  prod_cdn: prodCdn || null,
395
441
  after_cdn: afterCdn || null,
396
442
  verify_status: verifyStatus || null,
397
443
  proof_assessment_decision: proofAssessment.decision,
398
- proof_assessment_source: proofAssessment.source
444
+ proof_assessment_source: proofAssessment.source,
445
+ visual_delta_required: visualDeltaRequired,
446
+ visual_delta_status: typeof visualDelta.status === "string" ? visualDelta.status : null,
447
+ visual_delta_passed: typeof visualDelta.passed === "boolean" ? visualDelta.passed : null
399
448
  }
400
449
  };
401
450
  }
@@ -541,23 +590,36 @@ function mergeStateFromParams(statePath, params) {
541
590
  state.proof_assessment_source = null;
542
591
  } else {
543
592
  const parsed = JSON.parse(raw);
544
- state.proof_assessment = {
593
+ const assessment = {
545
594
  ...parsed,
546
595
  source: (parsed?.source || "supervising_agent").toString()
547
596
  };
597
+ const readyBlocker = assessment?.decision === "ready_to_ship" ? visualDeltaShipGateReason({ ...state, proof_assessment: assessment, proof_assessment_source: assessment.source }) : null;
598
+ if (readyBlocker) {
599
+ assessment.blocked_decision = assessment.decision;
600
+ assessment.decision = "needs_richer_proof";
601
+ if (assessment.recommended_stage === "ship") assessment.recommended_stage = "verify";
602
+ if (assessment.continue_with_stage === "ship") assessment.continue_with_stage = "verify";
603
+ const blockers = Array.isArray(assessment.blockers) ? assessment.blockers : [];
604
+ assessment.blockers = [...blockers, readyBlocker];
605
+ }
606
+ state.proof_assessment = assessment;
548
607
  state.proof_assessment_source = state.proof_assessment.source;
549
- if (typeof parsed?.decision === "string") {
550
- state.proof_decision = parsed.decision;
608
+ if (typeof state.proof_assessment?.decision === "string") {
609
+ state.proof_decision = state.proof_assessment.decision;
551
610
  }
552
611
  if (typeof parsed?.summary === "string") {
553
612
  state.proof_assessment_summary = normalizeOptionalString(parsed.summary) || null;
554
613
  }
555
- if (parsed?.decision === "ready_to_ship") {
614
+ if (state.proof_assessment?.decision === "ready_to_ship") {
556
615
  state.merge_recommendation = "ready-to-ship";
557
- } else if (typeof parsed?.decision === "string" && parsed.decision.trim()) {
616
+ } else if (typeof state.proof_assessment?.decision === "string" && state.proof_assessment.decision.trim()) {
558
617
  state.merge_recommendation = "do-not-merge";
559
618
  }
560
619
  appendProofSummaryLine(state, `Supervising proof assessment: ${state.proof_assessment.decision || "unknown"}`);
620
+ if (readyBlocker) {
621
+ appendProofSummaryLine(state, `Ready-to-ship assessment blocked: ${readyBlocker}`);
622
+ }
561
623
  if (state.proof_assessment_summary) {
562
624
  appendProofSummaryLine(state, `Assessment summary: ${state.proof_assessment_summary}`);
563
625
  }
@@ -679,7 +741,7 @@ function summarizeState(state) {
679
741
  state: selected
680
742
  };
681
743
  }
682
- var import_node_fs, import_node_crypto, import_node_path, import_node_url, import_meta, WORKFLOW_STAGE_ORDER, CHECKPOINT_CONTRACT_VERSION, BUNDLED_RIDDLE_PROOF_DIR, RIDDLE_PROOF_DIR_CANDIDATES, CHECKPOINT_CONTRACT_SPECS;
744
+ var import_node_fs, import_node_crypto, import_node_path, import_node_url, import_meta, WORKFLOW_STAGE_ORDER, CHECKPOINT_CONTRACT_VERSION, BUNDLED_RIDDLE_PROOF_DIR, RIDDLE_PROOF_DIR_CANDIDATES, VISUAL_FIRST_MODES, CHECKPOINT_CONTRACT_SPECS;
683
745
  var init_proof_run_core = __esm({
684
746
  "src/proof-run-core.ts"() {
685
747
  "use strict";
@@ -698,6 +760,16 @@ var init_proof_run_core = __esm({
698
760
  RIDDLE_PROOF_DIR_CANDIDATES = [
699
761
  BUNDLED_RIDDLE_PROOF_DIR
700
762
  ];
763
+ VISUAL_FIRST_MODES = /* @__PURE__ */ new Set([
764
+ "visual",
765
+ "render",
766
+ "interaction",
767
+ "ui",
768
+ "layout",
769
+ "screenshot",
770
+ "canvas",
771
+ "animation"
772
+ ]);
701
773
  CHECKPOINT_CONTRACT_SPECS = {
702
774
  setup_review: {
703
775
  purpose: "Inspect prepared workspace state before moving into recon."
@@ -1624,6 +1696,9 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
1624
1696
  if (reasons.some((reason) => reason.includes("proof_assessment"))) {
1625
1697
  return "resume with riddle_proof_review using decision=ready_to_ship only after the screenshots and semantic evidence visibly prove the request; otherwise choose needs_implementation or needs_richer_proof";
1626
1698
  }
1699
+ if (reasons.some((reason) => reason.includes("visual_delta"))) {
1700
+ return "rerun verify with a measured before/after visual delta, or choose needs_richer_proof instead of ready_to_ship for this visual proof";
1701
+ }
1627
1702
  if (reasons.some((reason) => reason.includes("after_cdn") || reason.includes("verify_status"))) {
1628
1703
  return "rerun verify with stronger proof framing so after evidence is captured before shipping";
1629
1704
  }
@@ -3584,6 +3659,7 @@ var import_node_child_process2 = require("child_process");
3584
3659
  var import_node_fs3 = require("fs");
3585
3660
  var import_node_path3 = __toESM(require("path"), 1);
3586
3661
  var import_node_crypto2 = __toESM(require("crypto"), 1);
3662
+ init_proof_run_core();
3587
3663
  var DEFAULT_MAX_ITERATIONS = 12;
3588
3664
  var DEFAULT_STAGE_ITERATION_LIMITS = {
3589
3665
  setup: 2,
@@ -3848,6 +3924,26 @@ function proofAssessmentContinuation(result, payload) {
3848
3924
  const proof_assessment_json = jsonParam(payload);
3849
3925
  return { ...baseContinuation(result), proof_assessment_json };
3850
3926
  }
3927
+ function proofAssessmentVisualBlocker(state, payload) {
3928
+ if (!proofAssessmentRequestsShip(payload)) return null;
3929
+ const source = nonEmptyString(payload.source) || "supervising_agent";
3930
+ return visualDeltaShipGateReason({
3931
+ ...state || {},
3932
+ proof_assessment: { ...payload, source },
3933
+ proof_assessment_source: source
3934
+ });
3935
+ }
3936
+ function blockedProofAssessmentPayload(payload, blocker) {
3937
+ const blockers = Array.isArray(payload.blockers) ? payload.blockers.filter((item) => typeof item === "string") : [];
3938
+ return {
3939
+ ...payload,
3940
+ blocked_decision: payload.decision || "ready_to_ship",
3941
+ decision: "needs_richer_proof",
3942
+ recommended_stage: payload.recommended_stage === "ship" ? "verify" : payload.recommended_stage || "verify",
3943
+ continue_with_stage: payload.continue_with_stage === "ship" ? "verify" : payload.continue_with_stage || "verify",
3944
+ blockers: [...blockers, blocker]
3945
+ };
3946
+ }
3851
3947
  function contextFor(request, state, result) {
3852
3948
  return {
3853
3949
  request,
@@ -4217,6 +4313,20 @@ async function routeCheckpoint(request, state, result, agent, input) {
4217
4313
  summary: assessment.summary,
4218
4314
  details: { payload }
4219
4315
  });
4316
+ const visualBlocker = proofAssessmentVisualBlocker({
4317
+ ...context.fullRiddleState || {},
4318
+ verification_mode: context.fullRiddleState?.verification_mode || request.verification_mode
4319
+ }, payload);
4320
+ if (visualBlocker) {
4321
+ recordEvent(state, {
4322
+ kind: "agent.proof_assessment.blocked",
4323
+ checkpoint,
4324
+ stage: "verify",
4325
+ summary: visualBlocker,
4326
+ details: { payload }
4327
+ });
4328
+ return { next: proofAssessmentContinuation(result, blockedProofAssessmentPayload(payload, visualBlocker)) };
4329
+ }
4220
4330
  if (effectiveShipMode(request, input.config) !== "ship" && proofAssessmentRequestsShip(payload)) {
4221
4331
  return {
4222
4332
  terminal: terminalResult(
package/dist/index.js CHANGED
@@ -13,7 +13,8 @@ import {
13
13
  createDisabledRiddleProofAgentAdapter,
14
14
  readRiddleProofRunStatus,
15
15
  runRiddleProofEngineHarness
16
- } from "./chunk-LIDYNU7Q.js";
16
+ } from "./chunk-CHKYSZLU.js";
17
+ import "./chunk-A5AWVY5A.js";
17
18
  import {
18
19
  runRiddleProof
19
20
  } from "./chunk-F6VFKS7K.js";