@riddledc/riddle-proof 0.7.43 → 0.7.44

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
@@ -148,6 +148,7 @@ or as a stronger proof base before a change loop.
148
148
  { "type": "selector_absent", "selector": "[data-testid='loading-spinner']" },
149
149
  { "type": "selector_count_equals", "selector": "[data-testid='pricing-card']", "expected_count": 3 },
150
150
  { "type": "text_visible", "text": "Start building" },
151
+ { "type": "text_visible", "text": "Compare plans", "viewports": ["desktop"] },
151
152
  { "type": "no_mobile_horizontal_overflow" },
152
153
  { "type": "no_fatal_console_errors" }
153
154
  ],
@@ -179,6 +180,11 @@ The package includes a generic starter profile at
179
180
  profile directory and replace the selector/text checks with app-specific
180
181
  invariants.
181
182
 
183
+ Checks normally apply to every captured viewport. Add `viewports` (or
184
+ `viewport_names`) to a check when responsive UI intentionally exposes an
185
+ invariant only on named viewports, such as desktop-only helper copy while phone
186
+ layouts keep the same route, link, and overflow contracts.
187
+
182
188
  `target.network_mocks` is optional. The Riddle runner registers these mocks
183
189
  before navigation, records each hit, and adds an implicit
184
190
  `network_mocks_succeeded` check when mocks are present. A mock supports
@@ -402,6 +402,7 @@ function normalizeCheck(input, index) {
402
402
  text: stringValue(input.text),
403
403
  pattern: stringValue(input.pattern),
404
404
  flags: stringValue(input.flags),
405
+ viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `checks[${index}] viewports`),
405
406
  allowed_console_texts: normalizeStringList(input.allowed_console_texts ?? input.allowedConsoleTexts ?? input.allow_console_texts ?? input.allowConsoleTexts, `checks[${index}] allowed_console_texts`),
406
407
  allowed_console_patterns: normalizeStringList(input.allowed_console_patterns ?? input.allowedConsolePatterns ?? input.allow_console_patterns ?? input.allowConsolePatterns, `checks[${index}] allowed_console_patterns`),
407
408
  allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
@@ -640,15 +641,22 @@ function successfulRoute(route, targetUrl) {
640
641
  const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
641
642
  return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
642
643
  }
644
+ function viewportsForCheck(check, viewports) {
645
+ if (!check.viewports?.length) return viewports;
646
+ const names = new Set(check.viewports);
647
+ return viewports.filter((viewport) => names.has(viewport.name));
648
+ }
643
649
  function assessCheckFromEvidence(check, evidence) {
644
- const viewports = evidence.viewports || [];
650
+ const viewports = viewportsForCheck(check, evidence.viewports || []);
645
651
  if (!viewports.length) {
646
652
  return {
647
653
  type: check.type,
648
654
  label: checkLabel(check),
649
655
  status: "failed",
650
- evidence: {},
651
- message: "No viewport evidence was captured."
656
+ evidence: {
657
+ expected_viewports: check.viewports || []
658
+ },
659
+ message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
652
660
  };
653
661
  }
654
662
  if (check.type === "route_loaded") {
@@ -1253,6 +1261,11 @@ function frameTextSample(frame) {
1253
1261
  frame && frame.text,
1254
1262
  ].map((part) => String(part || "")).filter(Boolean).join(" ");
1255
1263
  }
1264
+ function viewportsForCheck(check, viewports) {
1265
+ if (!Array.isArray(check.viewports) || !check.viewports.length) return viewports;
1266
+ const names = new Set(check.viewports);
1267
+ return viewports.filter((viewport) => names.has(viewport.name));
1268
+ }
1256
1269
  function summarizeRouteInventory(viewport, inventory) {
1257
1270
  const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
1258
1271
  const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
@@ -1443,9 +1456,22 @@ function assessProfile(profile, evidence) {
1443
1456
  });
1444
1457
  }
