@riddledc/riddle-proof 0.8.79 → 0.8.80

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 CHANGED
@@ -499,6 +499,7 @@ The package includes generic starter profiles:
499
499
  - `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
500
  - `examples/profiles/terminal-result-partial-evidence.json` for API-console terminal error or timeout receipts that preserve partial screenshot, console, and HAR evidence.
501
501
  - `examples/profiles/gameplay-window-call-until.json` for gameplay profiles that wait on a runtime state contract instead of a fixed sleep.
502
+ - `examples/profiles/ordered-trace.json` for interaction profiles that require named runtime events to occur in strict temporal order.
502
503
  - `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
504
 
504
505
  Copy one of those shapes into a repository profile directory and replace the
@@ -808,6 +809,44 @@ both first and last per-viewport receipts before filling remaining space, so
808
809
  late lifecycle phases such as terminal or restart remain visible in compact
809
810
  summaries.
810
811
 
812
+ Use `ordered_trace` when one point-in-time assertion cannot establish the
813
+ behavior, such as input -> response -> motion -> release -> idle. A labeled
814
+ `window_eval`, `window_call`, or `window_call_until` setup action must return a
815
+ JSON object containing the trace. The check declares predicates for each named
816
+ event and records the earliest strictly increasing witness indices plus compact
817
+ observed values:
818
+
819
+ ```json
820
+ {
821
+ "type": "ordered_trace",
822
+ "label": "input to release",
823
+ "setup_action_label": "capture interaction trace",
824
+ "trace_path": "trace",
825
+ "events": [
826
+ {
827
+ "label": "input",
828
+ "predicates": [{ "path": "target", "op": "abs_gte", "value": 0.8 }]
829
+ },
830
+ {
831
+ "label": "response",
832
+ "predicates": [{ "path": "applied", "op": "abs_gte", "value": 0.1 }]
833
+ },
834
+ {
835
+ "label": "released",
836
+ "predicates": [{ "path": "active", "op": "equals", "value": false }]
837
+ }
838
+ ]
839
+ }
840
+ ```
841
+
842
+ Supported predicate operators are `exists`, `equals`, `not_equals`, `truthy`,
843
+ `falsy`, `gt`, `gte`, `lt`, `lte`, `abs_gt`, `abs_gte`, `abs_lt`, and
844
+ `abs_lte`. Missing or empty traces and fields that never appear are
845
+ `proof_insufficient`; a complete trace that does not contain the sequence is a
846
+ `failed` product check. One sample cannot witness two successive events. This
847
+ keeps missing instrumentation distinct from a measured regression and lets an
848
+ ordinary Change Proof `check_status_transition` require `failed -> passed`.
849
+
811
850
  `target.timeout_sec` is optional. Use it for known-heavy profile targets so the
812
851
  profile carries its own hosted Riddle worker budget; an explicit CLI `--timeout`
813
852
  still overrides the profile value for one-off runs.
@@ -12,9 +12,9 @@ import {
12
12
  parseRiddleProofHandoffReceipt,
13
13
  riddleProofChangeReceiptHtml,
14
14
  riddleProofChangeReceiptMarkdown
15
- } from "./chunk-6VFS2JFR.js";
15
+ } from "./chunk-IL7MLYB5.js";
16
16
  import "./chunk-MEVVL4TI.js";
17
- import "./chunk-FCSJZBC5.js";
17
+ import "./chunk-ZNVYJFR7.js";
18
18
  import "./chunk-MLKGABMK.js";
19
19
  export {
20
20
  RIDDLE_PROOF_CHANGE_CONTRACT_VERSION,
@@ -5,7 +5,7 @@ import {
5
5
  createRiddleProofHandoffReceipt,
6
6
  parseRiddleProofChangeReceipt,
7
7
  parseRiddleProofHandoffReceipt
8
- } from "./chunk-6VFS2JFR.js";
8
+ } from "./chunk-IL7MLYB5.js";
9
9
  import {
10
10
  riddleProofPublicStateAllowsMergeRecommendation,
11
11
  riddleProofPublicStateMergeRecommendation,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  RIDDLE_PROOF_PROFILE_VERSION
3
- } from "./chunk-FCSJZBC5.js";
3
+ } from "./chunk-ZNVYJFR7.js";
4
4
 
