@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.
- package/dist/{chunk-R4BPJNAM.js → chunk-A5AWVY5A.js} +81 -6
- package/dist/{chunk-LIDYNU7Q.js → chunk-CHKYSZLU.js} +37 -0
- package/dist/engine-harness.cjs +117 -7
- package/dist/engine-harness.js +2 -1
- package/dist/index.cjs +117 -7
- package/dist/index.js +2 -1
- package/dist/proof-run-core.cjs +84 -6
- package/dist/proof-run-core.d.cts +8 -1
- package/dist/proof-run-core.d.ts +8 -1
- package/dist/proof-run-core.js +7 -1
- package/dist/proof-run-engine.cjs +81 -6
- package/dist/proof-run-engine.js +4 -1
- package/package.json +1 -1
- package/runtime/lib/ship.py +55 -1
- package/runtime/lib/verify.py +38 -4
- package/runtime/tests/recon_verify_smoke.py +61 -2
package/dist/proof-run-core.cjs
CHANGED
|
@@ -50,6 +50,9 @@ __export(proof_run_core_exports, {
|
|
|
50
50
|
setStageDecisionRequest: () => setStageDecisionRequest,
|
|
51
51
|
summarizeState: () => summarizeState,
|
|
52
52
|
validateShipGate: () => validateShipGate,
|
|
53
|
+
visualDeltaForState: () => visualDeltaForState,
|
|
54
|
+
visualDeltaRequiredForState: () => visualDeltaRequiredForState,
|
|
55
|
+
visualDeltaShipGateReason: () => visualDeltaShipGateReason,
|
|
53
56
|
workflowFile: () => workflowFile,
|
|
54
57
|
writeState: () => writeState
|
|
55
58
|
});
|
|
@@ -378,6 +381,54 @@ function normalizedProofAssessment(state = {}) {
|
|
|
378
381
|
source: source || null
|
|
379
382
|
};
|
|
380
383
|
}
|
|
384
|
+
var VISUAL_FIRST_MODES = /* @__PURE__ */ new Set([
|
|
385
|
+
"visual",
|
|
386
|
+
"render",
|
|
387
|
+
"interaction",
|
|
388
|
+
"ui",
|
|
389
|
+
"layout",
|
|
390
|
+
"screenshot",
|
|
391
|
+
"canvas",
|
|
392
|
+
"animation"
|
|
393
|
+
]);
|
|
394
|
+
function objectValue(value) {
|
|
395
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
396
|
+
}
|
|
397
|
+
function normalizedVerificationMode(state = {}) {
|
|
398
|
+
const bundle = objectValue(state?.evidence_bundle);
|
|
399
|
+
const bundleMode = String(bundle.verification_mode || "").trim().toLowerCase();
|
|
400
|
+
if (bundleMode) return bundleMode;
|
|
401
|
+
return String(state?.verification_mode || "proof").trim().toLowerCase() || "proof";
|
|
402
|
+
}
|
|
403
|
+
function visualDeltaRequiredForState(state = {}) {
|
|
404
|
+
const bundle = objectValue(state?.evidence_bundle);
|
|
405
|
+
const contract = objectValue(bundle.artifact_contract);
|
|
406
|
+
const required = objectValue(contract.required);
|
|
407
|
+
return required.visual_delta === true || VISUAL_FIRST_MODES.has(normalizedVerificationMode(state));
|
|
408
|
+
}
|
|
409
|
+
function visualDeltaForState(state = {}) {
|
|
410
|
+
const bundle = objectValue(state?.evidence_bundle);
|
|
411
|
+
const after = objectValue(bundle.after);
|
|
412
|
+
const afterDelta = objectValue(after.visual_delta);
|
|
413
|
+
if (Object.keys(afterDelta).length) return afterDelta;
|
|
414
|
+
const request = objectValue(state?.proof_assessment_request);
|
|
415
|
+
return objectValue(request.visual_delta);
|
|
416
|
+
}
|
|
417
|
+
function visualDeltaShipGateReason(state = {}) {
|
|
418
|
+
if (!visualDeltaRequiredForState(state)) return null;
|
|
419
|
+
const visualDelta = visualDeltaForState(state);
|
|
420
|
+
if (visualDelta.status === "measured" && visualDelta.passed === true) return null;
|
|
421
|
+
const status = String(visualDelta.status || "missing");
|
|
422
|
+
if (status === "unmeasured") {
|
|
423
|
+
return "visual_delta.status=unmeasured blocks ready_to_ship for visual/UI proof";
|
|
424
|
+
}
|
|
425
|
+
if (status === "measured" && visualDelta.passed === false) {
|
|
426
|
+
return "visual_delta.status=measured but visual_delta.passed=false blocks ready_to_ship for visual/UI proof";
|
|
427
|
+
}
|
|
428
|
+
const reason = String(visualDelta.reason || "").trim();
|
|
429
|
+
if (reason) return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof: ${reason}`;
|
|
430
|
+
return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof`;
|
|
431
|
+
}
|
|
381
432
|
function requiredBaselineLabelsForState(state = {}) {
|
|
382
433
|
const reference = normalizedReference(state);
|
|
383
434
|
const labels = [];
|
|
@@ -393,6 +444,10 @@ function validateShipGate(state = {}) {
|
|
|
393
444
|
const afterCdn = String(state?.after_cdn || "").trim();
|
|
394
445
|
const verifyStatus = String(state?.verify_status || "").trim();
|
|
395
446
|
const proofAssessment = normalizedProofAssessment(state);
|
|
447
|
+
const verificationMode = normalizedVerificationMode(state);
|
|
448
|
+
const visualDelta = visualDeltaForState(state);
|
|
449
|
+
const visualDeltaRequired = visualDeltaRequiredForState(state);
|
|
450
|
+
const visualDeltaBlocker = visualDeltaShipGateReason(state);
|
|
396
451
|
const reasons = [];
|
|
397
452
|
if (!["before", "prod", "both"].includes(reference)) {
|
|
398
453
|
reasons.push(`reference must be before, prod, or both; got ${reference}`);
|
|
@@ -421,19 +476,26 @@ function validateShipGate(state = {}) {
|
|
|
421
476
|
if (proofAssessment.decision !== "ready_to_ship") {
|
|
422
477
|
reasons.push("proof_assessment.decision must be ready_to_ship before ship");
|
|
423
478
|
}
|
|
479
|
+
if (visualDeltaBlocker) {
|
|
480
|
+
reasons.push(visualDeltaBlocker);
|
|
481
|
+
}
|
|
424
482
|
return {
|
|
425
483
|
ok: reasons.length === 0,
|
|
426
484
|
reasons,
|
|
427
485
|
required_baselines: requiredBaselines,
|
|
428
486
|
evidence: {
|
|
429
487
|
reference,
|
|
488
|
+
verification_mode: verificationMode || null,
|
|
430
489
|
prod_url: prodUrl || null,
|
|
431
490
|
before_cdn: beforeCdn || null,
|
|
432
491
|
prod_cdn: prodCdn || null,
|
|
433
492
|
after_cdn: afterCdn || null,
|
|
434
493
|
verify_status: verifyStatus || null,
|
|
435
494
|
proof_assessment_decision: proofAssessment.decision,
|
|
436
|
-
proof_assessment_source: proofAssessment.source
|
|
495
|
+
proof_assessment_source: proofAssessment.source,
|
|
496
|
+
visual_delta_required: visualDeltaRequired,
|
|
497
|
+
visual_delta_status: typeof visualDelta.status === "string" ? visualDelta.status : null,
|
|
498
|
+
visual_delta_passed: typeof visualDelta.passed === "boolean" ? visualDelta.passed : null
|
|
437
499
|
}
|
|
438
500
|
};
|
|
439
501
|
}
|
|
@@ -747,23 +809,36 @@ function mergeStateFromParams(statePath, params) {
|
|
|
747
809
|
state.proof_assessment_source = null;
|
|
748
810
|
} else {
|
|
749
811
|
const parsed = JSON.parse(raw);
|
|
750
|
-
|
|
812
|
+
const assessment = {
|
|
751
813
|
...parsed,
|
|
752
814
|
source: (parsed?.source || "supervising_agent").toString()
|
|
753
815
|
};
|
|
816
|
+
const readyBlocker = assessment?.decision === "ready_to_ship" ? visualDeltaShipGateReason({ ...state, proof_assessment: assessment, proof_assessment_source: assessment.source }) : null;
|
|
817
|
+
if (readyBlocker) {
|
|
818
|
+
assessment.blocked_decision = assessment.decision;
|
|
819
|
+
assessment.decision = "needs_richer_proof";
|
|
820
|
+
if (assessment.recommended_stage === "ship") assessment.recommended_stage = "verify";
|
|
821
|
+
if (assessment.continue_with_stage === "ship") assessment.continue_with_stage = "verify";
|
|
822
|
+
const blockers = Array.isArray(assessment.blockers) ? assessment.blockers : [];
|
|
823
|
+
assessment.blockers = [...blockers, readyBlocker];
|
|
824
|
+
}
|
|
825
|
+
state.proof_assessment = assessment;
|
|
754
826
|
state.proof_assessment_source = state.proof_assessment.source;
|
|
755
|
-
if (typeof
|
|
756
|
-
state.proof_decision =
|
|
827
|
+
if (typeof state.proof_assessment?.decision === "string") {
|
|
828
|
+
state.proof_decision = state.proof_assessment.decision;
|
|
757
829
|
}
|
|
758
830
|
if (typeof parsed?.summary === "string") {
|
|
759
831
|
state.proof_assessment_summary = normalizeOptionalString(parsed.summary) || null;
|
|
760
832
|
}
|
|
761
|
-
if (
|
|
833
|
+
if (state.proof_assessment?.decision === "ready_to_ship") {
|
|
762
834
|
state.merge_recommendation = "ready-to-ship";
|
|
763
|
-
} else if (typeof
|
|
835
|
+
} else if (typeof state.proof_assessment?.decision === "string" && state.proof_assessment.decision.trim()) {
|
|
764
836
|
state.merge_recommendation = "do-not-merge";
|
|
765
837
|
}
|
|
766
838
|
appendProofSummaryLine(state, `Supervising proof assessment: ${state.proof_assessment.decision || "unknown"}`);
|
|
839
|
+
if (readyBlocker) {
|
|
840
|
+
appendProofSummaryLine(state, `Ready-to-ship assessment blocked: ${readyBlocker}`);
|
|
841
|
+
}
|
|
767
842
|
if (state.proof_assessment_summary) {
|
|
768
843
|
appendProofSummaryLine(state, `Assessment summary: ${state.proof_assessment_summary}`);
|
|
769
844
|
}
|
|
@@ -907,6 +982,9 @@ function summarizeState(state) {
|
|
|
907
982
|
setStageDecisionRequest,
|
|
908
983
|
summarizeState,
|
|
909
984
|
validateShipGate,
|
|
985
|
+
visualDeltaForState,
|
|
986
|
+
visualDeltaRequiredForState,
|
|
987
|
+
visualDeltaShipGateReason,
|
|
910
988
|
workflowFile,
|
|
911
989
|
writeState
|
|
912
990
|
});
|
|
@@ -70,6 +70,7 @@ interface ShipGateValidation {
|
|
|
70
70
|
required_baselines: string[];
|
|
71
71
|
evidence: {
|
|
72
72
|
reference: string;
|
|
73
|
+
verification_mode: string | null;
|
|
73
74
|
prod_url: string | null;
|
|
74
75
|
before_cdn: string | null;
|
|
75
76
|
prod_cdn: string | null;
|
|
@@ -77,6 +78,9 @@ interface ShipGateValidation {
|
|
|
77
78
|
verify_status: string | null;
|
|
78
79
|
proof_assessment_decision: string | null;
|
|
79
80
|
proof_assessment_source: string | null;
|
|
81
|
+
visual_delta_required: boolean;
|
|
82
|
+
visual_delta_status: string | null;
|
|
83
|
+
visual_delta_passed: boolean | null;
|
|
80
84
|
};
|
|
81
85
|
}
|
|
82
86
|
declare function resolveRiddleProofDir(config?: PluginConfig): string;
|
|
@@ -192,6 +196,9 @@ declare function invalidateVerifyEvidence(state?: any): {
|
|
|
192
196
|
hadProofAssessment: boolean;
|
|
193
197
|
hadProofAssessmentRequest: boolean;
|
|
194
198
|
};
|
|
199
|
+
declare function visualDeltaRequiredForState(state?: any): boolean;
|
|
200
|
+
declare function visualDeltaForState(state?: any): Record<string, any>;
|
|
201
|
+
declare function visualDeltaShipGateReason(state?: any): string | null;
|
|
195
202
|
declare function requiredBaselineLabelsForState(state?: any): string[];
|
|
196
203
|
declare function validateShipGate(state?: any): ShipGateValidation;
|
|
197
204
|
declare function buildCheckpointContract(state: any, request: {
|
|
@@ -283,4 +290,4 @@ declare function summarizeState(state: any): {
|
|
|
283
290
|
};
|
|
284
291
|
};
|
|
285
292
|
|
|
286
|
-
export { BUNDLED_RIDDLE_PROOF_DIR, CHECKPOINT_CONTRACT_VERSION, type CheckpointInputContract, type PluginConfig, RIDDLE_PROOF_DIR_CANDIDATES, type ShipGateValidation, WORKFLOW_STAGE_ORDER, type WorkflowAction, type WorkflowParams, type WorkflowStage, buildCheckpointContract, buildSetupArgs, checkpointContinueStage, clearStageDecisionRequest, ensureAction, ensureStageLoopState, invalidateVerifyEvidence, mergeStateFromParams, readState, recordStageAttempt, requiredBaselineLabelsForState, resolveConfig, resolveRiddleProofDir, setStageDecisionRequest, summarizeState, validateShipGate, workflowFile, writeState };
|
|
293
|
+
export { BUNDLED_RIDDLE_PROOF_DIR, CHECKPOINT_CONTRACT_VERSION, type CheckpointInputContract, type PluginConfig, RIDDLE_PROOF_DIR_CANDIDATES, type ShipGateValidation, WORKFLOW_STAGE_ORDER, type WorkflowAction, type WorkflowParams, type WorkflowStage, buildCheckpointContract, buildSetupArgs, checkpointContinueStage, clearStageDecisionRequest, ensureAction, ensureStageLoopState, invalidateVerifyEvidence, mergeStateFromParams, readState, recordStageAttempt, requiredBaselineLabelsForState, resolveConfig, resolveRiddleProofDir, setStageDecisionRequest, summarizeState, validateShipGate, visualDeltaForState, visualDeltaRequiredForState, visualDeltaShipGateReason, workflowFile, writeState };
|
package/dist/proof-run-core.d.ts
CHANGED
|
@@ -70,6 +70,7 @@ interface ShipGateValidation {
|
|
|
70
70
|
required_baselines: string[];
|
|
71
71
|
evidence: {
|
|
72
72
|
reference: string;
|
|
73
|
+
verification_mode: string | null;
|
|
73
74
|
prod_url: string | null;
|
|
74
75
|
before_cdn: string | null;
|
|
75
76
|
prod_cdn: string | null;
|
|
@@ -77,6 +78,9 @@ interface ShipGateValidation {
|
|
|
77
78
|
verify_status: string | null;
|
|
78
79
|
proof_assessment_decision: string | null;
|
|
79
80
|
proof_assessment_source: string | null;
|
|
81
|
+
visual_delta_required: boolean;
|
|
82
|
+
visual_delta_status: string | null;
|
|
83
|
+
visual_delta_passed: boolean | null;
|
|
80
84
|
};
|
|
81
85
|
}
|
|
82
86
|
declare function resolveRiddleProofDir(config?: PluginConfig): string;
|
|
@@ -192,6 +196,9 @@ declare function invalidateVerifyEvidence(state?: any): {
|
|
|
192
196
|
hadProofAssessment: boolean;
|
|
193
197
|
hadProofAssessmentRequest: boolean;
|
|
194
198
|
};
|
|
199
|
+
declare function visualDeltaRequiredForState(state?: any): boolean;
|
|
200
|
+
declare function visualDeltaForState(state?: any): Record<string, any>;
|
|
201
|
+
declare function visualDeltaShipGateReason(state?: any): string | null;
|
|
195
202
|
declare function requiredBaselineLabelsForState(state?: any): string[];
|
|
196
203
|
declare function validateShipGate(state?: any): ShipGateValidation;
|
|
197
204
|
declare function buildCheckpointContract(state: any, request: {
|
|
@@ -283,4 +290,4 @@ declare function summarizeState(state: any): {
|
|
|
283
290
|
};
|
|
284
291
|
};
|
|
285
292
|
|
|
286
|
-
export { BUNDLED_RIDDLE_PROOF_DIR, CHECKPOINT_CONTRACT_VERSION, type CheckpointInputContract, type PluginConfig, RIDDLE_PROOF_DIR_CANDIDATES, type ShipGateValidation, WORKFLOW_STAGE_ORDER, type WorkflowAction, type WorkflowParams, type WorkflowStage, buildCheckpointContract, buildSetupArgs, checkpointContinueStage, clearStageDecisionRequest, ensureAction, ensureStageLoopState, invalidateVerifyEvidence, mergeStateFromParams, readState, recordStageAttempt, requiredBaselineLabelsForState, resolveConfig, resolveRiddleProofDir, setStageDecisionRequest, summarizeState, validateShipGate, workflowFile, writeState };
|
|
293
|
+
export { BUNDLED_RIDDLE_PROOF_DIR, CHECKPOINT_CONTRACT_VERSION, type CheckpointInputContract, type PluginConfig, RIDDLE_PROOF_DIR_CANDIDATES, type ShipGateValidation, WORKFLOW_STAGE_ORDER, type WorkflowAction, type WorkflowParams, type WorkflowStage, buildCheckpointContract, buildSetupArgs, checkpointContinueStage, clearStageDecisionRequest, ensureAction, ensureStageLoopState, invalidateVerifyEvidence, mergeStateFromParams, readState, recordStageAttempt, requiredBaselineLabelsForState, resolveConfig, resolveRiddleProofDir, setStageDecisionRequest, summarizeState, validateShipGate, visualDeltaForState, visualDeltaRequiredForState, visualDeltaShipGateReason, workflowFile, writeState };
|
package/dist/proof-run-core.js
CHANGED
|
@@ -19,9 +19,12 @@ import {
|
|
|
19
19
|
setStageDecisionRequest,
|
|
20
20
|
summarizeState,
|
|
21
21
|
validateShipGate,
|
|
22
|
+
visualDeltaForState,
|
|
23
|
+
visualDeltaRequiredForState,
|
|
24
|
+
visualDeltaShipGateReason,
|
|
22
25
|
workflowFile,
|
|
23
26
|
writeState
|
|
24
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-A5AWVY5A.js";
|
|
25
28
|
export {
|
|
26
29
|
BUNDLED_RIDDLE_PROOF_DIR,
|
|
27
30
|
CHECKPOINT_CONTRACT_VERSION,
|
|
@@ -43,6 +46,9 @@ export {
|
|
|
43
46
|
setStageDecisionRequest,
|
|
44
47
|
summarizeState,
|
|
45
48
|
validateShipGate,
|
|
49
|
+
visualDeltaForState,
|
|
50
|
+
visualDeltaRequiredForState,
|
|
51
|
+
visualDeltaShipGateReason,
|
|
46
52
|
workflowFile,
|
|
47
53
|
writeState
|
|
48
54
|
};
|
|
@@ -363,6 +363,54 @@ function normalizedProofAssessment(state = {}) {
|
|
|
363
363
|
source: source || null
|
|
364
364
|
};
|
|
365
365
|
}
|
|
366
|
+
var VISUAL_FIRST_MODES = /* @__PURE__ */ new Set([
|
|
367
|
+
"visual",
|
|
368
|
+
"render",
|
|
369
|
+
"interaction",
|
|
370
|
+
"ui",
|
|
371
|
+
"layout",
|
|
372
|
+
"screenshot",
|
|
373
|
+
"canvas",
|
|
374
|
+
"animation"
|
|
375
|
+
]);
|
|
376
|
+
function objectValue(value) {
|
|
377
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
378
|
+
}
|
|
379
|
+
function normalizedVerificationMode(state = {}) {
|
|
380
|
+
const bundle = objectValue(state?.evidence_bundle);
|
|
381
|
+
const bundleMode = String(bundle.verification_mode || "").trim().toLowerCase();
|
|
382
|
+
if (bundleMode) return bundleMode;
|
|
383
|
+
return String(state?.verification_mode || "proof").trim().toLowerCase() || "proof";
|
|
384
|
+
}
|
|
385
|
+
function visualDeltaRequiredForState(state = {}) {
|
|
386
|
+
const bundle = objectValue(state?.evidence_bundle);
|
|
387
|
+
const contract = objectValue(bundle.artifact_contract);
|
|
388
|
+
const required = objectValue(contract.required);
|
|
389
|
+
return required.visual_delta === true || VISUAL_FIRST_MODES.has(normalizedVerificationMode(state));
|
|
390
|
+
}
|
|
391
|
+
function visualDeltaForState(state = {}) {
|
|
392
|
+
const bundle = objectValue(state?.evidence_bundle);
|
|
393
|
+
const after = objectValue(bundle.after);
|
|
394
|
+
const afterDelta = objectValue(after.visual_delta);
|
|
395
|
+
if (Object.keys(afterDelta).length) return afterDelta;
|
|
396
|
+
const request = objectValue(state?.proof_assessment_request);
|
|
397
|
+
return objectValue(request.visual_delta);
|
|
398
|
+
}
|
|
399
|
+
function visualDeltaShipGateReason(state = {}) {
|
|
400
|
+
if (!visualDeltaRequiredForState(state)) return null;
|
|
401
|
+
const visualDelta = visualDeltaForState(state);
|
|
402
|
+
if (visualDelta.status === "measured" && visualDelta.passed === true) return null;
|
|
403
|
+
const status = String(visualDelta.status || "missing");
|
|
404
|
+
if (status === "unmeasured") {
|
|
405
|
+
return "visual_delta.status=unmeasured blocks ready_to_ship for visual/UI proof";
|
|
406
|
+
}
|
|
407
|
+
if (status === "measured" && visualDelta.passed === false) {
|
|
408
|
+
return "visual_delta.status=measured but visual_delta.passed=false blocks ready_to_ship for visual/UI proof";
|
|
409
|
+
}
|
|
410
|
+
const reason = String(visualDelta.reason || "").trim();
|
|
411
|
+
if (reason) return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof: ${reason}`;
|
|
412
|
+
return `visual_delta.status=${status} blocks ready_to_ship for visual/UI proof`;
|
|
413
|
+
}
|
|
366
414
|
function requiredBaselineLabelsForState(state = {}) {
|
|
367
415
|
const reference = normalizedReference(state);
|
|
368
416
|
const labels = [];
|
|
@@ -378,6 +426,10 @@ function validateShipGate(state = {}) {
|
|
|
378
426
|
const afterCdn = String(state?.after_cdn || "").trim();
|
|
379
427
|
const verifyStatus = String(state?.verify_status || "").trim();
|
|
380
428
|
const proofAssessment = normalizedProofAssessment(state);
|
|
429
|
+
const verificationMode = normalizedVerificationMode(state);
|
|
430
|
+
const visualDelta = visualDeltaForState(state);
|
|
431
|
+
const visualDeltaRequired = visualDeltaRequiredForState(state);
|
|
432
|
+
const visualDeltaBlocker = visualDeltaShipGateReason(state);
|
|
381
433
|
const reasons = [];
|
|
382
434
|
if (!["before", "prod", "both"].includes(reference)) {
|
|
383
435
|
reasons.push(`reference must be before, prod, or both; got ${reference}`);
|
|
@@ -406,19 +458,26 @@ function validateShipGate(state = {}) {
|
|
|
406
458
|
if (proofAssessment.decision !== "ready_to_ship") {
|
|
407
459
|
reasons.push("proof_assessment.decision must be ready_to_ship before ship");
|
|
408
460
|
}
|
|
461
|
+
if (visualDeltaBlocker) {
|
|
462
|
+
reasons.push(visualDeltaBlocker);
|
|
463
|
+
}
|
|
409
464
|
return {
|
|
410
465
|
ok: reasons.length === 0,
|
|
411
466
|
reasons,
|
|
412
467
|
required_baselines: requiredBaselines,
|
|
413
468
|
evidence: {
|
|
414
469
|
reference,
|
|
470
|
+
verification_mode: verificationMode || null,
|
|
415
471
|
prod_url: prodUrl || null,
|
|
416
472
|
before_cdn: beforeCdn || null,
|
|
417
473
|
prod_cdn: prodCdn || null,
|
|
418
474
|
after_cdn: afterCdn || null,
|
|
419
475
|
verify_status: verifyStatus || null,
|
|
420
476
|
proof_assessment_decision: proofAssessment.decision,
|
|
421
|
-
proof_assessment_source: proofAssessment.source
|
|
477
|
+
proof_assessment_source: proofAssessment.source,
|
|
478
|
+
visual_delta_required: visualDeltaRequired,
|
|
479
|
+
visual_delta_status: typeof visualDelta.status === "string" ? visualDelta.status : null,
|
|
480
|
+
visual_delta_passed: typeof visualDelta.passed === "boolean" ? visualDelta.passed : null
|
|
422
481
|
}
|
|
423
482
|
};
|
|
424
483
|
}
|
|
@@ -732,23 +791,36 @@ function mergeStateFromParams(statePath, params) {
|
|
|
732
791
|
state.proof_assessment_source = null;
|
|
733
792
|
} else {
|
|
734
793
|
const parsed = JSON.parse(raw);
|
|
735
|
-
|
|
794
|
+
const assessment = {
|
|
736
795
|
...parsed,
|
|
737
796
|
source: (parsed?.source || "supervising_agent").toString()
|
|
738
797
|
};
|
|
798
|
+
const readyBlocker = assessment?.decision === "ready_to_ship" ? visualDeltaShipGateReason({ ...state, proof_assessment: assessment, proof_assessment_source: assessment.source }) : null;
|
|
799
|
+
if (readyBlocker) {
|
|
800
|
+
assessment.blocked_decision = assessment.decision;
|
|
801
|
+
assessment.decision = "needs_richer_proof";
|
|
802
|
+
if (assessment.recommended_stage === "ship") assessment.recommended_stage = "verify";
|
|
803
|
+
if (assessment.continue_with_stage === "ship") assessment.continue_with_stage = "verify";
|
|
804
|
+
const blockers = Array.isArray(assessment.blockers) ? assessment.blockers : [];
|
|
805
|
+
assessment.blockers = [...blockers, readyBlocker];
|
|
806
|
+
}
|
|
807
|
+
state.proof_assessment = assessment;
|
|
739
808
|
state.proof_assessment_source = state.proof_assessment.source;
|
|
740
|
-
if (typeof
|
|
741
|
-
state.proof_decision =
|
|
809
|
+
if (typeof state.proof_assessment?.decision === "string") {
|
|
810
|
+
state.proof_decision = state.proof_assessment.decision;
|
|
742
811
|
}
|
|
743
812
|
if (typeof parsed?.summary === "string") {
|
|
744
813
|
state.proof_assessment_summary = normalizeOptionalString(parsed.summary) || null;
|
|
745
814
|
}
|
|
746
|
-
if (
|
|
815
|
+
if (state.proof_assessment?.decision === "ready_to_ship") {
|
|
747
816
|
state.merge_recommendation = "ready-to-ship";
|
|
748
|
-
} else if (typeof
|
|
817
|
+
} else if (typeof state.proof_assessment?.decision === "string" && state.proof_assessment.decision.trim()) {
|
|
749
818
|
state.merge_recommendation = "do-not-merge";
|
|
750
819
|
}
|
|
751
820
|
appendProofSummaryLine(state, `Supervising proof assessment: ${state.proof_assessment.decision || "unknown"}`);
|
|
821
|
+
if (readyBlocker) {
|
|
822
|
+
appendProofSummaryLine(state, `Ready-to-ship assessment blocked: ${readyBlocker}`);
|
|
823
|
+
}
|
|
752
824
|
if (state.proof_assessment_summary) {
|
|
753
825
|
appendProofSummaryLine(state, `Assessment summary: ${state.proof_assessment_summary}`);
|
|
754
826
|
}
|
|
@@ -1622,6 +1694,9 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
|
|
|
1622
1694
|
if (reasons.some((reason) => reason.includes("proof_assessment"))) {
|
|
1623
1695
|
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";
|
|
1624
1696
|
}
|
|
1697
|
+
if (reasons.some((reason) => reason.includes("visual_delta"))) {
|
|
1698
|
+
return "rerun verify with a measured before/after visual delta, or choose needs_richer_proof instead of ready_to_ship for this visual proof";
|
|
1699
|
+
}
|
|
1625
1700
|
if (reasons.some((reason) => reason.includes("after_cdn") || reason.includes("verify_status"))) {
|
|
1626
1701
|
return "rerun verify with stronger proof framing so after evidence is captured before shipping";
|
|
1627
1702
|
}
|
package/dist/proof-run-engine.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
validateShipGate,
|
|
15
15
|
workflowFile,
|
|
16
16
|
writeState
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-A5AWVY5A.js";
|
|
18
18
|
|
|
19
19
|
// src/proof-run-engine.ts
|
|
20
20
|
import { execFileSync } from "child_process";
|
|
@@ -770,6 +770,9 @@ async function executeWorkflow(params, pluginConfig, resolvedConfig) {
|
|
|
770
770
|
if (reasons.some((reason) => reason.includes("proof_assessment"))) {
|
|
771
771
|
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";
|
|
772
772
|
}
|
|
773
|
+
if (reasons.some((reason) => reason.includes("visual_delta"))) {
|
|
774
|
+
return "rerun verify with a measured before/after visual delta, or choose needs_richer_proof instead of ready_to_ship for this visual proof";
|
|
775
|
+
}
|
|
773
776
|
if (reasons.some((reason) => reason.includes("after_cdn") || reason.includes("verify_status"))) {
|
|
774
777
|
return "rerun verify with stronger proof framing so after evidence is captured before shipping";
|
|
775
778
|
}
|
package/package.json
CHANGED
package/runtime/lib/ship.py
CHANGED
|
@@ -9,6 +9,10 @@ from util import load_state, save_state, invoke, git
|
|
|
9
9
|
|
|
10
10
|
DISCORD_API = 'https://discord.com/api/v10'
|
|
11
11
|
SHIP_NOISE_PATHS = ('.codex', '.oc-smoke')
|
|
12
|
+
VISUAL_FIRST_MODES = {
|
|
13
|
+
'visual', 'render', 'interaction', 'ui', 'layout', 'screenshot',
|
|
14
|
+
'canvas', 'animation',
|
|
15
|
+
}
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
def read_json_file(path):
|
|
@@ -293,7 +297,11 @@ def record_ship_report(state, marked_ready=None):
|
|
|
293
297
|
def proof_assessment_is_ready(state):
|
|
294
298
|
assessment = state.get('proof_assessment') or {}
|
|
295
299
|
source = str(assessment.get('source') or state.get('proof_assessment_source') or '').strip().lower()
|
|
296
|
-
return
|
|
300
|
+
return (
|
|
301
|
+
source in ('supervising_agent', 'supervisor')
|
|
302
|
+
and assessment.get('decision') == 'ready_to_ship'
|
|
303
|
+
and not visual_delta_ship_blocker(state)
|
|
304
|
+
)
|
|
297
305
|
|
|
298
306
|
|
|
299
307
|
def effective_merge_recommendation(state):
|
|
@@ -310,6 +318,49 @@ def after_evidence_bundle(state):
|
|
|
310
318
|
return after if isinstance(after, dict) else {}
|
|
311
319
|
|
|
312
320
|
|
|
321
|
+
def normalized_verification_mode(state):
|
|
322
|
+
bundle = state.get('evidence_bundle') or {}
|
|
323
|
+
if isinstance(bundle, dict) and str(bundle.get('verification_mode') or '').strip():
|
|
324
|
+
return str(bundle.get('verification_mode')).strip().lower()
|
|
325
|
+
return str(state.get('verification_mode') or 'proof').strip().lower() or 'proof'
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def visual_delta_required_for_ship(state):
|
|
329
|
+
bundle = state.get('evidence_bundle') or {}
|
|
330
|
+
contract = bundle.get('artifact_contract') if isinstance(bundle, dict) else {}
|
|
331
|
+
required = contract.get('required') if isinstance(contract, dict) else {}
|
|
332
|
+
if isinstance(required, dict) and required.get('visual_delta') is True:
|
|
333
|
+
return True
|
|
334
|
+
return normalized_verification_mode(state) in VISUAL_FIRST_MODES
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def visual_delta_for_state(state):
|
|
338
|
+
after = after_evidence_bundle(state)
|
|
339
|
+
visual_delta = after.get('visual_delta') if isinstance(after, dict) else None
|
|
340
|
+
if isinstance(visual_delta, dict):
|
|
341
|
+
return visual_delta
|
|
342
|
+
request = state.get('proof_assessment_request') or {}
|
|
343
|
+
visual_delta = request.get('visual_delta') if isinstance(request, dict) else None
|
|
344
|
+
return visual_delta if isinstance(visual_delta, dict) else {}
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def visual_delta_ship_blocker(state):
|
|
348
|
+
if not visual_delta_required_for_ship(state):
|
|
349
|
+
return ''
|
|
350
|
+
visual_delta = visual_delta_for_state(state)
|
|
351
|
+
if visual_delta.get('status') == 'measured' and visual_delta.get('passed') is True:
|
|
352
|
+
return ''
|
|
353
|
+
status = str(visual_delta.get('status') or 'missing')
|
|
354
|
+
if status == 'unmeasured':
|
|
355
|
+
return 'visual_delta.status=unmeasured blocks ready_to_ship for visual/UI proof'
|
|
356
|
+
if status == 'measured' and visual_delta.get('passed') is False:
|
|
357
|
+
return 'visual_delta.status=measured but visual_delta.passed=false blocks ready_to_ship for visual/UI proof'
|
|
358
|
+
reason = str(visual_delta.get('reason') or '').strip()
|
|
359
|
+
if reason:
|
|
360
|
+
return f'visual_delta.status={status} blocks ready_to_ship for visual/UI proof: {reason}'
|
|
361
|
+
return f'visual_delta.status={status} blocks ready_to_ship for visual/UI proof'
|
|
362
|
+
|
|
363
|
+
|
|
313
364
|
def state_has_after_evidence(state):
|
|
314
365
|
if (state.get('after_cdn') or '').strip():
|
|
315
366
|
return True
|
|
@@ -581,6 +632,9 @@ if reference in ('prod', 'both'):
|
|
|
581
632
|
raise SystemExit('prod_url is required when reference=' + reference + ' before ship.')
|
|
582
633
|
if not prod_cdn:
|
|
583
634
|
raise SystemExit('prod_cdn is required before ship. Run recon/verify again and preserve the approved prod baseline.')
|
|
635
|
+
visual_delta_blocker = visual_delta_ship_blocker(s)
|
|
636
|
+
if visual_delta_blocker:
|
|
637
|
+
raise SystemExit(visual_delta_blocker + '. Rerun verify with measured before/after visual delta or return a non-shipping proof assessment.')
|
|
584
638
|
if proof_source not in ('supervising_agent', 'supervisor') or proof_assessment.get('decision') != 'ready_to_ship':
|
|
585
639
|
raise SystemExit('Supervising-agent proof_assessment.decision=ready_to_ship is required before ship.')
|
|
586
640
|
|
package/runtime/lib/verify.py
CHANGED
|
@@ -559,6 +559,34 @@ def visual_delta_applies(verification_mode):
|
|
|
559
559
|
return screenshot_required_for_mode(verification_mode)
|
|
560
560
|
|
|
561
561
|
|
|
562
|
+
def visual_delta_passes_ship_gate(visual_delta):
|
|
563
|
+
return (
|
|
564
|
+
isinstance(visual_delta, dict)
|
|
565
|
+
and visual_delta.get('status') == 'measured'
|
|
566
|
+
and visual_delta.get('passed') is True
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def visual_delta_blocker_for_mode(verification_mode, visual_delta):
|
|
571
|
+
if not visual_delta_applies(verification_mode):
|
|
572
|
+
return ''
|
|
573
|
+
if visual_delta_passes_ship_gate(visual_delta):
|
|
574
|
+
return ''
|
|
575
|
+
if not isinstance(visual_delta, dict):
|
|
576
|
+
status = 'missing'
|
|
577
|
+
reason = 'No visual_delta object was found in proof evidence.'
|
|
578
|
+
else:
|
|
579
|
+
status = str(visual_delta.get('status') or 'missing')
|
|
580
|
+
reason = str(visual_delta.get('reason') or '').strip()
|
|
581
|
+
if status == 'unmeasured':
|
|
582
|
+
return 'visual_delta.status=unmeasured blocks ready_to_ship for visual/UI proof; capture a measured before/after visual delta or choose needs_richer_proof.'
|
|
583
|
+
if status == 'measured' and isinstance(visual_delta, dict) and visual_delta.get('passed') is False:
|
|
584
|
+
return 'visual_delta.status=measured but visual_delta.passed=false blocks ready_to_ship for visual/UI proof; the measured change did not clear the threshold.'
|
|
585
|
+
if reason:
|
|
586
|
+
return f'visual_delta.status={status} blocks ready_to_ship for visual/UI proof: {reason}'
|
|
587
|
+
return f'visual_delta.status={status} blocks ready_to_ship for visual/UI proof.'
|
|
588
|
+
|
|
589
|
+
|
|
562
590
|
def list_value(value):
|
|
563
591
|
return value if isinstance(value, list) else []
|
|
564
592
|
|
|
@@ -684,11 +712,11 @@ def artifact_contract_for_mode(verification_mode):
|
|
|
684
712
|
'route_semantics': True,
|
|
685
713
|
'screenshot': screenshot_required_for_mode(mode),
|
|
686
714
|
'proof_evidence': proof_evidence_required_for_mode(mode),
|
|
715
|
+
'visual_delta': visual_delta_applies(mode),
|
|
687
716
|
},
|
|
688
717
|
'preferred': {
|
|
689
718
|
'page_state': True,
|
|
690
719
|
'structured_payload': mode in STRUCTURED_FIRST_MODES or proof_evidence_required_for_mode(mode),
|
|
691
|
-
'visual_delta': visual_delta_applies(mode),
|
|
692
720
|
},
|
|
693
721
|
'optional': {
|
|
694
722
|
'console_summary': True,
|
|
@@ -731,7 +759,7 @@ def artifact_signal_availability(state, after_observation, supporting, visual_de
|
|
|
731
759
|
),
|
|
732
760
|
'structured_payload': bool(supporting.get('has_structured_payload')),
|
|
733
761
|
'proof_evidence': bool(supporting.get('proof_evidence_present')),
|
|
734
|
-
'visual_delta':
|
|
762
|
+
'visual_delta': visual_delta_passes_ship_gate(visual_delta),
|
|
735
763
|
'console_summary': bool(supporting.get('console_entries')),
|
|
736
764
|
'json_artifacts': bool(supporting.get('data_outputs')),
|
|
737
765
|
'image_outputs': bool(supporting.get('image_outputs')),
|
|
@@ -1097,6 +1125,8 @@ def build_supervisor_assessment_request(state, payload, after_observation, requi
|
|
|
1097
1125
|
evidence_bundle['artifact_contract'] = artifact_contract
|
|
1098
1126
|
evidence_bundle['artifact_production'] = artifact_production
|
|
1099
1127
|
evidence_bundle['artifact_usage'] = artifact_usage
|
|
1128
|
+
visual_delta_blocker = visual_delta_blocker_for_mode(verification_mode, visual_delta)
|
|
1129
|
+
hard_blockers = [visual_delta_blocker] if visual_delta_blocker else []
|
|
1100
1130
|
|
|
1101
1131
|
return {
|
|
1102
1132
|
'status': 'needs_supervising_agent_assessment',
|
|
@@ -1112,14 +1142,15 @@ def build_supervisor_assessment_request(state, payload, after_observation, requi
|
|
|
1112
1142
|
'artifact_contract': artifact_contract,
|
|
1113
1143
|
'artifact_production': artifact_production,
|
|
1114
1144
|
'artifact_usage': artifact_usage,
|
|
1145
|
+
'hard_blockers': hard_blockers,
|
|
1115
1146
|
'instructions': [
|
|
1116
1147
|
'The supervising agent owns proof assessment. Inspect the recon baseline(s), after evidence, and any structured artifacts together.',
|
|
1117
1148
|
'Decide whether the evidence is ready_to_ship or should continue internally through author, implement, or recon.',
|
|
1149
|
+
'Hard blockers cannot be overridden by supervisor judgment; if hard_blockers is non-empty, do not choose ready_to_ship.',
|
|
1118
1150
|
'Do not mark ready_to_ship if the before/prod baseline is blank, shell-only, generic, or not visibly tied to the requested feature.',
|
|
1119
1151
|
'Use semantic_context.route plus headings/buttons/text anchors to ground route and content judgment before treating a screenshot as wrong-route.',
|
|
1120
1152
|
'For visual/UI modes, use screenshots plus after_observation.details.visible_text_sample, headings, buttons, links, canvas_count, and large_visible_elements to explain what the proof actually shows.',
|
|
1121
|
-
'For visual/UI polish, capture success is not proof. If visual_delta.status
|
|
1122
|
-
'If visual_delta.status=unmeasured for visual/UI proof, only choose ready_to_ship when the screenshots and page-state details let you name a clearly legible before/after change; otherwise request richer proof or another implementation pass.',
|
|
1153
|
+
'For visual/UI polish, capture success is not proof. If visual_delta.status is unmeasured, missing, not_applicable, or measured with passed=false, choose needs_implementation or needs_richer_proof instead of ready_to_ship.',
|
|
1123
1154
|
'For data/audio/log/metrics/custom modes, judge the structured evidence bundle and proof_evidence_sample directly; screenshots are optional supporting context.',
|
|
1124
1155
|
'The summary must name the concrete change, the target route/UI, what changed in after evidence, and why the stop condition is satisfied.',
|
|
1125
1156
|
'Only set escalation_target=human when you conclude the workflow has hit a real wall or is not converging.',
|
|
@@ -1374,6 +1405,9 @@ s['evidence_bundle'] = evidence_bundle
|
|
|
1374
1405
|
visual_delta = ((evidence_bundle.get('after') or {}).get('visual_delta') or {})
|
|
1375
1406
|
if visual_delta.get('status') != 'not_applicable':
|
|
1376
1407
|
summary_lines.append('Visual delta gate: ' + compact_value(visual_delta, limit=700))
|
|
1408
|
+
visual_delta_blocker = visual_delta_blocker_for_mode(s.get('verification_mode'), visual_delta)
|
|
1409
|
+
if visual_delta_blocker:
|
|
1410
|
+
summary_lines.append('Visual delta hard gate: ' + visual_delta_blocker)
|
|
1377
1411
|
|
|
1378
1412
|
proof_evidence_blocker = ''
|
|
1379
1413
|
if proof_evidence_required_for_mode(s.get('verification_mode')):
|