1445
1458
  for (const check of profile.checks || []) {
1459
+ const checkViewports = viewportsForCheck(check, viewports);
1460
+ if (!checkViewports.length) {
1461
+ checks.push({
1462
+ type: check.type,
1463
+ label: check.label || check.type,
1464
+ status: "failed",
1465
+ evidence: { expected_viewports: check.viewports || [] },
1466
+ message: Array.isArray(check.viewports) && check.viewports.length
1467
+ ? "No matching viewport evidence was captured for " + check.viewports.join(", ") + "."
1468
+ : "No viewport evidence was captured.",
1469
+ });
1470
+ continue;
1471
+ }
1446
1472
  if (check.type === "route_loaded") {
1447
1473
  const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
1448
- const failed = viewports.filter((viewport) => {
1474
+ const failed = checkViewports.filter((viewport) => {
1449
1475
  const route = { ...(viewport.route || {}), expected_path: expectedPath };
1450
1476
  route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
1451
1477
  return !routeOk(route, evidence.target_url);
@@ -1456,8 +1482,8 @@ function assessProfile(profile, evidence) {
1456
1482
  status: failed.length ? "failed" : "passed",
1457
1483
  evidence: {
1458
1484
  expected_path: expectedPath,
1459
- observed_paths: viewports.map((viewport) => viewport.route && viewport.route.observed),
1460
- http_statuses: viewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
1485
+ observed_paths: checkViewports.map((viewport) => viewport.route && viewport.route.observed),
1486
+ http_statuses: checkViewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
1461
1487
  },
1462
1488
  message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
1463
1489
  });
@@ -1465,24 +1491,24 @@ function assessProfile(profile, evidence) {
1465
1491
  }
1466
1492
  if (check.type === "selector_visible") {
1467
1493
  const selector = check.selector || "";
1468
- const failed = viewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
1494
+ const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
1469
1495
  checks.push({
1470
1496
  type: check.type,
1471
1497
  label: check.label || check.type,
1472
1498
  status: failed.length ? "failed" : "passed",
1473
- evidence: { selector, visible_counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
1499
+ evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
1474
1500
  message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
1475
1501
  });
1476
1502
  continue;
1477
1503
  }
1478
1504
  if (check.type === "selector_absent") {
1479
1505
  const selector = check.selector || "";
1480
- const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
1506
+ const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
1481
1507
  checks.push({
1482
1508
  type: check.type,
1483
1509
  label: check.label || check.type,
1484
1510
  status: failed.length ? "failed" : "passed",
1485
- evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1511
+ evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1486
1512
  message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
1487
1513
  });
1488
1514
  continue;
@@ -1490,12 +1516,12 @@ function assessProfile(profile, evidence) {
1490
1516
  if (check.type === "selector_count_at_least") {
1491
1517
  const selector = check.selector || "";
1492
1518
  const minCount = check.min_count == null ? 1 : check.min_count;
1493
- const failed = viewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
1519
+ const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
1494
1520
  checks.push({
1495
1521
  type: check.type,
1496
1522
  label: check.label || check.type,
1497
1523
  status: failed.length ? "failed" : "passed",
1498
- evidence: { selector, min_count: minCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1524
+ evidence: { selector, min_count: minCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1499
1525
  message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
1500
1526
  });
1501
1527
  continue;
@@ -1503,12 +1529,12 @@ function assessProfile(profile, evidence) {
1503
1529
  if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
1504
1530
  const selector = check.selector || "";
1505
1531
  const expectedCount = check.expected_count == null ? 0 : check.expected_count;
1506
- const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
1532
+ const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
1507
1533
  checks.push({
1508
1534
  type: check.type,
1509
1535
  label: check.label || check.type,
1510
1536
  status: failed.length ? "failed" : "passed",
1511
- evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1537
+ evidence: { selector, expected_count: expectedCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1512
1538
  message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
1513
1539
  });
1514
1540
  continue;
@@ -1516,7 +1542,7 @@ function assessProfile(profile, evidence) {
1516
1542
  if (check.type === "selector_text_order") {
1517
1543
  const selector = check.selector || "";
1518
1544
  const expectedTexts = check.expected_texts || [];
1519
- const results = viewports.map((viewport) => {
1545
+ const results = checkViewports.map((viewport) => {
1520
1546
  const texts = textSequenceForCheck(viewport, check);
1521
1547
  const match = textOrderMatch(texts, expectedTexts);
1522
1548
  return {
@@ -1538,7 +1564,7 @@ function assessProfile(profile, evidence) {
1538
1564
  }
1539
1565
  if (check.type === "frame_text_visible") {
1540
1566
  const selector = check.selector || "";
1541
- const results = viewports.map((viewport) => {
1567
+ const results = checkViewports.map((viewport) => {
1542
1568
  const frames = frameEvidenceForSelector(viewport, selector);
1543
1569
  const matches = frames
1544
1570
  .map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
@@ -1565,7 +1591,7 @@ function assessProfile(profile, evidence) {
1565
1591
  if (check.type === "frame_no_horizontal_overflow") {
1566
1592
  const selector = check.selector || "";
1567
1593
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
1568
- const results = viewports.map((viewport) => {
1594
+ const results = checkViewports.map((viewport) => {
1569
1595
  const frames = frameEvidenceForSelector(viewport, selector);
1570
1596
  const frameOverflows = frames.map((frame, index) => ({
1571
1597
  index,
@@ -1595,7 +1621,7 @@ function assessProfile(profile, evidence) {
1595
1621
  if (check.type === "text_visible" || check.type === "text_absent") {
1596
1622
  const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
1597
1623
  const expectedVisible = check.type === "text_visible";
1598
- const matches = viewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
1624
+ const matches = checkViewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
1599
1625
  const failed = matches.filter((matched) => matched !== expectedVisible).length;
1600
1626
  checks.push({
1601
1627
  type: check.type,
@@ -1607,7 +1633,7 @@ function assessProfile(profile, evidence) {
1607
1633
  continue;
1608
1634
  }
1609
1635
  if (check.type === "route_inventory") {
1610
- const inventories = viewports
1636
+ const inventories = checkViewports
1611
1637
  .map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
1612
1638
  .filter((item) => item.inventory && typeof item.inventory === "object");
1613
1639
  if (!inventories.length) {
@@ -1659,7 +1685,7 @@ function assessProfile(profile, evidence) {
1659
1685
  }
1660
1686
  if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
1661
1687
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
1662
- const applicable = check.type === "no_mobile_horizontal_overflow" ? viewports.filter((viewport) => viewport.width <= 820) : viewports;
1688
+ const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
1663
1689
  if (!applicable.length) {
1664
1690
  checks.push({
1665
1691
  type: check.type,
package/dist/cli.cjs CHANGED
@@ -7275,6 +7275,7 @@ function normalizeCheck(input, index) {
7275
7275
  text: stringValue2(input.text),
7276
7276
  pattern: stringValue2(input.pattern),
7277
7277
  flags: stringValue2(input.flags),
7278
+ viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `checks[${index}] viewports`),
7278
7279
  allowed_console_texts: normalizeStringList(input.allowed_console_texts ?? input.allowedConsoleTexts ?? input.allow_console_texts ?? input.allowConsoleTexts, `checks[${index}] allowed_console_texts`),
7279
7280
  allowed_console_patterns: normalizeStringList(input.allowed_console_patterns ?? input.allowedConsolePatterns ?? input.allow_console_patterns ?? input.allowConsolePatterns, `checks[${index}] allowed_console_patterns`),
7280
7281
  allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
@@ -7513,15 +7514,22 @@ function successfulRoute(route, targetUrl) {
7513
7514
  const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
7514
7515
  return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
7515
7516
  }
7517
+ function viewportsForCheck(check, viewports) {
7518
+ if (!check.viewports?.length) return viewports;
7519
+ const names = new Set(check.viewports);
7520
+ return viewports.filter((viewport) => names.has(viewport.name));
7521
+ }
7516
7522
  function assessCheckFromEvidence(check, evidence) {
7517
- const viewports = evidence.viewports || [];
7523
+ const viewports = viewportsForCheck(check, evidence.viewports || []);
7518
7524
  if (!viewports.length) {
7519
7525
  return {
7520
7526
  type: check.type,
7521
7527
  label: checkLabel(check),
7522
7528
  status: "failed",
7523
- evidence: {},
7524
- message: "No viewport evidence was captured."
7529
+ evidence: {
7530
+ expected_viewports: check.viewports || []
7531
+ },
7532
+ message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
7525
7533
  };
7526
7534
  }
7527
7535
  if (check.type === "route_loaded") {
@@ -8110,6 +8118,11 @@ function frameTextSample(frame) {
8110
8118
  frame && frame.text,
8111
8119
  ].map((part) => String(part || "")).filter(Boolean).join(" ");
8112
8120
  }
8121
+ function viewportsForCheck(check, viewports) {
8122
+ if (!Array.isArray(check.viewports) || !check.viewports.length) return viewports;
8123
+ const names = new Set(check.viewports);
8124
+ return viewports.filter((viewport) => names.has(viewport.name));
8125
+ }
8113
8126
  function summarizeRouteInventory(viewport, inventory) {
8114
8127
  const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
8115
8128
  const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
@@ -8300,9 +8313,22 @@ function assessProfile(profile, evidence) {
8300
8313
  });
8301
8314
  }
8302
8315
  for (const check of profile.checks || []) {
8316
+ const checkViewports = viewportsForCheck(check, viewports);
8317
+ if (!checkViewports.length) {
8318
+ checks.push({
8319
+ type: check.type,
8320
+ label: check.label || check.type,
8321
+ status: "failed",
8322
+ evidence: { expected_viewports: check.viewports || [] },
8323
+ message: Array.isArray(check.viewports) && check.viewports.length
8324
+ ? "No matching viewport evidence was captured for " + check.viewports.join(", ") + "."
8325
+ : "No viewport evidence was captured.",
8326
+ });
8327
+ continue;
8328
+ }
8303
8329
  if (check.type === "route_loaded") {
8304
8330
  const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
8305
- const failed = viewports.filter((viewport) => {
8331
+ const failed = checkViewports.filter((viewport) => {
8306
8332
  const route = { ...(viewport.route || {}), expected_path: expectedPath };
8307
8333
  route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
8308
8334
  return !routeOk(route, evidence.target_url);
@@ -8313,8 +8339,8 @@ function assessProfile(profile, evidence) {
8313
8339
  status: failed.length ? "failed" : "passed",
8314
8340
  evidence: {
8315
8341
  expected_path: expectedPath,
8316
- observed_paths: viewports.map((viewport) => viewport.route && viewport.route.observed),
8317
- http_statuses: viewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
8342
+ observed_paths: checkViewports.map((viewport) => viewport.route && viewport.route.observed),
8343
+ http_statuses: checkViewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
8318
8344
  },
8319
8345
  message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
8320
8346
  });
@@ -8322,24 +8348,24 @@ function assessProfile(profile, evidence) {
8322
8348
  }
8323
8349
  if (check.type === "selector_visible") {
8324
8350
  const selector = check.selector || "";
8325
- const failed = viewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
8351
+ const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
8326
8352
  checks.push({
8327
8353
  type: check.type,
8328
8354
  label: check.label || check.type,
8329
8355
  status: failed.length ? "failed" : "passed",
8330
- evidence: { selector, visible_counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
8356
+ evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
8331
8357
  message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
8332
8358
  });
8333
8359
  continue;
8334
8360
  }
8335
8361
  if (check.type === "selector_absent") {
8336
8362
  const selector = check.selector || "";
8337
- const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
8363
+ const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
8338
8364
  checks.push({
8339
8365
  type: check.type,
8340
8366
  label: check.label || check.type,
8341
8367
  status: failed.length ? "failed" : "passed",
8342
- evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
8368
+ evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
8343
8369
  message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
8344
8370
  });
8345
8371
  continue;
@@ -8347,12 +8373,12 @@ function assessProfile(profile, evidence) {
8347
8373
  if (check.type === "selector_count_at_least") {
8348
8374
  const selector = check.selector || "";
8349
8375
  const minCount = check.min_count == null ? 1 : check.min_count;
8350
- const failed = viewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
8376
+ const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
8351
8377
  checks.push({
8352
8378
  type: check.type,
8353
8379
  label: check.label || check.type,
8354
8380
  status: failed.length ? "failed" : "passed",
8355
- evidence: { selector, min_count: minCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
8381
+ evidence: { selector, min_count: minCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
8356
8382
  message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
8357
8383
  });
8358
8384
  continue;
@@ -8360,12 +8386,12 @@ function assessProfile(profile, evidence) {
8360
8386
  if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
8361
8387
  const selector = check.selector || "";
8362
8388
  const expectedCount = check.expected_count == null ? 0 : check.expected_count;
8363
- const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
8389
+ const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
8364
8390
  checks.push({
8365
8391
  type: check.type,
8366
8392
  label: check.label || check.type,
8367
8393
  status: failed.length ? "failed" : "passed",
8368
- evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
8394
+ evidence: { selector, expected_count: expectedCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
8369
8395
  message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
8370
8396
  });
8371
8397
  continue;
@@ -8373,7 +8399,7 @@ function assessProfile(profile, evidence) {
8373
8399
  if (check.type === "selector_text_order") {
8374
8400
  const selector = check.selector || "";
8375
8401
  const expectedTexts = check.expected_texts || [];
8376
- const results = viewports.map((viewport) => {
8402
+ const results = checkViewports.map((viewport) => {
8377
8403
  const texts = textSequenceForCheck(viewport, check);
8378
8404
  const match = textOrderMatch(texts, expectedTexts);
8379
8405
  return {
@@ -8395,7 +8421,7 @@ function assessProfile(profile, evidence) {
8395
8421
  }
8396
8422
  if (check.type === "frame_text_visible") {
8397
8423
  const selector = check.selector || "";
8398
- const results = viewports.map((viewport) => {
8424
+ const results = checkViewports.map((viewport) => {
8399
8425
  const frames = frameEvidenceForSelector(viewport, selector);
8400
8426
  const matches = frames
8401
8427
  .map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
@@ -8422,7 +8448,7 @@ function assessProfile(profile, evidence) {
8422
8448
  if (check.type === "frame_no_horizontal_overflow") {
8423
8449
  const selector = check.selector || "";
8424
8450
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
8425
- const results = viewports.map((viewport) => {
8451
+ const results = checkViewports.map((viewport) => {
8426
8452
  const frames = frameEvidenceForSelector(viewport, selector);
8427
8453
  const frameOverflows = frames.map((frame, index) => ({
8428
8454
  index,
@@ -8452,7 +8478,7 @@ function assessProfile(profile, evidence) {
8452
8478
  if (check.type === "text_visible" || check.type === "text_absent") {
8453
8479
  const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
8454
8480
  const expectedVisible = check.type === "text_visible";
8455
- const matches = viewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
8481
+ const matches = checkViewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
8456
8482
  const failed = matches.filter((matched) => matched !== expectedVisible).length;
8457
8483
  checks.push({
8458
8484
  type: check.type,
@@ -8464,7 +8490,7 @@ function assessProfile(profile, evidence) {
8464
8490
  continue;
8465
8491
  }
8466
8492
  if (check.type === "route_inventory") {
8467
- const inventories = viewports
8493
+ const inventories = checkViewports
8468
8494
  .map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
8469
8495
  .filter((item) => item.inventory && typeof item.inventory === "object");
8470
8496
  if (!inventories.length) {
@@ -8516,7 +8542,7 @@ function assessProfile(profile, evidence) {
8516
8542
  }
8517
8543
  if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
8518
8544
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
8519
- const applicable = check.type === "no_mobile_horizontal_overflow" ? viewports.filter((viewport) => viewport.width <= 820) : viewports;
8545
+ const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
8520
8546
  if (!applicable.length) {
8521
8547
  checks.push({
8522
8548
  type: check.type,
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-ZB5DSQF2.js";
13
+ } from "./chunk-G673RKAC.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport
package/dist/index.cjs CHANGED
@@ -9116,6 +9116,7 @@ function normalizeCheck(input, index) {
9116
9116
  text: stringValue5(input.text),
9117
9117
  pattern: stringValue5(input.pattern),
9118
9118
  flags: stringValue5(input.flags),
9119
+ viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `checks[${index}] viewports`),
9119
9120
  allowed_console_texts: normalizeStringList(input.allowed_console_texts ?? input.allowedConsoleTexts ?? input.allow_console_texts ?? input.allowConsoleTexts, `checks[${index}] allowed_console_texts`),
9120
9121
  allowed_console_patterns: normalizeStringList(input.allowed_console_patterns ?? input.allowedConsolePatterns ?? input.allow_console_patterns ?? input.allowConsolePatterns, `checks[${index}] allowed_console_patterns`),
9121
9122
  allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
@@ -9354,15 +9355,22 @@ function successfulRoute(route, targetUrl) {
9354
9355
  const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
9355
9356
  return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
9356
9357
  }
9358
+ function viewportsForCheck(check, viewports) {
9359
+ if (!check.viewports?.length) return viewports;
9360
+ const names = new Set(check.viewports);
9361
+ return viewports.filter((viewport) => names.has(viewport.name));
9362
+ }
9357
9363
  function assessCheckFromEvidence(check, evidence) {
9358
- const viewports = evidence.viewports || [];
9364
+ const viewports = viewportsForCheck(check, evidence.viewports || []);
9359
9365
  if (!viewports.length) {
9360
9366
  return {
9361
9367
  type: check.type,
9362
9368
  label: checkLabel(check),
9363
9369
  status: "failed",
9364
- evidence: {},
9365
- message: "No viewport evidence was captured."
9370
+ evidence: {
9371
+ expected_viewports: check.viewports || []
9372
+ },
9373
+ message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
9366
9374
  };
9367
9375
  }
9368
9376
  if (check.type === "route_loaded") {
@@ -9967,6 +9975,11 @@ function frameTextSample(frame) {
9967
9975
  frame && frame.text,
9968
9976
  ].map((part) => String(part || "")).filter(Boolean).join(" ");
9969
9977
  }
9978
+ function viewportsForCheck(check, viewports) {
9979
+ if (!Array.isArray(check.viewports) || !check.viewports.length) return viewports;
9980
+ const names = new Set(check.viewports);
9981
+ return viewports.filter((viewport) => names.has(viewport.name));
9982
+ }
9970
9983
  function summarizeRouteInventory(viewport, inventory) {
9971
9984
  const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
9972
9985
  const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
@@ -10157,9 +10170,22 @@ function assessProfile(profile, evidence) {
10157
10170
  });
10158
10171
  }
10159
10172
  for (const check of profile.checks || []) {
10173
+ const checkViewports = viewportsForCheck(check, viewports);
10174
+ if (!checkViewports.length) {
10175
+ checks.push({
10176
+ type: check.type,
10177
+ label: check.label || check.type,
10178
+ status: "failed",
10179
+ evidence: { expected_viewports: check.viewports || [] },
10180
+ message: Array.isArray(check.viewports) && check.viewports.length
10181
+ ? "No matching viewport evidence was captured for " + check.viewports.join(", ") + "."
10182
+ : "No viewport evidence was captured.",
10183
+ });
10184
+ continue;
10185
+ }
10160
10186
  if (check.type === "route_loaded") {
10161
10187
  const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
10162
- const failed = viewports.filter((viewport) => {
10188
+ const failed = checkViewports.filter((viewport) => {
10163
10189
  const route = { ...(viewport.route || {}), expected_path: expectedPath };
10164
10190
  route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
10165
10191
  return !routeOk(route, evidence.target_url);
@@ -10170,8 +10196,8 @@ function assessProfile(profile, evidence) {
10170
10196
  status: failed.length ? "failed" : "passed",
10171
10197
  evidence: {
10172
10198
  expected_path: expectedPath,
10173
- observed_paths: viewports.map((viewport) => viewport.route && viewport.route.observed),
10174
- http_statuses: viewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
10199
+ observed_paths: checkViewports.map((viewport) => viewport.route && viewport.route.observed),
10200
+ http_statuses: checkViewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
10175
10201
  },
10176
10202
  message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
10177
10203
  });
@@ -10179,24 +10205,24 @@ function assessProfile(profile, evidence) {
10179
10205
  }
10180
10206
  if (check.type === "selector_visible") {
10181
10207
  const selector = check.selector || "";
10182
- const failed = viewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
10208
+ const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
10183
10209
  checks.push({
10184
10210
  type: check.type,
10185
10211
  label: check.label || check.type,
10186
10212
  status: failed.length ? "failed" : "passed",
10187
- evidence: { selector, visible_counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
10213
+ evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
10188
10214
  message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
10189
10215
  });
10190
10216
  continue;
10191
10217
  }
10192
10218
  if (check.type === "selector_absent") {
10193
10219
  const selector = check.selector || "";
10194
- const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
10220
+ const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
10195
10221
  checks.push({
10196
10222
  type: check.type,
10197
10223
  label: check.label || check.type,
10198
10224
  status: failed.length ? "failed" : "passed",
10199
- evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
10225
+ evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
10200
10226
  message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
10201
10227
  });
10202
10228
  continue;
@@ -10204,12 +10230,12 @@ function assessProfile(profile, evidence) {
10204
10230
  if (check.type === "selector_count_at_least") {
10205
10231
  const selector = check.selector || "";
10206
10232
  const minCount = check.min_count == null ? 1 : check.min_count;
10207
- const failed = viewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
10233
+ const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
10208
10234
  checks.push({
10209
10235
  type: check.type,
10210
10236
  label: check.label || check.type,
10211
10237
  status: failed.length ? "failed" : "passed",
10212
- evidence: { selector, min_count: minCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
10238
+ evidence: { selector, min_count: minCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
10213
10239
  message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
10214
10240
  });
10215
10241
  continue;
@@ -10217,12 +10243,12 @@ function assessProfile(profile, evidence) {
10217
10243
  if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
10218
10244
  const selector = check.selector || "";
10219
10245
  const expectedCount = check.expected_count == null ? 0 : check.expected_count;
10220
- const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
10246
+ const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
10221
10247
  checks.push({
10222
10248
  type: check.type,
10223
10249
  label: check.label || check.type,
10224
10250
  status: failed.length ? "failed" : "passed",
10225
- evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
10251
+ evidence: { selector, expected_count: expectedCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
10226
10252
  message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
10227
10253
  });
10228
10254
  continue;
@@ -10230,7 +10256,7 @@ function assessProfile(profile, evidence) {
10230
10256
  if (check.type === "selector_text_order") {
10231
10257
  const selector = check.selector || "";
10232
10258
  const expectedTexts = check.expected_texts || [];
10233
- const results = viewports.map((viewport) => {
10259
+ const results = checkViewports.map((viewport) => {
10234
10260
  const texts = textSequenceForCheck(viewport, check);
10235
10261
  const match = textOrderMatch(texts, expectedTexts);
10236
10262
  return {
@@ -10252,7 +10278,7 @@ function assessProfile(profile, evidence) {
10252
10278
  }
10253
10279
  if (check.type === "frame_text_visible") {
10254
10280
  const selector = check.selector || "";
10255
- const results = viewports.map((viewport) => {
10281
+ const results = checkViewports.map((viewport) => {
10256
10282
  const frames = frameEvidenceForSelector(viewport, selector);
10257
10283
  const matches = frames
10258
10284
  .map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
@@ -10279,7 +10305,7 @@ function assessProfile(profile, evidence) {
10279
10305
  if (check.type === "frame_no_horizontal_overflow") {
10280
10306
  const selector = check.selector || "";
10281
10307
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
10282
- const results = viewports.map((viewport) => {
10308
+ const results = checkViewports.map((viewport) => {
10283
10309
  const frames = frameEvidenceForSelector(viewport, selector);
10284
10310
  const frameOverflows = frames.map((frame, index) => ({
10285
10311
  index,
@@ -10309,7 +10335,7 @@ function assessProfile(profile, evidence) {
10309
10335
  if (check.type === "text_visible" || check.type === "text_absent") {
10310
10336
  const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
10311
10337
  const expectedVisible = check.type === "text_visible";
10312
- const matches = viewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
10338
+ const matches = checkViewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
10313
10339
  const failed = matches.filter((matched) => matched !== expectedVisible).length;
10314
10340
  checks.push({
10315
10341
  type: check.type,
@@ -10321,7 +10347,7 @@ function assessProfile(profile, evidence) {
10321
10347
  continue;
10322
10348
  }
10323
10349
  if (check.type === "route_inventory") {
10324
- const inventories = viewports
10350
+ const inventories = checkViewports
10325
10351
  .map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
10326
10352
  .filter((item) => item.inventory && typeof item.inventory === "object");
10327
10353
  if (!inventories.length) {
@@ -10373,7 +10399,7 @@ function assessProfile(profile, evidence) {
10373
10399
  }
10374
10400
  if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
10375
10401
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
10376
- const applicable = check.type === "no_mobile_horizontal_overflow" ? viewports.filter((viewport) => viewport.width <= 820) : viewports;
10402
+ const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
10377
10403
  if (!applicable.length) {
10378
10404
  checks.push({
10379
10405
  type: check.type,
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  resolveRiddleProofProfileTimeoutSec,
59
59
  slugifyRiddleProofProfileName,
60
60
  summarizeRiddleProofProfileResult
61
- } from "./chunk-ZB5DSQF2.js";
61
+ } from "./chunk-G673RKAC.js";
62
62
  import {
63
63
  DEFAULT_RIDDLE_API_BASE_URL,
64
64
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -445,6 +445,7 @@ function normalizeCheck(input, index) {
445
445
  text: stringValue(input.text),
446
446
  pattern: stringValue(input.pattern),
447
447
  flags: stringValue(input.flags),
448
+ viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `checks[${index}] viewports`),
448
449
  allowed_console_texts: normalizeStringList(input.allowed_console_texts ?? input.allowedConsoleTexts ?? input.allow_console_texts ?? input.allowConsoleTexts, `checks[${index}] allowed_console_texts`),
449
450
  allowed_console_patterns: normalizeStringList(input.allowed_console_patterns ?? input.allowedConsolePatterns ?? input.allow_console_patterns ?? input.allowConsolePatterns, `checks[${index}] allowed_console_patterns`),
450
451
  allowed_page_error_texts: normalizeStringList(input.allowed_page_error_texts ?? input.allowedPageErrorTexts ?? input.allow_page_error_texts ?? input.allowPageErrorTexts, `checks[${index}] allowed_page_error_texts`),
@@ -683,15 +684,22 @@ function successfulRoute(route, targetUrl) {
683
684
  const matched = route.matched || routePathMatches(route.observed, route.expected_path, targetUrl);
684
685
  return matched && !route.error && (route.http_status === null || route.http_status === void 0 || route.http_status < 400);
685
686
  }
687
+ function viewportsForCheck(check, viewports) {
688
+ if (!check.viewports?.length) return viewports;
689
+ const names = new Set(check.viewports);
690
+ return viewports.filter((viewport) => names.has(viewport.name));
691
+ }
686
692
  function assessCheckFromEvidence(check, evidence) {
687
- const viewports = evidence.viewports || [];
693
+ const viewports = viewportsForCheck(check, evidence.viewports || []);
688
694
  if (!viewports.length) {
689
695
  return {
690
696
  type: check.type,
691
697
  label: checkLabel(check),
692
698
  status: "failed",
693
- evidence: {},
694
- message: "No viewport evidence was captured."
699
+ evidence: {
700
+ expected_viewports: check.viewports || []
701
+ },
702
+ message: check.viewports?.length ? `No matching viewport evidence was captured for ${check.viewports.join(", ")}.` : "No viewport evidence was captured."
695
703
  };
696
704
  }
697
705
  if (check.type === "route_loaded") {
@@ -1296,6 +1304,11 @@ function frameTextSample(frame) {
1296
1304
  frame && frame.text,
1297
1305
  ].map((part) => String(part || "")).filter(Boolean).join(" ");
1298
1306
  }
1307
+ function viewportsForCheck(check, viewports) {
1308
+ if (!Array.isArray(check.viewports) || !check.viewports.length) return viewports;
1309
+ const names = new Set(check.viewports);
1310
+ return viewports.filter((viewport) => names.has(viewport.name));
1311
+ }
1299
1312
  function summarizeRouteInventory(viewport, inventory) {
1300
1313
  const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
1301
1314
  const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
@@ -1486,9 +1499,22 @@ function assessProfile(profile, evidence) {
1486
1499
  });
1487
1500
  }
1488
1501
  for (const check of profile.checks || []) {
1502
+ const checkViewports = viewportsForCheck(check, viewports);
1503
+ if (!checkViewports.length) {
1504
+ checks.push({
1505
+ type: check.type,
1506
+ label: check.label || check.type,
1507
+ status: "failed",
1508
+ evidence: { expected_viewports: check.viewports || [] },
1509
+ message: Array.isArray(check.viewports) && check.viewports.length
1510
+ ? "No matching viewport evidence was captured for " + check.viewports.join(", ") + "."
1511
+ : "No viewport evidence was captured.",
1512
+ });
1513
+ continue;
1514
+ }
1489
1515
  if (check.type === "route_loaded") {
1490
1516
  const expectedPath = check.expected_path || new URL(evidence.target_url).pathname || "/";
1491
- const failed = viewports.filter((viewport) => {
1517
+ const failed = checkViewports.filter((viewport) => {
1492
1518
  const route = { ...(viewport.route || {}), expected_path: expectedPath };
1493
1519
  route.matched = routePathMatches(route.observed, expectedPath, evidence.target_url) || route.matched;
1494
1520
  return !routeOk(route, evidence.target_url);
@@ -1499,8 +1525,8 @@ function assessProfile(profile, evidence) {
1499
1525
  status: failed.length ? "failed" : "passed",
1500
1526
  evidence: {
1501
1527
  expected_path: expectedPath,
1502
- observed_paths: viewports.map((viewport) => viewport.route && viewport.route.observed),
1503
- http_statuses: viewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
1528
+ observed_paths: checkViewports.map((viewport) => viewport.route && viewport.route.observed),
1529
+ http_statuses: checkViewports.map((viewport) => viewport.route ? viewport.route.http_status ?? null : null),
1504
1530
  },
1505
1531
  message: failed.length ? "Route did not load as " + expectedPath + " in " + failed.length + " viewport(s)." : undefined,
1506
1532
  });
@@ -1508,24 +1534,24 @@ function assessProfile(profile, evidence) {
1508
1534
  }
1509
1535
  if (check.type === "selector_visible") {
1510
1536
  const selector = check.selector || "";
1511
- const failed = viewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
1537
+ const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].visible_count < 1);
1512
1538
  checks.push({
1513
1539
  type: check.type,
1514
1540
  label: check.label || check.type,
1515
1541
  status: failed.length ? "failed" : "passed",
1516
- evidence: { selector, visible_counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
1542
+ evidence: { selector, visible_counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].visible_count : 0) },
1517
1543
  message: failed.length ? "Selector " + selector + " was not visible in " + failed.length + " viewport(s)." : undefined,
1518
1544
  });
1519
1545
  continue;
1520
1546
  }
1521
1547
  if (check.type === "selector_absent") {
1522
1548
  const selector = check.selector || "";
1523
- const failed = viewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
1549
+ const failed = checkViewports.filter((viewport) => viewport.selectors && viewport.selectors[selector] && viewport.selectors[selector].count > 0);
1524
1550
  checks.push({
1525
1551
  type: check.type,
1526
1552
  label: check.label || check.type,
1527
1553
  status: failed.length ? "failed" : "passed",
1528
- evidence: { selector, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1554
+ evidence: { selector, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1529
1555
  message: failed.length ? "Selector " + selector + " was present in " + failed.length + " viewport(s)." : undefined,
1530
1556
  });
1531
1557
  continue;
@@ -1533,12 +1559,12 @@ function assessProfile(profile, evidence) {
1533
1559
  if (check.type === "selector_count_at_least") {
1534
1560
  const selector = check.selector || "";
1535
1561
  const minCount = check.min_count == null ? 1 : check.min_count;
1536
- const failed = viewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
1562
+ const failed = checkViewports.filter((viewport) => !viewport.selectors || !viewport.selectors[selector] || viewport.selectors[selector].count < minCount);
1537
1563
  checks.push({
1538
1564
  type: check.type,
1539
1565
  label: check.label || check.type,
1540
1566
  status: failed.length ? "failed" : "passed",
1541
- evidence: { selector, min_count: minCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1567
+ evidence: { selector, min_count: minCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1542
1568
  message: failed.length ? "Selector " + selector + " count was below " + minCount + " in " + failed.length + " viewport(s)." : undefined,
1543
1569
  });
1544
1570
  continue;
@@ -1546,12 +1572,12 @@ function assessProfile(profile, evidence) {
1546
1572
  if (check.type === "selector_count_equals" || check.type === "selector_count_equal" || check.type === "selector_count_eq") {
1547
1573
  const selector = check.selector || "";
1548
1574
  const expectedCount = check.expected_count == null ? 0 : check.expected_count;
1549
- const failed = viewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
1575
+ const failed = checkViewports.filter((viewport) => (viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) !== expectedCount);
1550
1576
  checks.push({
1551
1577
  type: check.type,
1552
1578
  label: check.label || check.type,
1553
1579
  status: failed.length ? "failed" : "passed",
1554
- evidence: { selector, expected_count: expectedCount, counts: viewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1580
+ evidence: { selector, expected_count: expectedCount, counts: checkViewports.map((viewport) => viewport.selectors && viewport.selectors[selector] ? viewport.selectors[selector].count : 0) },
1555
1581
  message: failed.length ? "Selector " + selector + " count did not equal " + expectedCount + " in " + failed.length + " viewport(s)." : undefined,
1556
1582
  });
1557
1583
  continue;
@@ -1559,7 +1585,7 @@ function assessProfile(profile, evidence) {
1559
1585
  if (check.type === "selector_text_order") {
1560
1586
  const selector = check.selector || "";
1561
1587
  const expectedTexts = check.expected_texts || [];
1562
- const results = viewports.map((viewport) => {
1588
+ const results = checkViewports.map((viewport) => {
1563
1589
  const texts = textSequenceForCheck(viewport, check);
1564
1590
  const match = textOrderMatch(texts, expectedTexts);
1565
1591
  return {
@@ -1581,7 +1607,7 @@ function assessProfile(profile, evidence) {
1581
1607
  }
1582
1608
  if (check.type === "frame_text_visible") {
1583
1609
  const selector = check.selector || "";
1584
- const results = viewports.map((viewport) => {
1610
+ const results = checkViewports.map((viewport) => {
1585
1611
  const frames = frameEvidenceForSelector(viewport, selector);
1586
1612
  const matches = frames
1587
1613
  .map((frame, index) => ({ index, frame, matched: textMatches(frameTextSample(frame), check) }))
@@ -1608,7 +1634,7 @@ function assessProfile(profile, evidence) {
1608
1634
  if (check.type === "frame_no_horizontal_overflow") {
1609
1635
  const selector = check.selector || "";
1610
1636
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
1611
- const results = viewports.map((viewport) => {
1637
+ const results = checkViewports.map((viewport) => {
1612
1638
  const frames = frameEvidenceForSelector(viewport, selector);
1613
1639
  const frameOverflows = frames.map((frame, index) => ({
1614
1640
  index,
@@ -1638,7 +1664,7 @@ function assessProfile(profile, evidence) {
1638
1664
  if (check.type === "text_visible" || check.type === "text_absent") {
1639
1665
  const key = check.pattern ? "pattern:" + check.pattern + "/" + (check.flags || "") : "text:" + (check.text || "");
1640
1666
  const expectedVisible = check.type === "text_visible";
1641
- const matches = viewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
1667
+ const matches = checkViewports.map((viewport) => viewport.text_matches && typeof viewport.text_matches[key] === "boolean" ? viewport.text_matches[key] : textMatches(viewport.body_text_sample || "", check));
1642
1668
  const failed = matches.filter((matched) => matched !== expectedVisible).length;
1643
1669
  checks.push({
1644
1670
  type: check.type,
@@ -1650,7 +1676,7 @@ function assessProfile(profile, evidence) {
1650
1676
  continue;
1651
1677
  }
1652
1678
  if (check.type === "route_inventory") {
1653
- const inventories = viewports
1679
+ const inventories = checkViewports
1654
1680
  .map((viewport) => ({ viewport: viewport.name, inventory: viewport.route_inventory }))
1655
1681
  .filter((item) => item.inventory && typeof item.inventory === "object");
1656
1682
  if (!inventories.length) {
@@ -1702,7 +1728,7 @@ function assessProfile(profile, evidence) {
1702
1728
  }
1703
1729
  if (check.type === "no_horizontal_overflow" || check.type === "no_mobile_horizontal_overflow") {
1704
1730
  const maxOverflow = check.max_overflow_px == null ? 4 : check.max_overflow_px;
1705
- const applicable = check.type === "no_mobile_horizontal_overflow" ? viewports.filter((viewport) => viewport.width <= 820) : viewports;
1731
+ const applicable = check.type === "no_mobile_horizontal_overflow" ? checkViewports.filter((viewport) => viewport.width <= 820) : checkViewports;
1706
1732
  if (!applicable.length) {
1707
1733
  checks.push({
1708
1734
  type: check.type,
@@ -83,6 +83,7 @@ interface RiddleProofProfileCheck {
83
83
  text?: string;
84
84
  pattern?: string;
85
85
  flags?: string;
86
+ viewports?: string[];
86
87
  allowed_console_texts?: string[];
87
88
  allowed_console_patterns?: string[];
88
89
  allowed_page_error_texts?: string[];
package/dist/profile.d.ts CHANGED
@@ -83,6 +83,7 @@ interface RiddleProofProfileCheck {
83
83
  text?: string;
84
84
  pattern?: string;
85
85
  flags?: string;
86
+ viewports?: string[];
86
87
  allowed_console_texts?: string[];
87
88
  allowed_console_patterns?: string[];
88
89
  allowed_page_error_texts?: string[];
package/dist/profile.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  resolveRiddleProofProfileTimeoutSec,
20
20
  slugifyRiddleProofProfileName,
21
21
  summarizeRiddleProofProfileResult
22
- } from "./chunk-ZB5DSQF2.js";
22
+ } from "./chunk-G673RKAC.js";
23
23
  export {
24
24
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
25
25
  RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
292
292
  blocking?: boolean;
293
293
  details?: Record<string, unknown>;
294
294
  ok: boolean;
295
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
295
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
296
296
  state_path: string;
297
297
  stage: any;
298
298
  summary: string;
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
382
382
  continueWithStage?: WorkflowStage | null;
383
383
  blocking?: boolean;
384
384
  details?: Record<string, unknown>;
385
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
385
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
386
386
  state_path: string;
387
387
  stage: any;
388
388
  checkpoint: string;
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
659
659
  error?: undefined;
660
660
  } | {
661
661
  ok: boolean;
662
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
662
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
663
663
  state_path: string;
664
664
  stage: any;
665
665
  summary: string;
@@ -292,7 +292,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
292
292
  blocking?: boolean;
293
293
  details?: Record<string, unknown>;
294
294
  ok: boolean;
295
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
295
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
296
296
  state_path: string;
297
297
  stage: any;
298
298
  summary: string;
@@ -382,7 +382,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
382
382
  continueWithStage?: WorkflowStage | null;
383
383
  blocking?: boolean;
384
384
  details?: Record<string, unknown>;
385
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
385
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
386
386
  state_path: string;
387
387
  stage: any;
388
388
  checkpoint: string;
@@ -659,7 +659,7 @@ declare function executeWorkflow(params: WorkflowParams, pluginConfig: any, reso
659
659
  error?: undefined;
660
660
  } | {
661
661
  ok: boolean;
662
- action: "author" | "recon" | "ship" | "implement" | "verify" | "setup" | "run";
662
+ action: "setup" | "recon" | "author" | "implement" | "verify" | "ship" | "run";
663
663
  state_path: string;
664
664
  stage: any;
665
665
  summary: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.43",
3
+ "version": "0.7.44",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",