5
5
  // src/profile-suggestions.ts
6
6
  var RIDDLE_PROOF_PROFILE_SUGGESTIONS_VERSION = "riddle-proof.profile-suggestions.v1";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-MEVVL4TI.js";
5
5
  import {
6
6
  RIDDLE_PROOF_PROFILE_STATUSES
7
- } from "./chunk-FCSJZBC5.js";
7
+ } from "./chunk-ZNVYJFR7.js";
8
8
 
9
9
  // src/change-proof.ts
10
10
  var RIDDLE_PROOF_CHANGE_CONTRACT_VERSION = "riddle-proof.change-contract.v1";
@@ -8,17 +8,17 @@ import {
8
8
  RIDDLE_PROOF_PR_COMMENT_MARKER,
9
9
  buildRiddleProofPrCommentMarkdown,
10
10
  summarizeRiddleProofPrComment
11
- } from "./chunk-7N6X54WG.js";
11
+ } from "./chunk-6T6ZAK77.js";
12
12
  import {
13
13
  suggestRiddleProofProfileChecks
14
- } from "./chunk-RQPCKRKT.js";
14
+ } from "./chunk-A4EF4M3B.js";
15
15
  import {
16
16
  assessRiddleProofChange,
17
17
  createRiddleProofChangeReceipt,
18
18
  createRiddleProofHandoffReceipt,
19
19
  riddleProofChangeReceiptHtml,
20
20
  riddleProofChangeReceiptMarkdown
21
- } from "./chunk-6VFS2JFR.js";
21
+ } from "./chunk-IL7MLYB5.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-FCSJZBC5.js";
44
+ } from "./chunk-ZNVYJFR7.js";
45
45
  import {
46
46
  createCodexExecAgentAdapter,
47
47
  runCodexExecAgentDoctor
@@ -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,
@@ -1776,6 +1973,51 @@ function dialogCountFieldForCheckType(type) {
1776
1973
  if (type === "dialog_dismiss_count_equals") return "dialog_dismiss_count";
1777
1974
  return "dialog_count";
1778
1975
  }
1976
+ function normalizeOrderedTraceEvents(value, label) {
1977
+ if (value === void 0) return void 0;
1978
+ if (!Array.isArray(value) || !value.length) throw new Error(`${label} must be a non-empty array.`);
1979
+ const seenLabels = /* @__PURE__ */ new Set();
1980
+ return value.map((item, eventIndex) => {
1981
+ const eventLabel = `${label}[${eventIndex}]`;
1982
+ if (!isRecord(item)) throw new Error(`${eventLabel} must be an object.`);
1983
+ const name = stringFromOwn(item, "label", "name", "event");
1984
+ if (!name) throw new Error(`${eventLabel}.label is required.`);
1985
+ if (seenLabels.has(name)) throw new Error(`${eventLabel}.label must be unique.`);
1986
+ seenLabels.add(name);
1987
+ const predicatesInput = item.predicates ?? item.all ?? item.where;
1988
+ if (!Array.isArray(predicatesInput) || !predicatesInput.length) {
1989
+ throw new Error(`${eventLabel}.predicates must be a non-empty array.`);
1990
+ }
1991
+ const predicates = predicatesInput.map((predicateInput, predicateIndex) => {
1992
+ const predicateLabel = `${eventLabel}.predicates[${predicateIndex}]`;
1993
+ if (!isRecord(predicateInput)) throw new Error(`${predicateLabel} must be an object.`);
1994
+ const path = stringFromOwn(predicateInput, "path", "field", "key");
1995
+ if (!path) throw new Error(`${predicateLabel}.path is required.`);
1996
+ const op = stringFromOwn(predicateInput, "op", "operator");
1997
+ if (!op || !RIDDLE_PROOF_ORDERED_TRACE_OPERATORS.includes(op)) {
1998
+ throw new Error(`${predicateLabel}.op must be one of ${RIDDLE_PROOF_ORDERED_TRACE_OPERATORS.join(", ")}.`);
1999
+ }
2000
+ const requiresValue = !["exists", "truthy", "falsy"].includes(op);
2001
+ const hasValue = hasOwn(predicateInput, "value") || hasOwn(predicateInput, "expected");
2002
+ if (requiresValue && !hasValue) throw new Error(`${predicateLabel}.value is required for ${op}.`);
2003
+ const value2 = hasOwn(predicateInput, "value") ? predicateInput.value : predicateInput.expected;
2004
+ if (["gt", "gte", "lt", "lte", "abs_gt", "abs_gte", "abs_lt", "abs_lte"].includes(op)) {
2005
+ if (typeof value2 !== "number" || !Number.isFinite(value2)) {
2006
+ throw new Error(`${predicateLabel}.value must be a finite number for ${op}.`);
2007
+ }
2008
+ if (op.startsWith("abs_") && value2 < 0) {
2009
+ throw new Error(`${predicateLabel}.value must be non-negative for ${op}.`);
2010
+ }
2011
+ }
2012
+ return {
2013
+ path,
2014
+ op,
2015
+ value: requiresValue ? toJsonValue(value2) : void 0
2016
+ };
2017
+ });
2018
+ return { label: name, predicates };
2019
+ });
2020
+ }
1779
2021
  function normalizeCheck(input, index) {
1780
2022
  if (!isRecord(input)) throw new Error(`checks[${index}] must be an object.`);
1781
2023
  const type = stringValue(input.type);
@@ -1826,6 +2068,17 @@ function normalizeCheck(input, index) {
1826
2068
  if (!stringValue(input.selector)) throw new Error(`checks[${index}] selector_text_order requires selector.`);
1827
2069
  if (!expectedTexts?.length) throw new Error(`checks[${index}] selector_text_order requires expected_texts.`);
1828
2070
  }
2071
+ const setupActionLabel = stringFromOwn(input, "setup_action_label", "setupActionLabel", "source_action_label", "sourceActionLabel");
2072
+ const tracePath = stringFromOwn(input, "trace_path", "tracePath", "path");
2073
+ const orderedTraceEvents = normalizeOrderedTraceEvents(
2074
+ input.events ?? input.sequence ?? input.ordered_events ?? input.orderedEvents,
2075
+ `checks[${index}].events`
2076
+ );
2077
+ if (type === "ordered_trace") {
2078
+ if (!setupActionLabel) throw new Error(`checks[${index}] ordered_trace requires setup_action_label.`);
2079
+ if (!tracePath) throw new Error(`checks[${index}] ordered_trace requires trace_path.`);
2080
+ if (!orderedTraceEvents?.length) throw new Error(`checks[${index}] ordered_trace requires events.`);
2081
+ }
1829
2082
  const expectedRoutes = normalizeRouteInventoryRoutes(input.expected_routes ?? input.expectedRoutes, index);
1830
2083
  if (type === "route_inventory" && !expectedRoutes?.length) {
1831
2084
  throw new Error(`checks[${index}] route_inventory requires expected_routes.`);
@@ -1898,6 +2151,9 @@ function normalizeCheck(input, index) {
1898
2151
  body_not_patterns: bodyNotPatterns,
1899
2152
  body_json_assertions: bodyJsonAssertions,
1900
2153
  expected_texts: expectedTexts,
2154
+ setup_action_label: type === "ordered_trace" ? setupActionLabel : void 0,
2155
+ trace_path: type === "ordered_trace" ? tracePath : void 0,
2156
+ events: type === "ordered_trace" ? orderedTraceEvents : void 0,
1901
2157
  link_selector: stringValue(input.link_selector) || stringValue(input.linkSelector),
1902
2158
  source_selector: stringValue(input.source_selector) || stringValue(input.sourceSelector),
1903
2159
  route_path_prefix: stringValue(input.route_path_prefix) || stringValue(input.routePathPrefix),
@@ -1964,6 +2220,20 @@ function normalizeRiddleProofProfile(input, options = {}) {
1964
2220
  const targetUrl = stringValue(options.url) || stringValue(targetInput.url);
1965
2221
  const route = stringValue(options.route) || stringValue(targetInput.route);
1966
2222
  if (!targetUrl && !route) throw new Error("profile.target requires url or route, or pass --url.");
2223
+ const setupActions = normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions);
2224
+ for (const [index, check] of checks.entries()) {
2225
+ if (check.type !== "ordered_trace") continue;
2226
+ const matches = (setupActions || []).filter((action) => action.label === check.setup_action_label);
2227
+ if (matches.length !== 1) {
2228
+ throw new Error(`checks[${index}] ordered_trace setup_action_label must match exactly one target.setup_actions label.`);
2229
+ }
2230
+ if (!["window_eval", "window_call", "window_call_until"].includes(matches[0].type)) {
2231
+ throw new Error(`checks[${index}] ordered_trace source action must capture a window_eval, window_call, or window_call_until return.`);
2232
+ }
2233
+ if (matches[0].capture_return === false) {
2234
+ throw new Error(`checks[${index}] ordered_trace source action must not set capture_return to false.`);
2235
+ }
2236
+ }
1967
2237
  return {
1968
2238
  version: RIDDLE_PROOF_PROFILE_VERSION,
1969
2239
  name: normalizeName(input.name, "riddle-proof-profile"),
@@ -1976,7 +2246,7 @@ function normalizeRiddleProofProfile(input, options = {}) {
1976
2246
  wait_for_selector: stringValue(targetInput.wait_for_selector) || stringValue(targetInput.waitForSelector),
1977
2247
  wait_ms: numberValue(targetInput.wait_ms) ?? numberValue(targetInput.waitMs),
1978
2248
  screenshot_full_page: normalizeTargetScreenshotFullPage(targetInput),
1979
- setup_actions: normalizeSetupActions(targetInput.setup_actions ?? targetInput.setupActions),
2249
+ setup_actions: setupActions,
1980
2250
  network_mocks: normalizeNetworkMocks(targetInput.network_mocks ?? targetInput.networkMocks)
1981
2251
  },
1982
2252
  checks,
@@ -2989,6 +3259,32 @@ function assessCheckFromEvidence(check, evidence) {
2989
3259
  message: failed ? `Selector ${key} text order failed in ${failed} viewport(s).` : void 0
2990
3260
  };
2991
3261
  }
3262
+ if (check.type === "ordered_trace") {
3263
+ const assessments = viewports.map((viewport) => ({
3264
+ viewport: viewport.name,
3265
+ assessment: assessRiddleProofOrderedTraceSetupResults(
3266
+ viewport.setup_action_results,
3267
+ check.setup_action_label || "",
3268
+ check.trace_path || "",
3269
+ check.events || []
3270
+ )
3271
+ }));
3272
+ const insufficient = assessments.filter((item) => item.assessment.status === "proof_insufficient");
3273
+ const failed = assessments.filter((item) => item.assessment.status === "failed");
3274
+ const status = insufficient.length ? "proof_insufficient" : failed.length ? "failed" : "passed";
3275
+ return {
3276
+ type: check.type,
3277
+ label: checkLabel(check),
3278
+ status,
3279
+ evidence: {
3280
+ setup_action_label: check.setup_action_label || "",
3281
+ trace_path: check.trace_path || "",
3282
+ events: toJsonValue((check.events || []).map((event) => event.label)),
3283
+ viewports: toJsonValue(assessments)
3284
+ },
3285
+ 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
3286
+ };
3287
+ }
2992
3288
  if (check.type === "observe_within") {
2993
3289
  const key = observeWithinKey(check);
2994
3290
  const timeoutMs = observeWithinTimeoutMs(check);
@@ -3488,6 +3784,7 @@ function profileStatusFromEvidence(profile, evidence, checks) {
3488
3784
  if (!viewports.length || !checks.length) return "proof_insufficient";
3489
3785
  if (viewports.some((viewport) => viewport.navigation_error)) return "environment_blocked";
3490
3786
  if (expectedViewportCount && viewports.length < expectedViewportCount) return "proof_insufficient";
3787
+ if (checks.some((check) => check.status === "proof_insufficient")) return "proof_insufficient";
3491
3788
  if (checks.some((check) => check.status === "needs_human_review")) return "needs_human_review";
3492
3789
  if (checks.some((check) => check.status === "failed")) return "product_regression";
3493
3790
  return "passed";
@@ -3817,8 +4114,12 @@ function createRiddleProofProfileInsufficientResult(input) {
3817
4114
  error: message
3818
4115
  };
3819
4116
  }
3820
- function runtimeScriptAssessmentSource() {
4117
+ function runtimeScriptAssessmentSource(includeOrderedTrace = false) {
4118
+ const orderedTraceSource = includeOrderedTrace ? String.raw`
4119
+ const assessRiddleProofOrderedTrace = ${assessRiddleProofOrderedTrace.toString()};
4120
+ const assessRiddleProofOrderedTraceSetupResults = ${assessRiddleProofOrderedTraceSetupResults.toString()};` : "";
3821
4121
  return String.raw`
4122
+ ${orderedTraceSource}
3822
4123
  function normalizeRoutePath(path) {
3823
4124
  const value = path || "/";
3824
4125
  if (value === "/") return "/";
@@ -4648,6 +4949,7 @@ function profileSetupWindowCallReceipts(results) {
4648
4949
  .map((result) => {
4649
4950
  const receipt = {
4650
4951
  ordinal: result.ordinal ?? null,
4952
+ label: result.label ?? null,
4651
4953
  ok: result.ok !== false,
4652
4954
  path: result.path ?? null,
4653
4955
  return_captured: result.return_captured ?? null,
@@ -4668,6 +4970,7 @@ function profileSetupWindowEvalReceipts(results) {
4668
4970
  .map((result) => {
4669
4971
  const receipt = {
4670
4972
  ordinal: result.ordinal ?? null,
4973
+ label: result.label ?? null,
4671
4974
  ok: result.ok !== false,
4672
4975
  script_length: result.script_length ?? null,
4673
4976
  return_captured: result.return_captured ?? null,
@@ -5483,6 +5786,36 @@ function assessProfile(profile, evidence) {
5483
5786
  });
5484
5787
  continue;
5485
5788
  }
5789
+ if (check.type === "ordered_trace") {
5790
+ const assessments = checkViewports.map((viewport) => ({
5791
+ viewport: viewport.name,
5792
+ assessment: assessRiddleProofOrderedTraceSetupResults(
5793
+ viewport.setup_action_results,
5794
+ check.setup_action_label || "",
5795
+ check.trace_path || "",
5796
+ check.events || [],
5797
+ ),
5798
+ }));
5799
+ const insufficient = assessments.filter((item) => item.assessment.status === "proof_insufficient");
5800
+ const failed = assessments.filter((item) => item.assessment.status === "failed");
5801
+ checks.push({
5802
+ type: check.type,
5803
+ label: check.label || check.type,
5804
+ status: insufficient.length ? "proof_insufficient" : failed.length ? "failed" : "passed",
5805
+ evidence: {
5806
+ setup_action_label: check.setup_action_label || "",
5807
+ trace_path: check.trace_path || "",
5808
+ events: (check.events || []).map((event) => event.label),
5809
+ viewports: assessments,
5810
+ },
5811
+ message: insufficient.length
5812
+ ? "Ordered trace evidence was insufficient in " + insufficient.length + " viewport(s)."
5813
+ : failed.length
5814
+ ? "Ordered trace did not contain the required event sequence in " + failed.length + " viewport(s)."
5815
+ : undefined,
5816
+ });
5817
+ continue;
5818
+ }
5486
5819
  if (check.type === "observe_within") {
5487
5820
  const key = observeWithinKey(check);
5488
5821
  const timeoutMs = observeWithinTimeoutMs(check);
@@ -5839,6 +6172,7 @@ function assessProfile(profile, evidence) {
5839
6172
  if (!viewports.length || !checks.length) status = "proof_insufficient";
5840
6173
  else if (viewports.some((viewport) => viewport.navigation_error)) status = "environment_blocked";
5841
6174
  else if (expectedViewportCount && viewports.length < expectedViewportCount) status = "proof_insufficient";
6175
+ else if (checks.some((check) => check.status === "proof_insufficient")) status = "proof_insufficient";
5842
6176
  else if (checks.some((check) => check.status === "needs_human_review")) status = "needs_human_review";
5843
6177
  else if (checks.some((check) => check.status === "failed")) status = "product_regression";
5844
6178
  const screenshotLabels = profileScreenshotLabels(viewports);
@@ -6770,7 +7104,7 @@ let activeViewportName = null;
6770
7104
  async function executeSetupAction(action, ordinal, viewport) {
6771
7105
  const type = setupActionType(action);
6772
7106
  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 };
7107
+ 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
7108
  const timeout = setupNumber(action.timeout_ms, 5000);
6775
7109
  try {
6776
7110
  if (type === "wait") {
@@ -9422,7 +9756,7 @@ async function captureViewport(viewport) {
9422
9756
  wait_error: waitError,
9423
9757
  };
9424
9758
  }
9425
- ${runtimeScriptAssessmentSource()}
9759
+ ${runtimeScriptAssessmentSource(profile.checks.some((check) => check.type === "ordered_trace"))}
9426
9760
  const viewports = [];
9427
9761
  function buildProfileEvidence(currentViewports) {
9428
9762
  const expectedViewportCount = (profile.target.viewports || []).length;
@@ -9633,7 +9967,10 @@ export {
9633
9967
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
9634
9968
  RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES,
9635
9969
  RIDDLE_PROOF_PROFILE_NETWORK_ABORT_ERROR_CODES,
9970
+ RIDDLE_PROOF_ORDERED_TRACE_OPERATORS,
9636
9971
  deriveRiddleProofArtifactBodyAssertions,
9972
+ assessRiddleProofOrderedTrace,
9973
+ assessRiddleProofOrderedTraceSetupResults,
9637
9974
  slugifyRiddleProofProfileName,
9638
9975
  collectRiddleProofProfileWarnings,
9639
9976
  normalizeRiddleProofProfile,
package/dist/cli/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import "../chunk-NEXWITV4.js";
1
+ import "../chunk-N7EETHYG.js";
2
2
  import "../chunk-5IFZSUPF.js";
3
3
  import "../chunk-JFQXAJH2.js";
4
- import "../chunk-7N6X54WG.js";
5
- import "../chunk-RQPCKRKT.js";
6
- import "../chunk-6VFS2JFR.js";
4
+ import "../chunk-6T6ZAK77.js";
5
+ import "../chunk-A4EF4M3B.js";
6
+ import "../chunk-IL7MLYB5.js";
7
7
  import "../chunk-MEVVL4TI.js";
8
- import "../chunk-FCSJZBC5.js";
8
+ import "../chunk-ZNVYJFR7.js";
9
9
  import "../chunk-KXLEN4SA.js";
10
10
  import "../chunk-WLUMLHII.js";
11
11
  import "../chunk-CGJX7LJO.js";