@riddledc/riddle-proof 0.8.79 → 0.8.81
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 +48 -0
- package/dist/change-proof.js +2 -2
- package/dist/{chunk-FCSJZBC5.js → chunk-2K3DIK7A.js} +346 -5
- package/dist/{chunk-7N6X54WG.js → chunk-7OAETQU3.js} +1 -1
- package/dist/{chunk-RQPCKRKT.js → chunk-FW7CKARF.js} +1 -1
- package/dist/{chunk-6VFS2JFR.js → chunk-LR4UYJDY.js} +1 -1
- package/dist/{chunk-NEXWITV4.js → chunk-QYZ3GKCP.js} +4 -4
- package/dist/cli/index.js +5 -5
- package/dist/cli.cjs +343 -5
- package/dist/cli.js +5 -5
- package/dist/index.cjs +349 -5
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +10 -4
- package/dist/pr-comment.js +3 -3
- package/dist/profile/index.cjs +349 -5
- package/dist/profile/index.d.cts +1 -1
- package/dist/profile/index.d.ts +1 -1
- package/dist/profile/index.js +7 -1
- package/dist/profile-suggestions.js +2 -2
- package/dist/profile.cjs +349 -5
- package/dist/profile.d.cts +40 -3
- package/dist/profile.d.ts +40 -3
- package/dist/profile.js +7 -1
- package/examples/profiles/ordered-trace.json +58 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -309,6 +309,15 @@ or as a stronger proof base before a change loop.
|
|
|
309
309
|
}
|
|
310
310
|
```
|
|
311
311
|
|
|
312
|
+
Viewport entries may set `hasTouch` and `isMobile` when the behavior under
|
|
313
|
+
test depends on a touch-capable browser context rather than dimensions alone.
|
|
314
|
+
The snake-case aliases `has_touch` and `is_mobile` are accepted at profile
|
|
315
|
+
input boundaries. For example:
|
|
316
|
+
|
|
317
|
+
```json
|
|
318
|
+
{ "name": "ipad", "width": 820, "height": 1180, "hasTouch": true, "isMobile": true }
|
|
319
|
+
```
|
|
320
|
+
|
|
312
321
|
For early proof authoring, `profile-suggest` can turn a route plus changed file,
|
|
313
322
|
selector, and changed-text hints into a conservative draft profile:
|
|
314
323
|
|
|
@@ -499,6 +508,7 @@ The package includes generic starter profiles:
|
|
|
499
508
|
- `examples/profiles/handled-recovery-action-malformed-success.json` for action recovery profiles where the request succeeds at HTTP level but returns an unusable body.
|
|
500
509
|
- `examples/profiles/terminal-result-partial-evidence.json` for API-console terminal error or timeout receipts that preserve partial screenshot, console, and HAR evidence.
|
|
501
510
|
- `examples/profiles/gameplay-window-call-until.json` for gameplay profiles that wait on a runtime state contract instead of a fixed sleep.
|
|
511
|
+
- `examples/profiles/ordered-trace.json` for interaction profiles that require named runtime events to occur in strict temporal order.
|
|
502
512
|
- `examples/profiles/spa-route-exit-state-hygiene.json` for SPA routes that must clean up short-lived proof helpers, receipts, timers, input state, or touch state after visible UI navigation exits the route.
|
|
503
513
|
|
|
504
514
|
Copy one of those shapes into a repository profile directory and replace the
|
|
@@ -808,6 +818,44 @@ both first and last per-viewport receipts before filling remaining space, so
|
|
|
808
818
|
late lifecycle phases such as terminal or restart remain visible in compact
|
|
809
819
|
summaries.
|
|
810
820
|
|
|
821
|
+
Use `ordered_trace` when one point-in-time assertion cannot establish the
|
|
822
|
+
behavior, such as input -> response -> motion -> release -> idle. A labeled
|
|
823
|
+
`window_eval`, `window_call`, or `window_call_until` setup action must return a
|
|
824
|
+
JSON object containing the trace. The check declares predicates for each named
|
|
825
|
+
event and records the earliest strictly increasing witness indices plus compact
|
|
826
|
+
observed values:
|
|
827
|
+
|
|
828
|
+
```json
|
|
829
|
+
{
|
|
830
|
+
"type": "ordered_trace",
|
|
831
|
+
"label": "input to release",
|
|
832
|
+
"setup_action_label": "capture interaction trace",
|
|
833
|
+
"trace_path": "trace",
|
|
834
|
+
"events": [
|
|
835
|
+
{
|
|
836
|
+
"label": "input",
|
|
837
|
+
"predicates": [{ "path": "target", "op": "abs_gte", "value": 0.8 }]
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
"label": "response",
|
|
841
|
+
"predicates": [{ "path": "applied", "op": "abs_gte", "value": 0.1 }]
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
"label": "released",
|
|
845
|
+
"predicates": [{ "path": "active", "op": "equals", "value": false }]
|
|
846
|
+
}
|
|
847
|
+
]
|
|
848
|
+
}
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
Supported predicate operators are `exists`, `equals`, `not_equals`, `truthy`,
|
|
852
|
+
`falsy`, `gt`, `gte`, `lt`, `lte`, `abs_gt`, `abs_gte`, `abs_lt`, and
|
|
853
|
+
`abs_lte`. Missing or empty traces and fields that never appear are
|
|
854
|
+
`proof_insufficient`; a complete trace that does not contain the sequence is a
|
|
855
|
+
`failed` product check. One sample cannot witness two successive events. This
|
|
856
|
+
keeps missing instrumentation distinct from a measured regression and lets an
|
|
857
|
+
ordinary Change Proof `check_status_transition` require `failed -> passed`.
|
|
858
|
+
|
|
811
859
|
`target.timeout_sec` is optional. Use it for known-heavy profile targets so the
|
|
812
860
|
profile carries its own hosted Riddle worker budget; an explicit CLI `--timeout`
|
|
813
861
|
still overrides the profile value for one-off runs.
|
package/dist/change-proof.js
CHANGED
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
parseRiddleProofHandoffReceipt,
|
|
13
13
|
riddleProofChangeReceiptHtml,
|
|
14
14
|
riddleProofChangeReceiptMarkdown
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-LR4UYJDY.js";
|
|
16
16
|
import "./chunk-MEVVL4TI.js";
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-2K3DIK7A.js";
|
|
18
18
|
import "./chunk-MLKGABMK.js";
|
|
19
19
|
export {
|
|
20
20
|
RIDDLE_PROOF_CHANGE_CONTRACT_VERSION,
|
|
@@ -26,6 +26,7 @@ var RIDDLE_PROOF_PROFILE_CHECK_TYPES = [
|
|
|
26
26
|
"selector_text_visible",
|
|
27
27
|
"selector_text_absent",
|
|
28
28
|
"selector_text_order",
|
|
29
|
+
"ordered_trace",
|
|
29
30
|
"observe_within",
|
|
30
31
|
"frame_text_visible",
|
|
31
32
|
"frame_url_equals",
|
|
@@ -89,6 +90,21 @@ var RIDDLE_PROOF_PROFILE_NETWORK_ABORT_ERROR_CODES = [
|
|
|
89
90
|
"timedout",
|
|
90
91
|
"failed"
|
|
91
92
|
];
|
|
93
|
+
var RIDDLE_PROOF_ORDERED_TRACE_OPERATORS = [
|
|
94
|
+
"exists",
|
|
95
|
+
"equals",
|
|
96
|
+
"not_equals",
|
|
97
|
+
"truthy",
|
|
98
|
+
"falsy",
|
|
99
|
+
"gt",
|
|
100
|
+
"gte",
|
|
101
|
+
"lt",
|
|
102
|
+
"lte",
|
|
103
|
+
"abs_gt",
|
|
104
|
+
"abs_gte",
|
|
105
|
+
"abs_lt",
|
|
106
|
+
"abs_lte"
|
|
107
|
+
];
|
|
92
108
|
function uniqueNonEmptyStrings(values) {
|
|
93
109
|
const seen = /* @__PURE__ */ new Set();
|
|
94
110
|
const result = [];
|
|
@@ -380,6 +396,185 @@ function resolveJsonPath(root, path) {
|
|
|
380
396
|
}
|
|
381
397
|
return { exists: true, value: current };
|
|
382
398
|
}
|
|
399
|
+
function assessRiddleProofOrderedTrace(trace, events) {
|
|
400
|
+
const insufficient = (reason, traceLength = Array.isArray(trace) ? trace.length : 0, witnesses2 = [], missingEvent, missingPaths) => ({
|
|
401
|
+
version: "riddle-proof.ordered-trace-assessment.v1",
|
|
402
|
+
status: "proof_insufficient",
|
|
403
|
+
trace_length: traceLength,
|
|
404
|
+
witnesses: witnesses2,
|
|
405
|
+
missing_event: missingEvent,
|
|
406
|
+
missing_paths: missingPaths,
|
|
407
|
+
reason
|
|
408
|
+
});
|
|
409
|
+
const parsePath = (path) => {
|
|
410
|
+
const segments = [];
|
|
411
|
+
let token = "";
|
|
412
|
+
const pushToken = () => {
|
|
413
|
+
const value = token.trim();
|
|
414
|
+
if (value) segments.push(value);
|
|
415
|
+
token = "";
|
|
416
|
+
};
|
|
417
|
+
for (let index = 0; index < path.length; index += 1) {
|
|
418
|
+
const char = path[index];
|
|
419
|
+
if (char === ".") {
|
|
420
|
+
pushToken();
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
if (char !== "[") {
|
|
424
|
+
token += char;
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
pushToken();
|
|
428
|
+
const closeIndex = path.indexOf("]", index + 1);
|
|
429
|
+
if (closeIndex === -1) throw new Error(`unterminated bracket at ${index}`);
|
|
430
|
+
const bracket = path.slice(index + 1, closeIndex).trim();
|
|
431
|
+
if (!bracket) throw new Error(`empty bracket at ${index}`);
|
|
432
|
+
if (/^\d+$/.test(bracket)) {
|
|
433
|
+
segments.push(Number(bracket));
|
|
434
|
+
} else {
|
|
435
|
+
segments.push(bracket.replace(/^['"]|['"]$/g, ""));
|
|
436
|
+
}
|
|
437
|
+
index = closeIndex;
|
|
438
|
+
}
|
|
439
|
+
pushToken();
|
|
440
|
+
return segments;
|
|
441
|
+
};
|
|
442
|
+
const resolve = (root, path) => {
|
|
443
|
+
let segments;
|
|
444
|
+
try {
|
|
445
|
+
segments = parsePath(path);
|
|
446
|
+
} catch {
|
|
447
|
+
return { exists: false };
|
|
448
|
+
}
|
|
449
|
+
let current = root;
|
|
450
|
+
for (const segment of segments) {
|
|
451
|
+
if (Array.isArray(current)) {
|
|
452
|
+
const index = typeof segment === "number" ? segment : /^\d+$/.test(segment) ? Number(segment) : -1;
|
|
453
|
+
if (index < 0 || index >= current.length) return { exists: false };
|
|
454
|
+
current = current[index];
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (typeof segment !== "string" || current === null || typeof current !== "object") return { exists: false };
|
|
458
|
+
if (!Object.hasOwn(current, segment)) return { exists: false };
|
|
459
|
+
current = current[segment];
|
|
460
|
+
}
|
|
461
|
+
return { exists: true, value: current };
|
|
462
|
+
};
|
|
463
|
+
const valuesEqual = (left, right) => {
|
|
464
|
+
if (Object.is(left, right)) return true;
|
|
465
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
466
|
+
return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => valuesEqual(value, right[index]));
|
|
467
|
+
}
|
|
468
|
+
if (left === null || right === null || typeof left !== "object" || typeof right !== "object") return false;
|
|
469
|
+
const leftRecord = left;
|
|
470
|
+
const rightRecord = right;
|
|
471
|
+
const leftKeys = Object.keys(leftRecord).sort();
|
|
472
|
+
const rightKeys = Object.keys(rightRecord).sort();
|
|
473
|
+
return leftKeys.length === rightKeys.length && leftKeys.every((key, index) => key === rightKeys[index] && valuesEqual(leftRecord[key], rightRecord[key]));
|
|
474
|
+
};
|
|
475
|
+
const numericOperator = (op) => [
|
|
476
|
+
"gt",
|
|
477
|
+
"gte",
|
|
478
|
+
"lt",
|
|
479
|
+
"lte",
|
|
480
|
+
"abs_gt",
|
|
481
|
+
"abs_gte",
|
|
482
|
+
"abs_lt",
|
|
483
|
+
"abs_lte"
|
|
484
|
+
].includes(op);
|
|
485
|
+
const matches = (value, predicate) => {
|
|
486
|
+
if (predicate.op === "exists") return true;
|
|
487
|
+
if (predicate.op === "equals") return valuesEqual(value, predicate.value);
|
|
488
|
+
if (predicate.op === "not_equals") return !valuesEqual(value, predicate.value);
|
|
489
|
+
if (predicate.op === "truthy") return Boolean(value);
|
|
490
|
+
if (predicate.op === "falsy") return !value;
|
|
491
|
+
const observed = typeof value === "number" ? value : Number.NaN;
|
|
492
|
+
const expected = typeof predicate.value === "number" ? predicate.value : Number.NaN;
|
|
493
|
+
if (!Number.isFinite(observed) || !Number.isFinite(expected)) return false;
|
|
494
|
+
const candidate = predicate.op.startsWith("abs_") ? Math.abs(observed) : observed;
|
|
495
|
+
if (predicate.op === "gt" || predicate.op === "abs_gt") return candidate > expected;
|
|
496
|
+
if (predicate.op === "gte" || predicate.op === "abs_gte") return candidate >= expected;
|
|
497
|
+
if (predicate.op === "lt" || predicate.op === "abs_lt") return candidate < expected;
|
|
498
|
+
return candidate <= expected;
|
|
499
|
+
};
|
|
500
|
+
if (!Array.isArray(trace) || trace.length === 0) return insufficient("trace_missing_or_empty");
|
|
501
|
+
if (!Array.isArray(events) || events.length === 0) return insufficient("events_missing", trace.length);
|
|
502
|
+
for (const event of events) {
|
|
503
|
+
const missingPaths = event.predicates.filter((predicate) => !trace.some((sample) => {
|
|
504
|
+
const resolved = resolve(sample, predicate.path);
|
|
505
|
+
return resolved.exists && (!numericOperator(predicate.op) || typeof resolved.value === "number" && Number.isFinite(resolved.value));
|
|
506
|
+
})).map((predicate) => predicate.path);
|
|
507
|
+
if (missingPaths.length) {
|
|
508
|
+
return insufficient("required_trace_field_missing", trace.length, [], event.label, Array.from(new Set(missingPaths)));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const witnesses = [];
|
|
512
|
+
let cursor = 0;
|
|
513
|
+
for (const event of events) {
|
|
514
|
+
let witnessIndex = -1;
|
|
515
|
+
for (let index = cursor; index < trace.length; index += 1) {
|
|
516
|
+
if (event.predicates.every((predicate) => {
|
|
517
|
+
const resolved = resolve(trace[index], predicate.path);
|
|
518
|
+
return resolved.exists && matches(resolved.value, predicate);
|
|
519
|
+
})) {
|
|
520
|
+
witnessIndex = index;
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (witnessIndex < 0) {
|
|
525
|
+
return {
|
|
526
|
+
version: "riddle-proof.ordered-trace-assessment.v1",
|
|
527
|
+
status: "failed",
|
|
528
|
+
trace_length: trace.length,
|
|
529
|
+
witnesses,
|
|
530
|
+
missing_event: event.label,
|
|
531
|
+
reason: "ordered_event_not_observed"
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
witnesses.push({
|
|
535
|
+
label: event.label,
|
|
536
|
+
index: witnessIndex,
|
|
537
|
+
observations: event.predicates.map((predicate) => {
|
|
538
|
+
const observed = resolve(trace[witnessIndex], predicate.path).value;
|
|
539
|
+
return {
|
|
540
|
+
path: predicate.path,
|
|
541
|
+
op: predicate.op,
|
|
542
|
+
expected: predicate.value,
|
|
543
|
+
observed
|
|
544
|
+
};
|
|
545
|
+
})
|
|
546
|
+
});
|
|
547
|
+
cursor = witnessIndex + 1;
|
|
548
|
+
}
|
|
549
|
+
return {
|
|
550
|
+
version: "riddle-proof.ordered-trace-assessment.v1",
|
|
551
|
+
status: "passed",
|
|
552
|
+
trace_length: trace.length,
|
|
553
|
+
witnesses
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
function assessRiddleProofOrderedTraceSetupResults(results, setupActionLabel, tracePath, events) {
|
|
557
|
+
const insufficient = (reason) => ({
|
|
558
|
+
version: "riddle-proof.ordered-trace-assessment.v1",
|
|
559
|
+
status: "proof_insufficient",
|
|
560
|
+
trace_length: 0,
|
|
561
|
+
witnesses: [],
|
|
562
|
+
reason
|
|
563
|
+
});
|
|
564
|
+
if (!Array.isArray(results)) return insufficient("setup_results_missing");
|
|
565
|
+
const source = results.find((item) => item && typeof item === "object" && !Array.isArray(item) && item.label === setupActionLabel);
|
|
566
|
+
if (!source) return insufficient("setup_action_result_missing");
|
|
567
|
+
if (!Object.hasOwn(source, "returned")) return insufficient("setup_action_return_missing");
|
|
568
|
+
const segments = tracePath.split(".").map((segment) => segment.trim()).filter(Boolean);
|
|
569
|
+
let trace = source.returned;
|
|
570
|
+
for (const segment of segments) {
|
|
571
|
+
if (trace === null || typeof trace !== "object" || Array.isArray(trace) || !Object.hasOwn(trace, segment)) {
|
|
572
|
+
return insufficient("trace_path_missing");
|
|
573
|
+
}
|
|
574
|
+
trace = trace[segment];
|
|
575
|
+
}
|
|
576
|
+
return assessRiddleProofOrderedTrace(trace, events);
|
|
577
|
+
}
|
|
383
578
|
function evaluateHttpStatusBodyJsonAssertion(root, assertion) {
|
|
384
579
|
const resolved = resolveJsonPath(root, assertion.path);
|
|
385
580
|
const errors = [];
|
|
@@ -486,6 +681,7 @@ function profileSetupWindowCallReceipts(results) {
|
|
|
486
681
|
return results.filter((result) => profileSetupResultAction(result) === "window_call").map((result) => {
|
|
487
682
|
const receipt = {
|
|
488
683
|
ordinal: result.ordinal ?? null,
|
|
684
|
+
label: result.label ?? null,
|
|
489
685
|
ok: result.ok !== false,
|
|
490
686
|
path: result.path ?? null,
|
|
491
687
|
return_captured: result.return_captured ?? null,
|
|
@@ -504,6 +700,7 @@ function profileSetupWindowEvalReceipts(results) {
|
|
|
504
700
|
return results.filter((result) => profileSetupResultAction(result) === "window_eval").map((result) => {
|
|
505
701
|
const receipt = {
|
|
506
702
|
ordinal: result.ordinal ?? null,
|
|
703
|
+
label: result.label ?? null,
|
|
507
704
|
ok: result.ok !== false,
|
|
508
705
|
script_length: result.script_length ?? null,
|
|
509
706
|
return_captured: result.return_captured ?? null,
|
|
@@ -947,10 +1144,14 @@ function normalizeViewport(input, index) {
|
|
|
947
1144
|
if (!width || !height || width < 100 || height < 100) {
|
|
948
1145
|
throw new Error(`target.viewports[${index}] requires numeric width and height >= 100.`);
|
|
949
1146
|
}
|
|
1147
|
+
const hasTouch = booleanValue(valueFromOwn(input, "hasTouch", "has_touch"));
|
|
1148
|
+
const isMobile = booleanValue(valueFromOwn(input, "isMobile", "is_mobile"));
|
|
950
1149
|
return {
|
|
951
1150
|
name: normalizeName(input.name || input.label, `viewport-${index + 1}`),
|
|
952
1151
|
width: Math.round(width),
|
|
953
|
-
height: Math.round(height)
|
|
1152
|
+
height: Math.round(height),
|
|
1153
|
+
...hasTouch === void 0 ? {} : { hasTouch },
|
|
1154
|
+
...isMobile === void 0 ? {} : { isMobile }
|
|
954
1155
|
};
|
|
955
1156
|
}
|
|
956
1157
|
function normalizeViewports(value) {
|
|
@@ -1776,6 +1977,51 @@ function dialogCountFieldForCheckType(type) {
|
|
|
1776
1977
|
if (type === "dialog_dismiss_count_equals") return "dialog_dismiss_count";
|
|
1777
1978
|
return "dialog_count";
|
|
1778
1979
|
}
|
|
1980
|
+
function normalizeOrderedTraceEvents(value, label) {
|
|
1981
|
+
if (value === void 0) return void 0;
|
|
1982
|
+
if (!Array.isArray(value) || !value.length) throw new Error(`${label} must be a non-empty array.`);
|
|
1983
|
+
const seenLabels = /* @__PURE__ */ new Set();
|
|
1984
|
+
return value.map((item, eventIndex) => {
|
|
1985
|
+
const eventLabel = `${label}[${eventIndex}]`;
|
|
1986
|
+
if (!isRecord(item)) throw new Error(`${eventLabel} must be an object.`);
|
|
1987
|
+
const name = stringFromOwn(item, "label", "name", "event");
|
|
1988
|
+
if (!name) throw new Error(`${eventLabel}.label is required.`);
|
|
1989
|
+
if (seenLabels.has(name)) throw new Error(`${eventLabel}.label must be unique.`);
|
|
1990
|
+
seenLabels.add(name);
|
|
1991
|
+
const predicatesInput = item.predicates ?? item.all ?? item.where;
|
|
1992
|
+
if (!Array.isArray(predicatesInput) || !predicatesInput.length) {
|
|
1993
|
+
throw new Error(`${eventLabel}.predicates must be a non-empty array.`);
|
|
1994
|
+
}
|
|
1995
|
+
const predicates = predicatesInput.map((predicateInput, predicateIndex) => {
|
|
1996
|
+
const predicateLabel = `${eventLabel}.predicates[${predicateIndex}]`;
|
|
1997
|
+
if (!isRecord(predicateInput)) throw new Error(`${predicateLabel} must be an object.`);
|
|
1998
|
+
const path = stringFromOwn(predicateInput, "path", "field", "key");
|
|
1999
|
+
if (!path) throw new Error(`${predicateLabel}.path is required.`);
|
|
2000
|
+
const op = stringFromOwn(predicateInput, "op", "operator");
|
|
2001
|
+
if (!op || !RIDDLE_PROOF_ORDERED_TRACE_OPERATORS.includes(op)) {
|
|
2002
|
+
throw new Error(`${predicateLabel}.op must be one of ${RIDDLE_PROOF_ORDERED_TRACE_OPERATORS.join(", ")}.`);
|
|
2003
|
+
}
|
|
2004
|
+
const requiresValue = !["exists", "truthy", "falsy"].includes(op);
|
|
2005
|
+
const hasValue = hasOwn(predicateInput, "value") || hasOwn(predicateInput, "expected");
|
|
2006
|
+
if (requiresValue && !hasValue) throw new Error(`${predicateLabel}.value is required for ${op}.`);
|
|
2007
|
+
const value2 = hasOwn(predicateInput, "value") ? predicateInput.value : predicateInput.expected;
|
|
2008
|
+
if (["gt", "gte", "lt", "lte", "abs_gt", "abs_gte", "abs_lt", "abs_lte"].includes(op)) {
|
|
2009
|
+
if (typeof value2 !== "number" || !Number.isFinite(value2)) {
|
|
2010
|
+
throw new Error(`${predicateLabel}.value must be a finite number for ${op}.`);
|
|
2011
|
+
}
|
|
2012
|
+
if (op.startsWith("abs_") && value2 < 0) {
|
|
2013
|
+
throw new Error(`${predicateLabel}.value must be non-negative for ${op}.`);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
return {
|
|
2017
|
+
path,
|
|
2018
|
+
op,
|
|
2019
|
+
value: requiresValue ? toJsonValue(value2) : void 0
|
|
2020
|
+
};
|
|
2021
|
+
});
|
|
2022
|
+
return { label: name, predicates };
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
1779
2025
|
function normalizeCheck(input, index) {
|
|
1780
2026
|
if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
|
|
1781
2027
|
const type = stringValue(input.type);
|
|
@@ -1826,6 +2072,17 @@ function normalizeCheck(input, index) {
|
|
|
1826
2072
|
if (!stringValue(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
|
|
1827
2073
|
if (!expectedTexts?.length) throw new Error(`checks[${index}] selector_text_order requires expected_texts.`);
|
|
1828
2074
|
}
|
|
2075
|
+
const setupActionLabel = stringFromOwn(input, "setup_action_label", "setupActionLabel", "source_action_label", "sourceActionLabel");
|
|
2076
|
+
const tracePath = stringFromOwn(input, "trace_path", "tracePath", "path");
|
|
2077
|
+
const orderedTraceEvents = normalizeOrderedTraceEvents(
|
|
2078
|
+
input.events ?? input.sequence ?? input.ordered_events ?? input.orderedEvents,
|
|
2079
|
+
`checks[${index}].events`
|
|
2080
|
+
);
|
|
2081
|
+
if (type === "ordered_trace") {
|
|
2082
|
+
if (!setupActionLabel) throw new Error(`checks[${index}] ordered_trace requires setup_action_label.`);
|
|
2083
|
+
if (!tracePath) throw new Error(`checks[${index}] ordered_trace requires trace_path.`);
|
|
2084
|
+
if (!orderedTraceEvents?.length) throw new Error(`checks[${index}] ordered_trace requires events.`);
|
|
2085
|
+
}
|
|
1829
2086
|
const expectedRoutes = normalizeRouteInventoryRoutes(input.expected_routes ?? input.expectedRoutes, index);
|
|
1830
2087
|
if (type === "route_inventory" && !expectedRoutes?.length) {
|
|
1831
2088
|
throw new Error(`checks[${index}] route_inventory requires expected_routes.`);
|
|
@@ -1898,6 +2155,9 @@ function normalizeCheck(input, index) {
|
|
|
1898
2155
|
body_not_patterns: bodyNotPatterns,
|
|
1899
2156
|
body_json_assertions: bodyJsonAssertions,
|
|
1900
2157
|
expected_texts: expectedTexts,
|
|
2158
|
+
setup_action_label: type === "ordered_trace" ? setupActionLabel : void 0,
|
|
2159
|
+
trace_path: type === "ordered_trace" ? tracePath : void 0,
|
|
2160
|
+
events: type === "ordered_trace" ? orderedTraceEvents : void 0,
|
|
1901
2161
|
link_selector: stringValue(input.link_selector) || stringValue(input.linkSelector),
|
|
1902
2162
|
source_selector: stringValue(input.source_selector) || stringValue(input.sourceSelector),
|
|
1903
2163
|
route_path_prefix: stringValue(input.route_path_prefix) || stringValue(input.routePathPrefix),
|
|
@@ -1964,6 +2224,20 @@ function normalizeRiddleProofProfile(input, options = {}) {
|
|
|
1964
2224
|
const targetUrl = stringValue(options.url) || stringValue(targetInput.url);
|
|
1965
2225
|
const route = stringValue(options.route) || stringValue(targetInput.route);
|
|
1966
2226
|
if (!targetUrl && !route) throw new Error("profile.target requires url or route, or pass --url.");
|
|
2227
|
+
const setupActions = normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions);
|
|
2228
|
+
for (const [index, check] of checks.entries()) {
|
|
2229
|
+
if (check.type !== "ordered_trace") continue;
|
|
2230
|
+
const matches = (setupActions || []).filter((action) => action.label === check.setup_action_label);
|
|
2231
|
+
if (matches.length !== 1) {
|
|
2232
|
+
throw new Error(`checks[${index}] ordered_trace setup_action_label must match exactly one target.setup_actions label.`);
|
|
2233
|
+
}
|
|
2234
|
+
if (!["window_eval", "window_call", "window_call_until"].includes(matches[0].type)) {
|
|
2235
|
+
throw new Error(`checks[${index}] ordered_trace source action must capture a window_eval, window_call, or window_call_until return.`);
|
|
2236
|
+
}
|
|
2237
|
+
if (matches[0].capture_return === false) {
|
|
2238
|
+
throw new Error(`checks[${index}] ordered_trace source action must not set capture_return to false.`);
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
1967
2241
|
return {
|
|
1968
2242
|
version: RIDDLE_PROOF_PROFILE_VERSION,
|
|
1969
2243
|
name: normalizeName(input.name, "riddle-proof-profile"),
|
|
@@ -1976,7 +2250,7 @@ function normalizeRiddleProofProfile(input, options = {}) {
|
|
|
1976
2250
|
wait_for_selector: stringValue(targetInput.wait_for_selector) || stringValue(targetInput.waitForSelector),
|
|
1977
2251
|
wait_ms: numberValue(targetInput.wait_ms) ?? numberValue(targetInput.waitMs),
|
|
1978
2252
|
screenshot_full_page: normalizeTargetScreenshotFullPage(targetInput),
|
|
1979
|
-
setup_actions:
|
|
2253
|
+
setup_actions: setupActions,
|
|
1980
2254
|
network_mocks: normalizeNetworkMocks(targetInput.network_mocks ?? targetInput.networkMocks)
|
|
1981
2255
|
},
|
|
1982
2256
|
checks,
|
|
@@ -2989,6 +3263,32 @@ function assessCheckFromEvidence(check, evidence) {
|
|
|
2989
3263
|
message: failed ? `Selector ${key} text order failed in ${failed} viewport(s).` : void 0
|
|
2990
3264
|
};
|
|
2991
3265
|
}
|
|
3266
|
+
if (check.type === "ordered_trace") {
|
|
3267
|
+
const assessments = viewports.map((viewport) => ({
|
|
3268
|
+
viewport: viewport.name,
|
|
3269
|
+
assessment: assessRiddleProofOrderedTraceSetupResults(
|
|
3270
|
+
viewport.setup_action_results,
|
|
3271
|
+
check.setup_action_label || "",
|
|
3272
|
+
check.trace_path || "",
|
|
3273
|
+
check.events || []
|
|
3274
|
+
)
|
|
3275
|
+
}));
|
|
3276
|
+
const insufficient = assessments.filter((item) => item.assessment.status === "proof_insufficient");
|
|
3277
|
+
const failed = assessments.filter((item) => item.assessment.status === "failed");
|
|
3278
|
+
const status = insufficient.length ? "proof_insufficient" : failed.length ? "failed" : "passed";
|
|
3279
|
+
return {
|
|
3280
|
+
type: check.type,
|
|
3281
|
+
label: checkLabel(check),
|
|
3282
|
+
status,
|
|
3283
|
+
evidence: {
|
|
3284
|
+
setup_action_label: check.setup_action_label || "",
|
|
3285
|
+
trace_path: check.trace_path || "",
|
|
3286
|
+
events: toJsonValue((check.events || []).map((event) => event.label)),
|
|
3287
|
+
viewports: toJsonValue(assessments)
|
|
3288
|
+
},
|
|
3289
|
+
message: insufficient.length ? `Ordered trace evidence was insufficient in ${insufficient.length} viewport(s).` : failed.length ? `Ordered trace did not contain the required event sequence in ${failed.length} viewport(s).` : void 0
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
2992
3292
|
if (check.type === "observe_within") {
|
|
2993
3293
|
const key = observeWithinKey(check);
|
|
2994
3294
|
const timeoutMs = observeWithinTimeoutMs(check);
|
|
@@ -3488,6 +3788,7 @@ function profileStatusFromEvidence(profile, evidence, checks) {
|
|
|
3488
3788
|
if (!viewports.length || !checks.length) return "proof_insufficient";
|
|
3489
3789
|
if (viewports.some((viewport) => viewport.navigation_error)) return "environment_blocked";
|
|
3490
3790
|
if (expectedViewportCount && viewports.length < expectedViewportCount) return "proof_insufficient";
|
|
3791
|
+
if (checks.some((check) => check.status === "proof_insufficient")) return "proof_insufficient";
|
|
3491
3792
|
if (checks.some((check) => check.status === "needs_human_review")) return "needs_human_review";
|
|
3492
3793
|
if (checks.some((check) => check.status === "failed")) return "product_regression";
|
|
3493
3794
|
return "passed";
|
|
@@ -3817,8 +4118,12 @@ function createRiddleProofProfileInsufficientResult(input) {
|
|
|
3817
4118
|
error: message
|
|
3818
4119
|
};
|
|
3819
4120
|
}
|
|
3820
|
-
function runtimeScriptAssessmentSource() {
|
|
4121
|
+
function runtimeScriptAssessmentSource(includeOrderedTrace = false) {
|
|
4122
|
+
const orderedTraceSource = includeOrderedTrace ? String.raw`
|
|
4123
|
+
const assessRiddleProofOrderedTrace = ${assessRiddleProofOrderedTrace.toString()};
|
|
4124
|
+
const assessRiddleProofOrderedTraceSetupResults = ${assessRiddleProofOrderedTraceSetupResults.toString()};` : "";
|
|
3821
4125
|
return String.raw`
|
|
4126
|
+
${orderedTraceSource}
|
|
3822
4127
|
function normalizeRoutePath(path) {
|
|
3823
4128
|
const value = path || "/";
|
|
3824
4129
|
if (value === "/") return "/";
|
|
@@ -4648,6 +4953,7 @@ function profileSetupWindowCallReceipts(results) {
|
|
|
4648
4953
|
.map((result) => {
|
|
4649
4954
|
const receipt = {
|
|
4650
4955
|
ordinal: result.ordinal ?? null,
|
|
4956
|
+
label: result.label ?? null,
|
|
4651
4957
|
ok: result.ok !== false,
|
|
4652
4958
|
path: result.path ?? null,
|
|
4653
4959
|
return_captured: result.return_captured ?? null,
|
|
@@ -4668,6 +4974,7 @@ function profileSetupWindowEvalReceipts(results) {
|
|
|
4668
4974
|
.map((result) => {
|
|
4669
4975
|
const receipt = {
|
|
4670
4976
|
ordinal: result.ordinal ?? null,
|
|
4977
|
+
label: result.label ?? null,
|
|
4671
4978
|
ok: result.ok !== false,
|
|
4672
4979
|
script_length: result.script_length ?? null,
|
|
4673
4980
|
return_captured: result.return_captured ?? null,
|
|
@@ -5483,6 +5790,36 @@ function assessProfile(profile, evidence) {
|
|
|
5483
5790
|
});
|
|
5484
5791
|
continue;
|
|
5485
5792
|
}
|
|
5793
|
+
if (check.type === "ordered_trace") {
|
|
5794
|
+
const assessments = checkViewports.map((viewport) => ({
|
|
5795
|
+
viewport: viewport.name,
|
|
5796
|
+
assessment: assessRiddleProofOrderedTraceSetupResults(
|
|
5797
|
+
viewport.setup_action_results,
|
|
5798
|
+
check.setup_action_label || "",
|
|
5799
|
+
check.trace_path || "",
|
|
5800
|
+
check.events || [],
|
|
5801
|
+
),
|
|
5802
|
+
}));
|
|
5803
|
+
const insufficient = assessments.filter((item) => item.assessment.status === "proof_insufficient");
|
|
5804
|
+
const failed = assessments.filter((item) => item.assessment.status === "failed");
|
|
5805
|
+
checks.push({
|
|
5806
|
+
type: check.type,
|
|
5807
|
+
label: check.label || check.type,
|
|
5808
|
+
status: insufficient.length ? "proof_insufficient" : failed.length ? "failed" : "passed",
|
|
5809
|
+
evidence: {
|
|
5810
|
+
setup_action_label: check.setup_action_label || "",
|
|
5811
|
+
trace_path: check.trace_path || "",
|
|
5812
|
+
events: (check.events || []).map((event) => event.label),
|
|
5813
|
+
viewports: assessments,
|
|
5814
|
+
},
|
|
5815
|
+
message: insufficient.length
|
|
5816
|
+
? "Ordered trace evidence was insufficient in " + insufficient.length + " viewport(s)."
|
|
5817
|
+
: failed.length
|
|
5818
|
+
? "Ordered trace did not contain the required event sequence in " + failed.length + " viewport(s)."
|
|
5819
|
+
: undefined,
|
|
5820
|
+
});
|
|
5821
|
+
continue;
|
|
5822
|
+
}
|
|
5486
5823
|
if (check.type === "observe_within") {
|
|
5487
5824
|
const key = observeWithinKey(check);
|
|
5488
5825
|
const timeoutMs = observeWithinTimeoutMs(check);
|
|
@@ -5839,6 +6176,7 @@ function assessProfile(profile, evidence) {
|
|
|
5839
6176
|
if (!viewports.length || !checks.length) status = "proof_insufficient";
|
|
5840
6177
|
else if (viewports.some((viewport) => viewport.navigation_error)) status = "environment_blocked";
|
|
5841
6178
|
else if (expectedViewportCount && viewports.length < expectedViewportCount) status = "proof_insufficient";
|
|
6179
|
+
else if (checks.some((check) => check.status === "proof_insufficient")) status = "proof_insufficient";
|
|
5842
6180
|
else if (checks.some((check) => check.status === "needs_human_review")) status = "needs_human_review";
|
|
5843
6181
|
else if (checks.some((check) => check.status === "failed")) status = "product_regression";
|
|
5844
6182
|
const screenshotLabels = profileScreenshotLabels(viewports);
|
|
@@ -6770,7 +7108,7 @@ let activeViewportName = null;
|
|
|
6770
7108
|
async function executeSetupAction(action, ordinal, viewport) {
|
|
6771
7109
|
const type = setupActionType(action);
|
|
6772
7110
|
const frameSelector = setupFrameSelector(action);
|
|
6773
|
-
const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null, optional: action.optional === true };
|
|
7111
|
+
const base = { ok: false, action: type || "unknown", ordinal, label: action.label || null, selector: action.selector || null, frame_selector: frameSelector || null, optional: action.optional === true };
|
|
6774
7112
|
const timeout = setupNumber(action.timeout_ms, 5000);
|
|
6775
7113
|
try {
|
|
6776
7114
|
if (type === "wait") {
|
|
@@ -9422,7 +9760,7 @@ async function captureViewport(viewport) {
|
|
|
9422
9760
|
wait_error: waitError,
|
|
9423
9761
|
};
|
|
9424
9762
|
}
|
|
9425
|
-
${runtimeScriptAssessmentSource()}
|
|
9763
|
+
${runtimeScriptAssessmentSource(profile.checks.some((check) => check.type === "ordered_trace"))}
|
|
9426
9764
|
const viewports = [];
|
|
9427
9765
|
function buildProfileEvidence(currentViewports) {
|
|
9428
9766
|
const expectedViewportCount = (profile.target.viewports || []).length;
|
|
@@ -9633,7 +9971,10 @@ export {
|
|
|
9633
9971
|
RIDDLE_PROOF_PROFILE_CHECK_TYPES,
|
|
9634
9972
|
RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES,
|
|
9635
9973
|
RIDDLE_PROOF_PROFILE_NETWORK_ABORT_ERROR_CODES,
|
|
9974
|
+
RIDDLE_PROOF_ORDERED_TRACE_OPERATORS,
|
|
9636
9975
|
deriveRiddleProofArtifactBodyAssertions,
|
|
9976
|
+
assessRiddleProofOrderedTrace,
|
|
9977
|
+
assessRiddleProofOrderedTraceSetupResults,
|
|
9637
9978
|
slugifyRiddleProofProfileName,
|
|
9638
9979
|
collectRiddleProofProfileWarnings,
|
|
9639
9980
|
normalizeRiddleProofProfile,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
createRiddleProofHandoffReceipt,
|
|
6
6
|
parseRiddleProofChangeReceipt,
|
|
7
7
|
parseRiddleProofHandoffReceipt
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LR4UYJDY.js";
|
|
9
9
|
import {
|
|
10
10
|
riddleProofPublicStateAllowsMergeRecommendation,
|
|
11
11
|
riddleProofPublicStateMergeRecommendation,
|
|
@@ -8,17 +8,17 @@ import {
|
|
|
8
8
|
RIDDLE_PROOF_PR_COMMENT_MARKER,
|
|
9
9
|
buildRiddleProofPrCommentMarkdown,
|
|
10
10
|
summarizeRiddleProofPrComment
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-7OAETQU3.js";
|
|
12
12
|
import {
|
|
13
13
|
suggestRiddleProofProfileChecks
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-FW7CKARF.js";
|
|
15
15
|
import {
|
|
16
16
|
assessRiddleProofChange,
|
|
17
17
|
createRiddleProofChangeReceipt,
|
|
18
18
|
createRiddleProofHandoffReceipt,
|
|
19
19
|
riddleProofChangeReceiptHtml,
|
|
20
20
|
riddleProofChangeReceiptMarkdown
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-LR4UYJDY.js";
|
|
22
22
|
import {
|
|
23
23
|
createRiddleProofObservationReceipt,
|
|
24
24
|
parseRiddlePreviewReceipt,
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
profileStatusExitCode,
|
|
42
42
|
resolveRiddleProofProfileTargetUrl,
|
|
43
43
|
resolveRiddleProofProfileTimeoutSec
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-2K3DIK7A.js";
|
|
45
45
|
import {
|
|
46
46
|
createCodexExecAgentAdapter,
|
|
47
47
|
runCodexExecAgentDoctor
|
package/dist/cli/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import "../chunk-
|
|
1
|
+
import "../chunk-QYZ3GKCP.js";
|
|
2
2
|
import "../chunk-5IFZSUPF.js";
|
|
3
3
|
import "../chunk-JFQXAJH2.js";
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
4
|
+
import "../chunk-7OAETQU3.js";
|
|
5
|
+
import "../chunk-FW7CKARF.js";
|
|
6
|
+
import "../chunk-LR4UYJDY.js";
|
|
7
7
|
import "../chunk-MEVVL4TI.js";
|
|
8
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-2K3DIK7A.js";
|
|
9
9
|
import "../chunk-KXLEN4SA.js";
|
|
10
10
|
import "../chunk-WLUMLHII.js";
|
|
11
11
|
import "../chunk-CGJX7LJO.js";
|