@riddledc/riddle-proof 0.8.73 → 0.8.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -0
- package/dist/advanced/index.d.cts +1 -1
- package/dist/advanced/index.d.ts +1 -1
- package/dist/advanced/proof-run-engine.d.cts +1 -1
- package/dist/advanced/proof-run-engine.d.ts +1 -1
- package/dist/change-proof.cjs +193 -0
- package/dist/change-proof.d.cts +74 -0
- package/dist/change-proof.d.ts +74 -0
- package/dist/change-proof.js +11 -0
- package/dist/chunk-6S7DZWVC.js +167 -0
- package/dist/{chunk-ICIJTEHD.js → chunk-AK2EPEVO.js} +215 -9
- package/dist/cli/index.js +4 -3
- package/dist/cli.cjs +369 -5
- package/dist/cli.js +4 -3
- package/dist/index.cjs +168 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +23 -15
- package/dist/{proof-run-engine-DpChFR5H.d.cts → proof-run-engine-Baiv6l3A.d.cts} +3 -3
- package/dist/{proof-run-engine-BqRoA3Do.d.ts → proof-run-engine-MiKZt9oY.d.ts} +3 -3
- package/dist/proof-run-engine.d.cts +1 -1
- package/dist/proof-run-engine.d.ts +1 -1
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ import { createRunResult, createRunState } from "@riddledc/riddle-proof";
|
|
|
49
49
|
import { runRiddleProof } from "@riddledc/riddle-proof/runner";
|
|
50
50
|
import { createCaptureDiagnostic } from "@riddledc/riddle-proof/diagnostics";
|
|
51
51
|
import { toRiddleProofRunParams } from "@riddledc/riddle-proof/openclaw";
|
|
52
|
+
import { assessRiddleProofChange } from "@riddledc/riddle-proof/change-proof";
|
|
52
53
|
```
|
|
53
54
|
|
|
54
55
|
The root export provides generic contracts and helpers. Integration-specific
|
|
@@ -56,6 +57,64 @@ adapters are exposed through subpaths such as
|
|
|
56
57
|
`@riddledc/riddle-proof/openclaw`, so wrappers can reuse the mapping logic
|
|
57
58
|
without depending on another plugin runtime.
|
|
58
59
|
|
|
60
|
+
### Change Proof Contracts
|
|
61
|
+
|
|
62
|
+
Use `assessRiddleProofChange` when one proof needs to compose two profile
|
|
63
|
+
results: a before target and an after target. The profile contract remains the
|
|
64
|
+
portable page-level sensor; the change contract binds that sensor to two
|
|
65
|
+
environments and checks the expected delta.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const result = assessRiddleProofChange({
|
|
69
|
+
version: "riddle-proof.change-contract.v1",
|
|
70
|
+
name: "Hero art appears after change",
|
|
71
|
+
deltas: [{
|
|
72
|
+
type: "check_status_transition",
|
|
73
|
+
check_label: "hero-art-visible",
|
|
74
|
+
before_status: "failed",
|
|
75
|
+
after_status: "passed",
|
|
76
|
+
}],
|
|
77
|
+
}, {
|
|
78
|
+
before_result: productionProfileResult,
|
|
79
|
+
after_result: previewProfileResult,
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The default group semantics are intentionally change-oriented: the before
|
|
84
|
+
group may be `passed` or `product_regression`, while the after group must be
|
|
85
|
+
`passed`. Environment blockers dominate, missing before/after evidence is
|
|
86
|
+
`proof_insufficient`, and a missing or failed required delta cannot become a
|
|
87
|
+
passing change proof.
|
|
88
|
+
|
|
89
|
+
The CLI can collect the two sides against hosted Riddle targets and write one
|
|
90
|
+
change receipt:
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
riddle-proof-loop run-change-proof \
|
|
94
|
+
--profile .riddle-proof/profiles/hero-art.json \
|
|
95
|
+
--change-contract .riddle-proof/change-contracts/hero-art-change.json \
|
|
96
|
+
--before-url https://example.com \
|
|
97
|
+
--after-url https://preview.example.com \
|
|
98
|
+
--output artifacts/riddle-proof/hero-art-change
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
It can also compare saved profile packets without starting new browser jobs:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
riddle-proof-loop run-change-proof \
|
|
105
|
+
--profile .riddle-proof/profiles/hero-art.json \
|
|
106
|
+
--change-contract .riddle-proof/change-contracts/hero-art-change.json \
|
|
107
|
+
--before-result artifacts/before/profile-result.json \
|
|
108
|
+
--after-result artifacts/after/profile-result.json \
|
|
109
|
+
--result-format compact-json
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
When `--output` / `--output-dir` is set, the command writes
|
|
113
|
+
`change-proof-result.json`, `summary.md`, and normalized copies of the
|
|
114
|
+
`before/profile-result.json` and `after/profile-result.json` inputs. A
|
|
115
|
+
non-passing change proof exits nonzero so CI and PR-comment automation can use
|
|
116
|
+
the receipt as a gate.
|
|
117
|
+
|
|
59
118
|
## Durable Loop CLI
|
|
60
119
|
|
|
61
120
|
The package publishes `riddle-proof-loop` as a host-agnostic runner surface for
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { b as runner } from '../runner-4LJ5z0D-.cjs';
|
|
2
2
|
export { l as engineHarness } from '../engine-harness-LBfqbFSe.cjs';
|
|
3
3
|
export { p as proofRunCore } from '../proof-run-core-7Dqm7RKM.cjs';
|
|
4
|
-
export { p as proofRunEngine } from '../proof-run-engine-
|
|
4
|
+
export { p as proofRunEngine } from '../proof-run-engine-Baiv6l3A.cjs';
|
|
5
5
|
import '../types.cjs';
|
|
6
6
|
import '../public-state.cjs';
|
package/dist/advanced/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { b as runner } from '../runner-BdQpOkZD.js';
|
|
2
2
|
export { l as engineHarness } from '../engine-harness-CMACHP6A.js';
|
|
3
3
|
export { p as proofRunCore } from '../proof-run-core-7Dqm7RKM.js';
|
|
4
|
-
export { p as proofRunEngine } from '../proof-run-engine-
|
|
4
|
+
export { p as proofRunEngine } from '../proof-run-engine-MiKZt9oY.js';
|
|
5
5
|
import '../types.js';
|
|
6
6
|
import '../public-state.js';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from '../proof-run-engine-
|
|
1
|
+
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from '../proof-run-engine-Baiv6l3A.cjs';
|
|
2
2
|
import '../proof-run-core-7Dqm7RKM.cjs';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from '../proof-run-engine-
|
|
1
|
+
export { R as RiddleProofEngine, c as createRiddleProofEngine, e as executeWorkflow } from '../proof-run-engine-MiKZt9oY.js';
|
|
2
2
|
import '../proof-run-core-7Dqm7RKM.js';
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/change-proof.ts
|
|
21
|
+
var change_proof_exports = {};
|
|
22
|
+
__export(change_proof_exports, {
|
|
23
|
+
RIDDLE_PROOF_CHANGE_CONTRACT_VERSION: () => RIDDLE_PROOF_CHANGE_CONTRACT_VERSION,
|
|
24
|
+
RIDDLE_PROOF_CHANGE_RESULT_VERSION: () => RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
25
|
+
assessRiddleProofChange: () => assessRiddleProofChange
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(change_proof_exports);
|
|
28
|
+
var RIDDLE_PROOF_CHANGE_CONTRACT_VERSION = "riddle-proof.change-contract.v1";
|
|
29
|
+
var RIDDLE_PROOF_CHANGE_RESULT_VERSION = "riddle-proof.change-result.v1";
|
|
30
|
+
var DEFAULT_BEFORE_STATUSES = ["passed", "product_regression"];
|
|
31
|
+
var DEFAULT_AFTER_STATUSES = ["passed"];
|
|
32
|
+
var DEFAULT_PROFILE_STATUS_TRANSITION_BEFORE = ["product_regression"];
|
|
33
|
+
var DEFAULT_PROFILE_STATUS_TRANSITION_AFTER = ["passed"];
|
|
34
|
+
var DEFAULT_CHECK_STATUS_TRANSITION_BEFORE = ["failed"];
|
|
35
|
+
var DEFAULT_CHECK_STATUS_TRANSITION_AFTER = ["passed"];
|
|
36
|
+
function listValue(value, fallback) {
|
|
37
|
+
if (Array.isArray(value)) return value;
|
|
38
|
+
if (value === void 0) return fallback;
|
|
39
|
+
return [value];
|
|
40
|
+
}
|
|
41
|
+
function includesValue(allowed, observed) {
|
|
42
|
+
return observed !== void 0 && allowed.includes(observed);
|
|
43
|
+
}
|
|
44
|
+
function groupResult(side, contract, result) {
|
|
45
|
+
const fallback = side === "before" ? DEFAULT_BEFORE_STATUSES : DEFAULT_AFTER_STATUSES;
|
|
46
|
+
const requiredStatus = listValue(contract?.required_status, fallback);
|
|
47
|
+
const label = contract?.label || side;
|
|
48
|
+
if (!result) {
|
|
49
|
+
return {
|
|
50
|
+
side,
|
|
51
|
+
label,
|
|
52
|
+
ok: false,
|
|
53
|
+
status: "missing",
|
|
54
|
+
required_status: requiredStatus,
|
|
55
|
+
message: `${label} profile result is missing.`
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const ok = includesValue(requiredStatus, result.status);
|
|
59
|
+
return {
|
|
60
|
+
side,
|
|
61
|
+
label,
|
|
62
|
+
ok,
|
|
63
|
+
profile_name: result.profile_name,
|
|
64
|
+
status: result.status,
|
|
65
|
+
required_status: requiredStatus,
|
|
66
|
+
summary: result.summary,
|
|
67
|
+
message: ok ? void 0 : `${label} profile status ${result.status} did not match required status ${requiredStatus.join(", ")}.`
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function findCheck(result, delta) {
|
|
71
|
+
return result.checks.find((check) => {
|
|
72
|
+
if (delta.check_label && check.label !== delta.check_label) return false;
|
|
73
|
+
if (delta.check_type && check.type !== delta.check_type) return false;
|
|
74
|
+
return Boolean(delta.check_label || delta.check_type);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function profileStatusTransitionResult(delta, before, after) {
|
|
78
|
+
const label = delta.label || "profile-status-transition";
|
|
79
|
+
if (!before || !after) {
|
|
80
|
+
return {
|
|
81
|
+
type: delta.type,
|
|
82
|
+
label,
|
|
83
|
+
status: "proof_insufficient",
|
|
84
|
+
before_observed: before?.status,
|
|
85
|
+
after_observed: after?.status,
|
|
86
|
+
message: `${label} needs both before and after profile results.`
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const beforeAllowed = listValue(delta.before_status, DEFAULT_PROFILE_STATUS_TRANSITION_BEFORE);
|
|
90
|
+
const afterAllowed = listValue(delta.after_status, DEFAULT_PROFILE_STATUS_TRANSITION_AFTER);
|
|
91
|
+
const passed = includesValue(beforeAllowed, before.status) && includesValue(afterAllowed, after.status);
|
|
92
|
+
return {
|
|
93
|
+
type: delta.type,
|
|
94
|
+
label,
|
|
95
|
+
status: passed ? "passed" : "failed",
|
|
96
|
+
before_observed: before.status,
|
|
97
|
+
after_observed: after.status,
|
|
98
|
+
message: passed ? void 0 : `${label} expected before ${beforeAllowed.join(", ")} and after ${afterAllowed.join(", ")}, got ${before.status} -> ${after.status}.`
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function checkStatusTransitionResult(delta, before, after) {
|
|
102
|
+
const label = delta.label || delta.check_label || delta.check_type || "check-status-transition";
|
|
103
|
+
if (!delta.check_label && !delta.check_type) {
|
|
104
|
+
return {
|
|
105
|
+
type: delta.type,
|
|
106
|
+
label,
|
|
107
|
+
status: "configuration_error",
|
|
108
|
+
message: `${label} needs check_label or check_type.`
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (!before || !after) {
|
|
112
|
+
return {
|
|
113
|
+
type: delta.type,
|
|
114
|
+
label,
|
|
115
|
+
status: "proof_insufficient",
|
|
116
|
+
before_observed: before?.status,
|
|
117
|
+
after_observed: after?.status,
|
|
118
|
+
message: `${label} needs both before and after profile results.`
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const beforeCheck = findCheck(before, delta);
|
|
122
|
+
const afterCheck = findCheck(after, delta);
|
|
123
|
+
if (!beforeCheck || !afterCheck) {
|
|
124
|
+
return {
|
|
125
|
+
type: delta.type,
|
|
126
|
+
label,
|
|
127
|
+
status: "proof_insufficient",
|
|
128
|
+
before_observed: beforeCheck?.status,
|
|
129
|
+
after_observed: afterCheck?.status,
|
|
130
|
+
message: `${label} was not present in ${!beforeCheck && !afterCheck ? "before or after" : !beforeCheck ? "before" : "after"} profile checks.`
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const beforeAllowed = listValue(delta.before_status, DEFAULT_CHECK_STATUS_TRANSITION_BEFORE);
|
|
134
|
+
const afterAllowed = listValue(delta.after_status, DEFAULT_CHECK_STATUS_TRANSITION_AFTER);
|
|
135
|
+
const passed = includesValue(beforeAllowed, beforeCheck.status) && includesValue(afterAllowed, afterCheck.status);
|
|
136
|
+
return {
|
|
137
|
+
type: delta.type,
|
|
138
|
+
label,
|
|
139
|
+
status: passed ? "passed" : "failed",
|
|
140
|
+
before_observed: beforeCheck.status,
|
|
141
|
+
after_observed: afterCheck.status,
|
|
142
|
+
message: passed ? void 0 : `${label} expected before ${beforeAllowed.join(", ")} and after ${afterAllowed.join(", ")}, got ${beforeCheck.status} -> ${afterCheck.status}.`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function assessDelta(delta, before, after) {
|
|
146
|
+
if (delta.type === "profile_status_transition") {
|
|
147
|
+
return profileStatusTransitionResult(delta, before, after);
|
|
148
|
+
}
|
|
149
|
+
return checkStatusTransitionResult(delta, before, after);
|
|
150
|
+
}
|
|
151
|
+
function collapsedChangeStatus(groups, deltas) {
|
|
152
|
+
const groupResults = [groups.before, groups.after];
|
|
153
|
+
if (groupResults.some((group) => group.status === "environment_blocked")) return "environment_blocked";
|
|
154
|
+
if (groupResults.some((group) => group.status === "configuration_error")) return "configuration_error";
|
|
155
|
+
if (deltas.some((delta) => delta.status === "configuration_error")) return "configuration_error";
|
|
156
|
+
if (groupResults.some((group) => group.status === "needs_human_review")) return "needs_human_review";
|
|
157
|
+
if (groupResults.some((group) => group.status === "missing" || group.status === "proof_insufficient")) return "proof_insufficient";
|
|
158
|
+
if (!deltas.length || deltas.some((delta) => delta.status === "proof_insufficient")) return "proof_insufficient";
|
|
159
|
+
if (groupResults.some((group) => !group.ok)) return "product_regression";
|
|
160
|
+
if (deltas.some((delta) => delta.status === "failed")) return "product_regression";
|
|
161
|
+
return "passed";
|
|
162
|
+
}
|
|
163
|
+
function changeSummary(name, status, deltas) {
|
|
164
|
+
if (status === "passed") return `${name} passed ${deltas.length} change delta(s).`;
|
|
165
|
+
if (status === "environment_blocked") return `${name} could not compare reliable evidence because an environment was blocked.`;
|
|
166
|
+
if (status === "configuration_error") return `${name} has an invalid change proof contract.`;
|
|
167
|
+
if (status === "needs_human_review") return `${name} needs human review before the change proof can pass.`;
|
|
168
|
+
if (status === "proof_insufficient") return `${name} did not produce enough before/after evidence for a change proof.`;
|
|
169
|
+
return `${name} failed ${deltas.filter((delta) => delta.status === "failed").length} change delta(s).`;
|
|
170
|
+
}
|
|
171
|
+
function assessRiddleProofChange(contract, input) {
|
|
172
|
+
const groups = {
|
|
173
|
+
before: groupResult("before", contract.before, input.before_result),
|
|
174
|
+
after: groupResult("after", contract.after, input.after_result)
|
|
175
|
+
};
|
|
176
|
+
const deltas = (contract.deltas || []).map((delta) => assessDelta(delta, input.before_result, input.after_result));
|
|
177
|
+
const status = collapsedChangeStatus(groups, deltas);
|
|
178
|
+
return {
|
|
179
|
+
version: RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
180
|
+
contract_name: contract.name,
|
|
181
|
+
status,
|
|
182
|
+
groups,
|
|
183
|
+
deltas,
|
|
184
|
+
summary: changeSummary(contract.name, status, deltas),
|
|
185
|
+
metadata: contract.metadata
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
189
|
+
0 && (module.exports = {
|
|
190
|
+
RIDDLE_PROOF_CHANGE_CONTRACT_VERSION,
|
|
191
|
+
RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
192
|
+
assessRiddleProofChange
|
|
193
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { RiddleProofProfileResult, RiddleProofProfileStatus, RiddleProofProfileCheckResult } from './profile.cjs';
|
|
2
|
+
import { JsonValue } from './types.cjs';
|
|
3
|
+
import './public-state.cjs';
|
|
4
|
+
|
|
5
|
+
declare const RIDDLE_PROOF_CHANGE_CONTRACT_VERSION: "riddle-proof.change-contract.v1";
|
|
6
|
+
declare const RIDDLE_PROOF_CHANGE_RESULT_VERSION: "riddle-proof.change-result.v1";
|
|
7
|
+
type RiddleProofChangeSide = "before" | "after";
|
|
8
|
+
type RiddleProofChangeStatus = RiddleProofProfileStatus;
|
|
9
|
+
type RiddleProofChangeDeltaStatus = "passed" | "failed" | "proof_insufficient" | "configuration_error";
|
|
10
|
+
type RiddleProofChangeProfileCheckStatus = RiddleProofProfileCheckResult["status"];
|
|
11
|
+
interface RiddleProofChangeGroupContract {
|
|
12
|
+
label?: string;
|
|
13
|
+
required_status?: RiddleProofProfileStatus | RiddleProofProfileStatus[];
|
|
14
|
+
}
|
|
15
|
+
interface RiddleProofProfileStatusTransitionDelta {
|
|
16
|
+
type: "profile_status_transition";
|
|
17
|
+
label?: string;
|
|
18
|
+
before_status?: RiddleProofProfileStatus | RiddleProofProfileStatus[];
|
|
19
|
+
after_status?: RiddleProofProfileStatus | RiddleProofProfileStatus[];
|
|
20
|
+
}
|
|
21
|
+
interface RiddleProofCheckStatusTransitionDelta {
|
|
22
|
+
type: "check_status_transition";
|
|
23
|
+
label?: string;
|
|
24
|
+
check_label?: string;
|
|
25
|
+
check_type?: string;
|
|
26
|
+
before_status?: RiddleProofChangeProfileCheckStatus | RiddleProofChangeProfileCheckStatus[];
|
|
27
|
+
after_status?: RiddleProofChangeProfileCheckStatus | RiddleProofChangeProfileCheckStatus[];
|
|
28
|
+
}
|
|
29
|
+
type RiddleProofChangeDelta = RiddleProofProfileStatusTransitionDelta | RiddleProofCheckStatusTransitionDelta;
|
|
30
|
+
interface RiddleProofChangeContract {
|
|
31
|
+
version?: typeof RIDDLE_PROOF_CHANGE_CONTRACT_VERSION;
|
|
32
|
+
name: string;
|
|
33
|
+
before?: RiddleProofChangeGroupContract;
|
|
34
|
+
after?: RiddleProofChangeGroupContract;
|
|
35
|
+
deltas: RiddleProofChangeDelta[];
|
|
36
|
+
metadata?: Record<string, JsonValue>;
|
|
37
|
+
}
|
|
38
|
+
interface RiddleProofChangeGroupResult {
|
|
39
|
+
side: RiddleProofChangeSide;
|
|
40
|
+
label: string;
|
|
41
|
+
ok: boolean;
|
|
42
|
+
profile_name?: string;
|
|
43
|
+
status: RiddleProofProfileStatus | "missing";
|
|
44
|
+
required_status: RiddleProofProfileStatus[];
|
|
45
|
+
summary?: string;
|
|
46
|
+
message?: string;
|
|
47
|
+
}
|
|
48
|
+
interface RiddleProofChangeDeltaResult {
|
|
49
|
+
type: RiddleProofChangeDelta["type"];
|
|
50
|
+
label: string;
|
|
51
|
+
status: RiddleProofChangeDeltaStatus;
|
|
52
|
+
before_observed?: JsonValue;
|
|
53
|
+
after_observed?: JsonValue;
|
|
54
|
+
message?: string;
|
|
55
|
+
}
|
|
56
|
+
interface RiddleProofChangeResult {
|
|
57
|
+
version: typeof RIDDLE_PROOF_CHANGE_RESULT_VERSION;
|
|
58
|
+
contract_name: string;
|
|
59
|
+
status: RiddleProofChangeStatus;
|
|
60
|
+
groups: {
|
|
61
|
+
before: RiddleProofChangeGroupResult;
|
|
62
|
+
after: RiddleProofChangeGroupResult;
|
|
63
|
+
};
|
|
64
|
+
deltas: RiddleProofChangeDeltaResult[];
|
|
65
|
+
summary: string;
|
|
66
|
+
metadata?: Record<string, JsonValue>;
|
|
67
|
+
}
|
|
68
|
+
interface AssessRiddleProofChangeInput {
|
|
69
|
+
before_result?: RiddleProofProfileResult;
|
|
70
|
+
after_result?: RiddleProofProfileResult;
|
|
71
|
+
}
|
|
72
|
+
declare function assessRiddleProofChange(contract: RiddleProofChangeContract, input: AssessRiddleProofChangeInput): RiddleProofChangeResult;
|
|
73
|
+
|
|
74
|
+
export { type AssessRiddleProofChangeInput, RIDDLE_PROOF_CHANGE_CONTRACT_VERSION, RIDDLE_PROOF_CHANGE_RESULT_VERSION, type RiddleProofChangeContract, type RiddleProofChangeDelta, type RiddleProofChangeDeltaResult, type RiddleProofChangeDeltaStatus, type RiddleProofChangeGroupContract, type RiddleProofChangeGroupResult, type RiddleProofChangeProfileCheckStatus, type RiddleProofChangeResult, type RiddleProofChangeSide, type RiddleProofChangeStatus, type RiddleProofCheckStatusTransitionDelta, type RiddleProofProfileStatusTransitionDelta, assessRiddleProofChange };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { RiddleProofProfileResult, RiddleProofProfileStatus, RiddleProofProfileCheckResult } from './profile.js';
|
|
2
|
+
import { JsonValue } from './types.js';
|
|
3
|
+
import './public-state.js';
|
|
4
|
+
|
|
5
|
+
declare const RIDDLE_PROOF_CHANGE_CONTRACT_VERSION: "riddle-proof.change-contract.v1";
|
|
6
|
+
declare const RIDDLE_PROOF_CHANGE_RESULT_VERSION: "riddle-proof.change-result.v1";
|
|
7
|
+
type RiddleProofChangeSide = "before" | "after";
|
|
8
|
+
type RiddleProofChangeStatus = RiddleProofProfileStatus;
|
|
9
|
+
type RiddleProofChangeDeltaStatus = "passed" | "failed" | "proof_insufficient" | "configuration_error";
|
|
10
|
+
type RiddleProofChangeProfileCheckStatus = RiddleProofProfileCheckResult["status"];
|
|
11
|
+
interface RiddleProofChangeGroupContract {
|
|
12
|
+
label?: string;
|
|
13
|
+
required_status?: RiddleProofProfileStatus | RiddleProofProfileStatus[];
|
|
14
|
+
}
|
|
15
|
+
interface RiddleProofProfileStatusTransitionDelta {
|
|
16
|
+
type: "profile_status_transition";
|
|
17
|
+
label?: string;
|
|
18
|
+
before_status?: RiddleProofProfileStatus | RiddleProofProfileStatus[];
|
|
19
|
+
after_status?: RiddleProofProfileStatus | RiddleProofProfileStatus[];
|
|
20
|
+
}
|
|
21
|
+
interface RiddleProofCheckStatusTransitionDelta {
|
|
22
|
+
type: "check_status_transition";
|
|
23
|
+
label?: string;
|
|
24
|
+
check_label?: string;
|
|
25
|
+
check_type?: string;
|
|
26
|
+
before_status?: RiddleProofChangeProfileCheckStatus | RiddleProofChangeProfileCheckStatus[];
|
|
27
|
+
after_status?: RiddleProofChangeProfileCheckStatus | RiddleProofChangeProfileCheckStatus[];
|
|
28
|
+
}
|
|
29
|
+
type RiddleProofChangeDelta = RiddleProofProfileStatusTransitionDelta | RiddleProofCheckStatusTransitionDelta;
|
|
30
|
+
interface RiddleProofChangeContract {
|
|
31
|
+
version?: typeof RIDDLE_PROOF_CHANGE_CONTRACT_VERSION;
|
|
32
|
+
name: string;
|
|
33
|
+
before?: RiddleProofChangeGroupContract;
|
|
34
|
+
after?: RiddleProofChangeGroupContract;
|
|
35
|
+
deltas: RiddleProofChangeDelta[];
|
|
36
|
+
metadata?: Record<string, JsonValue>;
|
|
37
|
+
}
|
|
38
|
+
interface RiddleProofChangeGroupResult {
|
|
39
|
+
side: RiddleProofChangeSide;
|
|
40
|
+
label: string;
|
|
41
|
+
ok: boolean;
|
|
42
|
+
profile_name?: string;
|
|
43
|
+
status: RiddleProofProfileStatus | "missing";
|
|
44
|
+
required_status: RiddleProofProfileStatus[];
|
|
45
|
+
summary?: string;
|
|
46
|
+
message?: string;
|
|
47
|
+
}
|
|
48
|
+
interface RiddleProofChangeDeltaResult {
|
|
49
|
+
type: RiddleProofChangeDelta["type"];
|
|
50
|
+
label: string;
|
|
51
|
+
status: RiddleProofChangeDeltaStatus;
|
|
52
|
+
before_observed?: JsonValue;
|
|
53
|
+
after_observed?: JsonValue;
|
|
54
|
+
message?: string;
|
|
55
|
+
}
|
|
56
|
+
interface RiddleProofChangeResult {
|
|
57
|
+
version: typeof RIDDLE_PROOF_CHANGE_RESULT_VERSION;
|
|
58
|
+
contract_name: string;
|
|
59
|
+
status: RiddleProofChangeStatus;
|
|
60
|
+
groups: {
|
|
61
|
+
before: RiddleProofChangeGroupResult;
|
|
62
|
+
after: RiddleProofChangeGroupResult;
|
|
63
|
+
};
|
|
64
|
+
deltas: RiddleProofChangeDeltaResult[];
|
|
65
|
+
summary: string;
|
|
66
|
+
metadata?: Record<string, JsonValue>;
|
|
67
|
+
}
|
|
68
|
+
interface AssessRiddleProofChangeInput {
|
|
69
|
+
before_result?: RiddleProofProfileResult;
|
|
70
|
+
after_result?: RiddleProofProfileResult;
|
|
71
|
+
}
|
|
72
|
+
declare function assessRiddleProofChange(contract: RiddleProofChangeContract, input: AssessRiddleProofChangeInput): RiddleProofChangeResult;
|
|
73
|
+
|
|
74
|
+
export { type AssessRiddleProofChangeInput, RIDDLE_PROOF_CHANGE_CONTRACT_VERSION, RIDDLE_PROOF_CHANGE_RESULT_VERSION, type RiddleProofChangeContract, type RiddleProofChangeDelta, type RiddleProofChangeDeltaResult, type RiddleProofChangeDeltaStatus, type RiddleProofChangeGroupContract, type RiddleProofChangeGroupResult, type RiddleProofChangeProfileCheckStatus, type RiddleProofChangeResult, type RiddleProofChangeSide, type RiddleProofChangeStatus, type RiddleProofCheckStatusTransitionDelta, type RiddleProofProfileStatusTransitionDelta, assessRiddleProofChange };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RIDDLE_PROOF_CHANGE_CONTRACT_VERSION,
|
|
3
|
+
RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
4
|
+
assessRiddleProofChange
|
|
5
|
+
} from "./chunk-6S7DZWVC.js";
|
|
6
|
+
import "./chunk-MLKGABMK.js";
|
|
7
|
+
export {
|
|
8
|
+
RIDDLE_PROOF_CHANGE_CONTRACT_VERSION,
|
|
9
|
+
RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
10
|
+
assessRiddleProofChange
|
|
11
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// src/change-proof.ts
|
|
2
|
+
var RIDDLE_PROOF_CHANGE_CONTRACT_VERSION = "riddle-proof.change-contract.v1";
|
|
3
|
+
var RIDDLE_PROOF_CHANGE_RESULT_VERSION = "riddle-proof.change-result.v1";
|
|
4
|
+
var DEFAULT_BEFORE_STATUSES = ["passed", "product_regression"];
|
|
5
|
+
var DEFAULT_AFTER_STATUSES = ["passed"];
|
|
6
|
+
var DEFAULT_PROFILE_STATUS_TRANSITION_BEFORE = ["product_regression"];
|
|
7
|
+
var DEFAULT_PROFILE_STATUS_TRANSITION_AFTER = ["passed"];
|
|
8
|
+
var DEFAULT_CHECK_STATUS_TRANSITION_BEFORE = ["failed"];
|
|
9
|
+
var DEFAULT_CHECK_STATUS_TRANSITION_AFTER = ["passed"];
|
|
10
|
+
function listValue(value, fallback) {
|
|
11
|
+
if (Array.isArray(value)) return value;
|
|
12
|
+
if (value === void 0) return fallback;
|
|
13
|
+
return [value];
|
|
14
|
+
}
|
|
15
|
+
function includesValue(allowed, observed) {
|
|
16
|
+
return observed !== void 0 && allowed.includes(observed);
|
|
17
|
+
}
|
|
18
|
+
function groupResult(side, contract, result) {
|
|
19
|
+
const fallback = side === "before" ? DEFAULT_BEFORE_STATUSES : DEFAULT_AFTER_STATUSES;
|
|
20
|
+
const requiredStatus = listValue(contract?.required_status, fallback);
|
|
21
|
+
const label = contract?.label || side;
|
|
22
|
+
if (!result) {
|
|
23
|
+
return {
|
|
24
|
+
side,
|
|
25
|
+
label,
|
|
26
|
+
ok: false,
|
|
27
|
+
status: "missing",
|
|
28
|
+
required_status: requiredStatus,
|
|
29
|
+
message: `${label} profile result is missing.`
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const ok = includesValue(requiredStatus, result.status);
|
|
33
|
+
return {
|
|
34
|
+
side,
|
|
35
|
+
label,
|
|
36
|
+
ok,
|
|
37
|
+
profile_name: result.profile_name,
|
|
38
|
+
status: result.status,
|
|
39
|
+
required_status: requiredStatus,
|
|
40
|
+
summary: result.summary,
|
|
41
|
+
message: ok ? void 0 : `${label} profile status ${result.status} did not match required status ${requiredStatus.join(", ")}.`
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function findCheck(result, delta) {
|
|
45
|
+
return result.checks.find((check) => {
|
|
46
|
+
if (delta.check_label && check.label !== delta.check_label) return false;
|
|
47
|
+
if (delta.check_type && check.type !== delta.check_type) return false;
|
|
48
|
+
return Boolean(delta.check_label || delta.check_type);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function profileStatusTransitionResult(delta, before, after) {
|
|
52
|
+
const label = delta.label || "profile-status-transition";
|
|
53
|
+
if (!before || !after) {
|
|
54
|
+
return {
|
|
55
|
+
type: delta.type,
|
|
56
|
+
label,
|
|
57
|
+
status: "proof_insufficient",
|
|
58
|
+
before_observed: before?.status,
|
|
59
|
+
after_observed: after?.status,
|
|
60
|
+
message: `${label} needs both before and after profile results.`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const beforeAllowed = listValue(delta.before_status, DEFAULT_PROFILE_STATUS_TRANSITION_BEFORE);
|
|
64
|
+
const afterAllowed = listValue(delta.after_status, DEFAULT_PROFILE_STATUS_TRANSITION_AFTER);
|
|
65
|
+
const passed = includesValue(beforeAllowed, before.status) && includesValue(afterAllowed, after.status);
|
|
66
|
+
return {
|
|
67
|
+
type: delta.type,
|
|
68
|
+
label,
|
|
69
|
+
status: passed ? "passed" : "failed",
|
|
70
|
+
before_observed: before.status,
|
|
71
|
+
after_observed: after.status,
|
|
72
|
+
message: passed ? void 0 : `${label} expected before ${beforeAllowed.join(", ")} and after ${afterAllowed.join(", ")}, got ${before.status} -> ${after.status}.`
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function checkStatusTransitionResult(delta, before, after) {
|
|
76
|
+
const label = delta.label || delta.check_label || delta.check_type || "check-status-transition";
|
|
77
|
+
if (!delta.check_label && !delta.check_type) {
|
|
78
|
+
return {
|
|
79
|
+
type: delta.type,
|
|
80
|
+
label,
|
|
81
|
+
status: "configuration_error",
|
|
82
|
+
message: `${label} needs check_label or check_type.`
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (!before || !after) {
|
|
86
|
+
return {
|
|
87
|
+
type: delta.type,
|
|
88
|
+
label,
|
|
89
|
+
status: "proof_insufficient",
|
|
90
|
+
before_observed: before?.status,
|
|
91
|
+
after_observed: after?.status,
|
|
92
|
+
message: `${label} needs both before and after profile results.`
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const beforeCheck = findCheck(before, delta);
|
|
96
|
+
const afterCheck = findCheck(after, delta);
|
|
97
|
+
if (!beforeCheck || !afterCheck) {
|
|
98
|
+
return {
|
|
99
|
+
type: delta.type,
|
|
100
|
+
label,
|
|
101
|
+
status: "proof_insufficient",
|
|
102
|
+
before_observed: beforeCheck?.status,
|
|
103
|
+
after_observed: afterCheck?.status,
|
|
104
|
+
message: `${label} was not present in ${!beforeCheck && !afterCheck ? "before or after" : !beforeCheck ? "before" : "after"} profile checks.`
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const beforeAllowed = listValue(delta.before_status, DEFAULT_CHECK_STATUS_TRANSITION_BEFORE);
|
|
108
|
+
const afterAllowed = listValue(delta.after_status, DEFAULT_CHECK_STATUS_TRANSITION_AFTER);
|
|
109
|
+
const passed = includesValue(beforeAllowed, beforeCheck.status) && includesValue(afterAllowed, afterCheck.status);
|
|
110
|
+
return {
|
|
111
|
+
type: delta.type,
|
|
112
|
+
label,
|
|
113
|
+
status: passed ? "passed" : "failed",
|
|
114
|
+
before_observed: beforeCheck.status,
|
|
115
|
+
after_observed: afterCheck.status,
|
|
116
|
+
message: passed ? void 0 : `${label} expected before ${beforeAllowed.join(", ")} and after ${afterAllowed.join(", ")}, got ${beforeCheck.status} -> ${afterCheck.status}.`
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function assessDelta(delta, before, after) {
|
|
120
|
+
if (delta.type === "profile_status_transition") {
|
|
121
|
+
return profileStatusTransitionResult(delta, before, after);
|
|
122
|
+
}
|
|
123
|
+
return checkStatusTransitionResult(delta, before, after);
|
|
124
|
+
}
|
|
125
|
+
function collapsedChangeStatus(groups, deltas) {
|
|
126
|
+
const groupResults = [groups.before, groups.after];
|
|
127
|
+
if (groupResults.some((group) => group.status === "environment_blocked")) return "environment_blocked";
|
|
128
|
+
if (groupResults.some((group) => group.status === "configuration_error")) return "configuration_error";
|
|
129
|
+
if (deltas.some((delta) => delta.status === "configuration_error")) return "configuration_error";
|
|
130
|
+
if (groupResults.some((group) => group.status === "needs_human_review")) return "needs_human_review";
|
|
131
|
+
if (groupResults.some((group) => group.status === "missing" || group.status === "proof_insufficient")) return "proof_insufficient";
|
|
132
|
+
if (!deltas.length || deltas.some((delta) => delta.status === "proof_insufficient")) return "proof_insufficient";
|
|
133
|
+
if (groupResults.some((group) => !group.ok)) return "product_regression";
|
|
134
|
+
if (deltas.some((delta) => delta.status === "failed")) return "product_regression";
|
|
135
|
+
return "passed";
|
|
136
|
+
}
|
|
137
|
+
function changeSummary(name, status, deltas) {
|
|
138
|
+
if (status === "passed") return `${name} passed ${deltas.length} change delta(s).`;
|
|
139
|
+
if (status === "environment_blocked") return `${name} could not compare reliable evidence because an environment was blocked.`;
|
|
140
|
+
if (status === "configuration_error") return `${name} has an invalid change proof contract.`;
|
|
141
|
+
if (status === "needs_human_review") return `${name} needs human review before the change proof can pass.`;
|
|
142
|
+
if (status === "proof_insufficient") return `${name} did not produce enough before/after evidence for a change proof.`;
|
|
143
|
+
return `${name} failed ${deltas.filter((delta) => delta.status === "failed").length} change delta(s).`;
|
|
144
|
+
}
|
|
145
|
+
function assessRiddleProofChange(contract, input) {
|
|
146
|
+
const groups = {
|
|
147
|
+
before: groupResult("before", contract.before, input.before_result),
|
|
148
|
+
after: groupResult("after", contract.after, input.after_result)
|
|
149
|
+
};
|
|
150
|
+
const deltas = (contract.deltas || []).map((delta) => assessDelta(delta, input.before_result, input.after_result));
|
|
151
|
+
const status = collapsedChangeStatus(groups, deltas);
|
|
152
|
+
return {
|
|
153
|
+
version: RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
154
|
+
contract_name: contract.name,
|
|
155
|
+
status,
|
|
156
|
+
groups,
|
|
157
|
+
deltas,
|
|
158
|
+
summary: changeSummary(contract.name, status, deltas),
|
|
159
|
+
metadata: contract.metadata
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export {
|
|
164
|
+
RIDDLE_PROOF_CHANGE_CONTRACT_VERSION,
|
|
165
|
+
RIDDLE_PROOF_CHANGE_RESULT_VERSION,
|
|
166
|
+
assessRiddleProofChange
|
|
167
|
+
};
|