@riddledc/riddle-proof 0.8.73 → 0.8.75
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/README.md +59 -0
- package/dist/advanced/index.d.cts +1 -1
- package/dist/advanced/index.d.ts +1 -1
- package/dist/advanced/proof-run-engine.d.cts +1 -1
- package/dist/advanced/proof-run-engine.d.ts +1 -1
- package/dist/change-proof.cjs +193 -0
- package/dist/change-proof.d.cts +74 -0
- package/dist/change-proof.d.ts +74 -0
- package/dist/change-proof.js +11 -0
- package/dist/chunk-6S7DZWVC.js +167 -0
- package/dist/{chunk-ICIJTEHD.js → chunk-AK2EPEVO.js} +215 -9
- package/dist/cli/index.js +4 -3
- package/dist/cli.cjs +369 -5
- package/dist/cli.js +4 -3
- package/dist/index.cjs +168 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +23 -15
- package/dist/{proof-run-engine-DpChFR5H.d.cts → proof-run-engine-Baiv6l3A.d.cts} +3 -3
- package/dist/{proof-run-engine-BqRoA3Do.d.ts → proof-run-engine-MiKZt9oY.d.ts} +3 -3
- package/dist/proof-run-engine.d.cts +1 -1
- package/dist/proof-run-engine.d.ts +1 -1
- package/package.json +7 -2
package/dist/cli.cjs
CHANGED
|
@@ -18608,6 +18608,167 @@ function suggestRiddleProofProfileChecks(input) {
|
|
|
18608
18608
|
};
|
|
18609
18609
|
}
|
|
18610
18610
|
|
|
18611
|
+
// src/change-proof.ts
|
|
18612
|
+
var RIDDLE_PROOF_CHANGE_RESULT_VERSION = "riddle-proof.change-result.v1";
|
|
18613
|
+
var DEFAULT_BEFORE_STATUSES = ["passed", "product_regression"];
|
|
18614
|
+
var DEFAULT_AFTER_STATUSES = ["passed"];
|
|
18615
|
+
var DEFAULT_PROFILE_STATUS_TRANSITION_BEFORE = ["product_regression"];
|
|
18616
|
+
var DEFAULT_PROFILE_STATUS_TRANSITION_AFTER = ["passed"];
|
|
18617
|
+
var DEFAULT_CHECK_STATUS_TRANSITION_BEFORE = ["failed"];
|
|
18618
|
+
var DEFAULT_CHECK_STATUS_TRANSITION_AFTER = ["passed"];
|
|
18619
|
+
function listValue(value, fallback) {
|
|
18620
|
+
if (Array.isArray(value)) return value;
|
|
18621
|
+
if (value === void 0) return fallback;
|
|
18622
|
+
return [value];
|
|
18623
|
+
}
|
|
18624
|
+
function includesValue(allowed, observed) {
|
|
18625
|
+
return observed !== void 0 && allowed.includes(observed);
|
|
18626
|
+
}
|
|
18627
|
+
function groupResult(side, contract, result) {
|
|
18628
|
+
const fallback = side === "before" ? DEFAULT_BEFORE_STATUSES : DEFAULT_AFTER_STATUSES;
|
|
18629
|
+
const requiredStatus = listValue(contract?.required_status, fallback);
|
|
18630
|
+
const label = contract?.label || side;
|
|
18631
|
+
if (!result) {
|
|
18632
|
+
return {
|
|
18633
|
+
side,
|
|
18634
|
+
label,
|
|
18635
|
+
ok: false,
|
|
18636
|
+
status: "missing",
|
|
18637
|
+
required_status: requiredStatus,
|
|
18638
|
+
message: `${label} profile result is missing.`
|
|
18639
|
+
};
|
|
18640
|
+
}
|
|
18641
|
+
const ok = includesValue(requiredStatus, result.status);
|
|
18642
|
+
return {
|
|
18643
|
+
side,
|
|
18644
|
+
label,
|
|
18645
|
+
ok,
|
|
18646
|
+
profile_name: result.profile_name,
|
|
18647
|
+
status: result.status,
|
|
18648
|
+
required_status: requiredStatus,
|
|
18649
|
+
summary: result.summary,
|
|
18650
|
+
message: ok ? void 0 : `${label} profile status ${result.status} did not match required status ${requiredStatus.join(", ")}.`
|
|
18651
|
+
};
|
|
18652
|
+
}
|
|
18653
|
+
function findCheck(result, delta) {
|
|
18654
|
+
return result.checks.find((check) => {
|
|
18655
|
+
if (delta.check_label && check.label !== delta.check_label) return false;
|
|
18656
|
+
if (delta.check_type && check.type !== delta.check_type) return false;
|
|
18657
|
+
return Boolean(delta.check_label || delta.check_type);
|
|
18658
|
+
});
|
|
18659
|
+
}
|
|
18660
|
+
function profileStatusTransitionResult(delta, before, after) {
|
|
18661
|
+
const label = delta.label || "profile-status-transition";
|
|
18662
|
+
if (!before || !after) {
|
|
18663
|
+
return {
|
|
18664
|
+
type: delta.type,
|
|
18665
|
+
label,
|
|
18666
|
+
status: "proof_insufficient",
|
|
18667
|
+
before_observed: before?.status,
|
|
18668
|
+
after_observed: after?.status,
|
|
18669
|
+
message: `${label} needs both before and after profile results.`
|
|
18670
|
+
};
|
|
18671
|
+
}
|
|
18672
|
+
const beforeAllowed = listValue(delta.before_status, DEFAULT_PROFILE_STATUS_TRANSITION_BEFORE);
|
|
18673
|
+
const afterAllowed = listValue(delta.after_status, DEFAULT_PROFILE_STATUS_TRANSITION_AFTER);
|
|
18674
|
+
const passed = includesValue(beforeAllowed, before.status) && includesValue(afterAllowed, after.status);
|
|
18675
|
+
return {
|
|
18676
|
+
type: delta.type,
|
|
18677
|
+
label,
|
|
18678
|
+
status: passed ? "passed" : "failed",
|
|
18679
|
+
before_observed: before.status,
|
|
18680
|
+
after_observed: after.status,
|
|
18681
|
+
message: passed ? void 0 : `${label} expected before ${beforeAllowed.join(", ")} and after ${afterAllowed.join(", ")}, got ${before.status} -> ${after.status}.`
|
|
18682
|
+
};
|
|
18683
|
+
}
|
|
18684
|
+
function checkStatusTransitionResult(delta, before, after) {
|
|
18685
|
+
const label = delta.label || delta.check_label || delta.check_type || "check-status-transition";
|
|
18686
|
+
if (!delta.check_label && !delta.check_type) {
|
|
18687
|
+
return {
|
|
18688
|
+
type: delta.type,
|
|
18689
|
+
label,
|
|
18690
|
+
status: "configuration_error",
|
|
18691
|
+
message: `${label} needs check_label or check_type.`
|
|
18692
|
+
};
|
|
18693
|
+
}
|
|
18694
|
+
if (!before || !after) {
|
|
18695
|
+
return {
|
|
18696
|
+
type: delta.type,
|
|
18697
|
+
label,
|
|
18698
|
+
status: "proof_insufficient",
|
|
18699
|
+
before_observed: before?.status,
|
|
18700
|
+
after_observed: after?.status,
|
|
18701
|
+
message: `${label} needs both before and after profile results.`
|
|
18702
|
+
};
|
|
18703
|
+
}
|
|
18704
|
+
const beforeCheck = findCheck(before, delta);
|
|
18705
|
+
const afterCheck = findCheck(after, delta);
|
|
18706
|
+
if (!beforeCheck || !afterCheck) {
|
|
18707
|
+
return {
|
|
18708
|
+
type: delta.type,
|
|
18709
|
+
label,
|
|
18710
|
+
status: "proof_insufficient",
|
|
18711
|
+
before_observed: beforeCheck?.status,
|
|
18712
|
+
after_observed: afterCheck?.status,
|
|
18713
|
+
message: `${label} was not present in ${!beforeCheck && !afterCheck ? "before or after" : !beforeCheck ? "before" : "after"} profile checks.`
|
|
18714
|
+
};
|
|
18715
|
+
}
|
|
18716
|
+
const beforeAllowed = listValue(delta.before_status, DEFAULT_CHECK_STATUS_TRANSITION_BEFORE);
|
|
18717
|
+
const afterAllowed = listValue(delta.after_status, DEFAULT_CHECK_STATUS_TRANSITION_AFTER);
|
|
18718
|
+
const passed = includesValue(beforeAllowed, beforeCheck.status) && includesValue(afterAllowed, afterCheck.status);
|
|
18719
|
+
return {
|
|
18720
|
+
type: delta.type,
|
|
18721
|
+
label,
|
|
18722
|
+
status: passed ? "passed" : "failed",
|
|
18723
|
+
before_observed: beforeCheck.status,
|
|
18724
|
+
after_observed: afterCheck.status,
|
|
18725
|
+
message: passed ? void 0 : `${label} expected before ${beforeAllowed.join(", ")} and after ${afterAllowed.join(", ")}, got ${beforeCheck.status} -> ${afterCheck.status}.`
|
|
18726
|
+
};
|
|
18727
|
+
}
|
|
18728
|
+
function assessDelta(delta, before, after) {
|
|
18729
|
+
if (delta.type === "profile_status_transition") {
|
|
18730
|
+
return profileStatusTransitionResult(delta, before, after);
|
|
18731
|
+
}
|
|
18732
|
+
return checkStatusTransitionResult(delta, before, after);
|
|
18733
|
+
}
|
|
18734
|
+
function collapsedChangeStatus(groups, deltas) {
|
|
18735
|
+
const groupResults = [groups.before, groups.after];
|
|
18736
|
+
if (groupResults.some((group) => group.status === "environment_blocked")) return "environment_blocked";
|
|
18737
|
+
if (groupResults.some((group) => group.status === "configuration_error")) return "configuration_error";
|
|
18738
|
+
if (deltas.some((delta) => delta.status === "configuration_error")) return "configuration_error";
|
|
18739
|
+
if (groupResults.some((group) => group.status === "needs_human_review")) return "needs_human_review";
|
|
18740
|
+
if (groupResults.some((group) => group.status === "missing" || group.status === "proof_insufficient")) return "proof_insufficient";
|
|
18741
|
+
if (!deltas.length || deltas.some((delta) => delta.status === "proof_insufficient")) return "proof_insufficient";
|
|
18742
|
+
if (groupResults.some((group) => !group.ok)) return "product_regression";
|
|
18743
|
+
if (deltas.some((delta) => delta.status === "failed")) return "product_regression";
|
|
18744
|
+
return "passed";
|
|
18745
|
+
}
|
|
18746
|
+
function changeSummary(name, status, deltas) {
|
|
18747
|
+
if (status === "passed") return `${name} passed ${deltas.length} change delta(s).`;
|
|
18748
|
+
if (status === "environment_blocked") return `${name} could not compare reliable evidence because an environment was blocked.`;
|
|
18749
|
+
if (status === "configuration_error") return `${name} has an invalid change proof contract.`;
|
|
18750
|
+
if (status === "needs_human_review") return `${name} needs human review before the change proof can pass.`;
|
|
18751
|
+
if (status === "proof_insufficient") return `${name} did not produce enough before/after evidence for a change proof.`;
|
|
18752
|
+
return `${name} failed ${deltas.filter((delta) => delta.status === "failed").length} change delta(s).`;
|
|
18753
|
+
}
|
|
18754
|
+
function assessRiddleProofChange(contract, input) {
|
|
18755
|
+
const groups = {
|
|
18756
|
+
before: groupResult("before", contract.before, input.before_result),
|
|
18757
|
+
after: groupResult("after", contract.after, input.after_result)
|
|
18758
|
+
};
|
|
18759
|
+
const deltas = (contract.deltas || []).map((delta) => assessDelta(delta, input.before_result, input.after_result));
|
|
18760
|
+
const status = collapsedChangeStatus(groups, deltas);
|
|
18761
|
+
return {
|
|
18762
|
+
version: RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
18763
|
+
contract_name: contract.name,
|
|
18764
|
+
status,
|
|
18765
|
+
groups,
|
|
18766
|
+
deltas,
|
|
18767
|
+
summary: changeSummary(contract.name, status, deltas),
|
|
18768
|
+
metadata: contract.metadata
|
|
18769
|
+
};
|
|
18770
|
+
}
|
|
18771
|
+
|
|
18611
18772
|
// src/cli.ts
|
|
18612
18773
|
var RIDDLE_PROFILE_BALANCE_PREFLIGHT_MIN_SECONDS_PER_JOB = 30;
|
|
18613
18774
|
var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
|
|
@@ -18619,8 +18780,13 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
|
|
|
18619
18780
|
"attempts",
|
|
18620
18781
|
"balancePreflight",
|
|
18621
18782
|
"baseUrl",
|
|
18783
|
+
"afterResult",
|
|
18784
|
+
"afterUrl",
|
|
18785
|
+
"beforeResult",
|
|
18786
|
+
"beforeUrl",
|
|
18622
18787
|
"candidateJson",
|
|
18623
18788
|
"candidatesJson",
|
|
18789
|
+
"changeContract",
|
|
18624
18790
|
"checkpointMode",
|
|
18625
18791
|
"checkpointVisibility",
|
|
18626
18792
|
"changedFiles",
|
|
@@ -18727,6 +18893,7 @@ function usage() {
|
|
|
18727
18893
|
" 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]",
|
|
18728
18894
|
" 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]",
|
|
18729
18895
|
" 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]",
|
|
18896
|
+
" 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]",
|
|
18730
18897
|
" riddle-proof-loop profile-suggest --route /path|--url <url> [--changed-files a,b] [--selectors .a,.b] [--changed-text-json <file|json|->] [--format json|profile]",
|
|
18731
18898
|
" 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>]",
|
|
18732
18899
|
" 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]",
|
|
@@ -19792,14 +19959,19 @@ function parseProfileViewports(value) {
|
|
|
19792
19959
|
};
|
|
19793
19960
|
});
|
|
19794
19961
|
}
|
|
19795
|
-
function
|
|
19796
|
-
|
|
19962
|
+
function readProfileRecordForCli(options) {
|
|
19963
|
+
return readJsonValue(optionString(options, "profile"), "--profile");
|
|
19964
|
+
}
|
|
19965
|
+
function normalizeProfileRecordForCli(rawProfile, options) {
|
|
19797
19966
|
return normalizeRiddleProofProfile(rawProfile, {
|
|
19798
19967
|
url: optionString(options, "url") ?? optionString(options, "baseUrl"),
|
|
19799
19968
|
route: optionString(options, "route"),
|
|
19800
19969
|
viewports: parseProfileViewports(optionString(options, "viewports") || optionString(options, "viewport"))
|
|
19801
19970
|
});
|
|
19802
19971
|
}
|
|
19972
|
+
function normalizeProfileForCli(options) {
|
|
19973
|
+
return normalizeProfileRecordForCli(readProfileRecordForCli(options), options);
|
|
19974
|
+
}
|
|
19803
19975
|
function profileHttpStatusPreflightSummary(result) {
|
|
19804
19976
|
const lines = [
|
|
19805
19977
|
result.summary,
|
|
@@ -22471,6 +22643,136 @@ function writeProfileOutput(outputDir, result) {
|
|
|
22471
22643
|
if (result.evidence?.dom_summary) (0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "dom-summary.json"), `${JSON.stringify(result.evidence.dom_summary, null, 2)}
|
|
22472
22644
|
`);
|
|
22473
22645
|
}
|
|
22646
|
+
function readChangeContractForCli(options) {
|
|
22647
|
+
const parsed = readJsonValue(optionString(options, "changeContract"), "--change-contract");
|
|
22648
|
+
if (parsed.version !== void 0 && parsed.version !== "riddle-proof.change-contract.v1") {
|
|
22649
|
+
throw new Error(`Unsupported change contract version ${parsed.version}. Expected riddle-proof.change-contract.v1.`);
|
|
22650
|
+
}
|
|
22651
|
+
if (typeof parsed.name !== "string" || !parsed.name.trim()) {
|
|
22652
|
+
throw new Error("--change-contract must include a non-empty name.");
|
|
22653
|
+
}
|
|
22654
|
+
if (!Array.isArray(parsed.deltas) || !parsed.deltas.length) {
|
|
22655
|
+
throw new Error("--change-contract must include at least one delta.");
|
|
22656
|
+
}
|
|
22657
|
+
return parsed;
|
|
22658
|
+
}
|
|
22659
|
+
function changeProofSideOutputDir(outputDir, side) {
|
|
22660
|
+
return outputDir ? import_node_path6.default.join(outputDir, side) : void 0;
|
|
22661
|
+
}
|
|
22662
|
+
function profileOptionsForChangeProofSide(options, url, outputDir) {
|
|
22663
|
+
const next = {
|
|
22664
|
+
...options,
|
|
22665
|
+
url,
|
|
22666
|
+
baseUrl: url
|
|
22667
|
+
};
|
|
22668
|
+
if (outputDir) {
|
|
22669
|
+
next.output = outputDir;
|
|
22670
|
+
next.outputDir = outputDir;
|
|
22671
|
+
} else {
|
|
22672
|
+
delete next.output;
|
|
22673
|
+
delete next.outputDir;
|
|
22674
|
+
}
|
|
22675
|
+
return next;
|
|
22676
|
+
}
|
|
22677
|
+
function compactChangeProofResult(run, options) {
|
|
22678
|
+
const outputDir = profileOutputDirOption(options);
|
|
22679
|
+
return {
|
|
22680
|
+
version: "riddle-proof.change-compact-result.v1",
|
|
22681
|
+
contract_name: run.result.contract_name,
|
|
22682
|
+
profile_name: run.profile.name,
|
|
22683
|
+
status: run.result.status,
|
|
22684
|
+
summary: run.result.summary,
|
|
22685
|
+
before: {
|
|
22686
|
+
source: run.beforeSource,
|
|
22687
|
+
profile_name: run.beforeResult.profile_name,
|
|
22688
|
+
status: run.beforeResult.status,
|
|
22689
|
+
summary: run.beforeResult.summary
|
|
22690
|
+
},
|
|
22691
|
+
after: {
|
|
22692
|
+
source: run.afterSource,
|
|
22693
|
+
profile_name: run.afterResult.profile_name,
|
|
22694
|
+
status: run.afterResult.status,
|
|
22695
|
+
summary: run.afterResult.summary
|
|
22696
|
+
},
|
|
22697
|
+
deltas: run.result.deltas.map((delta) => ({
|
|
22698
|
+
type: delta.type,
|
|
22699
|
+
label: delta.label,
|
|
22700
|
+
status: delta.status,
|
|
22701
|
+
before_observed: delta.before_observed,
|
|
22702
|
+
after_observed: delta.after_observed,
|
|
22703
|
+
message: delta.message
|
|
22704
|
+
})),
|
|
22705
|
+
metadata: run.result.metadata,
|
|
22706
|
+
output_dir: outputDir,
|
|
22707
|
+
output_files: outputDir ? {
|
|
22708
|
+
change_proof_result: "change-proof-result.json",
|
|
22709
|
+
summary: "summary.md",
|
|
22710
|
+
before_profile_result: "before/profile-result.json",
|
|
22711
|
+
after_profile_result: "after/profile-result.json"
|
|
22712
|
+
} : void 0
|
|
22713
|
+
};
|
|
22714
|
+
}
|
|
22715
|
+
function changeProofResultMarkdown(run) {
|
|
22716
|
+
const lines = [
|
|
22717
|
+
"# Riddle Proof Change Proof",
|
|
22718
|
+
"",
|
|
22719
|
+
`Contract: ${run.result.contract_name}`,
|
|
22720
|
+
`Profile: ${run.profile.name}`,
|
|
22721
|
+
`Status: ${run.result.status}`,
|
|
22722
|
+
"",
|
|
22723
|
+
run.result.summary,
|
|
22724
|
+
"",
|
|
22725
|
+
"## Before",
|
|
22726
|
+
"",
|
|
22727
|
+
`- source: ${markdownInlineCode(run.beforeSource, 160)}`,
|
|
22728
|
+
`- profile: ${run.beforeResult.profile_name}`,
|
|
22729
|
+
`- status: ${run.beforeResult.status}`,
|
|
22730
|
+
`- summary: ${run.beforeResult.summary}`,
|
|
22731
|
+
"",
|
|
22732
|
+
"## After",
|
|
22733
|
+
"",
|
|
22734
|
+
`- source: ${markdownInlineCode(run.afterSource, 160)}`,
|
|
22735
|
+
`- profile: ${run.afterResult.profile_name}`,
|
|
22736
|
+
`- status: ${run.afterResult.status}`,
|
|
22737
|
+
`- summary: ${run.afterResult.summary}`,
|
|
22738
|
+
"",
|
|
22739
|
+
"## Deltas",
|
|
22740
|
+
""
|
|
22741
|
+
];
|
|
22742
|
+
for (const delta of run.result.deltas) {
|
|
22743
|
+
const observed = [
|
|
22744
|
+
delta.before_observed !== void 0 ? `before ${markdownInlineCode(String(delta.before_observed))}` : "",
|
|
22745
|
+
delta.after_observed !== void 0 ? `after ${markdownInlineCode(String(delta.after_observed))}` : ""
|
|
22746
|
+
].filter(Boolean).join(" -> ");
|
|
22747
|
+
lines.push(`- ${delta.status}: ${delta.label}${observed ? ` (${observed})` : ""}${delta.message ? ` - ${delta.message}` : ""}`);
|
|
22748
|
+
}
|
|
22749
|
+
return `${lines.join("\n")}
|
|
22750
|
+
`;
|
|
22751
|
+
}
|
|
22752
|
+
function writeChangeProofOutput(outputDir, run) {
|
|
22753
|
+
if (!outputDir) return;
|
|
22754
|
+
(0, import_node_fs6.mkdirSync)(outputDir, { recursive: true });
|
|
22755
|
+
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "change-proof-result.json"), `${JSON.stringify(run.result, null, 2)}
|
|
22756
|
+
`);
|
|
22757
|
+
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "summary.md"), changeProofResultMarkdown(run));
|
|
22758
|
+
writeProfileOutput(changeProofSideOutputDir(outputDir, "before"), run.beforeResult);
|
|
22759
|
+
writeProfileOutput(changeProofSideOutputDir(outputDir, "after"), run.afterResult);
|
|
22760
|
+
}
|
|
22761
|
+
function writeRunChangeProofResult(run, options) {
|
|
22762
|
+
const format = runProfileResultFormatOption(options);
|
|
22763
|
+
if (format === "none") return;
|
|
22764
|
+
if (format === "summary") {
|
|
22765
|
+
process.stdout.write(changeProofResultMarkdown(run));
|
|
22766
|
+
return;
|
|
22767
|
+
}
|
|
22768
|
+
if (format === "compact-json") {
|
|
22769
|
+
process.stdout.write(`${JSON.stringify(compactChangeProofResult(run, options), null, 2)}
|
|
22770
|
+
`);
|
|
22771
|
+
return;
|
|
22772
|
+
}
|
|
22773
|
+
process.stdout.write(`${JSON.stringify(run.result, null, 2)}
|
|
22774
|
+
`);
|
|
22775
|
+
}
|
|
22474
22776
|
function writeRiddleJobReceipt(outputDir, input) {
|
|
22475
22777
|
if (!outputDir) return;
|
|
22476
22778
|
(0, import_node_fs6.mkdirSync)(outputDir, { recursive: true });
|
|
@@ -22776,13 +23078,16 @@ function runProfileAggregateInputPathsOption(options) {
|
|
|
22776
23078
|
}
|
|
22777
23079
|
return uniquePaths;
|
|
22778
23080
|
}
|
|
22779
|
-
function
|
|
22780
|
-
const parsed = readJsonValue(resultPath,
|
|
23081
|
+
function readProfileResultForCli(resultPath, label = resultPath) {
|
|
23082
|
+
const parsed = readJsonValue(resultPath, label);
|
|
22781
23083
|
if (parsed.version !== "riddle-proof.profile-result.v1" || !Array.isArray(parsed.checks)) {
|
|
22782
|
-
throw new Error(
|
|
23084
|
+
throw new Error(`${label} is not a riddle-proof.profile-result.v1 result: ${resultPath}`);
|
|
22783
23085
|
}
|
|
22784
23086
|
return parsed;
|
|
22785
23087
|
}
|
|
23088
|
+
function readProfileResultForAggregate(resultPath) {
|
|
23089
|
+
return readProfileResultForCli(resultPath, resultPath);
|
|
23090
|
+
}
|
|
22786
23091
|
function profileResultIsAggregateParent(result) {
|
|
22787
23092
|
const mode = cliString(cliRecord(result.riddle)?.mode);
|
|
22788
23093
|
return (mode === "split-viewports" || mode === "named-viewport-aggregate") && (result.evidence?.viewports?.length || 0) > 1;
|
|
@@ -23313,6 +23618,58 @@ async function runProfileForCli(profile, options) {
|
|
|
23313
23618
|
}
|
|
23314
23619
|
return runSingleRiddleProfileForCli(profile, options, { client, runner, outputDir: profileOutputDirOption(options) });
|
|
23315
23620
|
}
|
|
23621
|
+
async function runChangeProofForCli(rawProfile, options) {
|
|
23622
|
+
const contract = readChangeContractForCli(options);
|
|
23623
|
+
const beforeResultPath = optionString(options, "beforeResult");
|
|
23624
|
+
const afterResultPath = optionString(options, "afterResult");
|
|
23625
|
+
const beforeUrl = optionString(options, "beforeUrl");
|
|
23626
|
+
const afterUrl = optionString(options, "afterUrl");
|
|
23627
|
+
const hasResultInputs = Boolean(beforeResultPath || afterResultPath);
|
|
23628
|
+
const hasUrlInputs = Boolean(beforeUrl || afterUrl);
|
|
23629
|
+
if (hasResultInputs && hasUrlInputs) {
|
|
23630
|
+
throw new Error("run-change-proof accepts either --before-result/--after-result or --before-url/--after-url, not both.");
|
|
23631
|
+
}
|
|
23632
|
+
if (hasResultInputs && (!beforeResultPath || !afterResultPath)) {
|
|
23633
|
+
throw new Error("run-change-proof requires both --before-result and --after-result.");
|
|
23634
|
+
}
|
|
23635
|
+
if (!hasResultInputs && (!beforeUrl || !afterUrl)) {
|
|
23636
|
+
throw new Error("run-change-proof requires both --before-url and --after-url unless saved results are provided.");
|
|
23637
|
+
}
|
|
23638
|
+
const outputDir = profileOutputDirOption(options);
|
|
23639
|
+
let profile;
|
|
23640
|
+
let beforeResult;
|
|
23641
|
+
let afterResult;
|
|
23642
|
+
let beforeSource;
|
|
23643
|
+
let afterSource;
|
|
23644
|
+
if (hasResultInputs) {
|
|
23645
|
+
if (!beforeResultPath || !afterResultPath) {
|
|
23646
|
+
throw new Error("run-change-proof requires both --before-result and --after-result.");
|
|
23647
|
+
}
|
|
23648
|
+
profile = profileWithSelectedViewportNamesForCli(normalizeProfileRecordForCli(rawProfile, options), options);
|
|
23649
|
+
beforeResult = readProfileResultForCli(beforeResultPath, "--before-result");
|
|
23650
|
+
afterResult = readProfileResultForCli(afterResultPath, "--after-result");
|
|
23651
|
+
beforeSource = beforeResultPath;
|
|
23652
|
+
afterSource = afterResultPath;
|
|
23653
|
+
} else {
|
|
23654
|
+
if (!beforeUrl || !afterUrl) {
|
|
23655
|
+
throw new Error("run-change-proof requires both --before-url and --after-url unless saved results are provided.");
|
|
23656
|
+
}
|
|
23657
|
+
const beforeOptions = profileOptionsForChangeProofSide(options, beforeUrl, changeProofSideOutputDir(outputDir, "before"));
|
|
23658
|
+
const afterOptions = profileOptionsForChangeProofSide(options, afterUrl, changeProofSideOutputDir(outputDir, "after"));
|
|
23659
|
+
const beforeProfile = profileWithSelectedViewportNamesForCli(normalizeProfileRecordForCli(rawProfile, beforeOptions), beforeOptions);
|
|
23660
|
+
const afterProfile = profileWithSelectedViewportNamesForCli(normalizeProfileRecordForCli(rawProfile, afterOptions), afterOptions);
|
|
23661
|
+
profile = beforeProfile;
|
|
23662
|
+
beforeResult = await runProfileForCli(beforeProfile, beforeOptions);
|
|
23663
|
+
afterResult = await runProfileForCli(afterProfile, afterOptions);
|
|
23664
|
+
beforeSource = beforeUrl;
|
|
23665
|
+
afterSource = afterUrl;
|
|
23666
|
+
}
|
|
23667
|
+
const result = assessRiddleProofChange(contract, {
|
|
23668
|
+
before_result: beforeResult,
|
|
23669
|
+
after_result: afterResult
|
|
23670
|
+
});
|
|
23671
|
+
return { profile, contract, beforeResult, afterResult, result, beforeSource, afterSource };
|
|
23672
|
+
}
|
|
23316
23673
|
function requestForRun(options) {
|
|
23317
23674
|
const statePath = optionString(options, "statePath");
|
|
23318
23675
|
const withEngineModuleUrl = (request) => {
|
|
@@ -23373,6 +23730,13 @@ async function main() {
|
|
|
23373
23730
|
process.exitCode = profileStatusExitCode(profile, result.status);
|
|
23374
23731
|
return;
|
|
23375
23732
|
}
|
|
23733
|
+
if (command === "run-change-proof") {
|
|
23734
|
+
const run = await runChangeProofForCli(readProfileRecordForCli(options), options);
|
|
23735
|
+
writeChangeProofOutput(profileOutputDirOption(options), run);
|
|
23736
|
+
writeRunChangeProofResult(run, options);
|
|
23737
|
+
process.exitCode = run.result.status === "passed" ? 0 : 1;
|
|
23738
|
+
return;
|
|
23739
|
+
}
|
|
23376
23740
|
if (command === "regression-pack") {
|
|
23377
23741
|
const action = positional[1] || "run";
|
|
23378
23742
|
if (action !== "run") throw new Error("Only `regression-pack run` is supported.");
|
package/dist/cli.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
2
|
+
import "./chunk-AK2EPEVO.js";
|
|
3
3
|
import "./chunk-B2DP2LET.js";
|
|
4
|
+
import "./chunk-JFQXAJH2.js";
|
|
4
5
|
import "./chunk-CWRIXP5H.js";
|
|
5
6
|
import "./chunk-UE4I7RTI.js";
|
|
6
7
|
import "./chunk-GG2D3MFZ.js";
|
|
8
|
+
import "./chunk-6S7DZWVC.js";
|
|
9
|
+
import "./chunk-KXLEN4SA.js";
|
|
7
10
|
import "./chunk-WLUMLHII.js";
|
|
8
11
|
import "./chunk-CGJX7LJO.js";
|
|
9
12
|
import "./chunk-UTCL7FEZ.js";
|
|
10
13
|
import "./chunk-EKZXU6MU.js";
|
|
11
14
|
import "./chunk-DYE3URAQ.js";
|
|
12
|
-
import "./chunk-JFQXAJH2.js";
|
|
13
|
-
import "./chunk-KXLEN4SA.js";
|
|
14
15
|
import "./chunk-NKHZASNQ.js";
|
|
15
16
|
import "./chunk-ZAR7BWMN.js";
|
|
16
17
|
import "./chunk-MLKGABMK.js";
|
package/dist/index.cjs
CHANGED
|
@@ -3418,6 +3418,8 @@ __export(index_exports, {
|
|
|
3418
3418
|
RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION: () => RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION,
|
|
3419
3419
|
RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION: () => RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION,
|
|
3420
3420
|
RIDDLE_PROOF_CAPTURE_DIAGNOSTIC_VERSION: () => RIDDLE_PROOF_CAPTURE_DIAGNOSTIC_VERSION,
|
|
3421
|
+
RIDDLE_PROOF_CHANGE_CONTRACT_VERSION: () => RIDDLE_PROOF_CHANGE_CONTRACT_VERSION,
|
|
3422
|
+
RIDDLE_PROOF_CHANGE_RESULT_VERSION: () => RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
3421
3423
|
RIDDLE_PROOF_CHECKPOINT_PACKET_VERSION: () => RIDDLE_PROOF_CHECKPOINT_PACKET_VERSION,
|
|
3422
3424
|
RIDDLE_PROOF_CHECKPOINT_RESPONSE_VERSION: () => RIDDLE_PROOF_CHECKPOINT_RESPONSE_VERSION,
|
|
3423
3425
|
RIDDLE_PROOF_HOSTED_PROFILE_ARTIFACTS: () => RIDDLE_PROOF_HOSTED_PROFILE_ARTIFACTS,
|
|
@@ -3450,6 +3452,7 @@ __export(index_exports, {
|
|
|
3450
3452
|
assessBasicGameplayProgressionChecks: () => assessBasicGameplayProgressionChecks,
|
|
3451
3453
|
assessBasicGameplayRoute: () => assessBasicGameplayRoute,
|
|
3452
3454
|
assessPlayabilityEvidence: () => assessPlayabilityEvidence,
|
|
3455
|
+
assessRiddleProofChange: () => assessRiddleProofChange,
|
|
3453
3456
|
assessRiddleProofProfileArtifactCompleteness: () => assessRiddleProofProfileArtifactCompleteness,
|
|
3454
3457
|
assessRiddleProofProfileEvidence: () => assessRiddleProofProfileEvidence,
|
|
3455
3458
|
attachBasicGameplayArtifactScreenshotHashes: () => attachBasicGameplayArtifactScreenshotHashes,
|
|
@@ -19929,6 +19932,168 @@ function suggestRiddleProofProfileChecks(input) {
|
|
|
19929
19932
|
};
|
|
19930
19933
|
}
|
|
19931
19934
|
|
|
19935
|
+
// src/change-proof.ts
|
|
19936
|
+
var RIDDLE_PROOF_CHANGE_CONTRACT_VERSION = "riddle-proof.change-contract.v1";
|
|
19937
|
+
var RIDDLE_PROOF_CHANGE_RESULT_VERSION = "riddle-proof.change-result.v1";
|
|
19938
|
+
var DEFAULT_BEFORE_STATUSES = ["passed", "product_regression"];
|
|
19939
|
+
var DEFAULT_AFTER_STATUSES = ["passed"];
|
|
19940
|
+
var DEFAULT_PROFILE_STATUS_TRANSITION_BEFORE = ["product_regression"];
|
|
19941
|
+
var DEFAULT_PROFILE_STATUS_TRANSITION_AFTER = ["passed"];
|
|
19942
|
+
var DEFAULT_CHECK_STATUS_TRANSITION_BEFORE = ["failed"];
|
|
19943
|
+
var DEFAULT_CHECK_STATUS_TRANSITION_AFTER = ["passed"];
|
|
19944
|
+
function listValue3(value, fallback) {
|
|
19945
|
+
if (Array.isArray(value)) return value;
|
|
19946
|
+
if (value === void 0) return fallback;
|
|
19947
|
+
return [value];
|
|
19948
|
+
}
|
|
19949
|
+
function includesValue(allowed, observed) {
|
|
19950
|
+
return observed !== void 0 && allowed.includes(observed);
|
|
19951
|
+
}
|
|
19952
|
+
function groupResult(side, contract, result) {
|
|
19953
|
+
const fallback = side === "before" ? DEFAULT_BEFORE_STATUSES : DEFAULT_AFTER_STATUSES;
|
|
19954
|
+
const requiredStatus = listValue3(contract?.required_status, fallback);
|
|
19955
|
+
const label = contract?.label || side;
|
|
19956
|
+
if (!result) {
|
|
19957
|
+
return {
|
|
19958
|
+
side,
|
|
19959
|
+
label,
|
|
19960
|
+
ok: false,
|
|
19961
|
+
status: "missing",
|
|
19962
|
+
required_status: requiredStatus,
|
|
19963
|
+
message: `${label} profile result is missing.`
|
|
19964
|
+
};
|
|
19965
|
+
}
|
|
19966
|
+
const ok = includesValue(requiredStatus, result.status);
|
|
19967
|
+
return {
|
|
19968
|
+
side,
|
|
19969
|
+
label,
|
|
19970
|
+
ok,
|
|
19971
|
+
profile_name: result.profile_name,
|
|
19972
|
+
status: result.status,
|
|
19973
|
+
required_status: requiredStatus,
|
|
19974
|
+
summary: result.summary,
|
|
19975
|
+
message: ok ? void 0 : `${label} profile status ${result.status} did not match required status ${requiredStatus.join(", ")}.`
|
|
19976
|
+
};
|
|
19977
|
+
}
|
|
19978
|
+
function findCheck(result, delta) {
|
|
19979
|
+
return result.checks.find((check) => {
|
|
19980
|
+
if (delta.check_label && check.label !== delta.check_label) return false;
|
|
19981
|
+
if (delta.check_type && check.type !== delta.check_type) return false;
|
|
19982
|
+
return Boolean(delta.check_label || delta.check_type);
|
|
19983
|
+
});
|
|
19984
|
+
}
|
|
19985
|
+
function profileStatusTransitionResult(delta, before, after) {
|
|
19986
|
+
const label = delta.label || "profile-status-transition";
|
|
19987
|
+
if (!before || !after) {
|
|
19988
|
+
return {
|
|
19989
|
+
type: delta.type,
|
|
19990
|
+
label,
|
|
19991
|
+
status: "proof_insufficient",
|
|
19992
|
+
before_observed: before?.status,
|
|
19993
|
+
after_observed: after?.status,
|
|
19994
|
+
message: `${label} needs both before and after profile results.`
|
|
19995
|
+
};
|
|
19996
|
+
}
|
|
19997
|
+
const beforeAllowed = listValue3(delta.before_status, DEFAULT_PROFILE_STATUS_TRANSITION_BEFORE);
|
|
19998
|
+
const afterAllowed = listValue3(delta.after_status, DEFAULT_PROFILE_STATUS_TRANSITION_AFTER);
|
|
19999
|
+
const passed = includesValue(beforeAllowed, before.status) && includesValue(afterAllowed, after.status);
|
|
20000
|
+
return {
|
|
20001
|
+
type: delta.type,
|
|
20002
|
+
label,
|
|
20003
|
+
status: passed ? "passed" : "failed",
|
|
20004
|
+
before_observed: before.status,
|
|
20005
|
+
after_observed: after.status,
|
|
20006
|
+
message: passed ? void 0 : `${label} expected before ${beforeAllowed.join(", ")} and after ${afterAllowed.join(", ")}, got ${before.status} -> ${after.status}.`
|
|
20007
|
+
};
|
|
20008
|
+
}
|
|
20009
|
+
function checkStatusTransitionResult(delta, before, after) {
|
|
20010
|
+
const label = delta.label || delta.check_label || delta.check_type || "check-status-transition";
|
|
20011
|
+
if (!delta.check_label && !delta.check_type) {
|
|
20012
|
+
return {
|
|
20013
|
+
type: delta.type,
|
|
20014
|
+
label,
|
|
20015
|
+
status: "configuration_error",
|
|
20016
|
+
message: `${label} needs check_label or check_type.`
|
|
20017
|
+
};
|
|
20018
|
+
}
|
|
20019
|
+
if (!before || !after) {
|
|
20020
|
+
return {
|
|
20021
|
+
type: delta.type,
|
|
20022
|
+
label,
|
|
20023
|
+
status: "proof_insufficient",
|
|
20024
|
+
before_observed: before?.status,
|
|
20025
|
+
after_observed: after?.status,
|
|
20026
|
+
message: `${label} needs both before and after profile results.`
|
|
20027
|
+
};
|
|
20028
|
+
}
|
|
20029
|
+
const beforeCheck = findCheck(before, delta);
|
|
20030
|
+
const afterCheck = findCheck(after, delta);
|
|
20031
|
+
if (!beforeCheck || !afterCheck) {
|
|
20032
|
+
return {
|
|
20033
|
+
type: delta.type,
|
|
20034
|
+
label,
|
|
20035
|
+
status: "proof_insufficient",
|
|
20036
|
+
before_observed: beforeCheck?.status,
|
|
20037
|
+
after_observed: afterCheck?.status,
|
|
20038
|
+
message: `${label} was not present in ${!beforeCheck && !afterCheck ? "before or after" : !beforeCheck ? "before" : "after"} profile checks.`
|
|
20039
|
+
};
|
|
20040
|
+
}
|
|
20041
|
+
const beforeAllowed = listValue3(delta.before_status, DEFAULT_CHECK_STATUS_TRANSITION_BEFORE);
|
|
20042
|
+
const afterAllowed = listValue3(delta.after_status, DEFAULT_CHECK_STATUS_TRANSITION_AFTER);
|
|
20043
|
+
const passed = includesValue(beforeAllowed, beforeCheck.status) && includesValue(afterAllowed, afterCheck.status);
|
|
20044
|
+
return {
|
|
20045
|
+
type: delta.type,
|
|
20046
|
+
label,
|
|
20047
|
+
status: passed ? "passed" : "failed",
|
|
20048
|
+
before_observed: beforeCheck.status,
|
|
20049
|
+
after_observed: afterCheck.status,
|
|
20050
|
+
message: passed ? void 0 : `${label} expected before ${beforeAllowed.join(", ")} and after ${afterAllowed.join(", ")}, got ${beforeCheck.status} -> ${afterCheck.status}.`
|
|
20051
|
+
};
|
|
20052
|
+
}
|
|
20053
|
+
function assessDelta(delta, before, after) {
|
|
20054
|
+
if (delta.type === "profile_status_transition") {
|
|
20055
|
+
return profileStatusTransitionResult(delta, before, after);
|
|
20056
|
+
}
|
|
20057
|
+
return checkStatusTransitionResult(delta, before, after);
|
|
20058
|
+
}
|
|
20059
|
+
function collapsedChangeStatus(groups, deltas) {
|
|
20060
|
+
const groupResults = [groups.before, groups.after];
|
|
20061
|
+
if (groupResults.some((group) => group.status === "environment_blocked")) return "environment_blocked";
|
|
20062
|
+
if (groupResults.some((group) => group.status === "configuration_error")) return "configuration_error";
|
|
20063
|
+
if (deltas.some((delta) => delta.status === "configuration_error")) return "configuration_error";
|
|
20064
|
+
if (groupResults.some((group) => group.status === "needs_human_review")) return "needs_human_review";
|
|
20065
|
+
if (groupResults.some((group) => group.status === "missing" || group.status === "proof_insufficient")) return "proof_insufficient";
|
|
20066
|
+
if (!deltas.length || deltas.some((delta) => delta.status === "proof_insufficient")) return "proof_insufficient";
|
|
20067
|
+
if (groupResults.some((group) => !group.ok)) return "product_regression";
|
|
20068
|
+
if (deltas.some((delta) => delta.status === "failed")) return "product_regression";
|
|
20069
|
+
return "passed";
|
|
20070
|
+
}
|
|
20071
|
+
function changeSummary(name, status, deltas) {
|
|
20072
|
+
if (status === "passed") return `${name} passed ${deltas.length} change delta(s).`;
|
|
20073
|
+
if (status === "environment_blocked") return `${name} could not compare reliable evidence because an environment was blocked.`;
|
|
20074
|
+
if (status === "configuration_error") return `${name} has an invalid change proof contract.`;
|
|
20075
|
+
if (status === "needs_human_review") return `${name} needs human review before the change proof can pass.`;
|
|
20076
|
+
if (status === "proof_insufficient") return `${name} did not produce enough before/after evidence for a change proof.`;
|
|
20077
|
+
return `${name} failed ${deltas.filter((delta) => delta.status === "failed").length} change delta(s).`;
|
|
20078
|
+
}
|
|
20079
|
+
function assessRiddleProofChange(contract, input) {
|
|
20080
|
+
const groups = {
|
|
20081
|
+
before: groupResult("before", contract.before, input.before_result),
|
|
20082
|
+
after: groupResult("after", contract.after, input.after_result)
|
|
20083
|
+
};
|
|
20084
|
+
const deltas = (contract.deltas || []).map((delta) => assessDelta(delta, input.before_result, input.after_result));
|
|
20085
|
+
const status = collapsedChangeStatus(groups, deltas);
|
|
20086
|
+
return {
|
|
20087
|
+
version: RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
20088
|
+
contract_name: contract.name,
|
|
20089
|
+
status,
|
|
20090
|
+
groups,
|
|
20091
|
+
deltas,
|
|
20092
|
+
summary: changeSummary(contract.name, status, deltas),
|
|
20093
|
+
metadata: contract.metadata
|
|
20094
|
+
};
|
|
20095
|
+
}
|
|
20096
|
+
|
|
19932
20097
|
// src/riddle-client.ts
|
|
19933
20098
|
var import_node_child_process4 = require("child_process");
|
|
19934
20099
|
var import_node_fs5 = require("fs");
|
|
@@ -20903,6 +21068,8 @@ function buildRiddleProofPrCommentMarkdown(input) {
|
|
|
20903
21068
|
RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION,
|
|
20904
21069
|
RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION,
|
|
20905
21070
|
RIDDLE_PROOF_CAPTURE_DIAGNOSTIC_VERSION,
|
|
21071
|
+
RIDDLE_PROOF_CHANGE_CONTRACT_VERSION,
|
|
21072
|
+
RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
20906
21073
|
RIDDLE_PROOF_CHECKPOINT_PACKET_VERSION,
|
|
20907
21074
|
RIDDLE_PROOF_CHECKPOINT_RESPONSE_VERSION,
|
|
20908
21075
|
RIDDLE_PROOF_HOSTED_PROFILE_ARTIFACTS,
|
|
@@ -20935,6 +21102,7 @@ function buildRiddleProofPrCommentMarkdown(input) {
|
|
|
20935
21102
|
assessBasicGameplayProgressionChecks,
|
|
20936
21103
|
assessBasicGameplayRoute,
|
|
20937
21104
|
assessPlayabilityEvidence,
|
|
21105
|
+
assessRiddleProofChange,
|
|
20938
21106
|
assessRiddleProofProfileArtifactCompleteness,
|
|
20939
21107
|
assessRiddleProofProfileEvidence,
|
|
20940
21108
|
attachBasicGameplayArtifactScreenshotHashes,
|