@riddledc/riddle-proof 0.8.76 → 0.8.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -5
- 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 +991 -7
- package/dist/change-proof.d.cts +157 -1
- package/dist/change-proof.d.ts +157 -1
- package/dist/change-proof.js +25 -3
- package/dist/{chunk-B2DP2LET.js → chunk-5IFZSUPF.js} +52 -13
- package/dist/chunk-6VFS2JFR.js +886 -0
- package/dist/{chunk-CWRIXP5H.js → chunk-7N6X54WG.js} +181 -17
- package/dist/{chunk-GG2D3MFZ.js → chunk-FCSJZBC5.js} +26 -1
- package/dist/{chunk-AK2EPEVO.js → chunk-HSGZV2NK.js} +221 -63
- package/dist/chunk-MEVVL4TI.js +258 -0
- package/dist/{chunk-UE4I7RTI.js → chunk-RQPCKRKT.js} +1 -1
- package/dist/cli/index.js +7 -6
- package/dist/cli.cjs +1678 -361
- package/dist/cli.js +7 -6
- package/dist/index.cjs +1242 -42
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +46 -10
- package/dist/pr-comment.cjs +481 -17
- package/dist/pr-comment.d.cts +6 -1
- package/dist/pr-comment.d.ts +6 -1
- package/dist/pr-comment.js +6 -1
- package/dist/profile/index.cjs +26 -1
- package/dist/profile/index.js +1 -1
- package/dist/profile-suggestions.js +2 -2
- package/dist/profile.cjs +26 -1
- package/dist/profile.d.cts +7 -0
- package/dist/profile.d.ts +7 -0
- package/dist/profile.js +1 -1
- package/dist/{proof-run-engine-MiKZt9oY.d.ts → proof-run-engine-CsQshTUX.d.ts} +3 -3
- package/dist/{proof-run-engine-Baiv6l3A.d.cts → proof-run-engine-DmUqh7Cw.d.cts} +3 -3
- package/dist/proof-run-engine.d.cts +1 -1
- package/dist/proof-run-engine.d.ts +1 -1
- package/dist/receipts.cjs +286 -0
- package/dist/receipts.d.cts +115 -0
- package/dist/receipts.d.ts +115 -0
- package/dist/receipts.js +15 -0
- package/dist/riddle-client.cjs +98 -13
- package/dist/riddle-client.d.cts +18 -5
- package/dist/riddle-client.d.ts +18 -5
- package/dist/riddle-client.js +4 -1
- package/dist/runtime/index.cjs +98 -13
- package/dist/runtime/index.d.cts +5 -1
- package/dist/runtime/index.d.ts +5 -1
- package/dist/runtime/index.js +4 -1
- package/dist/runtime/riddle-client.cjs +98 -13
- package/dist/runtime/riddle-client.d.cts +5 -1
- package/dist/runtime/riddle-client.d.ts +5 -1
- package/dist/runtime/riddle-client.js +4 -1
- package/package.json +7 -2
- package/dist/chunk-6S7DZWVC.js +0 -167
package/dist/cli.cjs
CHANGED
|
@@ -7879,6 +7879,259 @@ var import_node_child_process4 = require("child_process");
|
|
|
7879
7879
|
var import_node_fs5 = require("fs");
|
|
7880
7880
|
var import_node_os2 = require("os");
|
|
7881
7881
|
var import_node_path5 = __toESM(require("path"), 1);
|
|
7882
|
+
|
|
7883
|
+
// src/receipts.ts
|
|
7884
|
+
var RIDDLE_PREVIEW_RECEIPT_VERSION = "riddle.preview-receipt.v1";
|
|
7885
|
+
var RIDDLE_PROOF_OBSERVATION_RECEIPT_VERSION = "riddle-proof.observation-receipt.v1";
|
|
7886
|
+
function isRecord(value) {
|
|
7887
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7888
|
+
}
|
|
7889
|
+
function requiredString(record, key, context) {
|
|
7890
|
+
const value = record[key];
|
|
7891
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
7892
|
+
throw new Error(`${context}.${key} must be a non-empty string.`);
|
|
7893
|
+
}
|
|
7894
|
+
return value.trim();
|
|
7895
|
+
}
|
|
7896
|
+
function normalizedSourceIdentity(value) {
|
|
7897
|
+
if (!isRecord(value)) return {};
|
|
7898
|
+
return {
|
|
7899
|
+
git_revision: typeof value.git_revision === "string" && value.git_revision.trim() ? value.git_revision.trim() : void 0,
|
|
7900
|
+
repository: typeof value.repository === "string" && value.repository.trim() ? value.repository.trim() : void 0,
|
|
7901
|
+
dirty: typeof value.dirty === "boolean" ? value.dirty : void 0,
|
|
7902
|
+
label: typeof value.label === "string" && value.label.trim() ? value.label.trim() : void 0
|
|
7903
|
+
};
|
|
7904
|
+
}
|
|
7905
|
+
function parseRiddlePreviewReceipt(value) {
|
|
7906
|
+
if (!isRecord(value)) throw new Error("Preview receipt must be an object.");
|
|
7907
|
+
if (value.version !== RIDDLE_PREVIEW_RECEIPT_VERSION) {
|
|
7908
|
+
throw new Error(`Unsupported Preview receipt version ${String(value.version || "missing")}.`);
|
|
7909
|
+
}
|
|
7910
|
+
if (!isRecord(value.source)) throw new Error("Preview receipt source must be an object.");
|
|
7911
|
+
const expiresAt = requiredString(value, "expires_at", "preview receipt");
|
|
7912
|
+
const publishedAt = requiredString(value, "published_at", "preview receipt");
|
|
7913
|
+
const contentDigest = requiredString(value, "content_digest", "preview receipt");
|
|
7914
|
+
if (!contentDigest.startsWith("sha256:") || contentDigest.length <= "sha256:".length) {
|
|
7915
|
+
throw new Error("preview receipt.content_digest must be a sha256 digest.");
|
|
7916
|
+
}
|
|
7917
|
+
if (!Number.isFinite(Date.parse(expiresAt)) || !Number.isFinite(Date.parse(publishedAt))) {
|
|
7918
|
+
throw new Error("Preview receipt timestamps must be valid ISO date strings.");
|
|
7919
|
+
}
|
|
7920
|
+
return {
|
|
7921
|
+
version: RIDDLE_PREVIEW_RECEIPT_VERSION,
|
|
7922
|
+
preview_id: requiredString(value, "preview_id", "preview receipt"),
|
|
7923
|
+
url: requiredString(value, "url", "preview receipt"),
|
|
7924
|
+
expires_at: expiresAt,
|
|
7925
|
+
content_digest: contentDigest,
|
|
7926
|
+
source: normalizedSourceIdentity(value.source),
|
|
7927
|
+
published_at: publishedAt
|
|
7928
|
+
};
|
|
7929
|
+
}
|
|
7930
|
+
function comparableArtifactName(value) {
|
|
7931
|
+
return (value || "").split(/[/?#]/).pop()?.toLowerCase().replace(/\.(png|jpe?g|gif|webp|avif|svg)$/u, "").replace(/[^a-z0-9_-]+/gu, "-").replace(/^-+|-+$/g, "") || "";
|
|
7932
|
+
}
|
|
7933
|
+
function artifactMatchesLabel(artifact, label) {
|
|
7934
|
+
const expected = comparableArtifactName(label);
|
|
7935
|
+
if (!expected) return false;
|
|
7936
|
+
return [artifact.name, artifact.url, artifact.path].map(comparableArtifactName).some((name) => name === expected || name.endsWith(`-${expected}`));
|
|
7937
|
+
}
|
|
7938
|
+
function artifactLooksLikeScreenshot(ref) {
|
|
7939
|
+
const text = [ref.name, ref.url, ref.path, ref.kind, ref.content_type].filter(Boolean).join(" ").toLowerCase();
|
|
7940
|
+
return /screenshot|\bimage\b/.test(text) || /\.(png|jpe?g|gif|webp|avif|svg)(\?|#|$)/.test(text);
|
|
7941
|
+
}
|
|
7942
|
+
function finalScreenshotLabels(result) {
|
|
7943
|
+
const explicit = result.artifacts.canonical_screenshots || [];
|
|
7944
|
+
if (explicit.length) return explicit;
|
|
7945
|
+
const fromEvidence = (result.evidence?.viewports || []).map((viewport) => viewport.screenshot_label).filter((label) => typeof label === "string" && Boolean(label.trim()));
|
|
7946
|
+
if (fromEvidence.length) return fromEvidence;
|
|
7947
|
+
return (result.artifacts.screenshots || []).slice(0, 1);
|
|
7948
|
+
}
|
|
7949
|
+
function setupScreenshotLabels(result) {
|
|
7950
|
+
const explicit = result.artifacts.setup_screenshots || [];
|
|
7951
|
+
if (explicit.length) return explicit;
|
|
7952
|
+
return (result.evidence?.viewports || []).flatMap(
|
|
7953
|
+
(viewport) => (viewport.setup_action_results || []).filter((action) => action.action === "screenshot" && action.ok !== false && typeof action.screenshot_label === "string").map((action) => String(action.screenshot_label))
|
|
7954
|
+
);
|
|
7955
|
+
}
|
|
7956
|
+
function observationArtifactFromRef(ref, canonicalLabels, setupLabels) {
|
|
7957
|
+
const base = {
|
|
7958
|
+
name: ref.name,
|
|
7959
|
+
role: ref.role || "artifact",
|
|
7960
|
+
url: ref.url,
|
|
7961
|
+
path: ref.path,
|
|
7962
|
+
kind: ref.kind,
|
|
7963
|
+
content_type: ref.content_type,
|
|
7964
|
+
source: ref.source
|
|
7965
|
+
};
|
|
7966
|
+
if (!artifactLooksLikeScreenshot(ref)) return base;
|
|
7967
|
+
if (canonicalLabels.some((label) => artifactMatchesLabel(base, label))) {
|
|
7968
|
+
return { ...base, role: "canonical_screenshot" };
|
|
7969
|
+
}
|
|
7970
|
+
if (setupLabels.some((label) => artifactMatchesLabel(base, label))) {
|
|
7971
|
+
return { ...base, role: "setup_screenshot" };
|
|
7972
|
+
}
|
|
7973
|
+
return { ...base, role: "diagnostic" };
|
|
7974
|
+
}
|
|
7975
|
+
function profileObservationArtifacts(result) {
|
|
7976
|
+
const canonicalLabels = finalScreenshotLabels(result);
|
|
7977
|
+
const setupLabels = setupScreenshotLabels(result);
|
|
7978
|
+
const artifacts = [];
|
|
7979
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7980
|
+
const add = (artifact) => {
|
|
7981
|
+
const key = artifact.url || artifact.path || `${artifact.role}:${artifact.name}`;
|
|
7982
|
+
if (!artifact.name || seen.has(key)) return;
|
|
7983
|
+
seen.add(key);
|
|
7984
|
+
artifacts.push(artifact);
|
|
7985
|
+
};
|
|
7986
|
+
for (const ref of result.artifacts.riddle_artifacts || []) {
|
|
7987
|
+
add(observationArtifactFromRef(ref, canonicalLabels, setupLabels));
|
|
7988
|
+
}
|
|
7989
|
+
for (const label of canonicalLabels) {
|
|
7990
|
+
if (!artifacts.some((artifact) => artifactMatchesLabel(artifact, label))) {
|
|
7991
|
+
add({ name: label, path: label, role: "canonical_screenshot", kind: "image" });
|
|
7992
|
+
}
|
|
7993
|
+
}
|
|
7994
|
+
for (const label of setupLabels) {
|
|
7995
|
+
if (!artifacts.some((artifact) => artifactMatchesLabel(artifact, label))) {
|
|
7996
|
+
add({ name: label, path: label, role: "setup_screenshot", kind: "image" });
|
|
7997
|
+
}
|
|
7998
|
+
}
|
|
7999
|
+
for (const label of result.artifacts.screenshots || []) {
|
|
8000
|
+
if (!artifacts.some((artifact) => artifactMatchesLabel(artifact, label))) {
|
|
8001
|
+
add({ name: label, path: label, role: "diagnostic", kind: "image" });
|
|
8002
|
+
}
|
|
8003
|
+
}
|
|
8004
|
+
return artifacts;
|
|
8005
|
+
}
|
|
8006
|
+
function mergeObservationArtifacts(profileArtifacts, suppliedArtifacts) {
|
|
8007
|
+
const artifacts = [...profileArtifacts];
|
|
8008
|
+
for (const supplied of suppliedArtifacts) {
|
|
8009
|
+
const suppliedNames = [supplied.name, supplied.url, supplied.path].map(comparableArtifactName).filter(Boolean);
|
|
8010
|
+
const existingIndex = artifacts.findIndex((artifact) => {
|
|
8011
|
+
if (supplied.url && artifact.url === supplied.url) return true;
|
|
8012
|
+
if (supplied.path && artifact.path === supplied.path) return true;
|
|
8013
|
+
return [artifact.name, artifact.url, artifact.path].map(comparableArtifactName).some((name) => name && suppliedNames.includes(name));
|
|
8014
|
+
});
|
|
8015
|
+
if (existingIndex < 0) {
|
|
8016
|
+
artifacts.push(supplied);
|
|
8017
|
+
continue;
|
|
8018
|
+
}
|
|
8019
|
+
const existing = artifacts[existingIndex];
|
|
8020
|
+
const role = supplied.role === "canonical_screenshot" || supplied.role === "setup_screenshot" ? supplied.role : existing.role === "canonical_screenshot" || existing.role === "setup_screenshot" ? existing.role : supplied.role === "artifact" ? existing.role : supplied.role;
|
|
8021
|
+
artifacts[existingIndex] = {
|
|
8022
|
+
...existing,
|
|
8023
|
+
...supplied,
|
|
8024
|
+
role
|
|
8025
|
+
};
|
|
8026
|
+
}
|
|
8027
|
+
return artifacts;
|
|
8028
|
+
}
|
|
8029
|
+
function defaultObservationId(role, capturedAt) {
|
|
8030
|
+
return `obs_${role}_${capturedAt.replace(/[^0-9]/g, "").slice(0, 17) || "unknown"}`;
|
|
8031
|
+
}
|
|
8032
|
+
function profileCheckCounts(result) {
|
|
8033
|
+
const counts = { total: result.checks.length, passed: 0, failed: 0, skipped: 0, needs_human_review: 0 };
|
|
8034
|
+
for (const check of result.checks) {
|
|
8035
|
+
if (check.status === "passed") counts.passed += 1;
|
|
8036
|
+
if (check.status === "failed") counts.failed += 1;
|
|
8037
|
+
if (check.status === "skipped") counts.skipped += 1;
|
|
8038
|
+
if (check.status === "needs_human_review") counts.needs_human_review += 1;
|
|
8039
|
+
}
|
|
8040
|
+
return counts;
|
|
8041
|
+
}
|
|
8042
|
+
function createRiddleProofObservationReceipt(input) {
|
|
8043
|
+
const capturedAt = input.captured_at || input.profile_result?.captured_at || (/* @__PURE__ */ new Date()).toISOString();
|
|
8044
|
+
const profileArtifacts = input.profile_result ? profileObservationArtifacts(input.profile_result) : [];
|
|
8045
|
+
const requestedCanonical = input.canonical_screenshot ? { ...input.canonical_screenshot, role: "canonical_screenshot" } : void 0;
|
|
8046
|
+
const artifacts = mergeObservationArtifacts(
|
|
8047
|
+
profileArtifacts,
|
|
8048
|
+
[...input.artifacts || [], ...requestedCanonical ? [requestedCanonical] : []]
|
|
8049
|
+
);
|
|
8050
|
+
const canonicalScreenshot = requestedCanonical ? artifacts.find((artifact) => artifact.role === "canonical_screenshot" && artifactMatchesLabel(artifact, requestedCanonical.name)) || requestedCanonical : artifacts.find((artifact) => artifact.role === "canonical_screenshot");
|
|
8051
|
+
const target = input.target.kind === "preview" && input.target.preview ? { ...input.target, preview: parseRiddlePreviewReceipt(input.target.preview) } : input.target;
|
|
8052
|
+
return {
|
|
8053
|
+
version: RIDDLE_PROOF_OBSERVATION_RECEIPT_VERSION,
|
|
8054
|
+
observation_id: input.observation_id || defaultObservationId(input.comparison_role, capturedAt),
|
|
8055
|
+
comparison_role: input.comparison_role,
|
|
8056
|
+
executor: input.executor,
|
|
8057
|
+
target,
|
|
8058
|
+
source: normalizedSourceIdentity(input.source || input.target.preview?.source),
|
|
8059
|
+
captured_at: capturedAt,
|
|
8060
|
+
artifacts,
|
|
8061
|
+
canonical_screenshot: canonicalScreenshot,
|
|
8062
|
+
profile_summary: input.profile_result ? {
|
|
8063
|
+
profile_name: input.profile_result.profile_name,
|
|
8064
|
+
status: input.profile_result.status,
|
|
8065
|
+
summary: input.profile_result.summary,
|
|
8066
|
+
route: input.profile_result.route,
|
|
8067
|
+
checks: profileCheckCounts(input.profile_result)
|
|
8068
|
+
} : void 0,
|
|
8069
|
+
proof: input.profile_result ? {
|
|
8070
|
+
profile_name: input.profile_result.profile_name,
|
|
8071
|
+
result: input.profile_result
|
|
8072
|
+
} : void 0,
|
|
8073
|
+
publication: input.publication,
|
|
8074
|
+
execution: input.execution,
|
|
8075
|
+
metadata: input.metadata
|
|
8076
|
+
};
|
|
8077
|
+
}
|
|
8078
|
+
function parseRiddleProofObservationReceipt(value) {
|
|
8079
|
+
if (!isRecord(value)) throw new Error("Observation receipt must be an object.");
|
|
8080
|
+
if (value.version !== RIDDLE_PROOF_OBSERVATION_RECEIPT_VERSION) {
|
|
8081
|
+
throw new Error(`Unsupported Observation receipt version ${String(value.version || "missing")}.`);
|
|
8082
|
+
}
|
|
8083
|
+
if (!isRecord(value.executor)) throw new Error("Observation receipt executor must be an object.");
|
|
8084
|
+
if (!isRecord(value.target)) throw new Error("Observation receipt target must be an object.");
|
|
8085
|
+
if (!isRecord(value.source)) throw new Error("Observation receipt source must be an object.");
|
|
8086
|
+
requiredString(value, "observation_id", "observation receipt");
|
|
8087
|
+
requiredString(value, "captured_at", "observation receipt");
|
|
8088
|
+
const executorKind = requiredString(value.executor, "kind", "observation receipt executor");
|
|
8089
|
+
if (!"local_playwright,riddle_hosted,browser_api,other".split(",").includes(executorKind)) {
|
|
8090
|
+
throw new Error(`Unsupported observation executor kind ${executorKind}.`);
|
|
8091
|
+
}
|
|
8092
|
+
if (value.executor.runner !== void 0) {
|
|
8093
|
+
requiredString(value.executor, "runner", "observation receipt executor");
|
|
8094
|
+
}
|
|
8095
|
+
const role = requiredString(value, "comparison_role", "observation receipt");
|
|
8096
|
+
if (!["before", "after", "standalone"].includes(role)) {
|
|
8097
|
+
throw new Error(`Unsupported observation comparison role ${role}.`);
|
|
8098
|
+
}
|
|
8099
|
+
const targetKind = requiredString(value.target, "kind", "observation receipt target");
|
|
8100
|
+
if (targetKind !== "url" && targetKind !== "preview") {
|
|
8101
|
+
throw new Error(`Unsupported observation target kind ${targetKind}.`);
|
|
8102
|
+
}
|
|
8103
|
+
requiredString(value.target, "url", "observation receipt target");
|
|
8104
|
+
if (targetKind === "preview") {
|
|
8105
|
+
if (value.target.preview === void 0) {
|
|
8106
|
+
throw new Error("Preview Observation target must include its Preview receipt.");
|
|
8107
|
+
}
|
|
8108
|
+
parseRiddlePreviewReceipt(value.target.preview);
|
|
8109
|
+
}
|
|
8110
|
+
if (!Array.isArray(value.artifacts)) throw new Error("Observation receipt artifacts must be an array.");
|
|
8111
|
+
const artifactRoles = /* @__PURE__ */ new Set(["canonical_screenshot", "setup_screenshot", "data", "diagnostic", "artifact"]);
|
|
8112
|
+
const artifacts = value.artifacts.map((artifact, index) => {
|
|
8113
|
+
if (!isRecord(artifact)) throw new Error(`Observation receipt artifact ${index} must be an object.`);
|
|
8114
|
+
requiredString(artifact, "name", `observation receipt artifact ${index}`);
|
|
8115
|
+
const artifactRole = requiredString(artifact, "role", `observation receipt artifact ${index}`);
|
|
8116
|
+
if (!artifactRoles.has(artifactRole)) {
|
|
8117
|
+
throw new Error(`Unsupported observation artifact role ${artifactRole}.`);
|
|
8118
|
+
}
|
|
8119
|
+
return artifact;
|
|
8120
|
+
});
|
|
8121
|
+
if (value.canonical_screenshot !== void 0) {
|
|
8122
|
+
if (!isRecord(value.canonical_screenshot) || value.canonical_screenshot.role !== "canonical_screenshot") {
|
|
8123
|
+
throw new Error("Observation canonical_screenshot must have the canonical_screenshot role.");
|
|
8124
|
+
}
|
|
8125
|
+
const canonical = value.canonical_screenshot;
|
|
8126
|
+
const included = artifacts.some((artifact) => artifact.role === "canonical_screenshot" && artifact.name === canonical.name && artifact.url === canonical.url && artifact.path === canonical.path);
|
|
8127
|
+
if (!included) {
|
|
8128
|
+
throw new Error("Observation canonical_screenshot must reference an artifact in the receipt.");
|
|
8129
|
+
}
|
|
8130
|
+
}
|
|
8131
|
+
return value;
|
|
8132
|
+
}
|
|
8133
|
+
|
|
8134
|
+
// src/riddle-client.ts
|
|
7882
8135
|
var DEFAULT_RIDDLE_API_BASE_URL = "https://api.riddledc.com";
|
|
7883
8136
|
var DEFAULT_RIDDLE_API_KEY_FILE = "/tmp/riddle-api-key";
|
|
7884
8137
|
var RIDDLE_UNSUBMITTED_WAKE_HINT = "hosted worker may still be waking or waiting for a lease";
|
|
@@ -7940,6 +8193,7 @@ async function getRiddleBalance(config = {}) {
|
|
|
7940
8193
|
}
|
|
7941
8194
|
function previewDeployResultFromRecord(input) {
|
|
7942
8195
|
const { record, id, label, framework, expiresAt, publishRecovered, publishError } = input;
|
|
8196
|
+
const receipt = record.receipt && typeof record.receipt === "object" ? parseRiddlePreviewReceipt(record.receipt) : void 0;
|
|
7943
8197
|
return {
|
|
7944
8198
|
ok: true,
|
|
7945
8199
|
id: String(record.id || record.preview_id || id),
|
|
@@ -7948,13 +8202,32 @@ function previewDeployResultFromRecord(input) {
|
|
|
7948
8202
|
preview_url: String(record.preview_url || ""),
|
|
7949
8203
|
file_count: typeof record.file_count === "number" ? record.file_count : void 0,
|
|
7950
8204
|
total_bytes: typeof record.total_bytes === "number" ? record.total_bytes : void 0,
|
|
7951
|
-
expires_at: expiresAt,
|
|
8205
|
+
expires_at: receipt?.expires_at || expiresAt,
|
|
8206
|
+
receipt,
|
|
7952
8207
|
publish_recovered: publishRecovered || void 0,
|
|
7953
8208
|
publish_error: publishError,
|
|
7954
8209
|
warnings: input.warnings?.length ? input.warnings : void 0,
|
|
7955
8210
|
raw: record
|
|
7956
8211
|
};
|
|
7957
8212
|
}
|
|
8213
|
+
function gitOutput(directory, args) {
|
|
8214
|
+
try {
|
|
8215
|
+
return (0, import_node_child_process4.execFileSync)("git", ["-C", directory, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
8216
|
+
} catch {
|
|
8217
|
+
return "";
|
|
8218
|
+
}
|
|
8219
|
+
}
|
|
8220
|
+
function detectRiddlePreviewSource(directory) {
|
|
8221
|
+
const gitRevision = gitOutput(directory, ["rev-parse", "HEAD"]);
|
|
8222
|
+
if (!gitRevision) return {};
|
|
8223
|
+
const repository = gitOutput(directory, ["config", "--get", "remote.origin.url"]);
|
|
8224
|
+
const status = gitOutput(directory, ["status", "--porcelain", "--untracked-files=normal"]);
|
|
8225
|
+
return {
|
|
8226
|
+
git_revision: gitRevision,
|
|
8227
|
+
repository: repository || void 0,
|
|
8228
|
+
dirty: Boolean(status)
|
|
8229
|
+
};
|
|
8230
|
+
}
|
|
7958
8231
|
function canRecoverPreviewPublish(error) {
|
|
7959
8232
|
return error instanceof RiddleApiError && PREVIEW_PUBLISH_RECOVERY_STATUSES.has(error.status);
|
|
7960
8233
|
}
|
|
@@ -8037,12 +8310,13 @@ async function waitForPublishedPreview(config, input) {
|
|
|
8037
8310
|
}
|
|
8038
8311
|
throw input.publishError;
|
|
8039
8312
|
}
|
|
8040
|
-
async function deployRiddlePreview(config, directory, label, framework = "static") {
|
|
8313
|
+
async function deployRiddlePreview(config, directory, label, framework = "static", options = {}) {
|
|
8041
8314
|
if (!directory?.trim()) throw new Error("directory is required");
|
|
8042
8315
|
if (!label?.trim()) throw new Error("label is required");
|
|
8043
8316
|
if (framework !== "spa" && framework !== "static") throw new Error("framework must be spa or static");
|
|
8044
8317
|
const startedAt = Date.now();
|
|
8045
8318
|
const warnings = collectRiddlePreviewDeployWarnings(directory, framework);
|
|
8319
|
+
const source = options.source || detectRiddlePreviewSource(directory);
|
|
8046
8320
|
const emitProgress = previewProgressEmitter(config, { label, framework, directory, startedAt, warnings });
|
|
8047
8321
|
await emitProgress({ stage: "validating", message: "checking preview input directory" });
|
|
8048
8322
|
const localSummary = summarizePreviewDirectory(directory);
|
|
@@ -8054,7 +8328,7 @@ async function deployRiddlePreview(config, directory, label, framework = "static
|
|
|
8054
8328
|
});
|
|
8055
8329
|
const created = await riddleRequestJson(config, "/v1/preview", {
|
|
8056
8330
|
method: "POST",
|
|
8057
|
-
body: JSON.stringify({ framework, label })
|
|
8331
|
+
body: JSON.stringify({ framework, label, source })
|
|
8058
8332
|
});
|
|
8059
8333
|
const id = String(created.id || "");
|
|
8060
8334
|
const uploadUrl = String(created.upload_url || "");
|
|
@@ -8196,8 +8470,8 @@ function collectRiddlePreviewDeployWarnings(directory, framework = "static") {
|
|
|
8196
8470
|
return [];
|
|
8197
8471
|
}
|
|
8198
8472
|
}
|
|
8199
|
-
async function deployRiddleStaticPreview(config, directory, label) {
|
|
8200
|
-
return deployRiddlePreview(config, directory, label, "static");
|
|
8473
|
+
async function deployRiddleStaticPreview(config, directory, label, options = {}) {
|
|
8474
|
+
return deployRiddlePreview(config, directory, label, "static", options);
|
|
8201
8475
|
}
|
|
8202
8476
|
function createTarball(directory, label, exclude = []) {
|
|
8203
8477
|
const scratch = (0, import_node_fs5.mkdtempSync)(import_node_path5.default.join((0, import_node_os2.tmpdir)(), "riddle-upload-"));
|
|
@@ -8319,21 +8593,31 @@ function parseTimestampMs(value) {
|
|
|
8319
8593
|
}
|
|
8320
8594
|
function buildPollSnapshot(jobId, job, input) {
|
|
8321
8595
|
const status = job?.status ? String(job.status) : null;
|
|
8596
|
+
const phase = job?.phase ? String(job.phase) : null;
|
|
8322
8597
|
const terminal = isTerminalRiddleJobStatus(status);
|
|
8323
8598
|
const createdAt = stringField(job, "created_at");
|
|
8324
8599
|
const submittedAt = stringField(job, "submitted_at");
|
|
8325
8600
|
const completedAt = stringField(job, "completed_at");
|
|
8326
8601
|
const createdMs = parseTimestampMs(createdAt);
|
|
8327
8602
|
const submittedMs = parseTimestampMs(submittedAt);
|
|
8603
|
+
const rawExecution = job?.execution && typeof job.execution === "object" && !Array.isArray(job.execution) ? job.execution : null;
|
|
8604
|
+
const enqueuedAt = rawExecution ? stringField(rawExecution, "enqueued_at") : null;
|
|
8605
|
+
const enqueuedMs = parseTimestampMs(enqueuedAt);
|
|
8606
|
+
const queueStartedMs = enqueuedMs ?? createdMs;
|
|
8607
|
+
const claimedAt = rawExecution ? stringField(rawExecution, "claimed_at") : null;
|
|
8608
|
+
const claimedMs = parseTimestampMs(claimedAt);
|
|
8328
8609
|
let queueElapsedMs = null;
|
|
8329
|
-
if (
|
|
8330
|
-
queueElapsedMs = Math.max(0,
|
|
8331
|
-
} else if (
|
|
8332
|
-
queueElapsedMs = Math.max(0,
|
|
8610
|
+
if (queueStartedMs !== null && claimedMs !== null) {
|
|
8611
|
+
queueElapsedMs = Math.max(0, claimedMs - queueStartedMs);
|
|
8612
|
+
} else if (queueStartedMs !== null && submittedMs !== null) {
|
|
8613
|
+
queueElapsedMs = Math.max(0, submittedMs - queueStartedMs);
|
|
8614
|
+
} else if (queueStartedMs !== null && !submittedAt && !terminal) {
|
|
8615
|
+
queueElapsedMs = Math.max(0, input.observedAt - queueStartedMs);
|
|
8333
8616
|
}
|
|
8334
8617
|
return {
|
|
8335
8618
|
job_id: jobId,
|
|
8336
8619
|
status,
|
|
8620
|
+
phase,
|
|
8337
8621
|
terminal,
|
|
8338
8622
|
attempt: input.attempt,
|
|
8339
8623
|
attempts: input.attempts,
|
|
@@ -8343,7 +8627,9 @@ function buildPollSnapshot(jobId, job, input) {
|
|
|
8343
8627
|
completed_at: completedAt,
|
|
8344
8628
|
queue_elapsed_ms: queueElapsedMs,
|
|
8345
8629
|
pre_submission_elapsed_ms: Math.max(0, Math.floor(input.preSubmissionElapsedMs ?? 0)),
|
|
8346
|
-
running_without_submission: Boolean(status && !terminal && !submittedAt)
|
|
8630
|
+
running_without_submission: Boolean(status && !terminal && !submittedAt),
|
|
8631
|
+
active_execution: Boolean(status === "running" && phase && phase !== "queued"),
|
|
8632
|
+
execution: rawExecution ? rawExecution : void 0
|
|
8347
8633
|
};
|
|
8348
8634
|
}
|
|
8349
8635
|
function pollMessage(snapshot, timedOut) {
|
|
@@ -8352,7 +8638,7 @@ function pollMessage(snapshot, timedOut) {
|
|
|
8352
8638
|
const wakeHint = snapshot.running_without_submission && !snapshot.created_at && !snapshot.submitted_at ? ` ${RIDDLE_UNSUBMITTED_WAKE_HINT}.` : "";
|
|
8353
8639
|
const queue = snapshot.queue_elapsed_ms !== null ? ` queue_elapsed_ms=${snapshot.queue_elapsed_ms}` : "";
|
|
8354
8640
|
const preSubmit = snapshot.pre_submission_elapsed_ms > 0 ? ` pre_submission_elapsed_ms=${snapshot.pre_submission_elapsed_ms}` : "";
|
|
8355
|
-
return `Riddle job ${snapshot.job_id} did not reach a terminal status after ${snapshot.attempt} poll attempts; status=${snapshot.status || "unknown"} submitted_at=${submitted}.${wakeHint}${queue}${preSubmit}`;
|
|
8641
|
+
return `Riddle job ${snapshot.job_id} did not reach a terminal status after ${snapshot.attempt} poll attempts; status=${snapshot.status || "unknown"} phase=${snapshot.phase || "unknown"} submitted_at=${submitted}.${wakeHint}${queue}${preSubmit}`;
|
|
8356
8642
|
}
|
|
8357
8643
|
async function pollRiddleJob(config, jobId, options = {}) {
|
|
8358
8644
|
if (!jobId?.trim()) throw new Error("jobId is required");
|
|
@@ -8386,6 +8672,7 @@ async function pollRiddleJob(config, jobId, options = {}) {
|
|
|
8386
8672
|
};
|
|
8387
8673
|
const progressKey = [
|
|
8388
8674
|
lastSnapshot.status || "unknown",
|
|
8675
|
+
lastSnapshot.phase || "unknown",
|
|
8389
8676
|
lastSnapshot.terminal ? "terminal" : "nonterminal",
|
|
8390
8677
|
lastSnapshot.submitted_at ? "submitted" : "unsubmitted"
|
|
8391
8678
|
].join(":");
|
|
@@ -8452,8 +8739,8 @@ function createRiddleApiClient(config = {}) {
|
|
|
8452
8739
|
apiKeySource: () => resolveRiddleApiKeySource(config),
|
|
8453
8740
|
requestJson: (pathname, init) => riddleRequestJson(config, pathname, init),
|
|
8454
8741
|
getBalance: () => getRiddleBalance(config),
|
|
8455
|
-
deployPreview: (directory, label, framework = "static") => deployRiddlePreview(config, directory, label, framework),
|
|
8456
|
-
deployStaticPreview: (directory, label) => deployRiddleStaticPreview(config, directory, label),
|
|
8742
|
+
deployPreview: (directory, label, framework = "static", options) => deployRiddlePreview(config, directory, label, framework, options),
|
|
8743
|
+
deployStaticPreview: (directory, label, options) => deployRiddleStaticPreview(config, directory, label, options),
|
|
8457
8744
|
runScript: (input) => runRiddleScript(config, input),
|
|
8458
8745
|
runServerPreview: (input) => runRiddleServerPreview(config, input),
|
|
8459
8746
|
pollJob: (jobId, options) => pollRiddleJob(config, jobId, options)
|
|
@@ -8584,7 +8871,7 @@ function deriveRiddleProofArtifactBodyAssertions(input) {
|
|
|
8584
8871
|
var DEFAULT_VIEWPORTS = [
|
|
8585
8872
|
{ name: "desktop", width: 1280, height: 800 }
|
|
8586
8873
|
];
|
|
8587
|
-
function
|
|
8874
|
+
function isRecord2(value) {
|
|
8588
8875
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8589
8876
|
}
|
|
8590
8877
|
function stringValue3(value) {
|
|
@@ -8615,7 +8902,7 @@ function booleanValue2(value) {
|
|
|
8615
8902
|
return typeof value === "boolean" ? value : void 0;
|
|
8616
8903
|
}
|
|
8617
8904
|
function horizontalBoundsOverflowPx(value) {
|
|
8618
|
-
if (!
|
|
8905
|
+
if (!isRecord2(value)) return 0;
|
|
8619
8906
|
let max = maxPositiveNumber(
|
|
8620
8907
|
value.overflow_px,
|
|
8621
8908
|
value.overflow,
|
|
@@ -8630,7 +8917,7 @@ function horizontalBoundsOverflowPx(value) {
|
|
|
8630
8917
|
return roundPixels(max);
|
|
8631
8918
|
}
|
|
8632
8919
|
function horizontalOffenderOverflowPx(value) {
|
|
8633
|
-
if (!
|
|
8920
|
+
if (!isRecord2(value)) return 0;
|
|
8634
8921
|
let max = maxPositiveNumber(
|
|
8635
8922
|
value.overflow,
|
|
8636
8923
|
value.overflow_px,
|
|
@@ -8641,8 +8928,8 @@ function horizontalOffenderOverflowPx(value) {
|
|
|
8641
8928
|
value.leftOverflowPx,
|
|
8642
8929
|
value.rightOverflowPx
|
|
8643
8930
|
);
|
|
8644
|
-
const clipped =
|
|
8645
|
-
if (
|
|
8931
|
+
const clipped = isRecord2(value.clipped || value.clip || value.clipping) ? value.clipped || value.clip || value.clipping : void 0;
|
|
8932
|
+
if (isRecord2(clipped)) {
|
|
8646
8933
|
max = Math.max(max, maxPositiveNumber(
|
|
8647
8934
|
clipped.left,
|
|
8648
8935
|
clipped.right,
|
|
@@ -8652,9 +8939,9 @@ function horizontalOffenderOverflowPx(value) {
|
|
|
8652
8939
|
clipped.rightPx
|
|
8653
8940
|
));
|
|
8654
8941
|
}
|
|
8655
|
-
const rect =
|
|
8942
|
+
const rect = isRecord2(value.rect || value.bounds || value.bounding_rect || value.boundingRect) ? value.rect || value.bounds || value.bounding_rect || value.boundingRect : void 0;
|
|
8656
8943
|
const viewportWidth = numberValue2(value.viewport_width ?? value.viewportWidth);
|
|
8657
|
-
if (
|
|
8944
|
+
if (isRecord2(rect) && viewportWidth !== void 0) {
|
|
8658
8945
|
const left = numberValue2(rect.left);
|
|
8659
8946
|
const right = numberValue2(rect.right);
|
|
8660
8947
|
if (left !== void 0 && left < 0) max = Math.max(max, Math.abs(left));
|
|
@@ -8663,7 +8950,7 @@ function horizontalOffenderOverflowPx(value) {
|
|
|
8663
8950
|
return roundPixels(max);
|
|
8664
8951
|
}
|
|
8665
8952
|
function boundsOffendersForEvidence(value) {
|
|
8666
|
-
if (!
|
|
8953
|
+
if (!isRecord2(value)) return [];
|
|
8667
8954
|
const offenders = [
|
|
8668
8955
|
...Array.isArray(value.overflow_offenders) ? value.overflow_offenders : [],
|
|
8669
8956
|
...Array.isArray(value.overflowOffenders) ? value.overflowOffenders : [],
|
|
@@ -8674,7 +8961,7 @@ function boundsOffendersForEvidence(value) {
|
|
|
8674
8961
|
...Array.isArray(value.clipping_offenders) ? value.clipping_offenders : [],
|
|
8675
8962
|
...Array.isArray(value.clippingOffenders) ? value.clippingOffenders : []
|
|
8676
8963
|
];
|
|
8677
|
-
return offenders.filter((item) =>
|
|
8964
|
+
return offenders.filter((item) => isRecord2(item)).sort((a, b) => horizontalOffenderOverflowPx(b) - horizontalOffenderOverflowPx(a));
|
|
8678
8965
|
}
|
|
8679
8966
|
function maxPositiveNumber(...values) {
|
|
8680
8967
|
let max = 0;
|
|
@@ -8692,11 +8979,11 @@ function timeoutSecValue(value) {
|
|
|
8692
8979
|
return number && number > 0 ? Math.ceil(number) : void 0;
|
|
8693
8980
|
}
|
|
8694
8981
|
function jsonRecord(value) {
|
|
8695
|
-
if (!
|
|
8982
|
+
if (!isRecord2(value)) return void 0;
|
|
8696
8983
|
return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
|
|
8697
8984
|
}
|
|
8698
8985
|
function stringRecord(value) {
|
|
8699
|
-
if (!
|
|
8986
|
+
if (!isRecord2(value)) return void 0;
|
|
8700
8987
|
const entries = Object.entries(value).map(([key, child]) => [key, String(child)]).filter(([key]) => key.trim());
|
|
8701
8988
|
return entries.length ? Object.fromEntries(entries) : void 0;
|
|
8702
8989
|
}
|
|
@@ -8705,7 +8992,7 @@ function toJsonValue(value) {
|
|
|
8705
8992
|
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
8706
8993
|
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
8707
8994
|
if (Array.isArray(value)) return value.map(toJsonValue);
|
|
8708
|
-
if (
|
|
8995
|
+
if (isRecord2(value)) return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toJsonValue(child)]));
|
|
8709
8996
|
return String(value);
|
|
8710
8997
|
}
|
|
8711
8998
|
function jsonValueType(value) {
|
|
@@ -8723,7 +9010,7 @@ function compactJsonAssertionSample(value, depth = 0) {
|
|
|
8723
9010
|
if (depth >= 2) return `[array:${value.length}]`;
|
|
8724
9011
|
return value.slice(0, 3).map((item) => compactJsonAssertionSample(item, depth + 1));
|
|
8725
9012
|
}
|
|
8726
|
-
if (
|
|
9013
|
+
if (isRecord2(value)) {
|
|
8727
9014
|
const entries = Object.entries(value).slice(0, 8);
|
|
8728
9015
|
if (depth >= 2) return `[object:${Object.keys(value).length} keys]`;
|
|
8729
9016
|
return Object.fromEntries(entries.map(([key, child]) => [key, compactJsonAssertionSample(child, depth + 1)]));
|
|
@@ -8738,7 +9025,7 @@ function attachJsonAssertionObservedValue(result, value) {
|
|
|
8738
9025
|
result.observed_sample = compactJsonAssertionSample(value);
|
|
8739
9026
|
return;
|
|
8740
9027
|
}
|
|
8741
|
-
if (type === "object" &&
|
|
9028
|
+
if (type === "object" && isRecord2(value)) {
|
|
8742
9029
|
const keyCount = Object.keys(value).length;
|
|
8743
9030
|
result.observed_key_count = keyCount;
|
|
8744
9031
|
result.observed_omitted_count = Math.max(0, keyCount - 8);
|
|
@@ -8756,7 +9043,7 @@ function deepJsonEqual(left, right) {
|
|
|
8756
9043
|
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) return false;
|
|
8757
9044
|
return left.every((item, index) => deepJsonEqual(item, right[index]));
|
|
8758
9045
|
}
|
|
8759
|
-
if (!
|
|
9046
|
+
if (!isRecord2(left) || !isRecord2(right)) return false;
|
|
8760
9047
|
const leftKeys = Object.keys(left).sort();
|
|
8761
9048
|
const rightKeys = Object.keys(right).sort();
|
|
8762
9049
|
if (!deepJsonEqual(leftKeys, rightKeys)) return false;
|
|
@@ -8769,7 +9056,7 @@ function jsonContains(observed, expected) {
|
|
|
8769
9056
|
if (Array.isArray(observed)) {
|
|
8770
9057
|
return observed.some((item) => deepJsonEqual(item, expected));
|
|
8771
9058
|
}
|
|
8772
|
-
if (
|
|
9059
|
+
if (isRecord2(observed) && isRecord2(expected)) {
|
|
8773
9060
|
return Object.entries(expected).every(([key, value]) => hasOwn(observed, key) && deepJsonEqual(observed[key], value));
|
|
8774
9061
|
}
|
|
8775
9062
|
return false;
|
|
@@ -8837,7 +9124,7 @@ function resolveJsonPath(root, path7) {
|
|
|
8837
9124
|
if (typeof segment === "number") {
|
|
8838
9125
|
return { exists: false };
|
|
8839
9126
|
}
|
|
8840
|
-
if (!
|
|
9127
|
+
if (!isRecord2(current) || !hasOwn(current, segment)) return { exists: false };
|
|
8841
9128
|
current = current[segment];
|
|
8842
9129
|
}
|
|
8843
9130
|
return { exists: true, value: current };
|
|
@@ -8911,9 +9198,9 @@ function profileSetupFrameUrls(viewport) {
|
|
|
8911
9198
|
const urls = [];
|
|
8912
9199
|
const frames = viewport.frames || {};
|
|
8913
9200
|
for (const container of Object.values(frames)) {
|
|
8914
|
-
if (!
|
|
9201
|
+
if (!isRecord2(container) || !Array.isArray(container.frames)) continue;
|
|
8915
9202
|
for (const frame of container.frames) {
|
|
8916
|
-
if (!
|
|
9203
|
+
if (!isRecord2(frame)) continue;
|
|
8917
9204
|
const url = typeof frame.url === "string" ? frame.url : void 0;
|
|
8918
9205
|
if (url && !urls.includes(url)) urls.push(url);
|
|
8919
9206
|
}
|
|
@@ -9007,7 +9294,7 @@ function profileSetupReturnSummaryFields(result) {
|
|
|
9007
9294
|
if (path8) fields.push({ path: path8 });
|
|
9008
9295
|
continue;
|
|
9009
9296
|
}
|
|
9010
|
-
if (!
|
|
9297
|
+
if (!isRecord2(item)) continue;
|
|
9011
9298
|
const path7 = stringValue3(item.path) ?? stringValue3(item.key) ?? stringValue3(item.json_path) ?? stringValue3(item.jsonPath);
|
|
9012
9299
|
if (!path7) continue;
|
|
9013
9300
|
const label = stringValue3(item.label) ?? stringValue3(item.name) ?? stringValue3(item.title);
|
|
@@ -9243,6 +9530,12 @@ function profileScreenshotLabels(viewports) {
|
|
|
9243
9530
|
}
|
|
9244
9531
|
return labels;
|
|
9245
9532
|
}
|
|
9533
|
+
function profileFinalScreenshotLabels(viewports) {
|
|
9534
|
+
return (viewports || []).map((viewport) => viewport.screenshot_label).filter((label) => typeof label === "string" && Boolean(label.trim()));
|
|
9535
|
+
}
|
|
9536
|
+
function profileAllSetupScreenshotLabels(viewports) {
|
|
9537
|
+
return (viewports || []).flatMap((viewport) => profileSetupScreenshotLabels(viewport.setup_action_results || []));
|
|
9538
|
+
}
|
|
9246
9539
|
function profileSetupSummary(viewports, actionCount, expectedActionCountByViewport, finalScreenshotFullPage) {
|
|
9247
9540
|
const normalizedFinalScreenshotFullPage = finalScreenshotFullPage === void 0 ? void 0 : finalScreenshotFullPage !== false;
|
|
9248
9541
|
const finalScreenshotCount = viewports.filter((viewport) => typeof viewport.screenshot_label === "string" && viewport.screenshot_label.trim()).length;
|
|
@@ -9397,7 +9690,7 @@ function slugifyRiddleProofProfileName(value) {
|
|
|
9397
9690
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "profile";
|
|
9398
9691
|
}
|
|
9399
9692
|
function normalizeViewport(input, index) {
|
|
9400
|
-
if (!
|
|
9693
|
+
if (!isRecord2(input)) throw new Error(`target.viewports[${index}] must be an object.`);
|
|
9401
9694
|
const width = numberValue2(input.width);
|
|
9402
9695
|
const height = numberValue2(input.height);
|
|
9403
9696
|
if (!width || !height || width < 100 || height < 100) {
|
|
@@ -9463,7 +9756,7 @@ function normalizeReturnSummaryFields(input, index) {
|
|
|
9463
9756
|
if (!path8) throw new Error(`target.setup_actions[${index}].return_summary_fields[${fieldIndex}] requires path.`);
|
|
9464
9757
|
return { path: path8 };
|
|
9465
9758
|
}
|
|
9466
|
-
if (!
|
|
9759
|
+
if (!isRecord2(field)) {
|
|
9467
9760
|
throw new Error(`target.setup_actions[${index}].return_summary_fields[${fieldIndex}] must be a string path or object.`);
|
|
9468
9761
|
}
|
|
9469
9762
|
const path7 = stringFromOwn(field, "path", "key", "json_path", "jsonPath");
|
|
@@ -9572,7 +9865,7 @@ function normalizeSetupActionNonNegativeNumber(input, index, outputKey, ...keys)
|
|
|
9572
9865
|
return value;
|
|
9573
9866
|
}
|
|
9574
9867
|
function normalizeSetupAction(input, index) {
|
|
9575
|
-
if (!
|
|
9868
|
+
if (!isRecord2(input)) throw new Error(`target.setup_actions[${index}] must be an object.`);
|
|
9576
9869
|
const type = normalizeSetupActionType(stringValue3(input.type), index);
|
|
9577
9870
|
const rawType = String(input.type || "").trim().replace(/-/g, "_").toLowerCase();
|
|
9578
9871
|
const selector = stringValue3(input.selector);
|
|
@@ -9864,7 +10157,7 @@ function normalizeTargetScreenshotFullPage(input) {
|
|
|
9864
10157
|
return values[0];
|
|
9865
10158
|
}
|
|
9866
10159
|
function normalizeNetworkMock(input, index) {
|
|
9867
|
-
if (!
|
|
10160
|
+
if (!isRecord2(input)) throw new Error(`target.network_mocks[${index}] must be an object.`);
|
|
9868
10161
|
const url = stringValue3(input.url) || stringValue3(input.glob) || stringValue3(input.pattern);
|
|
9869
10162
|
if (!url) throw new Error(`target.network_mocks[${index}] requires url.`);
|
|
9870
10163
|
const payload = normalizeNetworkMockResponsePayload(input, `target.network_mocks[${index}]`);
|
|
@@ -10019,7 +10312,7 @@ function normalizeNetworkMockResponses(value, mockIndex, defaults) {
|
|
|
10019
10312
|
abort_error_code: defaults.abort_error_code
|
|
10020
10313
|
};
|
|
10021
10314
|
return value.map((response, responseIndex) => {
|
|
10022
|
-
if (!
|
|
10315
|
+
if (!isRecord2(response)) {
|
|
10023
10316
|
throw new Error(`target.network_mocks[${mockIndex}].responses[${responseIndex}] must be an object.`);
|
|
10024
10317
|
}
|
|
10025
10318
|
return normalizeNetworkMockResponsePayload(
|
|
@@ -10116,7 +10409,7 @@ function normalizeRouteInventoryPath(value, label) {
|
|
|
10116
10409
|
}
|
|
10117
10410
|
function normalizeRouteInventoryRoute(input, index) {
|
|
10118
10411
|
if (typeof input === "string") return { path: normalizeRouteInventoryPath(input, `checks route_inventory expected_routes[${index}]`) };
|
|
10119
|
-
if (!
|
|
10412
|
+
if (!isRecord2(input)) throw new Error(`checks route_inventory expected_routes[${index}] must be a string or object.`);
|
|
10120
10413
|
return {
|
|
10121
10414
|
name: stringValue3(input.name),
|
|
10122
10415
|
path: normalizeRouteInventoryPath(input.path, `checks route_inventory expected_routes[${index}]`)
|
|
@@ -10195,7 +10488,7 @@ function normalizeHttpStatusBodyJsonAssertions(value, label) {
|
|
|
10195
10488
|
if (!path8) throw new Error(`${itemLabel} path must not be empty.`);
|
|
10196
10489
|
return { path: path8, exists: true };
|
|
10197
10490
|
}
|
|
10198
|
-
if (!
|
|
10491
|
+
if (!isRecord2(item)) throw new Error(`${itemLabel} must be an object or JSON path string.`);
|
|
10199
10492
|
const path7 = stringFromOwn(item, "path", "json_path", "jsonPath", "key");
|
|
10200
10493
|
if (!path7) throw new Error(`${itemLabel}.path is required.`);
|
|
10201
10494
|
const assertion = {
|
|
@@ -10233,7 +10526,7 @@ function dialogCountFieldForCheckType(type) {
|
|
|
10233
10526
|
return "dialog_count";
|
|
10234
10527
|
}
|
|
10235
10528
|
function normalizeCheck(input, index) {
|
|
10236
|
-
if (!
|
|
10529
|
+
if (!isRecord2(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
10237
10530
|
const type = stringValue3(input.type);
|
|
10238
10531
|
if (!type) throw new Error(`checks[${index}].type is required.`);
|
|
10239
10532
|
if (!isSupportedCheckType(type)) {
|
|
@@ -10398,7 +10691,7 @@ function normalizeFailurePolicy(input) {
|
|
|
10398
10691
|
configuration_error: "fail",
|
|
10399
10692
|
needs_human_review: "fail"
|
|
10400
10693
|
};
|
|
10401
|
-
if (!
|
|
10694
|
+
if (!isRecord2(input)) return defaults;
|
|
10402
10695
|
const next = { ...defaults };
|
|
10403
10696
|
for (const [key, value] of Object.entries(input)) {
|
|
10404
10697
|
if (!RIDDLE_PROOF_PROFILE_STATUSES.includes(key)) continue;
|
|
@@ -10409,12 +10702,12 @@ function normalizeFailurePolicy(input) {
|
|
|
10409
10702
|
return next;
|
|
10410
10703
|
}
|
|
10411
10704
|
function normalizeRiddleProofProfile(input, options = {}) {
|
|
10412
|
-
if (!
|
|
10705
|
+
if (!isRecord2(input)) throw new Error("profile must be a JSON object.");
|
|
10413
10706
|
const version = stringValue3(input.version) || RIDDLE_PROOF_PROFILE_VERSION;
|
|
10414
10707
|
if (version !== RIDDLE_PROOF_PROFILE_VERSION) {
|
|
10415
10708
|
throw new Error(`Unsupported profile version ${version}. Expected ${RIDDLE_PROOF_PROFILE_VERSION}.`);
|
|
10416
10709
|
}
|
|
10417
|
-
const targetInput =
|
|
10710
|
+
const targetInput = isRecord2(input.target) ? input.target : {};
|
|
10418
10711
|
const checks = Array.isArray(input.checks) ? input.checks.map(normalizeCheck) : [];
|
|
10419
10712
|
if (!checks.length) throw new Error("profile.checks must contain at least one check.");
|
|
10420
10713
|
const targetUrl = stringValue3(options.url) || stringValue3(targetInput.url);
|
|
@@ -10545,19 +10838,19 @@ function linkStatusContentTypeOk(result, check) {
|
|
|
10545
10838
|
function httpStatusBodyContainsFailures(result, check) {
|
|
10546
10839
|
const expected = check.body_contains?.filter(Boolean) ?? [];
|
|
10547
10840
|
if (!expected.length) return [];
|
|
10548
|
-
const observed =
|
|
10841
|
+
const observed = isRecord2(result.body_contains) ? result.body_contains : {};
|
|
10549
10842
|
return expected.filter((text) => observed[text] !== true);
|
|
10550
10843
|
}
|
|
10551
10844
|
function httpStatusBodyNotContainsFailures(result, check) {
|
|
10552
10845
|
const forbidden = check.body_not_contains?.filter(Boolean) ?? [];
|
|
10553
10846
|
if (!forbidden.length) return [];
|
|
10554
|
-
const observed =
|
|
10847
|
+
const observed = isRecord2(result.body_not_contains) ? result.body_not_contains : {};
|
|
10555
10848
|
return forbidden.filter((text) => observed[text] !== false);
|
|
10556
10849
|
}
|
|
10557
10850
|
function httpStatusBodyNotPatternFailures(result, check) {
|
|
10558
10851
|
const forbidden = check.body_not_patterns?.filter(Boolean) ?? [];
|
|
10559
10852
|
if (!forbidden.length) return [];
|
|
10560
|
-
const observed =
|
|
10853
|
+
const observed = isRecord2(result.body_not_patterns) ? result.body_not_patterns : {};
|
|
10561
10854
|
return forbidden.filter((pattern) => observed[pattern] !== false);
|
|
10562
10855
|
}
|
|
10563
10856
|
function httpStatusBodyJsonAssertionFailures(result, check) {
|
|
@@ -10573,7 +10866,7 @@ function httpStatusBodyJsonAssertionFailures(result, check) {
|
|
|
10573
10866
|
errors: ["body_json_assertions evidence missing"]
|
|
10574
10867
|
}));
|
|
10575
10868
|
}
|
|
10576
|
-
return result.body_json_assertions.filter((assertion) =>
|
|
10869
|
+
return result.body_json_assertions.filter((assertion) => isRecord2(assertion) && assertion.ok !== true).map((assertion) => ({
|
|
10577
10870
|
label: stringValue3(assertion.label) || stringValue3(assertion.path) || "json assertion",
|
|
10578
10871
|
path: stringValue3(assertion.path) || "",
|
|
10579
10872
|
ok: false,
|
|
@@ -10639,7 +10932,7 @@ async function responseBodyText(response) {
|
|
|
10639
10932
|
return { text: "", bytes: null };
|
|
10640
10933
|
}
|
|
10641
10934
|
function httpStatusRequestBody(check) {
|
|
10642
|
-
const headers =
|
|
10935
|
+
const headers = isRecord2(check.headers) ? Object.fromEntries(Object.entries(check.headers).map(([key, value]) => [key, String(value)]).filter(([key]) => key.trim())) : {};
|
|
10643
10936
|
if (check.body_json !== void 0) {
|
|
10644
10937
|
if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
|
|
10645
10938
|
headers["content-type"] = "application/json";
|
|
@@ -10716,11 +11009,11 @@ async function preflightHttpStatusCheck(check, index, targetUrl, fetchImpl) {
|
|
|
10716
11009
|
content_type: stringValue3(result.content_type) ?? null,
|
|
10717
11010
|
content_length: numberValue2(result.content_length) ?? null,
|
|
10718
11011
|
bytes: numberValue2(result.bytes) ?? null,
|
|
10719
|
-
body_contains:
|
|
11012
|
+
body_contains: isRecord2(result.body_contains) ? Object.fromEntries(Object.entries(result.body_contains).map(([key, value]) => [key, value === true])) : null,
|
|
10720
11013
|
body_contains_missing: bodyContainsMissing,
|
|
10721
|
-
body_not_contains:
|
|
11014
|
+
body_not_contains: isRecord2(result.body_not_contains) ? Object.fromEntries(Object.entries(result.body_not_contains).map(([key, value]) => [key, value === true])) : null,
|
|
10722
11015
|
body_not_contains_found: bodyNotContainsFound,
|
|
10723
|
-
body_not_patterns:
|
|
11016
|
+
body_not_patterns: isRecord2(result.body_not_patterns) ? Object.fromEntries(Object.entries(result.body_not_patterns).map(([key, value]) => [key, value === true])) : null,
|
|
10724
11017
|
body_not_patterns_found: bodyNotPatternsFound,
|
|
10725
11018
|
body_json_assertions: Array.isArray(result.body_json_assertions) ? result.body_json_assertions : null,
|
|
10726
11019
|
body_json_assertions_failed: bodyJsonAssertionsFailed
|
|
@@ -10748,7 +11041,7 @@ async function preflightRiddleProofProfileHttpStatusChecks(profile, options = {}
|
|
|
10748
11041
|
}
|
|
10749
11042
|
function httpStatusEvidenceForCheck(viewport, check) {
|
|
10750
11043
|
const evidence = viewport.http_statuses?.[httpStatusKey(check, viewport.url)];
|
|
10751
|
-
return
|
|
11044
|
+
return isRecord2(evidence) ? evidence : void 0;
|
|
10752
11045
|
}
|
|
10753
11046
|
function summarizeHttpStatusEvidence(viewport, check) {
|
|
10754
11047
|
const key = httpStatusKey(check, viewport.url);
|
|
@@ -10804,11 +11097,11 @@ function summarizeHttpStatusEvidence(viewport, check) {
|
|
|
10804
11097
|
content_type: stringValue3(statusEvidence.content_type) ?? null,
|
|
10805
11098
|
content_length: numberValue2(statusEvidence.content_length) ?? null,
|
|
10806
11099
|
bytes: linkStatusObservedBytes(statusEvidence) ?? null,
|
|
10807
|
-
body_contains:
|
|
11100
|
+
body_contains: isRecord2(statusEvidence.body_contains) ? toJsonValue(statusEvidence.body_contains) : null,
|
|
10808
11101
|
body_contains_missing: bodyContainsMissing,
|
|
10809
|
-
body_not_contains:
|
|
11102
|
+
body_not_contains: isRecord2(statusEvidence.body_not_contains) ? toJsonValue(statusEvidence.body_not_contains) : null,
|
|
10810
11103
|
body_not_contains_found: bodyNotContainsFound,
|
|
10811
|
-
body_not_patterns:
|
|
11104
|
+
body_not_patterns: isRecord2(statusEvidence.body_not_patterns) ? toJsonValue(statusEvidence.body_not_patterns) : null,
|
|
10812
11105
|
body_not_patterns_found: bodyNotPatternsFound,
|
|
10813
11106
|
body_json_assertions: Array.isArray(statusEvidence.body_json_assertions) ? toJsonValue(statusEvidence.body_json_assertions) : null,
|
|
10814
11107
|
body_json_assertions_failed: bodyJsonAssertionsFailed.map((assertion) => toJsonValue(assertion)),
|
|
@@ -10818,7 +11111,7 @@ function summarizeHttpStatusEvidence(viewport, check) {
|
|
|
10818
11111
|
}
|
|
10819
11112
|
function linkStatusEvidenceForCheck(viewport, check) {
|
|
10820
11113
|
const evidence = viewport.link_statuses?.[linkStatusSelector(check)];
|
|
10821
|
-
return
|
|
11114
|
+
return isRecord2(evidence) ? evidence : void 0;
|
|
10822
11115
|
}
|
|
10823
11116
|
function summarizeLinkStatusEvidence(viewport, check) {
|
|
10824
11117
|
const linkEvidence = linkStatusEvidenceForCheck(viewport, check);
|
|
@@ -10832,7 +11125,7 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
10832
11125
|
failures: [{ code: "link_status_evidence_missing" }]
|
|
10833
11126
|
};
|
|
10834
11127
|
}
|
|
10835
|
-
const results = Array.isArray(linkEvidence.results) ? linkEvidence.results.filter(
|
|
11128
|
+
const results = Array.isArray(linkEvidence.results) ? linkEvidence.results.filter(isRecord2) : [];
|
|
10836
11129
|
const totalCount = numberValue2(linkEvidence.total_count) ?? results.length;
|
|
10837
11130
|
const resultCount = numberValue2(linkEvidence.result_count) ?? totalCount;
|
|
10838
11131
|
const storedResultCount = numberValue2(linkEvidence.stored_result_count) ?? results.length;
|
|
@@ -10875,7 +11168,7 @@ function summarizeLinkStatusEvidence(viewport, check) {
|
|
|
10875
11168
|
if (check.min_count !== void 0 && totalCount < check.min_count) {
|
|
10876
11169
|
failures.push({ code: "link_status_count_below_minimum", min_count: check.min_count, actual: totalCount });
|
|
10877
11170
|
}
|
|
10878
|
-
const statusCounts =
|
|
11171
|
+
const statusCounts = isRecord2(linkEvidence.status_counts) ? linkEvidence.status_counts : {};
|
|
10879
11172
|
return {
|
|
10880
11173
|
viewport: viewport.name,
|
|
10881
11174
|
selector: linkStatusSelector(check),
|
|
@@ -10912,7 +11205,7 @@ function observeWithinKey(check) {
|
|
|
10912
11205
|
function textSequenceForCheck(viewport, check) {
|
|
10913
11206
|
const key = selectorKey(check);
|
|
10914
11207
|
const sequence = viewport.text_sequences?.[key];
|
|
10915
|
-
if (
|
|
11208
|
+
if (isRecord2(sequence)) {
|
|
10916
11209
|
const visibleMatchTexts = Array.isArray(sequence.visible_match_texts) ? sequence.visible_match_texts : [];
|
|
10917
11210
|
const matchTexts = Array.isArray(sequence.match_texts) ? sequence.match_texts : [];
|
|
10918
11211
|
const visibleTexts = Array.isArray(sequence.visible_texts) ? sequence.visible_texts : [];
|
|
@@ -10942,9 +11235,9 @@ function textOrderMatch(texts, expectedTexts) {
|
|
|
10942
11235
|
}
|
|
10943
11236
|
function frameEvidenceForSelector(viewport, selector) {
|
|
10944
11237
|
const container = viewport.frames?.[selector];
|
|
10945
|
-
if (!
|
|
11238
|
+
if (!isRecord2(container)) return [];
|
|
10946
11239
|
const frames = Array.isArray(container.frames) ? container.frames : [];
|
|
10947
|
-
return frames.filter((frame) =>
|
|
11240
|
+
return frames.filter((frame) => isRecord2(frame));
|
|
10948
11241
|
}
|
|
10949
11242
|
function frameTextSample(frame) {
|
|
10950
11243
|
const parts = [
|
|
@@ -10988,7 +11281,7 @@ function routeInventoryExpectedRouteSummaries(value, fallback) {
|
|
|
10988
11281
|
const path8 = route.trim();
|
|
10989
11282
|
return path8 ? { path: path8 } : void 0;
|
|
10990
11283
|
}
|
|
10991
|
-
if (!
|
|
11284
|
+
if (!isRecord2(route)) return void 0;
|
|
10992
11285
|
const path7 = stringValue3(route.path);
|
|
10993
11286
|
if (!path7) return void 0;
|
|
10994
11287
|
const name = stringValue3(route.name);
|
|
@@ -11070,11 +11363,11 @@ function textCheckFailureSamples(viewport, check) {
|
|
|
11070
11363
|
return fallback ? [fallback] : [];
|
|
11071
11364
|
}
|
|
11072
11365
|
function allowedMessageSample(input) {
|
|
11073
|
-
if (!
|
|
11366
|
+
if (!isRecord2(input)) return String(input || "");
|
|
11074
11367
|
const parts = [
|
|
11075
11368
|
input.text,
|
|
11076
11369
|
input.message,
|
|
11077
|
-
|
|
11370
|
+
isRecord2(input.location) ? input.location.url : void 0
|
|
11078
11371
|
];
|
|
11079
11372
|
return parts.map((part) => String(part || "")).filter(Boolean).join(" ");
|
|
11080
11373
|
}
|
|
@@ -11091,12 +11384,12 @@ function matchesAllowedMessage(input, texts, patterns) {
|
|
|
11091
11384
|
return false;
|
|
11092
11385
|
}
|
|
11093
11386
|
function consoleEventLocationUrl(input) {
|
|
11094
|
-
if (!
|
|
11387
|
+
if (!isRecord2(input) || !isRecord2(input.location)) return void 0;
|
|
11095
11388
|
return stringValue3(input.location.url);
|
|
11096
11389
|
}
|
|
11097
11390
|
function expectedFailedNetworkMockEvents(evidence) {
|
|
11098
11391
|
return (evidence.network_mocks || []).filter((event) => {
|
|
11099
|
-
if (!
|
|
11392
|
+
if (!isRecord2(event) || event.ok === false) return false;
|
|
11100
11393
|
const status = numberValue2(event.status);
|
|
11101
11394
|
const isHttpFailure = status !== void 0 && status >= 400;
|
|
11102
11395
|
const isAbortedMock = event.abort === true && Boolean(stringValue3(event.abort_error_code));
|
|
@@ -11126,7 +11419,7 @@ function expectedFailedNetworkMockConsoleEventSummary(event, evidence) {
|
|
|
11126
11419
|
abort_error_code: match ? stringValue3(match.abort_error_code) ?? null : null,
|
|
11127
11420
|
label: match ? stringValue3(match.label) ?? null : null,
|
|
11128
11421
|
response_label: match ? stringValue3(match.response_label) ?? null : null,
|
|
11129
|
-
text:
|
|
11422
|
+
text: isRecord2(event) && typeof event.text === "string" ? event.text.slice(0, 300) : sample.slice(0, 300)
|
|
11130
11423
|
};
|
|
11131
11424
|
}
|
|
11132
11425
|
function normalizeRoutePath2(path7) {
|
|
@@ -11647,7 +11940,7 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
11647
11940
|
};
|
|
11648
11941
|
}
|
|
11649
11942
|
if (check.type === "route_inventory") {
|
|
11650
|
-
const inventories = viewports.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory })).filter((item) =>
|
|
11943
|
+
const inventories = viewports.map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory })).filter((item) => isRecord2(item.inventory));
|
|
11651
11944
|
if (!inventories.length) {
|
|
11652
11945
|
return {
|
|
11653
11946
|
type: check.type,
|
|
@@ -12093,6 +12386,8 @@ function assessRiddleProofProfileEvidence(profile, evidence, options = {}) {
|
|
|
12093
12386
|
const status = profileStatusFromEvidence(profile, evidence, checks);
|
|
12094
12387
|
const firstViewport = evidence?.viewports?.[0];
|
|
12095
12388
|
const screenshots = profileScreenshotLabels(evidence?.viewports);
|
|
12389
|
+
const canonicalScreenshots = profileFinalScreenshotLabels(evidence?.viewports);
|
|
12390
|
+
const setupScreenshots = profileAllSetupScreenshotLabels(evidence?.viewports);
|
|
12096
12391
|
const result = {
|
|
12097
12392
|
version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
|
|
12098
12393
|
profile_name: profile.name,
|
|
@@ -12102,6 +12397,8 @@ function assessRiddleProofProfileEvidence(profile, evidence, options = {}) {
|
|
|
12102
12397
|
route: routeForViewport(firstViewport, evidence?.target_url),
|
|
12103
12398
|
artifacts: {
|
|
12104
12399
|
screenshots,
|
|
12400
|
+
canonical_screenshots: canonicalScreenshots,
|
|
12401
|
+
setup_screenshots: setupScreenshots,
|
|
12105
12402
|
console: "console.json",
|
|
12106
12403
|
proof_json: "proof.json",
|
|
12107
12404
|
dom_summary: "dom-summary.json",
|
|
@@ -13420,6 +13717,12 @@ function profileScreenshotLabels(viewports) {
|
|
|
13420
13717
|
}
|
|
13421
13718
|
return labels;
|
|
13422
13719
|
}
|
|
13720
|
+
function profileFinalScreenshotLabels(viewports) {
|
|
13721
|
+
return (viewports || []).map((viewport) => viewport && viewport.screenshot_label).filter(Boolean);
|
|
13722
|
+
}
|
|
13723
|
+
function profileAllSetupScreenshotLabels(viewports) {
|
|
13724
|
+
return (viewports || []).flatMap((viewport) => profileSetupScreenshotLabels(viewport && viewport.setup_action_results || []));
|
|
13725
|
+
}
|
|
13423
13726
|
function profileSetupSummary(viewports, actionCount, expectedActionCountsByViewport, finalScreenshotFullPage) {
|
|
13424
13727
|
const normalizedFinalScreenshotFullPage = finalScreenshotFullPage === undefined
|
|
13425
13728
|
? undefined
|
|
@@ -14288,6 +14591,8 @@ function assessProfile(profile, evidence) {
|
|
|
14288
14591
|
else if (checks.some((check) => check.status === "needs_human_review")) status = "needs_human_review";
|
|
14289
14592
|
else if (checks.some((check) => check.status === "failed")) status = "product_regression";
|
|
14290
14593
|
const screenshotLabels = profileScreenshotLabels(viewports);
|
|
14594
|
+
const canonicalScreenshotLabels = profileFinalScreenshotLabels(viewports);
|
|
14595
|
+
const setupScreenshotLabels = profileAllSetupScreenshotLabels(viewports);
|
|
14291
14596
|
const route = viewports[0] && viewports[0].route ? viewports[0].route : { requested: evidence.target_url, observed: "", matched: false, error: "missing viewport evidence" };
|
|
14292
14597
|
const passedChecks = checks.filter((check) => check.status === "passed").length;
|
|
14293
14598
|
const failedChecks = checks.filter((check) => check.status === "failed").length;
|
|
@@ -14304,7 +14609,14 @@ function assessProfile(profile, evidence) {
|
|
|
14304
14609
|
status,
|
|
14305
14610
|
baseline_policy: profile.baseline_policy || "invariant_only",
|
|
14306
14611
|
route,
|
|
14307
|
-
artifacts: {
|
|
14612
|
+
artifacts: {
|
|
14613
|
+
screenshots: screenshotLabels,
|
|
14614
|
+
canonical_screenshots: canonicalScreenshotLabels,
|
|
14615
|
+
setup_screenshots: setupScreenshotLabels,
|
|
14616
|
+
console: "console.json",
|
|
14617
|
+
proof_json: "proof.json",
|
|
14618
|
+
dom_summary: "dom-summary.json"
|
|
14619
|
+
},
|
|
14308
14620
|
checks,
|
|
14309
14621
|
summary,
|
|
14310
14622
|
captured_at: evidence.captured_at,
|
|
@@ -17975,7 +18287,7 @@ function collectRiddleProfileArtifactRefs(input) {
|
|
|
17975
18287
|
const indexes = /* @__PURE__ */ new Map();
|
|
17976
18288
|
const priorities = /* @__PURE__ */ new Map();
|
|
17977
18289
|
function add(item, source) {
|
|
17978
|
-
if (!
|
|
18290
|
+
if (!isRecord2(item)) return;
|
|
17979
18291
|
const url = stringValue3(item.url);
|
|
17980
18292
|
const path7 = stringValue3(item.path);
|
|
17981
18293
|
const rawName = stringValue3(item.name) || stringValue3(item.filename) || artifactNameFromPath(url || path7) || "";
|
|
@@ -18008,7 +18320,7 @@ function collectRiddleProfileArtifactRefs(input) {
|
|
|
18008
18320
|
for (const item of value) add(item, source);
|
|
18009
18321
|
return;
|
|
18010
18322
|
}
|
|
18011
|
-
if (!
|
|
18323
|
+
if (!isRecord2(value)) return;
|
|
18012
18324
|
for (const key of ["artifacts", "outputs", "screenshots", "files"]) {
|
|
18013
18325
|
if (Array.isArray(value[key])) visit(value[key], key);
|
|
18014
18326
|
}
|
|
@@ -18042,82 +18354,944 @@ var PROFILE_SINGLETON_ARTIFACT_NAMES = /* @__PURE__ */ new Set([
|
|
|
18042
18354
|
"dom-summary.json"
|
|
18043
18355
|
]);
|
|
18044
18356
|
function extractRiddleProofProfileResult(input) {
|
|
18045
|
-
if (!
|
|
18357
|
+
if (!isRecord2(input)) return void 0;
|
|
18046
18358
|
if (input.version === RIDDLE_PROOF_PROFILE_RESULT_VERSION) return input;
|
|
18047
18359
|
const candidates = [
|
|
18048
18360
|
input.result,
|
|
18049
18361
|
input.return_value,
|
|
18050
18362
|
input.value,
|
|
18051
18363
|
input.profile_result,
|
|
18052
|
-
|
|
18053
|
-
|
|
18364
|
+
isRecord2(input["proof.json"]) ? input["proof.json"] : void 0,
|
|
18365
|
+
isRecord2(input._proof_json) ? input._proof_json : void 0
|
|
18054
18366
|
];
|
|
18055
18367
|
for (const candidate of candidates) {
|
|
18056
18368
|
const result = extractRiddleProofProfileResult(candidate);
|
|
18057
18369
|
if (result) return result;
|
|
18058
18370
|
}
|
|
18059
|
-
if (
|
|
18371
|
+
if (isRecord2(input._artifact_json)) {
|
|
18060
18372
|
return extractRiddleProofProfileResult(input._artifact_json["proof.json"]);
|
|
18061
18373
|
}
|
|
18062
18374
|
return void 0;
|
|
18063
18375
|
}
|
|
18064
18376
|
|
|
18065
|
-
// src/
|
|
18066
|
-
var
|
|
18067
|
-
|
|
18068
|
-
|
|
18377
|
+
// src/change-proof.ts
|
|
18378
|
+
var RIDDLE_PROOF_CHANGE_RESULT_VERSION = "riddle-proof.change-result.v1";
|
|
18379
|
+
var RIDDLE_PROOF_CHANGE_RECEIPT_V1_VERSION = "riddle-proof.change-receipt.v1";
|
|
18380
|
+
var RIDDLE_PROOF_CHANGE_RECEIPT_VERSION = "riddle-proof.change-receipt.v2";
|
|
18381
|
+
var RIDDLE_PROOF_HANDOFF_RECEIPT_VERSION = "riddle-proof.handoff-receipt.v1";
|
|
18382
|
+
var DEFAULT_BEFORE_STATUSES = ["passed", "product_regression"];
|
|
18383
|
+
var DEFAULT_AFTER_STATUSES = ["passed"];
|
|
18384
|
+
var DEFAULT_PROFILE_STATUS_TRANSITION_BEFORE = ["product_regression"];
|
|
18385
|
+
var DEFAULT_PROFILE_STATUS_TRANSITION_AFTER = ["passed"];
|
|
18386
|
+
var DEFAULT_CHECK_STATUS_TRANSITION_BEFORE = ["failed"];
|
|
18387
|
+
var DEFAULT_CHECK_STATUS_TRANSITION_AFTER = ["passed"];
|
|
18388
|
+
function listValue(value, fallback) {
|
|
18389
|
+
if (Array.isArray(value)) return value;
|
|
18390
|
+
if (value === void 0) return fallback;
|
|
18391
|
+
return [value];
|
|
18069
18392
|
}
|
|
18070
|
-
function
|
|
18071
|
-
return
|
|
18393
|
+
function includesValue(allowed, observed) {
|
|
18394
|
+
return observed !== void 0 && allowed.includes(observed);
|
|
18072
18395
|
}
|
|
18073
|
-
function
|
|
18074
|
-
|
|
18396
|
+
function groupResult(side, contract, result) {
|
|
18397
|
+
const fallback = side === "before" ? DEFAULT_BEFORE_STATUSES : DEFAULT_AFTER_STATUSES;
|
|
18398
|
+
const requiredStatus = listValue(contract?.required_status, fallback);
|
|
18399
|
+
const label = contract?.label || side;
|
|
18400
|
+
if (!result) {
|
|
18401
|
+
return {
|
|
18402
|
+
side,
|
|
18403
|
+
label,
|
|
18404
|
+
ok: false,
|
|
18405
|
+
status: "missing",
|
|
18406
|
+
required_status: requiredStatus,
|
|
18407
|
+
message: `${label} profile result is missing.`
|
|
18408
|
+
};
|
|
18409
|
+
}
|
|
18410
|
+
const ok = includesValue(requiredStatus, result.status);
|
|
18411
|
+
return {
|
|
18412
|
+
side,
|
|
18413
|
+
label,
|
|
18414
|
+
ok,
|
|
18415
|
+
profile_name: result.profile_name,
|
|
18416
|
+
status: result.status,
|
|
18417
|
+
required_status: requiredStatus,
|
|
18418
|
+
summary: result.summary,
|
|
18419
|
+
message: ok ? void 0 : `${label} profile status ${result.status} did not match required status ${requiredStatus.join(", ")}.`
|
|
18420
|
+
};
|
|
18075
18421
|
}
|
|
18076
|
-
function
|
|
18077
|
-
return
|
|
18422
|
+
function findCheck(result, delta) {
|
|
18423
|
+
return result.checks.find((check) => {
|
|
18424
|
+
if (delta.check_label && check.label !== delta.check_label) return false;
|
|
18425
|
+
if (delta.check_type && check.type !== delta.check_type) return false;
|
|
18426
|
+
return Boolean(delta.check_label || delta.check_type);
|
|
18427
|
+
});
|
|
18078
18428
|
}
|
|
18079
|
-
function
|
|
18080
|
-
|
|
18429
|
+
function profileStatusTransitionResult(delta, before, after) {
|
|
18430
|
+
const label = delta.label || "profile-status-transition";
|
|
18431
|
+
if (!before || !after) {
|
|
18432
|
+
return {
|
|
18433
|
+
type: delta.type,
|
|
18434
|
+
label,
|
|
18435
|
+
status: "proof_insufficient",
|
|
18436
|
+
before_observed: before?.status,
|
|
18437
|
+
after_observed: after?.status,
|
|
18438
|
+
message: `${label} needs both before and after profile results.`
|
|
18439
|
+
};
|
|
18440
|
+
}
|
|
18441
|
+
const beforeAllowed = listValue(delta.before_status, DEFAULT_PROFILE_STATUS_TRANSITION_BEFORE);
|
|
18442
|
+
const afterAllowed = listValue(delta.after_status, DEFAULT_PROFILE_STATUS_TRANSITION_AFTER);
|
|
18443
|
+
const passed = includesValue(beforeAllowed, before.status) && includesValue(afterAllowed, after.status);
|
|
18444
|
+
return {
|
|
18445
|
+
type: delta.type,
|
|
18446
|
+
label,
|
|
18447
|
+
status: passed ? "passed" : "failed",
|
|
18448
|
+
before_observed: before.status,
|
|
18449
|
+
after_observed: after.status,
|
|
18450
|
+
message: passed ? void 0 : `${label} expected before ${beforeAllowed.join(", ")} and after ${afterAllowed.join(", ")}, got ${before.status} -> ${after.status}.`
|
|
18451
|
+
};
|
|
18081
18452
|
}
|
|
18082
|
-
function
|
|
18083
|
-
|
|
18084
|
-
|
|
18085
|
-
|
|
18453
|
+
function checkStatusTransitionResult(delta, before, after) {
|
|
18454
|
+
const label = delta.label || delta.check_label || delta.check_type || "check-status-transition";
|
|
18455
|
+
if (!delta.check_label && !delta.check_type) {
|
|
18456
|
+
return {
|
|
18457
|
+
type: delta.type,
|
|
18458
|
+
label,
|
|
18459
|
+
status: "configuration_error",
|
|
18460
|
+
message: `${label} needs check_label or check_type.`
|
|
18461
|
+
};
|
|
18086
18462
|
}
|
|
18087
|
-
|
|
18463
|
+
if (!before || !after) {
|
|
18464
|
+
return {
|
|
18465
|
+
type: delta.type,
|
|
18466
|
+
label,
|
|
18467
|
+
status: "proof_insufficient",
|
|
18468
|
+
before_observed: before?.status,
|
|
18469
|
+
after_observed: after?.status,
|
|
18470
|
+
message: `${label} needs both before and after profile results.`
|
|
18471
|
+
};
|
|
18472
|
+
}
|
|
18473
|
+
const beforeCheck = findCheck(before, delta);
|
|
18474
|
+
const afterCheck = findCheck(after, delta);
|
|
18475
|
+
if (!beforeCheck || !afterCheck) {
|
|
18476
|
+
return {
|
|
18477
|
+
type: delta.type,
|
|
18478
|
+
label,
|
|
18479
|
+
status: "proof_insufficient",
|
|
18480
|
+
before_observed: beforeCheck?.status,
|
|
18481
|
+
after_observed: afterCheck?.status,
|
|
18482
|
+
message: `${label} was not present in ${!beforeCheck && !afterCheck ? "before or after" : !beforeCheck ? "before" : "after"} profile checks.`
|
|
18483
|
+
};
|
|
18484
|
+
}
|
|
18485
|
+
const beforeAllowed = listValue(delta.before_status, DEFAULT_CHECK_STATUS_TRANSITION_BEFORE);
|
|
18486
|
+
const afterAllowed = listValue(delta.after_status, DEFAULT_CHECK_STATUS_TRANSITION_AFTER);
|
|
18487
|
+
const passed = includesValue(beforeAllowed, beforeCheck.status) && includesValue(afterAllowed, afterCheck.status);
|
|
18488
|
+
return {
|
|
18489
|
+
type: delta.type,
|
|
18490
|
+
label,
|
|
18491
|
+
status: passed ? "passed" : "failed",
|
|
18492
|
+
before_observed: beforeCheck.status,
|
|
18493
|
+
after_observed: afterCheck.status,
|
|
18494
|
+
message: passed ? void 0 : `${label} expected before ${beforeAllowed.join(", ")} and after ${afterAllowed.join(", ")}, got ${beforeCheck.status} -> ${afterCheck.status}.`
|
|
18495
|
+
};
|
|
18088
18496
|
}
|
|
18089
|
-
function
|
|
18090
|
-
|
|
18091
|
-
|
|
18092
|
-
|
|
18093
|
-
return
|
|
18497
|
+
function assessDelta(delta, before, after) {
|
|
18498
|
+
if (delta.type === "profile_status_transition") {
|
|
18499
|
+
return profileStatusTransitionResult(delta, before, after);
|
|
18500
|
+
}
|
|
18501
|
+
return checkStatusTransitionResult(delta, before, after);
|
|
18094
18502
|
}
|
|
18095
|
-
function
|
|
18096
|
-
|
|
18097
|
-
if (raw) return raw;
|
|
18098
|
-
return fallback;
|
|
18503
|
+
function profileResultFromObservation(observation) {
|
|
18504
|
+
return observation?.proof?.result;
|
|
18099
18505
|
}
|
|
18100
|
-
function
|
|
18101
|
-
|
|
18102
|
-
|
|
18103
|
-
|
|
18104
|
-
|
|
18105
|
-
|
|
18106
|
-
|
|
18107
|
-
|
|
18108
|
-
if (!url || seen.has(url)) continue;
|
|
18109
|
-
seen.add(url);
|
|
18110
|
-
const name = artifactDisplayName(artifact.name, `artifact-${index + 1}`);
|
|
18111
|
-
artifacts.push({
|
|
18112
|
-
name,
|
|
18113
|
-
url,
|
|
18114
|
-
kind: artifactKind(name, url),
|
|
18115
|
-
size_bytes: numberValue3(artifact.size)
|
|
18116
|
-
});
|
|
18506
|
+
function previewReceiptCoversTarget(previewUrl, targetUrl) {
|
|
18507
|
+
try {
|
|
18508
|
+
const preview = new URL(previewUrl);
|
|
18509
|
+
const target = new URL(targetUrl);
|
|
18510
|
+
const previewPath = preview.pathname.replace(/\/+$/u, "");
|
|
18511
|
+
return preview.origin === target.origin && (target.pathname === previewPath || target.pathname.startsWith(`${previewPath}/`));
|
|
18512
|
+
} catch {
|
|
18513
|
+
return false;
|
|
18117
18514
|
}
|
|
18118
|
-
return artifacts;
|
|
18119
18515
|
}
|
|
18120
|
-
function
|
|
18516
|
+
function sourceBindingResult(side, requirement, observation, expectedRevision, evaluatedAt) {
|
|
18517
|
+
const expected = expectedRevision || requirement?.expected_git_revision;
|
|
18518
|
+
const required = Boolean(
|
|
18519
|
+
requirement?.preview_receipt_required || requirement?.require_clean_source || requirement?.require_content_digest || requirement?.expected_git_revision || expected
|
|
18520
|
+
);
|
|
18521
|
+
if (!required) return { side, required: false, ok: true, status: "not_required" };
|
|
18522
|
+
if (!observation) {
|
|
18523
|
+
return {
|
|
18524
|
+
side,
|
|
18525
|
+
required: true,
|
|
18526
|
+
ok: false,
|
|
18527
|
+
status: "missing",
|
|
18528
|
+
expected_git_revision: expected,
|
|
18529
|
+
message: `${side} source binding requires an Observation receipt.`
|
|
18530
|
+
};
|
|
18531
|
+
}
|
|
18532
|
+
const preview = observation.target.preview;
|
|
18533
|
+
if (requirement?.preview_receipt_required && !preview) {
|
|
18534
|
+
return {
|
|
18535
|
+
side,
|
|
18536
|
+
required: true,
|
|
18537
|
+
ok: false,
|
|
18538
|
+
status: "missing",
|
|
18539
|
+
expected_git_revision: expected,
|
|
18540
|
+
message: `${side} source binding requires a Preview receipt.`
|
|
18541
|
+
};
|
|
18542
|
+
}
|
|
18543
|
+
if (preview && !previewReceiptCoversTarget(preview.url, observation.target.url)) {
|
|
18544
|
+
return {
|
|
18545
|
+
side,
|
|
18546
|
+
required: true,
|
|
18547
|
+
ok: false,
|
|
18548
|
+
status: "mismatched",
|
|
18549
|
+
expected_git_revision: expected,
|
|
18550
|
+
observed_git_revision: preview.source.git_revision || observation.source.git_revision,
|
|
18551
|
+
content_digest: preview.content_digest,
|
|
18552
|
+
preview_id: preview.preview_id,
|
|
18553
|
+
preview_url: preview.url,
|
|
18554
|
+
target_url: observation.target.url,
|
|
18555
|
+
message: `${side} observation target is not contained by Preview ${preview.preview_id}.`
|
|
18556
|
+
};
|
|
18557
|
+
}
|
|
18558
|
+
const observed = preview?.source.git_revision || observation.source.git_revision;
|
|
18559
|
+
const digestRequired = requirement?.require_content_digest === true || requirement?.preview_receipt_required === true;
|
|
18560
|
+
if (digestRequired && !preview?.content_digest) {
|
|
18561
|
+
return {
|
|
18562
|
+
side,
|
|
18563
|
+
required: true,
|
|
18564
|
+
ok: false,
|
|
18565
|
+
status: "missing",
|
|
18566
|
+
expected_git_revision: expected,
|
|
18567
|
+
observed_git_revision: observed,
|
|
18568
|
+
preview_id: preview?.preview_id,
|
|
18569
|
+
preview_url: preview?.url,
|
|
18570
|
+
target_url: observation.target.url,
|
|
18571
|
+
message: `${side} Preview receipt is missing a content digest.`
|
|
18572
|
+
};
|
|
18573
|
+
}
|
|
18574
|
+
if (expected && !observed) {
|
|
18575
|
+
return {
|
|
18576
|
+
side,
|
|
18577
|
+
required: true,
|
|
18578
|
+
ok: false,
|
|
18579
|
+
status: "missing",
|
|
18580
|
+
expected_git_revision: expected,
|
|
18581
|
+
content_digest: preview?.content_digest,
|
|
18582
|
+
preview_id: preview?.preview_id,
|
|
18583
|
+
preview_url: preview?.url,
|
|
18584
|
+
target_url: observation.target.url,
|
|
18585
|
+
message: `${side} evidence is missing its source Git revision.`
|
|
18586
|
+
};
|
|
18587
|
+
}
|
|
18588
|
+
if (expected && observed !== expected) {
|
|
18589
|
+
return {
|
|
18590
|
+
side,
|
|
18591
|
+
required: true,
|
|
18592
|
+
ok: false,
|
|
18593
|
+
status: "mismatched",
|
|
18594
|
+
expected_git_revision: expected,
|
|
18595
|
+
observed_git_revision: observed,
|
|
18596
|
+
content_digest: preview?.content_digest,
|
|
18597
|
+
preview_id: preview?.preview_id,
|
|
18598
|
+
preview_url: preview?.url,
|
|
18599
|
+
target_url: observation.target.url,
|
|
18600
|
+
message: `${side} evidence came from Git revision ${observed}, not expected revision ${expected}.`
|
|
18601
|
+
};
|
|
18602
|
+
}
|
|
18603
|
+
const cleanRequired = requirement?.require_clean_source === true || requirement?.preview_receipt_required === true;
|
|
18604
|
+
const dirty = preview?.source.dirty ?? observation.source.dirty;
|
|
18605
|
+
if (cleanRequired && dirty !== false) {
|
|
18606
|
+
return {
|
|
18607
|
+
side,
|
|
18608
|
+
required: true,
|
|
18609
|
+
ok: false,
|
|
18610
|
+
status: dirty === true ? "mismatched" : "missing",
|
|
18611
|
+
expected_git_revision: expected,
|
|
18612
|
+
observed_git_revision: observed,
|
|
18613
|
+
content_digest: preview?.content_digest,
|
|
18614
|
+
preview_id: preview?.preview_id,
|
|
18615
|
+
preview_url: preview?.url,
|
|
18616
|
+
target_url: observation.target.url,
|
|
18617
|
+
message: dirty === true ? `${side} evidence was built from a dirty worktree and is not bound only to its Git revision.` : `${side} evidence does not record whether its source worktree was clean.`
|
|
18618
|
+
};
|
|
18619
|
+
}
|
|
18620
|
+
const evaluatedMs = Date.parse(evaluatedAt);
|
|
18621
|
+
const expiresMs = preview?.expires_at ? Date.parse(preview.expires_at) : Number.NaN;
|
|
18622
|
+
if (preview && Number.isFinite(evaluatedMs) && Number.isFinite(expiresMs) && expiresMs <= evaluatedMs) {
|
|
18623
|
+
return {
|
|
18624
|
+
side,
|
|
18625
|
+
required: true,
|
|
18626
|
+
ok: false,
|
|
18627
|
+
status: "stale",
|
|
18628
|
+
expected_git_revision: expected,
|
|
18629
|
+
observed_git_revision: observed,
|
|
18630
|
+
content_digest: preview.content_digest,
|
|
18631
|
+
preview_id: preview.preview_id,
|
|
18632
|
+
preview_url: preview.url,
|
|
18633
|
+
target_url: observation.target.url,
|
|
18634
|
+
expires_at: preview.expires_at,
|
|
18635
|
+
message: `${side} Preview receipt expired at ${preview.expires_at}.`
|
|
18636
|
+
};
|
|
18637
|
+
}
|
|
18638
|
+
return {
|
|
18639
|
+
side,
|
|
18640
|
+
required: true,
|
|
18641
|
+
ok: true,
|
|
18642
|
+
status: "matched",
|
|
18643
|
+
expected_git_revision: expected,
|
|
18644
|
+
observed_git_revision: observed,
|
|
18645
|
+
content_digest: preview?.content_digest,
|
|
18646
|
+
preview_id: preview?.preview_id,
|
|
18647
|
+
preview_url: preview?.url,
|
|
18648
|
+
target_url: observation.target.url,
|
|
18649
|
+
expires_at: preview?.expires_at
|
|
18650
|
+
};
|
|
18651
|
+
}
|
|
18652
|
+
function collapsedChangeStatus(groups, deltas, sourceBindings) {
|
|
18653
|
+
const groupResults = [groups.before, groups.after];
|
|
18654
|
+
if (groupResults.some((group) => group.status === "environment_blocked")) return "environment_blocked";
|
|
18655
|
+
if (groupResults.some((group) => group.status === "configuration_error")) return "configuration_error";
|
|
18656
|
+
if (deltas.some((delta) => delta.status === "configuration_error")) return "configuration_error";
|
|
18657
|
+
if (groupResults.some((group) => group.status === "needs_human_review")) return "needs_human_review";
|
|
18658
|
+
if (groupResults.some((group) => group.status === "missing" || group.status === "proof_insufficient")) return "proof_insufficient";
|
|
18659
|
+
if ([sourceBindings.before, sourceBindings.after].some((binding) => binding.required && !binding.ok)) return "proof_insufficient";
|
|
18660
|
+
if (!deltas.length || deltas.some((delta) => delta.status === "proof_insufficient")) return "proof_insufficient";
|
|
18661
|
+
if (groupResults.some((group) => !group.ok)) return "product_regression";
|
|
18662
|
+
if (deltas.some((delta) => delta.status === "failed")) return "product_regression";
|
|
18663
|
+
return "passed";
|
|
18664
|
+
}
|
|
18665
|
+
function changeSummary(name, status, deltas) {
|
|
18666
|
+
if (status === "passed") return `${name} passed ${deltas.length} change delta(s).`;
|
|
18667
|
+
if (status === "environment_blocked") return `${name} could not compare reliable evidence because an environment was blocked.`;
|
|
18668
|
+
if (status === "configuration_error") return `${name} has an invalid change proof contract.`;
|
|
18669
|
+
if (status === "needs_human_review") return `${name} needs human review before the change proof can pass.`;
|
|
18670
|
+
if (status === "proof_insufficient") return `${name} did not produce enough before/after evidence for a change proof.`;
|
|
18671
|
+
return `${name} failed ${deltas.filter((delta) => delta.status === "failed").length} change delta(s).`;
|
|
18672
|
+
}
|
|
18673
|
+
function assessRiddleProofChange(contract, input) {
|
|
18674
|
+
const beforeResult = input.before_result || profileResultFromObservation(input.before_observation);
|
|
18675
|
+
const afterResult = input.after_result || profileResultFromObservation(input.after_observation);
|
|
18676
|
+
const groups = {
|
|
18677
|
+
before: groupResult("before", contract.before, beforeResult),
|
|
18678
|
+
after: groupResult("after", contract.after, afterResult)
|
|
18679
|
+
};
|
|
18680
|
+
const deltas = (contract.deltas || []).map((delta) => assessDelta(delta, beforeResult, afterResult));
|
|
18681
|
+
const evaluatedAt = input.evaluated_at || (/* @__PURE__ */ new Date()).toISOString();
|
|
18682
|
+
const sourceBindings = {
|
|
18683
|
+
before: sourceBindingResult(
|
|
18684
|
+
"before",
|
|
18685
|
+
contract.source_binding?.before,
|
|
18686
|
+
input.before_observation,
|
|
18687
|
+
input.expected_source_revisions?.before,
|
|
18688
|
+
evaluatedAt
|
|
18689
|
+
),
|
|
18690
|
+
after: sourceBindingResult(
|
|
18691
|
+
"after",
|
|
18692
|
+
contract.source_binding?.after,
|
|
18693
|
+
input.after_observation,
|
|
18694
|
+
input.expected_source_revisions?.after,
|
|
18695
|
+
evaluatedAt
|
|
18696
|
+
)
|
|
18697
|
+
};
|
|
18698
|
+
const status = collapsedChangeStatus(groups, deltas, sourceBindings);
|
|
18699
|
+
return {
|
|
18700
|
+
version: RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
18701
|
+
contract_name: contract.name,
|
|
18702
|
+
status,
|
|
18703
|
+
groups,
|
|
18704
|
+
deltas,
|
|
18705
|
+
source_bindings: sourceBindings,
|
|
18706
|
+
summary: changeSummary(contract.name, status, deltas),
|
|
18707
|
+
metadata: contract.metadata
|
|
18708
|
+
};
|
|
18709
|
+
}
|
|
18710
|
+
function changeReceiptVerdict(status) {
|
|
18711
|
+
if (status === "passed") return "mergeable";
|
|
18712
|
+
if (status === "environment_blocked") return "environment_blocked";
|
|
18713
|
+
if (status === "configuration_error") return "configuration_error";
|
|
18714
|
+
if (status === "needs_human_review") return "needs_human_review";
|
|
18715
|
+
if (status === "proof_insufficient") return "proof_insufficient";
|
|
18716
|
+
return "not_mergeable";
|
|
18717
|
+
}
|
|
18718
|
+
function profileCheckCounts2(result) {
|
|
18719
|
+
const counts = {
|
|
18720
|
+
total: result.checks.length,
|
|
18721
|
+
passed: 0,
|
|
18722
|
+
failed: 0,
|
|
18723
|
+
skipped: 0,
|
|
18724
|
+
needs_human_review: 0
|
|
18725
|
+
};
|
|
18726
|
+
for (const check of result.checks) {
|
|
18727
|
+
if (check.status === "passed") counts.passed += 1;
|
|
18728
|
+
if (check.status === "failed") counts.failed += 1;
|
|
18729
|
+
if (check.status === "skipped") counts.skipped += 1;
|
|
18730
|
+
if (check.status === "needs_human_review") counts.needs_human_review += 1;
|
|
18731
|
+
}
|
|
18732
|
+
return counts;
|
|
18733
|
+
}
|
|
18734
|
+
function artifactIsScreenshot(artifact) {
|
|
18735
|
+
if (artifact.kind !== "image") return false;
|
|
18736
|
+
return /screenshot|\.png|\.jpe?g|\.webp|\.gif|\.avif|\.svg/i.test([artifact.name, artifact.url, artifact.path].filter(Boolean).join(" "));
|
|
18737
|
+
}
|
|
18738
|
+
function metadataStringList(metadata, key) {
|
|
18739
|
+
const value = metadata?.[key];
|
|
18740
|
+
if (!Array.isArray(value)) return [];
|
|
18741
|
+
return value.filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => item.trim());
|
|
18742
|
+
}
|
|
18743
|
+
function changeRecommendation(verdict) {
|
|
18744
|
+
if (verdict === "mergeable") {
|
|
18745
|
+
return {
|
|
18746
|
+
merge_recommended: true,
|
|
18747
|
+
verdict,
|
|
18748
|
+
label: "Merge recommended",
|
|
18749
|
+
reason: "The declared before/after delta contract passed with sufficient bound evidence."
|
|
18750
|
+
};
|
|
18751
|
+
}
|
|
18752
|
+
const reason = verdict === "not_mergeable" ? "The declared change delta did not pass." : verdict === "environment_blocked" ? "The environment blocked reliable evidence collection." : verdict === "needs_human_review" ? "The evidence requires human review." : verdict === "configuration_error" ? "The change contract is invalid." : "The change proof did not produce sufficient bound evidence.";
|
|
18753
|
+
return { merge_recommended: false, verdict, label: "Merge not recommended", reason };
|
|
18754
|
+
}
|
|
18755
|
+
function noShippingAuthorization() {
|
|
18756
|
+
return { status: "not_granted", authorized: false, source: "none" };
|
|
18757
|
+
}
|
|
18758
|
+
function recommendationMatches(actual, expected) {
|
|
18759
|
+
return actual?.merge_recommended === expected.merge_recommended && actual.verdict === expected.verdict && actual.label === expected.label && actual.reason === expected.reason;
|
|
18760
|
+
}
|
|
18761
|
+
function assertShippingAuthorizationConsistent(authorization, receiptName) {
|
|
18762
|
+
if (!authorization) throw new Error(`${receiptName} shipping authorization is required.`);
|
|
18763
|
+
if (authorization.status !== "not_granted" && authorization.status !== "granted") {
|
|
18764
|
+
throw new Error(`${receiptName} shipping authorization status is invalid.`);
|
|
18765
|
+
}
|
|
18766
|
+
if (authorization.source !== "none" && authorization.source !== "human" && authorization.source !== "automation") {
|
|
18767
|
+
throw new Error(`${receiptName} shipping authorization source is invalid.`);
|
|
18768
|
+
}
|
|
18769
|
+
const granted = authorization.status === "granted";
|
|
18770
|
+
if (authorization.authorized !== granted) {
|
|
18771
|
+
throw new Error(`${receiptName} shipping authorization status and authorized flag must agree.`);
|
|
18772
|
+
}
|
|
18773
|
+
if (authorization.source === "none" === granted) {
|
|
18774
|
+
throw new Error(`${receiptName} shipping authorization source must identify an authorizer only when granted.`);
|
|
18775
|
+
}
|
|
18776
|
+
}
|
|
18777
|
+
function observationArtifactMatches(actual, expected) {
|
|
18778
|
+
if (!actual || !expected) return actual === expected;
|
|
18779
|
+
return actual.name === expected.name && actual.role === expected.role && actual.url === expected.url && actual.path === expected.path && actual.kind === expected.kind && actual.content_type === expected.content_type && actual.source === expected.source;
|
|
18780
|
+
}
|
|
18781
|
+
function observationExecutor(result) {
|
|
18782
|
+
return result.runner === "local-playwright" ? { kind: "local_playwright", runner: result.runner } : {
|
|
18783
|
+
kind: "riddle_hosted",
|
|
18784
|
+
runner: result.runner,
|
|
18785
|
+
job_id: result.riddle?.job_id
|
|
18786
|
+
};
|
|
18787
|
+
}
|
|
18788
|
+
function observationForReceiptSide(side, result, source, sourceIdentity) {
|
|
18789
|
+
if (!result) throw new Error(`${side}_result or ${side}_observation is required.`);
|
|
18790
|
+
if (!source) throw new Error(`${side}_source or ${side}_observation is required.`);
|
|
18791
|
+
return createRiddleProofObservationReceipt({
|
|
18792
|
+
comparison_role: side,
|
|
18793
|
+
executor: observationExecutor(result),
|
|
18794
|
+
target: { kind: "url", url: source },
|
|
18795
|
+
source: sourceIdentity,
|
|
18796
|
+
profile_result: result,
|
|
18797
|
+
execution: result.riddle?.execution,
|
|
18798
|
+
publication: source.startsWith("http") ? { kind: result.runner === "local-playwright" ? "other" : "riddle_cdn", url: source } : { kind: "local", path: source }
|
|
18799
|
+
});
|
|
18800
|
+
}
|
|
18801
|
+
function createRiddleProofChangeReceipt(input) {
|
|
18802
|
+
const before = input.before_observation ? parseRiddleProofObservationReceipt(input.before_observation) : observationForReceiptSide("before", input.before_result, input.before_source, input.before_source_identity);
|
|
18803
|
+
const after = input.after_observation ? parseRiddleProofObservationReceipt(input.after_observation) : observationForReceiptSide("after", input.after_result, input.after_source, input.after_source_identity);
|
|
18804
|
+
const verdict = changeReceiptVerdict(input.result.status);
|
|
18805
|
+
const shippingAuthorization = input.shipping_authorization || noShippingAuthorization();
|
|
18806
|
+
assertShippingAuthorizationConsistent(shippingAuthorization, "Change receipt");
|
|
18807
|
+
return {
|
|
18808
|
+
version: RIDDLE_PROOF_CHANGE_RECEIPT_VERSION,
|
|
18809
|
+
contract_name: input.result.contract_name,
|
|
18810
|
+
profile_name: input.profile_name,
|
|
18811
|
+
status: input.result.status,
|
|
18812
|
+
verdict,
|
|
18813
|
+
summary: input.result.summary,
|
|
18814
|
+
before,
|
|
18815
|
+
after,
|
|
18816
|
+
groups: input.result.groups,
|
|
18817
|
+
source_bindings: input.result.source_bindings,
|
|
18818
|
+
deltas: input.result.deltas.map((delta) => ({
|
|
18819
|
+
type: delta.type,
|
|
18820
|
+
label: delta.label,
|
|
18821
|
+
status: delta.status,
|
|
18822
|
+
before_observed: delta.before_observed,
|
|
18823
|
+
after_observed: delta.after_observed,
|
|
18824
|
+
message: delta.message
|
|
18825
|
+
})),
|
|
18826
|
+
recommendation: changeRecommendation(verdict),
|
|
18827
|
+
shipping_authorization: shippingAuthorization,
|
|
18828
|
+
proves: metadataStringList(input.contract.metadata, "required_receipts"),
|
|
18829
|
+
does_not_prove: metadataStringList(input.contract.metadata, "does_not_prove"),
|
|
18830
|
+
metadata: input.result.metadata
|
|
18831
|
+
};
|
|
18832
|
+
}
|
|
18833
|
+
function isRecord3(value) {
|
|
18834
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
18835
|
+
}
|
|
18836
|
+
function migratedObservationFromLegacySide(side, legacy) {
|
|
18837
|
+
const legacyArtifacts = legacy.artifacts.map((artifact) => ({
|
|
18838
|
+
name: artifact.name,
|
|
18839
|
+
url: artifact.url,
|
|
18840
|
+
path: artifact.path,
|
|
18841
|
+
source: artifact.source,
|
|
18842
|
+
kind: artifact.kind,
|
|
18843
|
+
role: legacy.screenshots[0] && artifactTarget(artifact) === artifactTarget(legacy.screenshots[0]) ? "canonical_screenshot" : artifact.kind === "image" ? "diagnostic" : artifact.kind === "data" ? "data" : "artifact"
|
|
18844
|
+
}));
|
|
18845
|
+
const canonicalScreenshot = legacyArtifacts.find((artifact) => artifact.role === "canonical_screenshot");
|
|
18846
|
+
return {
|
|
18847
|
+
version: "riddle-proof.observation-receipt.v1",
|
|
18848
|
+
observation_id: `obs_${side}_migrated`,
|
|
18849
|
+
comparison_role: side,
|
|
18850
|
+
executor: { kind: "other", runner: "legacy-change-receipt" },
|
|
18851
|
+
target: { kind: "url", url: legacy.source },
|
|
18852
|
+
source: {},
|
|
18853
|
+
captured_at: legacy.captured_at || (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
18854
|
+
artifacts: legacyArtifacts,
|
|
18855
|
+
canonical_screenshot: canonicalScreenshot,
|
|
18856
|
+
profile_summary: {
|
|
18857
|
+
profile_name: legacy.profile_name,
|
|
18858
|
+
status: legacy.status,
|
|
18859
|
+
summary: legacy.summary,
|
|
18860
|
+
route: legacy.route,
|
|
18861
|
+
checks: legacy.checks
|
|
18862
|
+
},
|
|
18863
|
+
metadata: { migrated_from: RIDDLE_PROOF_CHANGE_RECEIPT_V1_VERSION }
|
|
18864
|
+
};
|
|
18865
|
+
}
|
|
18866
|
+
function migrateRiddleProofChangeReceipt(legacy) {
|
|
18867
|
+
const recommendation = changeRecommendation(legacy.verdict);
|
|
18868
|
+
return {
|
|
18869
|
+
version: RIDDLE_PROOF_CHANGE_RECEIPT_VERSION,
|
|
18870
|
+
contract_name: legacy.contract_name,
|
|
18871
|
+
profile_name: legacy.profile_name,
|
|
18872
|
+
status: legacy.status,
|
|
18873
|
+
verdict: legacy.verdict,
|
|
18874
|
+
summary: legacy.summary,
|
|
18875
|
+
before: migratedObservationFromLegacySide("before", legacy.before),
|
|
18876
|
+
after: migratedObservationFromLegacySide("after", legacy.after),
|
|
18877
|
+
groups: {
|
|
18878
|
+
before: {
|
|
18879
|
+
side: "before",
|
|
18880
|
+
label: "before",
|
|
18881
|
+
ok: legacy.before.status === "passed" || legacy.before.status === "product_regression",
|
|
18882
|
+
profile_name: legacy.before.profile_name,
|
|
18883
|
+
status: legacy.before.status,
|
|
18884
|
+
required_status: DEFAULT_BEFORE_STATUSES,
|
|
18885
|
+
summary: legacy.before.summary
|
|
18886
|
+
},
|
|
18887
|
+
after: {
|
|
18888
|
+
side: "after",
|
|
18889
|
+
label: "after",
|
|
18890
|
+
ok: legacy.after.status === "passed",
|
|
18891
|
+
profile_name: legacy.after.profile_name,
|
|
18892
|
+
status: legacy.after.status,
|
|
18893
|
+
required_status: DEFAULT_AFTER_STATUSES,
|
|
18894
|
+
summary: legacy.after.summary
|
|
18895
|
+
}
|
|
18896
|
+
},
|
|
18897
|
+
source_bindings: {
|
|
18898
|
+
before: { side: "before", required: false, ok: true, status: "not_required" },
|
|
18899
|
+
after: { side: "after", required: false, ok: true, status: "not_required" }
|
|
18900
|
+
},
|
|
18901
|
+
deltas: legacy.deltas,
|
|
18902
|
+
recommendation,
|
|
18903
|
+
shipping_authorization: noShippingAuthorization(),
|
|
18904
|
+
proves: legacy.proves,
|
|
18905
|
+
does_not_prove: legacy.does_not_prove,
|
|
18906
|
+
metadata: legacy.metadata
|
|
18907
|
+
};
|
|
18908
|
+
}
|
|
18909
|
+
function parseRiddleProofChangeReceipt(value) {
|
|
18910
|
+
if (!isRecord3(value)) throw new Error("Change receipt must be an object.");
|
|
18911
|
+
if (value.version === RIDDLE_PROOF_CHANGE_RECEIPT_V1_VERSION) {
|
|
18912
|
+
return migrateRiddleProofChangeReceipt(value);
|
|
18913
|
+
}
|
|
18914
|
+
if (value.version !== RIDDLE_PROOF_CHANGE_RECEIPT_VERSION) {
|
|
18915
|
+
throw new Error(`Unsupported Change receipt version ${String(value.version || "missing")}.`);
|
|
18916
|
+
}
|
|
18917
|
+
const receipt = value;
|
|
18918
|
+
if (!RIDDLE_PROOF_PROFILE_STATUSES.includes(receipt.status)) {
|
|
18919
|
+
throw new Error(`Unsupported Change receipt status ${String(receipt.status || "missing")}.`);
|
|
18920
|
+
}
|
|
18921
|
+
parseRiddleProofObservationReceipt(receipt.before);
|
|
18922
|
+
parseRiddleProofObservationReceipt(receipt.after);
|
|
18923
|
+
if (receipt.before.comparison_role !== "before" || receipt.after.comparison_role !== "after") {
|
|
18924
|
+
throw new Error("Change receipt observations must preserve their before and after comparison roles.");
|
|
18925
|
+
}
|
|
18926
|
+
const expectedVerdict = changeReceiptVerdict(receipt.status);
|
|
18927
|
+
if (receipt.verdict !== expectedVerdict) {
|
|
18928
|
+
throw new Error("Change receipt verdict must match its evaluated status.");
|
|
18929
|
+
}
|
|
18930
|
+
if (!recommendationMatches(receipt.recommendation, changeRecommendation(expectedVerdict))) {
|
|
18931
|
+
throw new Error("Change receipt recommendation must be derived from the Change receipt verdict.");
|
|
18932
|
+
}
|
|
18933
|
+
assertShippingAuthorizationConsistent(receipt.shipping_authorization, "Change receipt");
|
|
18934
|
+
return receipt;
|
|
18935
|
+
}
|
|
18936
|
+
function createRiddleProofHandoffReceipt(changeReceipt, options = {}) {
|
|
18937
|
+
const change = parseRiddleProofChangeReceipt(changeReceipt);
|
|
18938
|
+
const authorization = options.shipping_authorization || change.shipping_authorization;
|
|
18939
|
+
assertShippingAuthorizationConsistent(authorization, "Handoff receipt");
|
|
18940
|
+
return {
|
|
18941
|
+
version: RIDDLE_PROOF_HANDOFF_RECEIPT_VERSION,
|
|
18942
|
+
change,
|
|
18943
|
+
verdict: change.verdict,
|
|
18944
|
+
recommendation: change.recommendation,
|
|
18945
|
+
shipping_authorization: authorization,
|
|
18946
|
+
canonical_pair: {
|
|
18947
|
+
before: change.before.canonical_screenshot,
|
|
18948
|
+
after: change.after.canonical_screenshot
|
|
18949
|
+
},
|
|
18950
|
+
created_at: options.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
18951
|
+
};
|
|
18952
|
+
}
|
|
18953
|
+
function parseRiddleProofHandoffReceipt(value) {
|
|
18954
|
+
if (!isRecord3(value) || value.version !== RIDDLE_PROOF_HANDOFF_RECEIPT_VERSION) {
|
|
18955
|
+
throw new Error(`Unsupported Handoff receipt version ${String(isRecord3(value) ? value.version || "missing" : "missing")}.`);
|
|
18956
|
+
}
|
|
18957
|
+
const receipt = value;
|
|
18958
|
+
if (!Number.isFinite(Date.parse(receipt.created_at))) {
|
|
18959
|
+
throw new Error("Handoff receipt created_at must be a valid timestamp.");
|
|
18960
|
+
}
|
|
18961
|
+
const change = parseRiddleProofChangeReceipt(receipt.change);
|
|
18962
|
+
if (receipt.verdict !== change.verdict || !recommendationMatches(receipt.recommendation, change.recommendation)) {
|
|
18963
|
+
throw new Error("Handoff receipt must preserve the Change receipt verdict and recommendation.");
|
|
18964
|
+
}
|
|
18965
|
+
if (!observationArtifactMatches(receipt.canonical_pair?.before, change.before.canonical_screenshot) || !observationArtifactMatches(receipt.canonical_pair?.after, change.after.canonical_screenshot)) {
|
|
18966
|
+
throw new Error("Handoff receipt canonical pair must project the Change receipt observations.");
|
|
18967
|
+
}
|
|
18968
|
+
assertShippingAuthorizationConsistent(receipt.shipping_authorization, "Handoff receipt");
|
|
18969
|
+
return receipt;
|
|
18970
|
+
}
|
|
18971
|
+
function markdownTableCell(value) {
|
|
18972
|
+
return String(value ?? "").replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
18973
|
+
}
|
|
18974
|
+
function artifactTarget(artifact) {
|
|
18975
|
+
return artifact.url || artifact.path;
|
|
18976
|
+
}
|
|
18977
|
+
function observationArtifactKind(artifact) {
|
|
18978
|
+
if (artifact.role === "canonical_screenshot" || artifact.role === "setup_screenshot" || artifact.kind === "image") return "image";
|
|
18979
|
+
if (artifact.role === "data" || /\.(json|md|txt|html|log|har)(\?|#|$)/i.test(artifact.name)) return "data";
|
|
18980
|
+
return "artifact";
|
|
18981
|
+
}
|
|
18982
|
+
function receiptSideView(side, observation) {
|
|
18983
|
+
const artifacts = observation.artifacts.map((artifact) => ({
|
|
18984
|
+
side,
|
|
18985
|
+
name: artifact.name,
|
|
18986
|
+
kind: observationArtifactKind(artifact),
|
|
18987
|
+
url: artifact.url,
|
|
18988
|
+
path: artifact.path,
|
|
18989
|
+
source: artifact.source
|
|
18990
|
+
}));
|
|
18991
|
+
const summary = observation.profile_summary;
|
|
18992
|
+
const canonical = observation.canonical_screenshot;
|
|
18993
|
+
const screenshots = artifacts.filter(artifactIsScreenshot);
|
|
18994
|
+
if (canonical) {
|
|
18995
|
+
const canonicalTarget = canonical.url || canonical.path;
|
|
18996
|
+
screenshots.sort((left, right) => {
|
|
18997
|
+
const leftCanonical = (left.url || left.path) === canonicalTarget || left.name === canonical.name;
|
|
18998
|
+
const rightCanonical = (right.url || right.path) === canonicalTarget || right.name === canonical.name;
|
|
18999
|
+
return Number(rightCanonical) - Number(leftCanonical);
|
|
19000
|
+
});
|
|
19001
|
+
}
|
|
19002
|
+
return {
|
|
19003
|
+
side,
|
|
19004
|
+
source: observation.target.url,
|
|
19005
|
+
profile_name: summary?.profile_name || observation.proof?.profile_name || "profile",
|
|
19006
|
+
status: summary?.status || observation.proof?.result.status || "proof_insufficient",
|
|
19007
|
+
summary: summary?.summary || observation.proof?.result.summary || "No profile summary recorded.",
|
|
19008
|
+
route: summary?.route,
|
|
19009
|
+
captured_at: observation.captured_at,
|
|
19010
|
+
checks: summary?.checks || (observation.proof ? profileCheckCounts2(observation.proof.result) : {
|
|
19011
|
+
total: 0,
|
|
19012
|
+
passed: 0,
|
|
19013
|
+
failed: 0,
|
|
19014
|
+
skipped: 0,
|
|
19015
|
+
needs_human_review: 0
|
|
19016
|
+
}),
|
|
19017
|
+
screenshots,
|
|
19018
|
+
artifacts
|
|
19019
|
+
};
|
|
19020
|
+
}
|
|
19021
|
+
function markdownLink(label, target) {
|
|
19022
|
+
return `[${label.replace(/\]/g, "\\]")}](${target})`;
|
|
19023
|
+
}
|
|
19024
|
+
function appendMarkdownArtifacts(lines, title, artifacts) {
|
|
19025
|
+
lines.push(`### ${title}`, "");
|
|
19026
|
+
if (!artifacts.length) {
|
|
19027
|
+
lines.push("- No screenshot artifacts recorded.", "");
|
|
19028
|
+
return;
|
|
19029
|
+
}
|
|
19030
|
+
for (const artifact of artifacts.slice(0, 6)) {
|
|
19031
|
+
const target = artifactTarget(artifact);
|
|
19032
|
+
if (target && artifact.kind === "image") {
|
|
19033
|
+
lines.push(`![${artifact.name.replace(/\]/g, "\\]")}](${target})`);
|
|
19034
|
+
} else if (target) {
|
|
19035
|
+
lines.push(`- ${markdownLink(artifact.name, target)}`);
|
|
19036
|
+
} else {
|
|
19037
|
+
lines.push(`- ${artifact.name}`);
|
|
19038
|
+
}
|
|
19039
|
+
}
|
|
19040
|
+
if (artifacts.length > 6) lines.push(`- ${artifacts.length - 6} more artifact(s) omitted`);
|
|
19041
|
+
lines.push("");
|
|
19042
|
+
}
|
|
19043
|
+
function riddleProofChangeReceiptMarkdown(receipt) {
|
|
19044
|
+
const before = receiptSideView("before", receipt.before);
|
|
19045
|
+
const after = receiptSideView("after", receipt.after);
|
|
19046
|
+
const lines = [
|
|
19047
|
+
"# Riddle Proof Change Receipt",
|
|
19048
|
+
"",
|
|
19049
|
+
`**Verdict:** ${receipt.verdict}`,
|
|
19050
|
+
`**Status:** ${receipt.status}`,
|
|
19051
|
+
`**Contract:** ${receipt.contract_name}`,
|
|
19052
|
+
receipt.profile_name ? `**Profile:** ${receipt.profile_name}` : "",
|
|
19053
|
+
`**Recommendation:** ${receipt.recommendation.label}`,
|
|
19054
|
+
`**Shipping authorization:** ${receipt.shipping_authorization.status}`,
|
|
19055
|
+
"",
|
|
19056
|
+
receipt.summary,
|
|
19057
|
+
"",
|
|
19058
|
+
"## Evidence Pair",
|
|
19059
|
+
"",
|
|
19060
|
+
"| Side | Source | Status | Checks |",
|
|
19061
|
+
"| --- | --- | --- | --- |",
|
|
19062
|
+
`| Before | ${markdownTableCell(before.source)} | ${before.status}${receipt.groups.before.ok ? " (matched baseline)" : " (baseline mismatch)"} | ${before.checks.passed} passed / ${before.checks.failed} failed |`,
|
|
19063
|
+
`| After | ${markdownTableCell(after.source)} | ${after.status} | ${after.checks.passed} passed / ${after.checks.failed} failed |`,
|
|
19064
|
+
"",
|
|
19065
|
+
"## Source Binding",
|
|
19066
|
+
"",
|
|
19067
|
+
"| Side | Status | Git revision | Preview | Digest |",
|
|
19068
|
+
"| --- | --- | --- | --- | --- |",
|
|
19069
|
+
`| Before | ${receipt.source_bindings.before.status} | ${markdownTableCell(receipt.source_bindings.before.observed_git_revision || "not recorded")} | ${markdownTableCell(receipt.source_bindings.before.preview_id || "not required")} | ${markdownTableCell(receipt.source_bindings.before.content_digest || "not required")} |`,
|
|
19070
|
+
`| After | ${receipt.source_bindings.after.status} | ${markdownTableCell(receipt.source_bindings.after.observed_git_revision || "not recorded")} | ${markdownTableCell(receipt.source_bindings.after.preview_id || "not required")} | ${markdownTableCell(receipt.source_bindings.after.content_digest || "not required")} |`,
|
|
19071
|
+
"",
|
|
19072
|
+
"## Delta Checks",
|
|
19073
|
+
"",
|
|
19074
|
+
"| Delta | Status | Before | After |",
|
|
19075
|
+
"| --- | --- | --- | --- |"
|
|
19076
|
+
].filter(Boolean);
|
|
19077
|
+
for (const delta of receipt.deltas) {
|
|
19078
|
+
lines.push(`| ${markdownTableCell(delta.label)} | ${delta.status} | ${markdownTableCell(delta.before_observed)} | ${markdownTableCell(delta.after_observed)} |`);
|
|
19079
|
+
if (delta.message) lines.push(`| ${markdownTableCell(`${delta.label} message`)} | ${markdownTableCell(delta.message)} | | |`);
|
|
19080
|
+
}
|
|
19081
|
+
lines.push("");
|
|
19082
|
+
appendMarkdownArtifacts(lines, "Before Canonical Screenshot", before.screenshots.slice(0, 1));
|
|
19083
|
+
appendMarkdownArtifacts(lines, "After Canonical Screenshot", after.screenshots.slice(0, 1));
|
|
19084
|
+
if (receipt.proves.length) {
|
|
19085
|
+
lines.push("## What This Proves", "");
|
|
19086
|
+
for (const claim of receipt.proves) lines.push(`- ${claim}`);
|
|
19087
|
+
lines.push("");
|
|
19088
|
+
}
|
|
19089
|
+
if (receipt.does_not_prove.length) {
|
|
19090
|
+
lines.push("## What This Does Not Prove", "");
|
|
19091
|
+
for (const claim of receipt.does_not_prove) lines.push(`- ${claim}`);
|
|
19092
|
+
lines.push("");
|
|
19093
|
+
}
|
|
19094
|
+
const linkedArtifacts = [...before.artifacts, ...after.artifacts].filter((artifact) => artifactTarget(artifact)).slice(0, 24);
|
|
19095
|
+
if (linkedArtifacts.length) {
|
|
19096
|
+
lines.push("## Artifacts", "");
|
|
19097
|
+
for (const artifact of linkedArtifacts) {
|
|
19098
|
+
lines.push(`- ${artifact.side}: ${markdownLink(artifact.name, artifactTarget(artifact) || "")}`);
|
|
19099
|
+
}
|
|
19100
|
+
lines.push("");
|
|
19101
|
+
}
|
|
19102
|
+
return `${lines.join("\n").trim()}
|
|
19103
|
+
`;
|
|
19104
|
+
}
|
|
19105
|
+
function escapeHtml(value) {
|
|
19106
|
+
return String(value ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
19107
|
+
}
|
|
19108
|
+
function htmlArtifactCard(artifact) {
|
|
19109
|
+
const target = artifactTarget(artifact);
|
|
19110
|
+
if (!target) return `<p>${escapeHtml(artifact.name)}</p>`;
|
|
19111
|
+
if (artifact.kind === "image") {
|
|
19112
|
+
return `<figure><img src="${escapeHtml(target)}" alt="${escapeHtml(artifact.name)}"><figcaption>${escapeHtml(artifact.name)}</figcaption></figure>`;
|
|
19113
|
+
}
|
|
19114
|
+
return `<p><a href="${escapeHtml(target)}">${escapeHtml(artifact.name)}</a></p>`;
|
|
19115
|
+
}
|
|
19116
|
+
function htmlArtifactLink(artifact) {
|
|
19117
|
+
const target = artifactTarget(artifact);
|
|
19118
|
+
if (!target) return escapeHtml(artifact.name);
|
|
19119
|
+
return `<a href="${escapeHtml(target)}">${escapeHtml(artifact.name)}</a>`;
|
|
19120
|
+
}
|
|
19121
|
+
function htmlList(items, emptyText) {
|
|
19122
|
+
if (!items.length) return `<p class="muted">${escapeHtml(emptyText)}</p>`;
|
|
19123
|
+
return `<ul>${items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul>`;
|
|
19124
|
+
}
|
|
19125
|
+
function riddleProofChangeReceiptHtml(receipt) {
|
|
19126
|
+
const before = receiptSideView("before", receipt.before);
|
|
19127
|
+
const after = receiptSideView("after", receipt.after);
|
|
19128
|
+
const artifactLinks = [...before.artifacts, ...after.artifacts].filter((artifact) => artifactTarget(artifact)).slice(0, 32);
|
|
19129
|
+
const deltaRows = receipt.deltas.map((delta) => `
|
|
19130
|
+
<tr>
|
|
19131
|
+
<td>${escapeHtml(delta.label)}</td>
|
|
19132
|
+
<td><span class="status ${escapeHtml(delta.status)}">${escapeHtml(delta.status)}</span></td>
|
|
19133
|
+
<td>${escapeHtml(delta.before_observed)}</td>
|
|
19134
|
+
<td>${escapeHtml(delta.after_observed)}</td>
|
|
19135
|
+
</tr>
|
|
19136
|
+
${delta.message ? `<tr><td class="muted">${escapeHtml(delta.label)} message</td><td colspan="3">${escapeHtml(delta.message)}</td></tr>` : ""}`).join("");
|
|
19137
|
+
return `<!doctype html>
|
|
19138
|
+
<html lang="en">
|
|
19139
|
+
<head>
|
|
19140
|
+
<meta charset="utf-8">
|
|
19141
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
19142
|
+
<title>${escapeHtml(receipt.contract_name)} - Riddle Proof Change Receipt</title>
|
|
19143
|
+
<style>
|
|
19144
|
+
:root { color-scheme: light dark; --bg: #0f141b; --panel: #171f29; --text: #eef4f8; --muted: #aebbc8; --border: #314152; --ok: #69d089; --bad: #ff7a7a; --warn: #ffd166; }
|
|
19145
|
+
body { margin: 0; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: var(--bg); color: var(--text); }
|
|
19146
|
+
main { max-width: 1120px; margin: 0 auto; padding: 32px 20px 48px; }
|
|
19147
|
+
header, section { border: 1px solid var(--border); background: var(--panel); border-radius: 8px; padding: 20px; margin: 0 0 18px; }
|
|
19148
|
+
h1, h2, h3, p { margin-top: 0; }
|
|
19149
|
+
h1 { font-size: clamp(1.6rem, 3vw, 2.4rem); }
|
|
19150
|
+
h2 { font-size: 1.1rem; margin-bottom: 14px; }
|
|
19151
|
+
.muted { color: var(--muted); }
|
|
19152
|
+
.verdict { display: inline-flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: 999px; border: 1px solid var(--border); font-weight: 700; }
|
|
19153
|
+
.mergeable, .passed { color: var(--ok); }
|
|
19154
|
+
.not_mergeable, .failed, .product_regression { color: var(--bad); }
|
|
19155
|
+
.proof_insufficient, .environment_blocked, .needs_human_review, .configuration_error { color: var(--warn); }
|
|
19156
|
+
.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
|
|
19157
|
+
.side { border: 1px solid var(--border); border-radius: 8px; padding: 14px; min-width: 0; }
|
|
19158
|
+
code { overflow-wrap: anywhere; color: var(--muted); }
|
|
19159
|
+
table { width: 100%; border-collapse: collapse; }
|
|
19160
|
+
th, td { border-bottom: 1px solid var(--border); padding: 10px 8px; text-align: left; vertical-align: top; }
|
|
19161
|
+
figure { margin: 0; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; background: #0b1017; }
|
|
19162
|
+
img { display: block; width: 100%; height: auto; }
|
|
19163
|
+
figcaption { padding: 8px 10px; color: var(--muted); font-size: 0.9rem; }
|
|
19164
|
+
ul { margin-bottom: 0; }
|
|
19165
|
+
a { color: #8bc7ff; }
|
|
19166
|
+
@media (max-width: 760px) { .grid { grid-template-columns: 1fr; } main { padding: 18px 12px 32px; } }
|
|
19167
|
+
</style>
|
|
19168
|
+
</head>
|
|
19169
|
+
<body>
|
|
19170
|
+
<main>
|
|
19171
|
+
<header>
|
|
19172
|
+
<p class="verdict ${escapeHtml(receipt.verdict)}">${escapeHtml(receipt.verdict)}</p>
|
|
19173
|
+
<h1>${escapeHtml(receipt.contract_name)}</h1>
|
|
19174
|
+
<p>${escapeHtml(receipt.summary)}</p>
|
|
19175
|
+
<p class="muted">Status: ${escapeHtml(receipt.status)}${receipt.profile_name ? ` | Profile: ${escapeHtml(receipt.profile_name)}` : ""}</p>
|
|
19176
|
+
<p>${escapeHtml(receipt.recommendation.label)}. Shipping authorization: ${escapeHtml(receipt.shipping_authorization.status)}.</p>
|
|
19177
|
+
</header>
|
|
19178
|
+
<section>
|
|
19179
|
+
<h2>Evidence Pair</h2>
|
|
19180
|
+
<div class="grid">
|
|
19181
|
+
<div class="side">
|
|
19182
|
+
<h3>Before</h3>
|
|
19183
|
+
<p><code>${escapeHtml(before.source)}</code></p>
|
|
19184
|
+
<p>Status: <strong class="${escapeHtml(before.status)}">${escapeHtml(before.status)}</strong>${receipt.groups.before.ok ? " (matched baseline)" : " (baseline mismatch)"}</p>
|
|
19185
|
+
<p>${escapeHtml(before.summary)}</p>
|
|
19186
|
+
<p class="muted">${before.checks.passed} passed / ${before.checks.failed} failed checks</p>
|
|
19187
|
+
</div>
|
|
19188
|
+
<div class="side">
|
|
19189
|
+
<h3>After</h3>
|
|
19190
|
+
<p><code>${escapeHtml(after.source)}</code></p>
|
|
19191
|
+
<p>Status: <strong class="${escapeHtml(after.status)}">${escapeHtml(after.status)}</strong></p>
|
|
19192
|
+
<p>${escapeHtml(after.summary)}</p>
|
|
19193
|
+
<p class="muted">${after.checks.passed} passed / ${after.checks.failed} failed checks</p>
|
|
19194
|
+
</div>
|
|
19195
|
+
</div>
|
|
19196
|
+
</section>
|
|
19197
|
+
<section>
|
|
19198
|
+
<h2>Source Binding</h2>
|
|
19199
|
+
<table>
|
|
19200
|
+
<thead><tr><th>Side</th><th>Status</th><th>Git revision</th><th>Preview</th><th>Digest</th></tr></thead>
|
|
19201
|
+
<tbody>
|
|
19202
|
+
<tr><td>Before</td><td>${escapeHtml(receipt.source_bindings.before.status)}</td><td>${escapeHtml(receipt.source_bindings.before.observed_git_revision || "not recorded")}</td><td>${escapeHtml(receipt.source_bindings.before.preview_id || "not required")}</td><td>${escapeHtml(receipt.source_bindings.before.content_digest || "not required")}</td></tr>
|
|
19203
|
+
<tr><td>After</td><td>${escapeHtml(receipt.source_bindings.after.status)}</td><td>${escapeHtml(receipt.source_bindings.after.observed_git_revision || "not recorded")}</td><td>${escapeHtml(receipt.source_bindings.after.preview_id || "not required")}</td><td>${escapeHtml(receipt.source_bindings.after.content_digest || "not required")}</td></tr>
|
|
19204
|
+
</tbody>
|
|
19205
|
+
</table>
|
|
19206
|
+
</section>
|
|
19207
|
+
<section>
|
|
19208
|
+
<h2>Delta Checks</h2>
|
|
19209
|
+
<table>
|
|
19210
|
+
<thead><tr><th>Delta</th><th>Status</th><th>Before</th><th>After</th></tr></thead>
|
|
19211
|
+
<tbody>${deltaRows}</tbody>
|
|
19212
|
+
</table>
|
|
19213
|
+
</section>
|
|
19214
|
+
<section>
|
|
19215
|
+
<h2>Screenshots</h2>
|
|
19216
|
+
<div class="grid">
|
|
19217
|
+
<div>${before.screenshots.slice(0, 1).map(htmlArtifactCard).join("") || `<p class="muted">No canonical before screenshot recorded.</p>`}</div>
|
|
19218
|
+
<div>${after.screenshots.slice(0, 1).map(htmlArtifactCard).join("") || `<p class="muted">No canonical after screenshot recorded.</p>`}</div>
|
|
19219
|
+
</div>
|
|
19220
|
+
</section>
|
|
19221
|
+
<section>
|
|
19222
|
+
<h2>What This Proves</h2>
|
|
19223
|
+
${htmlList(receipt.proves, "No explicit proof claims were recorded in contract metadata.")}
|
|
19224
|
+
</section>
|
|
19225
|
+
<section>
|
|
19226
|
+
<h2>What This Does Not Prove</h2>
|
|
19227
|
+
${htmlList(receipt.does_not_prove, "No explicit limits were recorded in contract metadata.")}
|
|
19228
|
+
</section>
|
|
19229
|
+
<section>
|
|
19230
|
+
<h2>Artifacts</h2>
|
|
19231
|
+
${artifactLinks.length ? `<ul>${artifactLinks.map((artifact) => `<li>${escapeHtml(artifact.side)}: ${htmlArtifactLink(artifact)}</li>`).join("")}</ul>` : `<p class="muted">No linked artifacts recorded.</p>`}
|
|
19232
|
+
</section>
|
|
19233
|
+
</main>
|
|
19234
|
+
</body>
|
|
19235
|
+
</html>
|
|
19236
|
+
`;
|
|
19237
|
+
}
|
|
19238
|
+
|
|
19239
|
+
// src/pr-comment.ts
|
|
19240
|
+
var RIDDLE_PROOF_PR_COMMENT_MARKER = "<!-- riddle-proof:pr-comment:v1 -->";
|
|
19241
|
+
function asRecord2(value) {
|
|
19242
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
19243
|
+
}
|
|
19244
|
+
function asArray(value) {
|
|
19245
|
+
return Array.isArray(value) ? value : [];
|
|
19246
|
+
}
|
|
19247
|
+
function stringValue4(value) {
|
|
19248
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
19249
|
+
}
|
|
19250
|
+
function numberValue3(value) {
|
|
19251
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
19252
|
+
}
|
|
19253
|
+
function booleanValue3(value) {
|
|
19254
|
+
return typeof value === "boolean" ? value : void 0;
|
|
19255
|
+
}
|
|
19256
|
+
function firstStringValue2(...values) {
|
|
19257
|
+
for (const value of values) {
|
|
19258
|
+
const text = stringValue4(value);
|
|
19259
|
+
if (text) return text;
|
|
19260
|
+
}
|
|
19261
|
+
return void 0;
|
|
19262
|
+
}
|
|
19263
|
+
function artifactKind(name, url) {
|
|
19264
|
+
const target = `${name} ${url}`.toLowerCase();
|
|
19265
|
+
if (/\.(png|jpe?g|gif|webp|avif|svg)(\?|#|$)/.test(target)) return "image";
|
|
19266
|
+
if (/\.(json|har|txt|md|html|log)(\?|#|$)/.test(target)) return "data";
|
|
19267
|
+
return "artifact";
|
|
19268
|
+
}
|
|
19269
|
+
function artifactDisplayName(value, fallback) {
|
|
19270
|
+
const raw = stringValue4(value);
|
|
19271
|
+
if (raw) return raw;
|
|
19272
|
+
return fallback;
|
|
19273
|
+
}
|
|
19274
|
+
function collectArtifacts(runResponse) {
|
|
19275
|
+
const proofResult = asRecord2(runResponse.proofResult);
|
|
19276
|
+
const outputs = asArray(proofResult.outputs);
|
|
19277
|
+
const artifacts = [];
|
|
19278
|
+
const seen = /* @__PURE__ */ new Set();
|
|
19279
|
+
for (const [index, item] of outputs.entries()) {
|
|
19280
|
+
const artifact = asRecord2(item);
|
|
19281
|
+
const url = stringValue4(artifact.url);
|
|
19282
|
+
if (!url || seen.has(url)) continue;
|
|
19283
|
+
seen.add(url);
|
|
19284
|
+
const name = artifactDisplayName(artifact.name, `artifact-${index + 1}`);
|
|
19285
|
+
artifacts.push({
|
|
19286
|
+
name,
|
|
19287
|
+
url,
|
|
19288
|
+
kind: artifactKind(name, url),
|
|
19289
|
+
size_bytes: numberValue3(artifact.size)
|
|
19290
|
+
});
|
|
19291
|
+
}
|
|
19292
|
+
return artifacts;
|
|
19293
|
+
}
|
|
19294
|
+
function collectProfileArtifacts(result) {
|
|
18121
19295
|
const resultArtifacts = asRecord2(result.artifacts);
|
|
18122
19296
|
const artifacts = [];
|
|
18123
19297
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -18137,6 +19311,34 @@ function collectProfileArtifacts(result) {
|
|
|
18137
19311
|
}
|
|
18138
19312
|
return artifacts;
|
|
18139
19313
|
}
|
|
19314
|
+
function collectChangeReceiptArtifacts(result) {
|
|
19315
|
+
const artifacts = [];
|
|
19316
|
+
const seen = /* @__PURE__ */ new Set();
|
|
19317
|
+
const collectSide = (sideName, side) => {
|
|
19318
|
+
const candidates = [
|
|
19319
|
+
side.canonical_screenshot,
|
|
19320
|
+
...asArray(side.screenshots),
|
|
19321
|
+
...asArray(side.artifacts)
|
|
19322
|
+
].filter(Boolean);
|
|
19323
|
+
for (const [index, item] of candidates.entries()) {
|
|
19324
|
+
const artifact = asRecord2(item);
|
|
19325
|
+
const url = stringValue4(artifact.url) || stringValue4(artifact.path);
|
|
19326
|
+
if (!url || seen.has(url)) continue;
|
|
19327
|
+
seen.add(url);
|
|
19328
|
+
const fallbackName = `${sideName}-artifact-${index + 1}`;
|
|
19329
|
+
const name = `${sideName}/${artifactDisplayName(artifact.name, fallbackName)}`;
|
|
19330
|
+
artifacts.push({
|
|
19331
|
+
name,
|
|
19332
|
+
url,
|
|
19333
|
+
kind: artifactKind(name, url),
|
|
19334
|
+
size_bytes: numberValue3(artifact.size_bytes) ?? numberValue3(artifact.size)
|
|
19335
|
+
});
|
|
19336
|
+
}
|
|
19337
|
+
};
|
|
19338
|
+
collectSide("before", asRecord2(result.before));
|
|
19339
|
+
collectSide("after", asRecord2(result.after));
|
|
19340
|
+
return artifacts;
|
|
19341
|
+
}
|
|
18140
19342
|
function mergeArtifacts(...artifactLists) {
|
|
18141
19343
|
const artifacts = [];
|
|
18142
19344
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -18163,7 +19365,7 @@ function pageSummaries(result) {
|
|
|
18163
19365
|
}
|
|
18164
19366
|
return pages;
|
|
18165
19367
|
}
|
|
18166
|
-
function
|
|
19368
|
+
function profileCheckCounts3(result) {
|
|
18167
19369
|
const checks = asArray(result.checks);
|
|
18168
19370
|
if (checks.length) {
|
|
18169
19371
|
let passed2 = 0;
|
|
@@ -18192,6 +19394,33 @@ function profilePageSummaries(result, counts) {
|
|
|
18192
19394
|
failed: counts.failed
|
|
18193
19395
|
}];
|
|
18194
19396
|
}
|
|
19397
|
+
function changeReceiptCheckCounts(result) {
|
|
19398
|
+
const deltas = asArray(result.deltas);
|
|
19399
|
+
if (!deltas.length) return void 0;
|
|
19400
|
+
let passed = 0;
|
|
19401
|
+
let failed = 0;
|
|
19402
|
+
for (const item of deltas) {
|
|
19403
|
+
const status = stringValue4(asRecord2(item).status);
|
|
19404
|
+
if (status === "passed") passed += 1;
|
|
19405
|
+
if (status === "failed" || status === "proof_insufficient" || status === "configuration_error") failed += 1;
|
|
19406
|
+
}
|
|
19407
|
+
return { passed, failed };
|
|
19408
|
+
}
|
|
19409
|
+
function changeReceiptPageSummaries(result) {
|
|
19410
|
+
const pages = [];
|
|
19411
|
+
for (const sideName of ["before", "after"]) {
|
|
19412
|
+
const side = asRecord2(result[sideName]);
|
|
19413
|
+
if (!Object.keys(side).length) continue;
|
|
19414
|
+
const profileSummary = asRecord2(side.profile_summary);
|
|
19415
|
+
const checks = Object.keys(asRecord2(side.checks)).length ? asRecord2(side.checks) : asRecord2(profileSummary.checks);
|
|
19416
|
+
pages.push({
|
|
19417
|
+
route: firstStringValue2(asRecord2(side.target).url, side.source, profileSummary.profile_name, side.profile_name) || sideName,
|
|
19418
|
+
passed: numberValue3(checks.passed) ?? 0,
|
|
19419
|
+
failed: numberValue3(checks.failed) ?? 0
|
|
19420
|
+
});
|
|
19421
|
+
}
|
|
19422
|
+
return pages;
|
|
19423
|
+
}
|
|
18195
19424
|
function summarizeExplicitChecks(value) {
|
|
18196
19425
|
let passed = 0;
|
|
18197
19426
|
let failed = 0;
|
|
@@ -18247,11 +19476,27 @@ function isProfileResult(result) {
|
|
|
18247
19476
|
const version = stringValue4(result.version);
|
|
18248
19477
|
return version === "riddle-proof.profile-result.v1" || version === "riddle-proof.profile-compact-result.v1";
|
|
18249
19478
|
}
|
|
19479
|
+
function isChangeReceiptResult(result) {
|
|
19480
|
+
const version = stringValue4(result.version);
|
|
19481
|
+
return version === RIDDLE_PROOF_CHANGE_RECEIPT_V1_VERSION || version === RIDDLE_PROOF_CHANGE_RECEIPT_VERSION;
|
|
19482
|
+
}
|
|
19483
|
+
function isHandoffReceiptResult(result) {
|
|
19484
|
+
return stringValue4(result.version) === RIDDLE_PROOF_HANDOFF_RECEIPT_VERSION;
|
|
19485
|
+
}
|
|
19486
|
+
function isChangeResult(result) {
|
|
19487
|
+
const version = stringValue4(result.version);
|
|
19488
|
+
return version === "riddle-proof.change-result.v1" || version === "riddle-proof.change-compact-result.v1";
|
|
19489
|
+
}
|
|
18250
19490
|
function summarizeRiddleProofPrComment(input) {
|
|
18251
19491
|
const runResponse = asRecord2(input.runResponse);
|
|
18252
|
-
const
|
|
19492
|
+
const inputResult = asRecord2(input.result);
|
|
19493
|
+
const handoffReceipt = isHandoffReceiptResult(inputResult) ? parseRiddleProofHandoffReceipt(inputResult) : void 0;
|
|
19494
|
+
const parsedChangeReceipt = handoffReceipt?.change || (isChangeReceiptResult(inputResult) ? parseRiddleProofChangeReceipt(inputResult) : void 0);
|
|
19495
|
+
const result = parsedChangeReceipt ? asRecord2(parsedChangeReceipt) : inputResult;
|
|
18253
19496
|
const proofResult = asRecord2(runResponse.proofResult);
|
|
18254
19497
|
const profileResult = isProfileResult(result);
|
|
19498
|
+
const changeReceiptResult = Boolean(parsedChangeReceipt);
|
|
19499
|
+
const changeResult = changeReceiptResult || isChangeResult(result);
|
|
18255
19500
|
const profileRiddle = asRecord2(result.riddle);
|
|
18256
19501
|
const preview = asRecord2(runResponse.preview);
|
|
18257
19502
|
const resultRunCard = asRecord2(result.run_card);
|
|
@@ -18259,16 +19504,19 @@ function summarizeRiddleProofPrComment(input) {
|
|
|
18259
19504
|
const resultDetails = asRecord2(result.details);
|
|
18260
19505
|
const resultRaw = asRecord2(result.raw);
|
|
18261
19506
|
const rawDetails = asRecord2(resultRaw.details);
|
|
18262
|
-
const profileChecks = profileResult ?
|
|
19507
|
+
const profileChecks = profileResult ? profileCheckCounts3(result) : void 0;
|
|
19508
|
+
const changeChecks = changeResult ? changeReceiptCheckCounts(result) : void 0;
|
|
18263
19509
|
const artifacts = mergeArtifacts(
|
|
18264
19510
|
collectArtifacts(runResponse),
|
|
18265
|
-
profileResult ? collectProfileArtifacts(result) : []
|
|
19511
|
+
profileResult ? collectProfileArtifacts(result) : [],
|
|
19512
|
+
changeReceiptResult ? collectChangeReceiptArtifacts(result) : []
|
|
18266
19513
|
);
|
|
18267
|
-
const pages = profileResult ? profilePageSummaries(result, profileChecks) : pageSummaries(result);
|
|
19514
|
+
const pages = changeReceiptResult ? changeReceiptPageSummaries(result) : profileResult ? profilePageSummaries(result, profileChecks) : pageSummaries(result);
|
|
18268
19515
|
const checkSource = { ...result };
|
|
18269
19516
|
delete checkSource.ok;
|
|
18270
19517
|
const nestedChecks = summarizeExplicitChecks(checkSource);
|
|
18271
|
-
const
|
|
19518
|
+
const resultStatus = firstStringValue2(result.status, stopCondition.status);
|
|
19519
|
+
const ok = changeReceiptResult ? parsedChangeReceipt?.recommendation.merge_recommended ?? false : changeResult ? resultStatus === "passed" : booleanValue3(result.ok) ?? booleanValue3(runResponse.ok) ?? null;
|
|
18272
19520
|
const checkpointSummary = checkpointSummaryFrom2(
|
|
18273
19521
|
result.checkpoint_summary,
|
|
18274
19522
|
stopCondition.checkpoint_summary,
|
|
@@ -18276,19 +19524,19 @@ function summarizeRiddleProofPrComment(input) {
|
|
|
18276
19524
|
rawDetails.checkpoint_summary,
|
|
18277
19525
|
proofResult.checkpoint_summary
|
|
18278
19526
|
);
|
|
18279
|
-
const publicState = summarizeRiddleProofPublicState({
|
|
19527
|
+
const publicState = changeReceiptResult ? void 0 : summarizeRiddleProofPublicState({
|
|
18280
19528
|
...result,
|
|
18281
|
-
status:
|
|
19529
|
+
status: resultStatus,
|
|
18282
19530
|
checkpoint_summary: checkpointSummary || result.checkpoint_summary
|
|
18283
19531
|
});
|
|
18284
|
-
const mergeRecommendation = riddleProofPublicStateMergeRecommendation(
|
|
19532
|
+
const mergeRecommendation = publicState ? riddleProofPublicStateMergeRecommendation(
|
|
18285
19533
|
publicState,
|
|
18286
19534
|
firstStringValue2(result.merge_recommendation, stopCondition.merge_recommendation, resultRaw.merge_recommendation)
|
|
18287
|
-
);
|
|
19535
|
+
) : void 0;
|
|
18288
19536
|
return {
|
|
18289
19537
|
ok,
|
|
18290
19538
|
status: firstStringValue2(proofResult.status, profileRiddle.status),
|
|
18291
|
-
result_status: publicState
|
|
19539
|
+
result_status: changeResult ? resultStatus : publicState?.status,
|
|
18292
19540
|
job_id: firstStringValue2(proofResult.job_id, profileRiddle.job_id),
|
|
18293
19541
|
duration_ms: numberValue3(proofResult.duration_ms) ?? numberValue3(profileRiddle.elapsed_ms),
|
|
18294
19542
|
proof_url: stringValue4(runResponse.proofUrl),
|
|
@@ -18296,17 +19544,17 @@ function summarizeRiddleProofPrComment(input) {
|
|
|
18296
19544
|
preview_url: stringValue4(preview.preview_url) || stringValue4(preview.url),
|
|
18297
19545
|
preview_publish_recovered: booleanValue3(preview.publish_recovered),
|
|
18298
19546
|
preview_publish_error: stringValue4(preview.publish_error),
|
|
18299
|
-
ship_held: publicState
|
|
18300
|
-
shipping_disabled: publicState
|
|
18301
|
-
ship_authorized: publicState
|
|
18302
|
-
merge_ready: publicState
|
|
18303
|
-
sync_allowed: publicState
|
|
19547
|
+
ship_held: publicState?.ship_held,
|
|
19548
|
+
shipping_disabled: publicState?.shipping_disabled,
|
|
19549
|
+
ship_authorized: changeReceiptResult ? parsedChangeReceipt?.shipping_authorization.authorized : publicState?.ship_authorized,
|
|
19550
|
+
merge_ready: publicState?.merge_ready,
|
|
19551
|
+
sync_allowed: publicState?.sync_allowed,
|
|
18304
19552
|
proof_decision: firstStringValue2(result.proof_decision, stopCondition.proof_decision, resultRaw.proof_decision),
|
|
18305
|
-
merge_recommendation: mergeRecommendation,
|
|
19553
|
+
merge_recommendation: changeReceiptResult ? parsedChangeReceipt?.recommendation.label : mergeRecommendation,
|
|
18306
19554
|
checkpoint_summary: checkpointSummary,
|
|
18307
19555
|
public_state: publicState,
|
|
18308
|
-
passed_checks: profileChecks?.passed ?? nestedChecks.passed,
|
|
18309
|
-
failed_checks: profileChecks?.failed ?? nestedChecks.failed,
|
|
19556
|
+
passed_checks: changeChecks?.passed ?? profileChecks?.passed ?? nestedChecks.passed,
|
|
19557
|
+
failed_checks: changeChecks?.failed ?? profileChecks?.failed ?? nestedChecks.failed,
|
|
18310
19558
|
pages,
|
|
18311
19559
|
artifacts,
|
|
18312
19560
|
primary_image: selectPrimaryImage(artifacts)
|
|
@@ -18319,7 +19567,7 @@ function formatDuration(ms) {
|
|
|
18319
19567
|
const remainder = seconds % 60;
|
|
18320
19568
|
return minutes > 0 ? `${minutes}m${String(remainder).padStart(2, "0")}s` : `${seconds}s`;
|
|
18321
19569
|
}
|
|
18322
|
-
function
|
|
19570
|
+
function markdownLink2(label, url) {
|
|
18323
19571
|
return `[${label.replace(/\]/g, "\\]")}](${url})`;
|
|
18324
19572
|
}
|
|
18325
19573
|
function resultLabel(summary) {
|
|
@@ -18370,7 +19618,88 @@ function checkpointSummaryLine(summary) {
|
|
|
18370
19618
|
summary.latest_decision ? `latest decision \`${summary.latest_decision}\`` : ""
|
|
18371
19619
|
].filter(Boolean).join("; ");
|
|
18372
19620
|
}
|
|
19621
|
+
function markdownTableValue(value) {
|
|
19622
|
+
return String(value ?? "").replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
19623
|
+
}
|
|
19624
|
+
function observationArtifactTarget(artifact) {
|
|
19625
|
+
return artifact?.url || artifact?.path;
|
|
19626
|
+
}
|
|
19627
|
+
function canonicalScreenshotCell(artifact, fallback) {
|
|
19628
|
+
const target = observationArtifactTarget(artifact);
|
|
19629
|
+
if (!artifact || !target) return fallback;
|
|
19630
|
+
if (artifact.url) return `![${artifact.name.replace(/\]/g, "\\]")}](${artifact.url})`;
|
|
19631
|
+
return markdownTableValue(artifact.path || artifact.name);
|
|
19632
|
+
}
|
|
19633
|
+
function shortRevision(value) {
|
|
19634
|
+
return value ? value.slice(0, 12) : "not recorded";
|
|
19635
|
+
}
|
|
19636
|
+
function buildRiddleProofHandoffPrCommentMarkdown(handoff, input = {}) {
|
|
19637
|
+
const receipt = parseRiddleProofHandoffReceipt(handoff);
|
|
19638
|
+
const change = receipt.change;
|
|
19639
|
+
const beforeStatus = change.before.profile_summary?.status || "not recorded";
|
|
19640
|
+
const afterStatus = change.after.profile_summary?.status || "not recorded";
|
|
19641
|
+
const beforeBaseline = change.groups.before.ok ? "matched baseline" : "baseline mismatch";
|
|
19642
|
+
const lines = [
|
|
19643
|
+
RIDDLE_PROOF_PR_COMMENT_MARKER,
|
|
19644
|
+
`## ${input.title?.trim() || "Riddle Change Proof"}`,
|
|
19645
|
+
"",
|
|
19646
|
+
`**Result:** ${receipt.recommendation.label}`,
|
|
19647
|
+
`**Evidence verdict:** \`${receipt.verdict}\``,
|
|
19648
|
+
`**Shipping authorization:** ${receipt.shipping_authorization.status}`,
|
|
19649
|
+
`**Contract:** ${change.contract_name}`
|
|
19650
|
+
];
|
|
19651
|
+
if (input.goal?.trim()) lines.push(`**Goal:** ${input.goal.trim()}`);
|
|
19652
|
+
if (input.successCriteria?.trim()) lines.push(`**Success criteria:** ${input.successCriteria.trim()}`);
|
|
19653
|
+
lines.push("", change.summary, "", "### Canonical Before / After", "");
|
|
19654
|
+
lines.push("| Before | After |");
|
|
19655
|
+
lines.push("| --- | --- |");
|
|
19656
|
+
lines.push(`| ${beforeBaseline}: \`${markdownTableValue(beforeStatus)}\` | \`${markdownTableValue(afterStatus)}\` |`);
|
|
19657
|
+
lines.push(`| ${canonicalScreenshotCell(receipt.canonical_pair.before, "No canonical before screenshot")} | ${canonicalScreenshotCell(receipt.canonical_pair.after, "No canonical after screenshot")} |`);
|
|
19658
|
+
lines.push("", "### Source Binding", "");
|
|
19659
|
+
lines.push("| Side | Target | Git revision | Binding | Preview | Digest |");
|
|
19660
|
+
lines.push("| --- | --- | --- | --- | --- | --- |");
|
|
19661
|
+
for (const side of ["before", "after"]) {
|
|
19662
|
+
const observation = change[side];
|
|
19663
|
+
const binding = change.source_bindings[side];
|
|
19664
|
+
lines.push(`| ${side} | ${markdownTableValue(observation.target.url)} | \`${shortRevision(binding.observed_git_revision || observation.source.git_revision)}\` | ${binding.status} | ${markdownTableValue(binding.preview_id || observation.target.preview?.preview_id || "not required")} | \`${markdownTableValue(binding.content_digest || observation.target.preview?.content_digest || "not required")}\` |`);
|
|
19665
|
+
}
|
|
19666
|
+
lines.push("", "### Declared Delta", "");
|
|
19667
|
+
lines.push("| Measurement | Status | Before | After |");
|
|
19668
|
+
lines.push("| --- | --- | --- | --- |");
|
|
19669
|
+
for (const delta of change.deltas) {
|
|
19670
|
+
lines.push(`| ${markdownTableValue(delta.label)} | ${delta.status} | ${markdownTableValue(delta.before_observed)} | ${markdownTableValue(delta.after_observed)} |`);
|
|
19671
|
+
}
|
|
19672
|
+
if (change.proves.length) {
|
|
19673
|
+
lines.push("", "### What This Proves", "");
|
|
19674
|
+
for (const claim of change.proves) lines.push(`- ${claim}`);
|
|
19675
|
+
}
|
|
19676
|
+
if (change.does_not_prove.length) {
|
|
19677
|
+
lines.push("", "### Limits", "");
|
|
19678
|
+
for (const claim of change.does_not_prove) lines.push(`- ${claim}`);
|
|
19679
|
+
}
|
|
19680
|
+
const linkedArtifacts = [
|
|
19681
|
+
...change.before.artifacts.map((artifact) => ({ side: "before", artifact })),
|
|
19682
|
+
...change.after.artifacts.map((artifact) => ({ side: "after", artifact }))
|
|
19683
|
+
].filter(({ artifact }) => Boolean(artifact.url));
|
|
19684
|
+
if (linkedArtifacts.length) {
|
|
19685
|
+
lines.push("", "### Artifacts", "");
|
|
19686
|
+
for (const { side, artifact } of linkedArtifacts.slice(0, 24)) {
|
|
19687
|
+
lines.push(`- ${side}: ${markdownLink2(artifact.name, artifact.url || "")}`);
|
|
19688
|
+
}
|
|
19689
|
+
}
|
|
19690
|
+
lines.push("", input.source?.trim() ? `_Source: ${input.source.trim()}_` : "_Rendered from the Handoff receipt without recomputing its verdict._");
|
|
19691
|
+
return `${lines.join("\n").trim()}
|
|
19692
|
+
`;
|
|
19693
|
+
}
|
|
18373
19694
|
function buildRiddleProofPrCommentMarkdown(input) {
|
|
19695
|
+
const result = asRecord2(input.result);
|
|
19696
|
+
if (isHandoffReceiptResult(result)) {
|
|
19697
|
+
return buildRiddleProofHandoffPrCommentMarkdown(parseRiddleProofHandoffReceipt(result), input);
|
|
19698
|
+
}
|
|
19699
|
+
if (isChangeReceiptResult(result)) {
|
|
19700
|
+
const handoff = createRiddleProofHandoffReceipt(parseRiddleProofChangeReceipt(result));
|
|
19701
|
+
return buildRiddleProofHandoffPrCommentMarkdown(handoff, input);
|
|
19702
|
+
}
|
|
18374
19703
|
const summary = summarizeRiddleProofPrComment(input);
|
|
18375
19704
|
const title = input.title?.trim() || "Riddle Proof Evidence";
|
|
18376
19705
|
const lines = [
|
|
@@ -18394,10 +19723,10 @@ function buildRiddleProofPrCommentMarkdown(input) {
|
|
|
18394
19723
|
if (summary.proof_decision) lines.push(`**Proof decision:** \`${summary.proof_decision}\``);
|
|
18395
19724
|
if (shouldRenderMergeRecommendation(summary)) lines.push(`**Merge recommendation:** ${summary.merge_recommendation}`);
|
|
18396
19725
|
if (summary.checkpoint_summary) lines.push(`**Checkpoints:** ${checkpointSummaryLine(summary.checkpoint_summary)}`);
|
|
18397
|
-
if (summary.proof_url) lines.push(`**Proof URL:** ${
|
|
19726
|
+
if (summary.proof_url) lines.push(`**Proof URL:** ${markdownLink2(summary.proof_url, summary.proof_url)}`);
|
|
18398
19727
|
if (summary.preview_id || summary.preview_url) {
|
|
18399
19728
|
const previewLabel = summary.preview_id ? `\`${summary.preview_id}\`` : "preview";
|
|
18400
|
-
lines.push(`**Preview:** ${summary.preview_url ?
|
|
19729
|
+
lines.push(`**Preview:** ${summary.preview_url ? markdownLink2(previewLabel, summary.preview_url) : previewLabel}`);
|
|
18401
19730
|
}
|
|
18402
19731
|
if (summary.preview_publish_recovered) {
|
|
18403
19732
|
const detail = summary.preview_publish_error ? `: ${summary.preview_publish_error}` : "";
|
|
@@ -18422,7 +19751,7 @@ function buildRiddleProofPrCommentMarkdown(input) {
|
|
|
18422
19751
|
if (linkedArtifacts.length) {
|
|
18423
19752
|
lines.push("### Artifacts");
|
|
18424
19753
|
for (const artifact of linkedArtifacts) {
|
|
18425
|
-
lines.push(`- ${
|
|
19754
|
+
lines.push(`- ${markdownLink2(artifact.name, artifact.url)}`);
|
|
18426
19755
|
}
|
|
18427
19756
|
if (summary.artifacts.length - (summary.primary_image ? 1 : 0) > linkedArtifacts.length) {
|
|
18428
19757
|
lines.push(`- ${summary.artifacts.length - (summary.primary_image ? 1 : 0) - linkedArtifacts.length} more artifact(s) omitted`);
|
|
@@ -18608,167 +19937,6 @@ function suggestRiddleProofProfileChecks(input) {
|
|
|
18608
19937
|
};
|
|
18609
19938
|
}
|
|
18610
19939
|
|
|
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
|
-
|
|
18772
19940
|
// src/cli.ts
|
|
18773
19941
|
var RIDDLE_PROFILE_BALANCE_PREFLIGHT_MIN_SECONDS_PER_JOB = 30;
|
|
18774
19942
|
var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
|
|
@@ -18781,8 +19949,14 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
|
|
|
18781
19949
|
"balancePreflight",
|
|
18782
19950
|
"baseUrl",
|
|
18783
19951
|
"afterResult",
|
|
19952
|
+
"afterObservation",
|
|
19953
|
+
"afterPreviewReceipt",
|
|
19954
|
+
"afterSourceRevision",
|
|
18784
19955
|
"afterUrl",
|
|
18785
19956
|
"beforeResult",
|
|
19957
|
+
"beforeObservation",
|
|
19958
|
+
"beforePreviewReceipt",
|
|
19959
|
+
"beforeSourceRevision",
|
|
18786
19960
|
"beforeUrl",
|
|
18787
19961
|
"candidateJson",
|
|
18788
19962
|
"candidatesJson",
|
|
@@ -18836,6 +20010,7 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
|
|
|
18836
20010
|
"pack",
|
|
18837
20011
|
"packFile",
|
|
18838
20012
|
"profile",
|
|
20013
|
+
"previewReceipt",
|
|
18839
20014
|
"proofDir",
|
|
18840
20015
|
"pr",
|
|
18841
20016
|
"progressEveryMs",
|
|
@@ -18859,6 +20034,9 @@ var KNOWN_CLI_OPTIONS = /* @__PURE__ */ new Set([
|
|
|
18859
20034
|
"scriptFile",
|
|
18860
20035
|
"selectors",
|
|
18861
20036
|
"sourceKind",
|
|
20037
|
+
"sourceDirty",
|
|
20038
|
+
"sourceRepository",
|
|
20039
|
+
"sourceRevision",
|
|
18862
20040
|
"splitViewports",
|
|
18863
20041
|
"stateDir",
|
|
18864
20042
|
"statePath",
|
|
@@ -18890,16 +20068,16 @@ function usage() {
|
|
|
18890
20068
|
" riddle-proof-loop respond --state-path <path> --response-json <file|json|->",
|
|
18891
20069
|
" riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
|
|
18892
20070
|
" riddle-proof-loop status --state-path <path>",
|
|
18893
|
-
" riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--
|
|
20071
|
+
" riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--preview-receipt <file|json>] [--source-revision <sha>] [--runner riddle] [--viewport-name <name[,name...]>] [--strict true|false; default false] [--split-viewports true|false; default false] [--balance-preflight true|false; default true] [--poll-attempts n] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json] [--quiet]",
|
|
18894
20072
|
" 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]",
|
|
18895
20073
|
" 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]",
|
|
20074
|
+
" riddle-proof-loop run-change-proof --profile <file|json|-> --change-contract <file|json|-> (--before-url <url> --after-url <url> | --before-result <file> --after-result <file> | --before-observation <file> --after-observation <file>) [--before-preview-receipt <file|json>] [--after-preview-receipt <file|json>] [--before-source-revision <sha>] [--after-source-revision <sha>] [--viewport-name <name[,name...]>] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json]",
|
|
18897
20075
|
" riddle-proof-loop profile-suggest --route /path|--url <url> [--changed-files a,b] [--selectors .a,.b] [--changed-text-json <file|json|->] [--format json|profile]",
|
|
18898
20076
|
" 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>]",
|
|
18899
20077
|
" 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]",
|
|
18900
20078
|
" riddle-proof-loop profile-body-assertions --artifact <file|url|-> --candidates-json <file|json|-> [--required-json <file|json|->] [--format json|body-contains]",
|
|
18901
20079
|
" riddle-proof-loop profile-http-status-preflight --profile <file|json|-> --url <base-url> [--format json|summary]",
|
|
18902
|
-
" riddle-proof-loop riddle-preview-deploy <build-dir> <label> [--framework spa|static] [--quiet]",
|
|
20080
|
+
" riddle-proof-loop riddle-preview-deploy <build-dir> <label> [--framework spa|static] [--output <dir>] [--quiet]",
|
|
18903
20081
|
" riddle-proof-loop riddle-server-preview <directory> --script-file <file> [--path /route] [--wait-for-selector selector]",
|
|
18904
20082
|
" riddle-proof-loop riddle-run-script --url <url> --script-file <file> [--viewport 1280x720] [--strict true|false]",
|
|
18905
20083
|
" riddle-proof-loop riddle-poll <job-id> [--wait] [--attempts n] [--quiet]",
|
|
@@ -19059,6 +20237,7 @@ function compactRunProfileResult(result, options) {
|
|
|
19059
20237
|
output_dir: outputDir,
|
|
19060
20238
|
output_files: outputDir ? {
|
|
19061
20239
|
profile_result: "profile-result.json",
|
|
20240
|
+
observation_receipt: "observation-receipt.json",
|
|
19062
20241
|
summary: "summary.md",
|
|
19063
20242
|
proof_json: result.evidence ? "proof.json" : void 0,
|
|
19064
20243
|
console: result.evidence?.console ? "console.json" : void 0,
|
|
@@ -19687,12 +20866,13 @@ function formatByteCount(bytes) {
|
|
|
19687
20866
|
}
|
|
19688
20867
|
function riddlePollProgressLine(snapshot) {
|
|
19689
20868
|
const submittedAt = snapshot.submitted_at || "not-submitted";
|
|
19690
|
-
const queuePart = snapshot.running_without_submission ? `
|
|
20869
|
+
const queuePart = snapshot.active_execution ? `${snapshot.queue_elapsed_ms !== null ? ` queue=${formatPollDuration(snapshot.queue_elapsed_ms)}` : ""} active_execution=${formatPollDuration(snapshot.elapsed_ms)}` : snapshot.running_without_submission ? ` waiting_for_worker_claim=${formatPollDuration(snapshot.pre_submission_elapsed_ms)} worker_wake=possible${snapshot.queue_elapsed_ms !== null ? ` queued_for=${formatPollDuration(snapshot.queue_elapsed_ms)}` : ""}` : snapshot.queue_elapsed_ms !== null ? ` queue=${formatPollDuration(snapshot.queue_elapsed_ms)}` : "";
|
|
19691
20870
|
const terminalPart = snapshot.terminal ? " terminal=true" : "";
|
|
19692
20871
|
return [
|
|
19693
20872
|
"[riddle-poll]",
|
|
19694
20873
|
snapshot.job_id,
|
|
19695
20874
|
`status=${snapshot.status || "unknown"}`,
|
|
20875
|
+
`phase=${snapshot.phase || "unknown"}`,
|
|
19696
20876
|
`attempt=${snapshot.attempt}/${snapshot.attempts}`,
|
|
19697
20877
|
`elapsed=${formatPollDuration(snapshot.elapsed_ms)}`,
|
|
19698
20878
|
`submitted_at=${submittedAt}${queuePart}${terminalPart}`
|
|
@@ -22630,10 +23810,48 @@ function profileHttpStatusSummaryMarkdown(result) {
|
|
|
22630
23810
|
}
|
|
22631
23811
|
return lines;
|
|
22632
23812
|
}
|
|
22633
|
-
function
|
|
23813
|
+
function profileResultTargetUrl(result) {
|
|
23814
|
+
const legacyRoute = result.route;
|
|
23815
|
+
return result.route.observed || result.route.requested || legacyRoute.url || result.evidence?.target_url || "unknown";
|
|
23816
|
+
}
|
|
23817
|
+
function profileObservationForOutput(result, outputDir, options) {
|
|
23818
|
+
if (options.observation) return options.observation;
|
|
23819
|
+
const preview = options.previewReceipt;
|
|
23820
|
+
const targetUrl = profileResultTargetUrl(result);
|
|
23821
|
+
return createRiddleProofObservationReceipt({
|
|
23822
|
+
comparison_role: options.comparisonRole || "standalone",
|
|
23823
|
+
executor: result.runner === "local-playwright" ? { kind: "local_playwright", runner: result.runner } : {
|
|
23824
|
+
kind: "riddle_hosted",
|
|
23825
|
+
runner: result.runner,
|
|
23826
|
+
job_id: result.riddle?.job_id,
|
|
23827
|
+
worker_id: typeof result.riddle?.execution?.worker_id === "string" ? result.riddle.execution.worker_id : void 0
|
|
23828
|
+
},
|
|
23829
|
+
target: preview ? { kind: "preview", url: targetUrl, preview } : { kind: "url", url: targetUrl },
|
|
23830
|
+
source: options.source || preview?.source,
|
|
23831
|
+
profile_result: result,
|
|
23832
|
+
publication: { kind: "local", path: outputDir },
|
|
23833
|
+
execution: result.riddle?.execution
|
|
23834
|
+
});
|
|
23835
|
+
}
|
|
23836
|
+
function profileOutputOptionsForCli(options) {
|
|
23837
|
+
const previewInput = optionString(options, "previewReceipt");
|
|
23838
|
+
const previewReceipt = previewInput ? parseRiddlePreviewReceipt(readJsonValue(previewInput, "--preview-receipt")) : void 0;
|
|
23839
|
+
const sourceDirty = optionBoolean(options, "sourceDirty");
|
|
23840
|
+
const source = {
|
|
23841
|
+
...previewReceipt?.source || {},
|
|
23842
|
+
...optionString(options, "sourceRevision") ? { git_revision: optionString(options, "sourceRevision") } : {},
|
|
23843
|
+
...optionString(options, "sourceRepository") ? { repository: optionString(options, "sourceRepository") } : {},
|
|
23844
|
+
...typeof sourceDirty === "boolean" ? { dirty: sourceDirty } : {}
|
|
23845
|
+
};
|
|
23846
|
+
return { previewReceipt, source };
|
|
23847
|
+
}
|
|
23848
|
+
function writeProfileOutput(outputDir, result, options = {}) {
|
|
22634
23849
|
if (!outputDir) return;
|
|
22635
23850
|
(0, import_node_fs6.mkdirSync)(outputDir, { recursive: true });
|
|
22636
23851
|
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "profile-result.json"), `${JSON.stringify(result, null, 2)}
|
|
23852
|
+
`);
|
|
23853
|
+
const observation = profileObservationForOutput(result, outputDir, options);
|
|
23854
|
+
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "observation-receipt.json"), `${JSON.stringify(observation, null, 2)}
|
|
22637
23855
|
`);
|
|
22638
23856
|
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "summary.md"), profileResultMarkdown(result));
|
|
22639
23857
|
if (result.evidence) (0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "proof.json"), `${JSON.stringify(result, null, 2)}
|
|
@@ -22674,6 +23892,12 @@ function profileOptionsForChangeProofSide(options, url, outputDir) {
|
|
|
22674
23892
|
}
|
|
22675
23893
|
return next;
|
|
22676
23894
|
}
|
|
23895
|
+
function observationForChangeSide(observation, side) {
|
|
23896
|
+
if (observation.comparison_role !== "standalone" && observation.comparison_role !== side) {
|
|
23897
|
+
throw new Error(`${side} observation has incompatible comparison_role ${observation.comparison_role}.`);
|
|
23898
|
+
}
|
|
23899
|
+
return observation.comparison_role === side ? observation : { ...observation, comparison_role: side };
|
|
23900
|
+
}
|
|
22677
23901
|
function compactChangeProofResult(run, options) {
|
|
22678
23902
|
const outputDir = profileOutputDirOption(options);
|
|
22679
23903
|
return {
|
|
@@ -22706,57 +23930,37 @@ function compactChangeProofResult(run, options) {
|
|
|
22706
23930
|
output_dir: outputDir,
|
|
22707
23931
|
output_files: outputDir ? {
|
|
22708
23932
|
change_proof_result: "change-proof-result.json",
|
|
23933
|
+
change_proof_receipt: "change-proof-receipt.json",
|
|
23934
|
+
change_proof_receipt_markdown: "change-proof-receipt.md",
|
|
23935
|
+
change_proof_receipt_html: "change-proof-receipt.html",
|
|
23936
|
+
handoff_receipt: "handoff-receipt.json",
|
|
23937
|
+
handoff_receipt_markdown: "handoff-receipt.md",
|
|
22709
23938
|
summary: "summary.md",
|
|
22710
23939
|
before_profile_result: "before/profile-result.json",
|
|
22711
|
-
after_profile_result: "after/profile-result.json"
|
|
23940
|
+
after_profile_result: "after/profile-result.json",
|
|
23941
|
+
before_observation_receipt: "before/observation-receipt.json",
|
|
23942
|
+
after_observation_receipt: "after/observation-receipt.json"
|
|
22712
23943
|
} : void 0
|
|
22713
23944
|
};
|
|
22714
23945
|
}
|
|
22715
23946
|
function changeProofResultMarkdown(run) {
|
|
22716
|
-
|
|
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
|
-
`;
|
|
23947
|
+
return riddleProofChangeReceiptMarkdown(run.receipt);
|
|
22751
23948
|
}
|
|
22752
23949
|
function writeChangeProofOutput(outputDir, run) {
|
|
22753
23950
|
if (!outputDir) return;
|
|
22754
23951
|
(0, import_node_fs6.mkdirSync)(outputDir, { recursive: true });
|
|
22755
23952
|
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "change-proof-result.json"), `${JSON.stringify(run.result, null, 2)}
|
|
22756
23953
|
`);
|
|
23954
|
+
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "change-proof-receipt.json"), `${JSON.stringify(run.receipt, null, 2)}
|
|
23955
|
+
`);
|
|
23956
|
+
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "change-proof-receipt.md"), riddleProofChangeReceiptMarkdown(run.receipt));
|
|
23957
|
+
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "change-proof-receipt.html"), riddleProofChangeReceiptHtml(run.receipt));
|
|
23958
|
+
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "handoff-receipt.json"), `${JSON.stringify(run.handoff, null, 2)}
|
|
23959
|
+
`);
|
|
23960
|
+
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "handoff-receipt.md"), buildRiddleProofPrCommentMarkdown({ result: run.handoff }));
|
|
22757
23961
|
(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);
|
|
23962
|
+
writeProfileOutput(changeProofSideOutputDir(outputDir, "before"), run.beforeResult, { observation: run.beforeObservation });
|
|
23963
|
+
writeProfileOutput(changeProofSideOutputDir(outputDir, "after"), run.afterResult, { observation: run.afterObservation });
|
|
22760
23964
|
}
|
|
22761
23965
|
function writeRunChangeProofResult(run, options) {
|
|
22762
23966
|
const format = runProfileResultFormatOption(options);
|
|
@@ -22855,6 +24059,7 @@ function withRiddleMetadata(result, input) {
|
|
|
22855
24059
|
...result.riddle || {},
|
|
22856
24060
|
job_id: input.job_id || result.riddle?.job_id,
|
|
22857
24061
|
status: input.status ?? result.riddle?.status,
|
|
24062
|
+
phase: poll?.phase ?? result.riddle?.phase,
|
|
22858
24063
|
terminal: input.terminal ?? result.riddle?.terminal,
|
|
22859
24064
|
created_at: poll?.created_at ?? result.riddle?.created_at,
|
|
22860
24065
|
submitted_at: poll?.submitted_at ?? result.riddle?.submitted_at,
|
|
@@ -22867,7 +24072,8 @@ function withRiddleMetadata(result, input) {
|
|
|
22867
24072
|
timed_out: poll?.timed_out ?? result.riddle?.timed_out,
|
|
22868
24073
|
retry_count: input.retryCount ?? result.riddle?.retry_count,
|
|
22869
24074
|
stale_job_ids: staleJobIds?.length ? staleJobIds : result.riddle?.stale_job_ids,
|
|
22870
|
-
artifact_recovery: input.artifactRecovery ?? result.riddle?.artifact_recovery
|
|
24075
|
+
artifact_recovery: input.artifactRecovery ?? result.riddle?.artifact_recovery,
|
|
24076
|
+
execution: poll?.execution ?? result.riddle?.execution
|
|
22871
24077
|
},
|
|
22872
24078
|
artifacts: {
|
|
22873
24079
|
...result.artifacts,
|
|
@@ -22935,6 +24141,7 @@ function riddleMetadataFromPoll(jobId, poll) {
|
|
|
22935
24141
|
return {
|
|
22936
24142
|
job_id: jobId,
|
|
22937
24143
|
status: poll.status,
|
|
24144
|
+
phase: poll.poll?.phase,
|
|
22938
24145
|
terminal: poll.terminal,
|
|
22939
24146
|
created_at: poll.poll?.created_at,
|
|
22940
24147
|
submitted_at: poll.poll?.submitted_at,
|
|
@@ -22944,7 +24151,8 @@ function riddleMetadataFromPoll(jobId, poll) {
|
|
|
22944
24151
|
elapsed_ms: poll.poll?.elapsed_ms,
|
|
22945
24152
|
attempt: poll.poll?.attempt,
|
|
22946
24153
|
attempts: poll.poll?.attempts,
|
|
22947
|
-
timed_out: poll.poll?.timed_out
|
|
24154
|
+
timed_out: poll.poll?.timed_out,
|
|
24155
|
+
execution: poll.poll?.execution
|
|
22948
24156
|
};
|
|
22949
24157
|
}
|
|
22950
24158
|
function profileUnsubmittedRetryTimeoutMs(options) {
|
|
@@ -23490,7 +24698,7 @@ async function runSplitViewportProfileForCli(profile, options, input) {
|
|
|
23490
24698
|
const childProfile = profileForSplitViewport(profile, viewport);
|
|
23491
24699
|
const childOutputDir = outputDir ? splitViewportOutputDir(outputDir, viewport.name, seenOutputNames) : void 0;
|
|
23492
24700
|
const result2 = await runSingleRiddleProfileForCli(childProfile, options, { ...input, outputDir: childOutputDir });
|
|
23493
|
-
if (childOutputDir) writeProfileOutput(childOutputDir, result2);
|
|
24701
|
+
if (childOutputDir) writeProfileOutput(childOutputDir, result2, profileOutputOptionsForCli(options));
|
|
23494
24702
|
childRuns.push({ viewport, profile: childProfile, result: result2 });
|
|
23495
24703
|
}
|
|
23496
24704
|
const artifacts = childRuns.flatMap(splitViewportArtifactRefs);
|
|
@@ -23622,18 +24830,34 @@ async function runChangeProofForCli(rawProfile, options) {
|
|
|
23622
24830
|
const contract = readChangeContractForCli(options);
|
|
23623
24831
|
const beforeResultPath = optionString(options, "beforeResult");
|
|
23624
24832
|
const afterResultPath = optionString(options, "afterResult");
|
|
24833
|
+
const beforeObservationPath = optionString(options, "beforeObservation");
|
|
24834
|
+
const afterObservationPath = optionString(options, "afterObservation");
|
|
23625
24835
|
const beforeUrl = optionString(options, "beforeUrl");
|
|
23626
24836
|
const afterUrl = optionString(options, "afterUrl");
|
|
24837
|
+
const beforePreviewInput = optionString(options, "beforePreviewReceipt");
|
|
24838
|
+
const afterPreviewInput = optionString(options, "afterPreviewReceipt");
|
|
24839
|
+
const beforePreview = beforePreviewInput ? parseRiddlePreviewReceipt(readJsonValue(beforePreviewInput, "--before-preview-receipt")) : void 0;
|
|
24840
|
+
const afterPreview = afterPreviewInput ? parseRiddlePreviewReceipt(readJsonValue(afterPreviewInput, "--after-preview-receipt")) : void 0;
|
|
23627
24841
|
const hasResultInputs = Boolean(beforeResultPath || afterResultPath);
|
|
24842
|
+
const hasObservationInputs = Boolean(beforeObservationPath || afterObservationPath);
|
|
23628
24843
|
const hasUrlInputs = Boolean(beforeUrl || afterUrl);
|
|
23629
|
-
if (hasResultInputs
|
|
23630
|
-
throw new Error("run-change-proof accepts
|
|
24844
|
+
if ([hasResultInputs, hasObservationInputs, hasUrlInputs].filter(Boolean).length > 1) {
|
|
24845
|
+
throw new Error("run-change-proof accepts one before/after input pair: result, observation, or URL.");
|
|
23631
24846
|
}
|
|
23632
24847
|
if (hasResultInputs && (!beforeResultPath || !afterResultPath)) {
|
|
23633
24848
|
throw new Error("run-change-proof requires both --before-result and --after-result.");
|
|
23634
24849
|
}
|
|
23635
|
-
if (
|
|
23636
|
-
throw new Error("run-change-proof requires both --before-
|
|
24850
|
+
if (hasObservationInputs && (!beforeObservationPath || !afterObservationPath)) {
|
|
24851
|
+
throw new Error("run-change-proof requires both --before-observation and --after-observation.");
|
|
24852
|
+
}
|
|
24853
|
+
if (hasUrlInputs && (!beforeUrl || !afterUrl)) {
|
|
24854
|
+
throw new Error("run-change-proof requires both --before-url and --after-url.");
|
|
24855
|
+
}
|
|
24856
|
+
if (!hasResultInputs && !hasObservationInputs && !hasUrlInputs) {
|
|
24857
|
+
throw new Error("run-change-proof requires a before/after result, observation, or URL pair.");
|
|
24858
|
+
}
|
|
24859
|
+
if ((beforePreview || afterPreview) && !hasUrlInputs) {
|
|
24860
|
+
throw new Error("--before-preview-receipt and --after-preview-receipt require the URL input mode.");
|
|
23637
24861
|
}
|
|
23638
24862
|
const outputDir = profileOutputDirOption(options);
|
|
23639
24863
|
let profile;
|
|
@@ -23641,15 +24865,53 @@ async function runChangeProofForCli(rawProfile, options) {
|
|
|
23641
24865
|
let afterResult;
|
|
23642
24866
|
let beforeSource;
|
|
23643
24867
|
let afterSource;
|
|
23644
|
-
|
|
24868
|
+
let beforeObservation;
|
|
24869
|
+
let afterObservation;
|
|
24870
|
+
if (hasObservationInputs) {
|
|
24871
|
+
if (!beforeObservationPath || !afterObservationPath) {
|
|
24872
|
+
throw new Error("run-change-proof requires both --before-observation and --after-observation.");
|
|
24873
|
+
}
|
|
24874
|
+
profile = profileWithSelectedViewportNamesForCli(normalizeProfileRecordForCli(rawProfile, options), options);
|
|
24875
|
+
beforeObservation = observationForChangeSide(
|
|
24876
|
+
parseRiddleProofObservationReceipt(readJsonValue(beforeObservationPath, "--before-observation")),
|
|
24877
|
+
"before"
|
|
24878
|
+
);
|
|
24879
|
+
afterObservation = observationForChangeSide(
|
|
24880
|
+
parseRiddleProofObservationReceipt(readJsonValue(afterObservationPath, "--after-observation")),
|
|
24881
|
+
"after"
|
|
24882
|
+
);
|
|
24883
|
+
if (!beforeObservation.proof?.result || !afterObservation.proof?.result) {
|
|
24884
|
+
throw new Error("Change Proof observations must include their profile results.");
|
|
24885
|
+
}
|
|
24886
|
+
beforeResult = beforeObservation.proof.result;
|
|
24887
|
+
afterResult = afterObservation.proof.result;
|
|
24888
|
+
beforeSource = beforeObservation.target.url;
|
|
24889
|
+
afterSource = afterObservation.target.url;
|
|
24890
|
+
} else if (hasResultInputs) {
|
|
23645
24891
|
if (!beforeResultPath || !afterResultPath) {
|
|
23646
24892
|
throw new Error("run-change-proof requires both --before-result and --after-result.");
|
|
23647
24893
|
}
|
|
23648
24894
|
profile = profileWithSelectedViewportNamesForCli(normalizeProfileRecordForCli(rawProfile, options), options);
|
|
23649
24895
|
beforeResult = readProfileResultForCli(beforeResultPath, "--before-result");
|
|
23650
24896
|
afterResult = readProfileResultForCli(afterResultPath, "--after-result");
|
|
23651
|
-
beforeSource =
|
|
23652
|
-
afterSource =
|
|
24897
|
+
beforeSource = profileResultTargetUrl(beforeResult);
|
|
24898
|
+
afterSource = profileResultTargetUrl(afterResult);
|
|
24899
|
+
beforeObservation = createRiddleProofObservationReceipt({
|
|
24900
|
+
comparison_role: "before",
|
|
24901
|
+
executor: { kind: beforeResult.runner === "local-playwright" ? "local_playwright" : "riddle_hosted", runner: beforeResult.runner, job_id: beforeResult.riddle?.job_id },
|
|
24902
|
+
target: { kind: "url", url: beforeSource },
|
|
24903
|
+
profile_result: beforeResult,
|
|
24904
|
+
execution: beforeResult.riddle?.execution,
|
|
24905
|
+
publication: { kind: "local", path: beforeResultPath }
|
|
24906
|
+
});
|
|
24907
|
+
afterObservation = createRiddleProofObservationReceipt({
|
|
24908
|
+
comparison_role: "after",
|
|
24909
|
+
executor: { kind: afterResult.runner === "local-playwright" ? "local_playwright" : "riddle_hosted", runner: afterResult.runner, job_id: afterResult.riddle?.job_id },
|
|
24910
|
+
target: { kind: "url", url: afterSource },
|
|
24911
|
+
profile_result: afterResult,
|
|
24912
|
+
execution: afterResult.riddle?.execution,
|
|
24913
|
+
publication: { kind: "local", path: afterResultPath }
|
|
24914
|
+
});
|
|
23653
24915
|
} else {
|
|
23654
24916
|
if (!beforeUrl || !afterUrl) {
|
|
23655
24917
|
throw new Error("run-change-proof requires both --before-url and --after-url unless saved results are provided.");
|
|
@@ -23663,12 +24925,54 @@ async function runChangeProofForCli(rawProfile, options) {
|
|
|
23663
24925
|
afterResult = await runProfileForCli(afterProfile, afterOptions);
|
|
23664
24926
|
beforeSource = beforeUrl;
|
|
23665
24927
|
afterSource = afterUrl;
|
|
24928
|
+
beforeObservation = createRiddleProofObservationReceipt({
|
|
24929
|
+
comparison_role: "before",
|
|
24930
|
+
executor: { kind: "riddle_hosted", runner: beforeResult.runner, job_id: beforeResult.riddle?.job_id },
|
|
24931
|
+
target: beforePreview ? { kind: "preview", url: beforeSource, preview: beforePreview } : { kind: "url", url: beforeSource },
|
|
24932
|
+
source: beforePreview?.source,
|
|
24933
|
+
profile_result: beforeResult,
|
|
24934
|
+
execution: beforeResult.riddle?.execution
|
|
24935
|
+
});
|
|
24936
|
+
afterObservation = createRiddleProofObservationReceipt({
|
|
24937
|
+
comparison_role: "after",
|
|
24938
|
+
executor: { kind: "riddle_hosted", runner: afterResult.runner, job_id: afterResult.riddle?.job_id },
|
|
24939
|
+
target: afterPreview ? { kind: "preview", url: afterSource, preview: afterPreview } : { kind: "url", url: afterSource },
|
|
24940
|
+
source: afterPreview?.source,
|
|
24941
|
+
profile_result: afterResult,
|
|
24942
|
+
execution: afterResult.riddle?.execution
|
|
24943
|
+
});
|
|
23666
24944
|
}
|
|
23667
24945
|
const result = assessRiddleProofChange(contract, {
|
|
23668
24946
|
before_result: beforeResult,
|
|
23669
|
-
after_result: afterResult
|
|
24947
|
+
after_result: afterResult,
|
|
24948
|
+
before_observation: beforeObservation,
|
|
24949
|
+
after_observation: afterObservation,
|
|
24950
|
+
expected_source_revisions: {
|
|
24951
|
+
before: optionString(options, "beforeSourceRevision"),
|
|
24952
|
+
after: optionString(options, "afterSourceRevision")
|
|
24953
|
+
}
|
|
24954
|
+
});
|
|
24955
|
+
const receipt = createRiddleProofChangeReceipt({
|
|
24956
|
+
contract,
|
|
24957
|
+
result,
|
|
24958
|
+
before_observation: beforeObservation,
|
|
24959
|
+
after_observation: afterObservation,
|
|
24960
|
+
profile_name: profile.name
|
|
23670
24961
|
});
|
|
23671
|
-
|
|
24962
|
+
const handoff = createRiddleProofHandoffReceipt(receipt);
|
|
24963
|
+
return {
|
|
24964
|
+
profile,
|
|
24965
|
+
contract,
|
|
24966
|
+
beforeResult,
|
|
24967
|
+
afterResult,
|
|
24968
|
+
result,
|
|
24969
|
+
receipt,
|
|
24970
|
+
handoff,
|
|
24971
|
+
beforeObservation,
|
|
24972
|
+
afterObservation,
|
|
24973
|
+
beforeSource,
|
|
24974
|
+
afterSource
|
|
24975
|
+
};
|
|
23672
24976
|
}
|
|
23673
24977
|
function requestForRun(options) {
|
|
23674
24978
|
const statePath = optionString(options, "statePath");
|
|
@@ -23720,7 +25024,7 @@ async function main() {
|
|
|
23720
25024
|
if (command === "run-profile") {
|
|
23721
25025
|
const profile = profileWithSelectedViewportNamesForCli(normalizeProfileForCli(options), options);
|
|
23722
25026
|
const result = positional[1] === "recover" ? await recoverProfileForCli(profile, options) : positional[1] === "aggregate" ? await aggregateProfileResultsForCli(profile, options) : await runProfileForCli(profile, options);
|
|
23723
|
-
writeProfileOutput(profileOutputDirOption(options), result);
|
|
25027
|
+
writeProfileOutput(profileOutputDirOption(options), result, profileOutputOptionsForCli(options));
|
|
23724
25028
|
const diagnosticLine = profileCliDiagnosticLine(result);
|
|
23725
25029
|
if (diagnosticLine && optionBoolean(options, "quiet") !== true) {
|
|
23726
25030
|
process.stderr.write(`${diagnosticLine}
|
|
@@ -23751,7 +25055,10 @@ async function main() {
|
|
|
23751
25055
|
const explicitResultJsonPath = optionString(options, "resultJson");
|
|
23752
25056
|
const runResponsePath = explicitRunResponsePath || defaultProofDirJsonPath(proofDir, "riddle-run-response.json");
|
|
23753
25057
|
const resultJsonPaths = explicitResultJsonPath ? [explicitResultJsonPath] : [
|
|
25058
|
+
defaultProofDirJsonPath(proofDir, "handoff-receipt.json"),
|
|
23754
25059
|
defaultProofDirJsonPath(proofDir, "result.json"),
|
|
25060
|
+
defaultProofDirJsonPath(proofDir, "change-proof-receipt.json"),
|
|
25061
|
+
defaultProofDirJsonPath(proofDir, "change-proof-result.json"),
|
|
23755
25062
|
defaultProofDirJsonPath(proofDir, "profile-result.json")
|
|
23756
25063
|
];
|
|
23757
25064
|
const runResponse = readJsonFileIfExists(runResponsePath);
|
|
@@ -23870,6 +25177,16 @@ async function main() {
|
|
|
23870
25177
|
};
|
|
23871
25178
|
}
|
|
23872
25179
|
const result = await createRiddleApiClient(clientConfig).deployPreview(buildDir, label, previewFrameworkOption(options));
|
|
25180
|
+
const outputDir = profileOutputDirOption(options);
|
|
25181
|
+
if (outputDir) {
|
|
25182
|
+
(0, import_node_fs6.mkdirSync)(outputDir, { recursive: true });
|
|
25183
|
+
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "preview-deploy-result.json"), `${JSON.stringify(result, null, 2)}
|
|
25184
|
+
`);
|
|
25185
|
+
if (result.receipt) {
|
|
25186
|
+
(0, import_node_fs6.writeFileSync)(import_node_path6.default.join(outputDir, "preview-receipt.json"), `${JSON.stringify(result.receipt, null, 2)}
|
|
25187
|
+
`);
|
|
25188
|
+
}
|
|
25189
|
+
}
|
|
23873
25190
|
for (const warning of result.warnings ?? []) {
|
|
23874
25191
|
process.stderr.write(`Warning: ${warning}
|
|
23875
25192
|
`);
|