@riddledc/riddle-proof 0.7.83 → 0.7.85

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.
@@ -226,13 +226,15 @@ function profileScreenshotLabels(viewports) {
226
226
  }
227
227
  return labels;
228
228
  }
229
- function profileSetupSummary(viewports, actionCount) {
229
+ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewport) {
230
230
  return toJsonValue({
231
231
  viewport_count: viewports.length,
232
232
  action_count: actionCount ?? null,
233
233
  viewports: viewports.map((viewport) => {
234
+ const expectedActionCount = expectedActionCountByViewport?.get(viewport.name) ?? actionCount;
234
235
  const results = viewport.setup_action_results || [];
235
- const failed = results.filter((result) => result.ok === false);
236
+ const failed = results.filter((result) => result.ok === false && result.optional !== true);
237
+ const optionalFailed = results.filter((result) => result.ok === false && result.optional === true);
236
238
  const successfulClicks = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false);
237
239
  const clickCountValues = successfulClicks.map((result) => typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : void 0).filter((value) => value !== void 0);
238
240
  const clickedItems = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false).map((result) => {
@@ -254,7 +256,8 @@ function profileSetupSummary(viewports, actionCount) {
254
256
  })).filter((item) => item.text).slice(-6);
255
257
  return {
256
258
  name: viewport.name,
257
- ok: (actionCount === void 0 ? results.length > 0 : results.length >= actionCount) && failed.length === 0,
259
+ expected_action_count: expectedActionCount ?? null,
260
+ ok: (expectedActionCount === void 0 ? results.length > 0 : results.length >= expectedActionCount) && failed.length === 0,
258
261
  result_count: results.length,
259
262
  observed_path: viewport.route?.observed ?? null,
260
263
  final_url: viewport.url ?? null,
@@ -274,6 +277,13 @@ function profileSetupSummary(viewports, actionCount) {
274
277
  selector: result.selector ?? null,
275
278
  frame_selector: result.frame_selector ?? null,
276
279
  reason: result.reason ?? result.error ?? null
280
+ })),
281
+ optional_failed: optionalFailed.map((result) => ({
282
+ ordinal: result.ordinal ?? null,
283
+ action: profileSetupResultAction(result),
284
+ selector: result.selector ?? null,
285
+ frame_selector: result.frame_selector ?? null,
286
+ reason: result.reason ?? result.error ?? null
277
287
  }))
278
288
  };
279
289
  })
@@ -491,6 +501,8 @@ function normalizeSetupAction(input, index) {
491
501
  repeat: normalizeSetupActionRepeat(input, index),
492
502
  reload: input.reload === true,
493
503
  storage: normalizeSetupActionStorage(input.storage, index),
504
+ viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `target.setup_actions[${index}] viewports`),
505
+ optional: input.optional === true || input.required === false,
494
506
  continue_on_failure: input.continue_on_failure === true || input.continueOnFailure === true
495
507
  };
496
508
  }
@@ -1063,6 +1075,13 @@ function viewportsForCheck(check, viewports) {
1063
1075
  const names = new Set(check.viewports);
1064
1076
  return viewports.filter((viewport) => names.has(viewport.name));
1065
1077
  }
1078
+ function setupActionAppliesToViewport(action, viewportName) {
1079
+ if (!action.viewports?.length) return true;
1080
+ return Boolean(viewportName && action.viewports.includes(viewportName));
1081
+ }
1082
+ function setupActionsForViewport(actions, viewportName) {
1083
+ return (actions || []).filter((action) => setupActionAppliesToViewport(action, viewportName));
1084
+ }
1066
1085
  function assessCheckFromEvidence(check, evidence) {
1067
1086
  const viewports = viewportsForCheck(check, evidence.viewports || []);
1068
1087
  if (!viewports.length) {
@@ -1443,10 +1462,24 @@ function assessSetupActionsFromEvidence(profile, evidence) {
1443
1462
  const actionCount = profile.target.setup_actions.length;
1444
1463
  const failed = [];
1445
1464
  const viewports = evidence.viewports || [];
1465
+ const expectedActionCountByViewport = /* @__PURE__ */ new Map();
1466
+ const capturedViewportNames = new Set(viewports.map((viewport) => viewport.name));
1467
+ for (const action of profile.target.setup_actions) {
1468
+ if (action.viewports?.length && !action.viewports.some((name) => capturedViewportNames.has(name))) {
1469
+ failed.push({
1470
+ viewport: null,
1471
+ action: action.type,
1472
+ selector: action.selector ?? null,
1473
+ reason: `setup action scoped to missing viewport(s): ${action.viewports.join(", ")}`
1474
+ });
1475
+ }
1476
+ }
1446
1477
  for (const viewport of viewports) {
1478
+ const expectedActionCount = setupActionsForViewport(profile.target.setup_actions, viewport.name).length;
1479
+ expectedActionCountByViewport.set(viewport.name, expectedActionCount);
1447
1480
  const results = viewport.setup_action_results || [];
1448
1481
  for (const result of results) {
1449
- if (result.ok === false) {
1482
+ if (result.ok === false && result.optional !== true) {
1450
1483
  failed.push({
1451
1484
  viewport: viewport.name,
1452
1485
  action: result.action ?? result.type ?? null,
@@ -1457,12 +1490,12 @@ function assessSetupActionsFromEvidence(profile, evidence) {
1457
1490
  });
1458
1491
  }
1459
1492
  }
1460
- if (results.length < actionCount && results.every((result) => result.ok !== false)) {
1493
+ if (results.length < expectedActionCount && results.every((result) => result.ok !== false || result.optional === true)) {
1461
1494
  failed.push({
1462
1495
  viewport: viewport.name,
1463
1496
  action: "setup_actions",
1464
1497
  selector: null,
1465
- reason: `missing setup action results: ${results.length}/${actionCount}`
1498
+ reason: `missing setup action results: ${results.length}/${expectedActionCount}`
1466
1499
  });
1467
1500
  }
1468
1501
  }
@@ -1474,10 +1507,11 @@ function assessSetupActionsFromEvidence(profile, evidence) {
1474
1507
  action_count: actionCount,
1475
1508
  viewports: viewports.map((viewport) => ({
1476
1509
  name: viewport.name,
1477
- ok: (viewport.setup_action_results || []).length >= actionCount && (viewport.setup_action_results || []).every((result) => result.ok !== false),
1510
+ expected_action_count: expectedActionCountByViewport.get(viewport.name) ?? actionCount,
1511
+ ok: (viewport.setup_action_results || []).length >= (expectedActionCountByViewport.get(viewport.name) ?? actionCount) && (viewport.setup_action_results || []).every((result) => result.ok !== false || result.optional === true),
1478
1512
  result_count: (viewport.setup_action_results || []).length
1479
1513
  })),
1480
- setup_summary: profileSetupSummary(viewports, actionCount),
1514
+ setup_summary: profileSetupSummary(viewports, actionCount, expectedActionCountByViewport),
1481
1515
  failed
1482
1516
  },
1483
1517
  message: failed.length ? `Setup actions failed in ${failed.length} viewport action(s).` : void 0
@@ -1863,6 +1897,13 @@ function viewportsForCheck(check, viewports) {
1863
1897
  const names = new Set(check.viewports);
1864
1898
  return viewports.filter((viewport) => names.has(viewport.name));
1865
1899
  }
1900
+ function setupActionAppliesToViewport(action, viewportName) {
1901
+ if (!Array.isArray(action.viewports) || !action.viewports.length) return true;
1902
+ return Boolean(viewportName && action.viewports.includes(viewportName));
1903
+ }
1904
+ function setupActionsForViewport(actions, viewportName) {
1905
+ return (actions || []).filter((action) => setupActionAppliesToViewport(action, viewportName));
1906
+ }
1866
1907
  function summarizeRouteInventory(viewport, inventory) {
1867
1908
  const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
1868
1909
  const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
@@ -2011,13 +2052,17 @@ function profileScreenshotLabels(viewports) {
2011
2052
  }
2012
2053
  return labels;
2013
2054
  }
2014
- function profileSetupSummary(viewports, actionCount) {
2055
+ function profileSetupSummary(viewports, actionCount, expectedActionCountsByViewport) {
2015
2056
  return {
2016
2057
  viewport_count: (viewports || []).length,
2017
2058
  action_count: actionCount ?? null,
2018
2059
  viewports: (viewports || []).map((viewport) => {
2060
+ const expectedActionCount = expectedActionCountsByViewport && expectedActionCountsByViewport[viewport.name] !== undefined
2061
+ ? expectedActionCountsByViewport[viewport.name]
2062
+ : actionCount;
2019
2063
  const results = viewport.setup_action_results || [];
2020
- const failed = results.filter((result) => result && result.ok === false);
2064
+ const failed = results.filter((result) => result && result.ok === false && result.optional !== true);
2065
+ const optionalFailed = results.filter((result) => result && result.ok === false && result.optional === true);
2021
2066
  const successfulClicks = results.filter((result) => result && profileSetupResultAction(result) === "click" && result.ok !== false);
2022
2067
  const clickCountValues = successfulClicks
2023
2068
  .map((result) => typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : undefined)
@@ -2051,7 +2096,8 @@ function profileSetupSummary(viewports, actionCount) {
2051
2096
  .slice(-6);
2052
2097
  return {
2053
2098
  name: viewport.name,
2054
- ok: (actionCount === undefined ? results.length > 0 : results.length >= actionCount) && failed.length === 0,
2099
+ expected_action_count: expectedActionCount ?? null,
2100
+ ok: (expectedActionCount === undefined ? results.length > 0 : results.length >= expectedActionCount) && failed.length === 0,
2055
2101
  result_count: results.length,
2056
2102
  observed_path: viewport.route && viewport.route.observed || null,
2057
2103
  final_url: viewport.url || null,
@@ -2072,6 +2118,13 @@ function profileSetupSummary(viewports, actionCount) {
2072
2118
  frame_selector: result.frame_selector ?? null,
2073
2119
  reason: result.reason || result.error || null,
2074
2120
  })),
2121
+ optional_failed: optionalFailed.map((result) => ({
2122
+ ordinal: result.ordinal ?? null,
2123
+ action: profileSetupResultAction(result),
2124
+ selector: result.selector ?? null,
2125
+ frame_selector: result.frame_selector ?? null,
2126
+ reason: result.reason || result.error || null,
2127
+ })),
2075
2128
  };
2076
2129
  }),
2077
2130
  };
@@ -2174,10 +2227,24 @@ function assessProfile(profile, evidence) {
2174
2227
  if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
2175
2228
  const actionCount = profile.target.setup_actions.length;
2176
2229
  const failed = [];
2230
+ const expectedActionCountsByViewport = {};
2231
+ const capturedViewportNames = new Set(viewports.map((viewport) => viewport.name));
2232
+ for (const action of profile.target.setup_actions) {
2233
+ if (Array.isArray(action.viewports) && action.viewports.length && !action.viewports.some((name) => capturedViewportNames.has(name))) {
2234
+ failed.push({
2235
+ viewport: null,
2236
+ action: action.type,
2237
+ selector: action.selector || null,
2238
+ reason: "setup action scoped to missing viewport(s): " + action.viewports.join(", "),
2239
+ });
2240
+ }
2241
+ }
2177
2242
  for (const viewport of viewports) {
2243
+ const expectedActionCount = setupActionsForViewport(profile.target.setup_actions, viewport.name).length;
2244
+ expectedActionCountsByViewport[viewport.name] = expectedActionCount;
2178
2245
  const results = viewport.setup_action_results || [];
2179
2246
  for (const result of results) {
2180
- if (result && result.ok === false) {
2247
+ if (result && result.ok === false && result.optional !== true) {
2181
2248
  failed.push({
2182
2249
  viewport: viewport.name,
2183
2250
  action: result.action || result.type || null,
@@ -2188,12 +2255,12 @@ function assessProfile(profile, evidence) {
2188
2255
  });
2189
2256
  }
2190
2257
  }
2191
- if (results.length < actionCount && results.every((result) => !result || result.ok !== false)) {
2258
+ if (results.length < expectedActionCount && results.every((result) => !result || result.ok !== false || result.optional === true)) {
2192
2259
  failed.push({
2193
2260
  viewport: viewport.name,
2194
2261
  action: "setup_actions",
2195
2262
  selector: null,
2196
- reason: "missing setup action results: " + results.length + "/" + actionCount,
2263
+ reason: "missing setup action results: " + results.length + "/" + expectedActionCount,
2197
2264
  });
2198
2265
  }
2199
2266
  }
@@ -2205,11 +2272,12 @@ function assessProfile(profile, evidence) {
2205
2272
  action_count: actionCount,
2206
2273
  viewports: viewports.map((viewport) => ({
2207
2274
  name: viewport.name,
2208
- ok: (viewport.setup_action_results || []).length >= actionCount
2209
- && (viewport.setup_action_results || []).every((result) => !result || result.ok !== false),
2275
+ expected_action_count: expectedActionCountsByViewport[viewport.name] ?? actionCount,
2276
+ ok: (viewport.setup_action_results || []).length >= (expectedActionCountsByViewport[viewport.name] ?? actionCount)
2277
+ && (viewport.setup_action_results || []).every((result) => !result || result.ok !== false || result.optional === true),
2210
2278
  result_count: (viewport.setup_action_results || []).length,
2211
2279
  })),
2212
- setup_summary: profileSetupSummary(viewports, actionCount),
2280
+ setup_summary: profileSetupSummary(viewports, actionCount, expectedActionCountsByViewport),
2213
2281
  failed,
2214
2282
  },
2215
2283
  message: failed.length ? "Setup actions failed in " + failed.length + " viewport action(s)." : undefined,
@@ -3003,7 +3071,7 @@ async function registerNetworkMocks(mocks) {
3003
3071
  async function executeSetupAction(action, ordinal, viewport) {
3004
3072
  const type = setupActionType(action);
3005
3073
  const frameSelector = setupFrameSelector(action);
3006
- const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null };
3074
+ const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null, optional: action.optional === true };
3007
3075
  const timeout = setupNumber(action.timeout_ms, 5000);
3008
3076
  try {
3009
3077
  if (type === "wait") {
@@ -3477,7 +3545,7 @@ async function executeSetupActions(actions, viewport) {
3477
3545
  : result);
3478
3546
  const afterMs = setupNumber(action.after_ms, 0);
3479
3547
  if (afterMs) await page.waitForTimeout(afterMs);
3480
- if (result.ok === false && action.continue_on_failure !== true) {
3548
+ if (result.ok === false && action.continue_on_failure !== true && action.optional !== true) {
3481
3549
  shouldStop = true;
3482
3550
  break;
3483
3551
  }
@@ -3946,7 +4014,7 @@ async function captureViewport(viewport) {
3946
4014
  await page.waitForTimeout(profile.target.wait_ms);
3947
4015
  }
3948
4016
  const setupActionResults = (!navigationError && !waitError)
3949
- ? await executeSetupActions(profile.target.setup_actions || [], viewport)
4017
+ ? await executeSetupActions(setupActionsForViewport(profile.target.setup_actions || [], viewport.name), viewport)
3950
4018
  : [];
3951
4019
  const dom = await page.evaluate(() => {
3952
4020
  const body = document.body;
package/dist/cli.cjs CHANGED
@@ -7113,13 +7113,15 @@ function profileScreenshotLabels(viewports) {
7113
7113
  }
7114
7114
  return labels;
7115
7115
  }
7116
- function profileSetupSummary(viewports, actionCount) {
7116
+ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewport) {
7117
7117
  return toJsonValue({
7118
7118
  viewport_count: viewports.length,
7119
7119
  action_count: actionCount ?? null,
7120
7120
  viewports: viewports.map((viewport) => {
7121
+ const expectedActionCount = expectedActionCountByViewport?.get(viewport.name) ?? actionCount;
7121
7122
  const results = viewport.setup_action_results || [];
7122
- const failed = results.filter((result) => result.ok === false);
7123
+ const failed = results.filter((result) => result.ok === false && result.optional !== true);
7124
+ const optionalFailed = results.filter((result) => result.ok === false && result.optional === true);
7123
7125
  const successfulClicks = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false);
7124
7126
  const clickCountValues = successfulClicks.map((result) => typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : void 0).filter((value) => value !== void 0);
7125
7127
  const clickedItems = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false).map((result) => {
@@ -7141,7 +7143,8 @@ function profileSetupSummary(viewports, actionCount) {
7141
7143
  })).filter((item) => item.text).slice(-6);
7142
7144
  return {
7143
7145
  name: viewport.name,
7144
- ok: (actionCount === void 0 ? results.length > 0 : results.length >= actionCount) && failed.length === 0,
7146
+ expected_action_count: expectedActionCount ?? null,
7147
+ ok: (expectedActionCount === void 0 ? results.length > 0 : results.length >= expectedActionCount) && failed.length === 0,
7145
7148
  result_count: results.length,
7146
7149
  observed_path: viewport.route?.observed ?? null,
7147
7150
  final_url: viewport.url ?? null,
@@ -7161,6 +7164,13 @@ function profileSetupSummary(viewports, actionCount) {
7161
7164
  selector: result.selector ?? null,
7162
7165
  frame_selector: result.frame_selector ?? null,
7163
7166
  reason: result.reason ?? result.error ?? null
7167
+ })),
7168
+ optional_failed: optionalFailed.map((result) => ({
7169
+ ordinal: result.ordinal ?? null,
7170
+ action: profileSetupResultAction(result),
7171
+ selector: result.selector ?? null,
7172
+ frame_selector: result.frame_selector ?? null,
7173
+ reason: result.reason ?? result.error ?? null
7164
7174
  }))
7165
7175
  };
7166
7176
  })
@@ -7378,6 +7388,8 @@ function normalizeSetupAction(input, index) {
7378
7388
  repeat: normalizeSetupActionRepeat(input, index),
7379
7389
  reload: input.reload === true,
7380
7390
  storage: normalizeSetupActionStorage(input.storage, index),
7391
+ viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `target.setup_actions[${index}] viewports`),
7392
+ optional: input.optional === true || input.required === false,
7381
7393
  continue_on_failure: input.continue_on_failure === true || input.continueOnFailure === true
7382
7394
  };
7383
7395
  }
@@ -7950,6 +7962,13 @@ function viewportsForCheck(check, viewports) {
7950
7962
  const names = new Set(check.viewports);
7951
7963
  return viewports.filter((viewport) => names.has(viewport.name));
7952
7964
  }
7965
+ function setupActionAppliesToViewport(action, viewportName) {
7966
+ if (!action.viewports?.length) return true;
7967
+ return Boolean(viewportName && action.viewports.includes(viewportName));
7968
+ }
7969
+ function setupActionsForViewport(actions, viewportName) {
7970
+ return (actions || []).filter((action) => setupActionAppliesToViewport(action, viewportName));
7971
+ }
7953
7972
  function assessCheckFromEvidence(check, evidence) {
7954
7973
  const viewports = viewportsForCheck(check, evidence.viewports || []);
7955
7974
  if (!viewports.length) {
@@ -8330,10 +8349,24 @@ function assessSetupActionsFromEvidence(profile, evidence) {
8330
8349
  const actionCount = profile.target.setup_actions.length;
8331
8350
  const failed = [];
8332
8351
  const viewports = evidence.viewports || [];
8352
+ const expectedActionCountByViewport = /* @__PURE__ */ new Map();
8353
+ const capturedViewportNames = new Set(viewports.map((viewport) => viewport.name));
8354
+ for (const action of profile.target.setup_actions) {
8355
+ if (action.viewports?.length && !action.viewports.some((name) => capturedViewportNames.has(name))) {
8356
+ failed.push({
8357
+ viewport: null,
8358
+ action: action.type,
8359
+ selector: action.selector ?? null,
8360
+ reason: `setup action scoped to missing viewport(s): ${action.viewports.join(", ")}`
8361
+ });
8362
+ }
8363
+ }
8333
8364
  for (const viewport of viewports) {
8365
+ const expectedActionCount = setupActionsForViewport(profile.target.setup_actions, viewport.name).length;
8366
+ expectedActionCountByViewport.set(viewport.name, expectedActionCount);
8334
8367
  const results = viewport.setup_action_results || [];
8335
8368
  for (const result of results) {
8336
- if (result.ok === false) {
8369
+ if (result.ok === false && result.optional !== true) {
8337
8370
  failed.push({
8338
8371
  viewport: viewport.name,
8339
8372
  action: result.action ?? result.type ?? null,
@@ -8344,12 +8377,12 @@ function assessSetupActionsFromEvidence(profile, evidence) {
8344
8377
  });
8345
8378
  }
8346
8379
  }
8347
- if (results.length < actionCount && results.every((result) => result.ok !== false)) {
8380
+ if (results.length < expectedActionCount && results.every((result) => result.ok !== false || result.optional === true)) {
8348
8381
  failed.push({
8349
8382
  viewport: viewport.name,
8350
8383
  action: "setup_actions",
8351
8384
  selector: null,
8352
- reason: `missing setup action results: ${results.length}/${actionCount}`
8385
+ reason: `missing setup action results: ${results.length}/${expectedActionCount}`
8353
8386
  });
8354
8387
  }
8355
8388
  }
@@ -8361,10 +8394,11 @@ function assessSetupActionsFromEvidence(profile, evidence) {
8361
8394
  action_count: actionCount,
8362
8395
  viewports: viewports.map((viewport) => ({
8363
8396
  name: viewport.name,
8364
- ok: (viewport.setup_action_results || []).length >= actionCount && (viewport.setup_action_results || []).every((result) => result.ok !== false),
8397
+ expected_action_count: expectedActionCountByViewport.get(viewport.name) ?? actionCount,
8398
+ ok: (viewport.setup_action_results || []).length >= (expectedActionCountByViewport.get(viewport.name) ?? actionCount) && (viewport.setup_action_results || []).every((result) => result.ok !== false || result.optional === true),
8365
8399
  result_count: (viewport.setup_action_results || []).length
8366
8400
  })),
8367
- setup_summary: profileSetupSummary(viewports, actionCount),
8401
+ setup_summary: profileSetupSummary(viewports, actionCount, expectedActionCountByViewport),
8368
8402
  failed
8369
8403
  },
8370
8404
  message: failed.length ? `Setup actions failed in ${failed.length} viewport action(s).` : void 0
@@ -8734,6 +8768,13 @@ function viewportsForCheck(check, viewports) {
8734
8768
  const names = new Set(check.viewports);
8735
8769
  return viewports.filter((viewport) => names.has(viewport.name));
8736
8770
  }
8771
+ function setupActionAppliesToViewport(action, viewportName) {
8772
+ if (!Array.isArray(action.viewports) || !action.viewports.length) return true;
8773
+ return Boolean(viewportName && action.viewports.includes(viewportName));
8774
+ }
8775
+ function setupActionsForViewport(actions, viewportName) {
8776
+ return (actions || []).filter((action) => setupActionAppliesToViewport(action, viewportName));
8777
+ }
8737
8778
  function summarizeRouteInventory(viewport, inventory) {
8738
8779
  const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
8739
8780
  const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
@@ -8882,13 +8923,17 @@ function profileScreenshotLabels(viewports) {
8882
8923
  }
8883
8924
  return labels;
8884
8925
  }
8885
- function profileSetupSummary(viewports, actionCount) {
8926
+ function profileSetupSummary(viewports, actionCount, expectedActionCountsByViewport) {
8886
8927
  return {
8887
8928
  viewport_count: (viewports || []).length,
8888
8929
  action_count: actionCount ?? null,
8889
8930
  viewports: (viewports || []).map((viewport) => {
8931
+ const expectedActionCount = expectedActionCountsByViewport && expectedActionCountsByViewport[viewport.name] !== undefined
8932
+ ? expectedActionCountsByViewport[viewport.name]
8933
+ : actionCount;
8890
8934
  const results = viewport.setup_action_results || [];
8891
- const failed = results.filter((result) => result && result.ok === false);
8935
+ const failed = results.filter((result) => result && result.ok === false && result.optional !== true);
8936
+ const optionalFailed = results.filter((result) => result && result.ok === false && result.optional === true);
8892
8937
  const successfulClicks = results.filter((result) => result && profileSetupResultAction(result) === "click" && result.ok !== false);
8893
8938
  const clickCountValues = successfulClicks
8894
8939
  .map((result) => typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : undefined)
@@ -8922,7 +8967,8 @@ function profileSetupSummary(viewports, actionCount) {
8922
8967
  .slice(-6);
8923
8968
  return {
8924
8969
  name: viewport.name,
8925
- ok: (actionCount === undefined ? results.length > 0 : results.length >= actionCount) && failed.length === 0,
8970
+ expected_action_count: expectedActionCount ?? null,
8971
+ ok: (expectedActionCount === undefined ? results.length > 0 : results.length >= expectedActionCount) && failed.length === 0,
8926
8972
  result_count: results.length,
8927
8973
  observed_path: viewport.route && viewport.route.observed || null,
8928
8974
  final_url: viewport.url || null,
@@ -8943,6 +8989,13 @@ function profileSetupSummary(viewports, actionCount) {
8943
8989
  frame_selector: result.frame_selector ?? null,
8944
8990
  reason: result.reason || result.error || null,
8945
8991
  })),
8992
+ optional_failed: optionalFailed.map((result) => ({
8993
+ ordinal: result.ordinal ?? null,
8994
+ action: profileSetupResultAction(result),
8995
+ selector: result.selector ?? null,
8996
+ frame_selector: result.frame_selector ?? null,
8997
+ reason: result.reason || result.error || null,
8998
+ })),
8946
8999
  };
8947
9000
  }),
8948
9001
  };
@@ -9045,10 +9098,24 @@ function assessProfile(profile, evidence) {
9045
9098
  if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
9046
9099
  const actionCount = profile.target.setup_actions.length;
9047
9100
  const failed = [];
9101
+ const expectedActionCountsByViewport = {};
9102
+ const capturedViewportNames = new Set(viewports.map((viewport) => viewport.name));
9103
+ for (const action of profile.target.setup_actions) {
9104
+ if (Array.isArray(action.viewports) && action.viewports.length && !action.viewports.some((name) => capturedViewportNames.has(name))) {
9105
+ failed.push({
9106
+ viewport: null,
9107
+ action: action.type,
9108
+ selector: action.selector || null,
9109
+ reason: "setup action scoped to missing viewport(s): " + action.viewports.join(", "),
9110
+ });
9111
+ }
9112
+ }
9048
9113
  for (const viewport of viewports) {
9114
+ const expectedActionCount = setupActionsForViewport(profile.target.setup_actions, viewport.name).length;
9115
+ expectedActionCountsByViewport[viewport.name] = expectedActionCount;
9049
9116
  const results = viewport.setup_action_results || [];
9050
9117
  for (const result of results) {
9051
- if (result && result.ok === false) {
9118
+ if (result && result.ok === false && result.optional !== true) {
9052
9119
  failed.push({
9053
9120
  viewport: viewport.name,
9054
9121
  action: result.action || result.type || null,
@@ -9059,12 +9126,12 @@ function assessProfile(profile, evidence) {
9059
9126
  });
9060
9127
  }
9061
9128
  }
9062
- if (results.length < actionCount && results.every((result) => !result || result.ok !== false)) {
9129
+ if (results.length < expectedActionCount && results.every((result) => !result || result.ok !== false || result.optional === true)) {
9063
9130
  failed.push({
9064
9131
  viewport: viewport.name,
9065
9132
  action: "setup_actions",
9066
9133
  selector: null,
9067
- reason: "missing setup action results: " + results.length + "/" + actionCount,
9134
+ reason: "missing setup action results: " + results.length + "/" + expectedActionCount,
9068
9135
  });
9069
9136
  }
9070
9137
  }
@@ -9076,11 +9143,12 @@ function assessProfile(profile, evidence) {
9076
9143
  action_count: actionCount,
9077
9144
  viewports: viewports.map((viewport) => ({
9078
9145
  name: viewport.name,
9079
- ok: (viewport.setup_action_results || []).length >= actionCount
9080
- && (viewport.setup_action_results || []).every((result) => !result || result.ok !== false),
9146
+ expected_action_count: expectedActionCountsByViewport[viewport.name] ?? actionCount,
9147
+ ok: (viewport.setup_action_results || []).length >= (expectedActionCountsByViewport[viewport.name] ?? actionCount)
9148
+ && (viewport.setup_action_results || []).every((result) => !result || result.ok !== false || result.optional === true),
9081
9149
  result_count: (viewport.setup_action_results || []).length,
9082
9150
  })),
9083
- setup_summary: profileSetupSummary(viewports, actionCount),
9151
+ setup_summary: profileSetupSummary(viewports, actionCount, expectedActionCountsByViewport),
9084
9152
  failed,
9085
9153
  },
9086
9154
  message: failed.length ? "Setup actions failed in " + failed.length + " viewport action(s)." : undefined,
@@ -9874,7 +9942,7 @@ async function registerNetworkMocks(mocks) {
9874
9942
  async function executeSetupAction(action, ordinal, viewport) {
9875
9943
  const type = setupActionType(action);
9876
9944
  const frameSelector = setupFrameSelector(action);
9877
- const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null };
9945
+ const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null, optional: action.optional === true };
9878
9946
  const timeout = setupNumber(action.timeout_ms, 5000);
9879
9947
  try {
9880
9948
  if (type === "wait") {
@@ -10348,7 +10416,7 @@ async function executeSetupActions(actions, viewport) {
10348
10416
  : result);
10349
10417
  const afterMs = setupNumber(action.after_ms, 0);
10350
10418
  if (afterMs) await page.waitForTimeout(afterMs);
10351
- if (result.ok === false && action.continue_on_failure !== true) {
10419
+ if (result.ok === false && action.continue_on_failure !== true && action.optional !== true) {
10352
10420
  shouldStop = true;
10353
10421
  break;
10354
10422
  }
@@ -10817,7 +10885,7 @@ async function captureViewport(viewport) {
10817
10885
  await page.waitForTimeout(profile.target.wait_ms);
10818
10886
  }
10819
10887
  const setupActionResults = (!navigationError && !waitError)
10820
- ? await executeSetupActions(profile.target.setup_actions || [], viewport)
10888
+ ? await executeSetupActions(setupActionsForViewport(profile.target.setup_actions || [], viewport.name), viewport)
10821
10889
  : [];
10822
10890
  const dom = await page.evaluate(() => {
10823
10891
  const body = document.body;
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-3RUIKF4C.js";
13
+ } from "./chunk-3Y35UIZO.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport
package/dist/index.cjs CHANGED
@@ -8954,13 +8954,15 @@ function profileScreenshotLabels(viewports) {
8954
8954
  }
8955
8955
  return labels;
8956
8956
  }
8957
- function profileSetupSummary(viewports, actionCount) {
8957
+ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewport) {
8958
8958
  return toJsonValue({
8959
8959
  viewport_count: viewports.length,
8960
8960
  action_count: actionCount ?? null,
8961
8961
  viewports: viewports.map((viewport) => {
8962
+ const expectedActionCount = expectedActionCountByViewport?.get(viewport.name) ?? actionCount;
8962
8963
  const results = viewport.setup_action_results || [];
8963
- const failed = results.filter((result) => result.ok === false);
8964
+ const failed = results.filter((result) => result.ok === false && result.optional !== true);
8965
+ const optionalFailed = results.filter((result) => result.ok === false && result.optional === true);
8964
8966
  const successfulClicks = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false);
8965
8967
  const clickCountValues = successfulClicks.map((result) => typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : void 0).filter((value) => value !== void 0);
8966
8968
  const clickedItems = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false).map((result) => {
@@ -8982,7 +8984,8 @@ function profileSetupSummary(viewports, actionCount) {
8982
8984
  })).filter((item) => item.text).slice(-6);
8983
8985
  return {
8984
8986
  name: viewport.name,
8985
- ok: (actionCount === void 0 ? results.length > 0 : results.length >= actionCount) && failed.length === 0,
8987
+ expected_action_count: expectedActionCount ?? null,
8988
+ ok: (expectedActionCount === void 0 ? results.length > 0 : results.length >= expectedActionCount) && failed.length === 0,
8986
8989
  result_count: results.length,
8987
8990
  observed_path: viewport.route?.observed ?? null,
8988
8991
  final_url: viewport.url ?? null,
@@ -9002,6 +9005,13 @@ function profileSetupSummary(viewports, actionCount) {
9002
9005
  selector: result.selector ?? null,
9003
9006
  frame_selector: result.frame_selector ?? null,
9004
9007
  reason: result.reason ?? result.error ?? null
9008
+ })),
9009
+ optional_failed: optionalFailed.map((result) => ({
9010
+ ordinal: result.ordinal ?? null,
9011
+ action: profileSetupResultAction(result),
9012
+ selector: result.selector ?? null,
9013
+ frame_selector: result.frame_selector ?? null,
9014
+ reason: result.reason ?? result.error ?? null
9005
9015
  }))
9006
9016
  };
9007
9017
  })
@@ -9219,6 +9229,8 @@ function normalizeSetupAction(input, index) {
9219
9229
  repeat: normalizeSetupActionRepeat(input, index),
9220
9230
  reload: input.reload === true,
9221
9231
  storage: normalizeSetupActionStorage(input.storage, index),
9232
+ viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `target.setup_actions[${index}] viewports`),
9233
+ optional: input.optional === true || input.required === false,
9222
9234
  continue_on_failure: input.continue_on_failure === true || input.continueOnFailure === true
9223
9235
  };
9224
9236
  }
@@ -9791,6 +9803,13 @@ function viewportsForCheck(check, viewports) {
9791
9803
  const names = new Set(check.viewports);
9792
9804
  return viewports.filter((viewport) => names.has(viewport.name));
9793
9805
  }
9806
+ function setupActionAppliesToViewport(action, viewportName) {
9807
+ if (!action.viewports?.length) return true;
9808
+ return Boolean(viewportName && action.viewports.includes(viewportName));
9809
+ }
9810
+ function setupActionsForViewport(actions, viewportName) {
9811
+ return (actions || []).filter((action) => setupActionAppliesToViewport(action, viewportName));
9812
+ }
9794
9813
  function assessCheckFromEvidence(check, evidence) {
9795
9814
  const viewports = viewportsForCheck(check, evidence.viewports || []);
9796
9815
  if (!viewports.length) {
@@ -10171,10 +10190,24 @@ function assessSetupActionsFromEvidence(profile, evidence) {
10171
10190
  const actionCount = profile.target.setup_actions.length;
10172
10191
  const failed = [];
10173
10192
  const viewports = evidence.viewports || [];
10193
+ const expectedActionCountByViewport = /* @__PURE__ */ new Map();
10194
+ const capturedViewportNames = new Set(viewports.map((viewport) => viewport.name));
10195
+ for (const action of profile.target.setup_actions) {
10196
+ if (action.viewports?.length && !action.viewports.some((name) => capturedViewportNames.has(name))) {
10197
+ failed.push({
10198
+ viewport: null,
10199
+ action: action.type,
10200
+ selector: action.selector ?? null,
10201
+ reason: `setup action scoped to missing viewport(s): ${action.viewports.join(", ")}`
10202
+ });
10203
+ }
10204
+ }
10174
10205
  for (const viewport of viewports) {
10206
+ const expectedActionCount = setupActionsForViewport(profile.target.setup_actions, viewport.name).length;
10207
+ expectedActionCountByViewport.set(viewport.name, expectedActionCount);
10175
10208
  const results = viewport.setup_action_results || [];
10176
10209
  for (const result of results) {
10177
- if (result.ok === false) {
10210
+ if (result.ok === false && result.optional !== true) {
10178
10211
  failed.push({
10179
10212
  viewport: viewport.name,
10180
10213
  action: result.action ?? result.type ?? null,
@@ -10185,12 +10218,12 @@ function assessSetupActionsFromEvidence(profile, evidence) {
10185
10218
  });
10186
10219
  }
10187
10220
  }
10188
- if (results.length < actionCount && results.every((result) => result.ok !== false)) {
10221
+ if (results.length < expectedActionCount && results.every((result) => result.ok !== false || result.optional === true)) {
10189
10222
  failed.push({
10190
10223
  viewport: viewport.name,
10191
10224
  action: "setup_actions",
10192
10225
  selector: null,
10193
- reason: `missing setup action results: ${results.length}/${actionCount}`
10226
+ reason: `missing setup action results: ${results.length}/${expectedActionCount}`
10194
10227
  });
10195
10228
  }
10196
10229
  }
@@ -10202,10 +10235,11 @@ function assessSetupActionsFromEvidence(profile, evidence) {
10202
10235
  action_count: actionCount,
10203
10236
  viewports: viewports.map((viewport) => ({
10204
10237
  name: viewport.name,
10205
- ok: (viewport.setup_action_results || []).length >= actionCount && (viewport.setup_action_results || []).every((result) => result.ok !== false),
10238
+ expected_action_count: expectedActionCountByViewport.get(viewport.name) ?? actionCount,
10239
+ ok: (viewport.setup_action_results || []).length >= (expectedActionCountByViewport.get(viewport.name) ?? actionCount) && (viewport.setup_action_results || []).every((result) => result.ok !== false || result.optional === true),
10206
10240
  result_count: (viewport.setup_action_results || []).length
10207
10241
  })),
10208
- setup_summary: profileSetupSummary(viewports, actionCount),
10242
+ setup_summary: profileSetupSummary(viewports, actionCount, expectedActionCountByViewport),
10209
10243
  failed
10210
10244
  },
10211
10245
  message: failed.length ? `Setup actions failed in ${failed.length} viewport action(s).` : void 0
@@ -10591,6 +10625,13 @@ function viewportsForCheck(check, viewports) {
10591
10625
  const names = new Set(check.viewports);
10592
10626
  return viewports.filter((viewport) => names.has(viewport.name));
10593
10627
  }
10628
+ function setupActionAppliesToViewport(action, viewportName) {
10629
+ if (!Array.isArray(action.viewports) || !action.viewports.length) return true;
10630
+ return Boolean(viewportName && action.viewports.includes(viewportName));
10631
+ }
10632
+ function setupActionsForViewport(actions, viewportName) {
10633
+ return (actions || []).filter((action) => setupActionAppliesToViewport(action, viewportName));
10634
+ }
10594
10635
  function summarizeRouteInventory(viewport, inventory) {
10595
10636
  const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
10596
10637
  const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
@@ -10739,13 +10780,17 @@ function profileScreenshotLabels(viewports) {
10739
10780
  }
10740
10781
  return labels;
10741
10782
  }
10742
- function profileSetupSummary(viewports, actionCount) {
10783
+ function profileSetupSummary(viewports, actionCount, expectedActionCountsByViewport) {
10743
10784
  return {
10744
10785
  viewport_count: (viewports || []).length,
10745
10786
  action_count: actionCount ?? null,
10746
10787
  viewports: (viewports || []).map((viewport) => {
10788
+ const expectedActionCount = expectedActionCountsByViewport && expectedActionCountsByViewport[viewport.name] !== undefined
10789
+ ? expectedActionCountsByViewport[viewport.name]
10790
+ : actionCount;
10747
10791
  const results = viewport.setup_action_results || [];
10748
- const failed = results.filter((result) => result && result.ok === false);
10792
+ const failed = results.filter((result) => result && result.ok === false && result.optional !== true);
10793
+ const optionalFailed = results.filter((result) => result && result.ok === false && result.optional === true);
10749
10794
  const successfulClicks = results.filter((result) => result && profileSetupResultAction(result) === "click" && result.ok !== false);
10750
10795
  const clickCountValues = successfulClicks
10751
10796
  .map((result) => typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : undefined)
@@ -10779,7 +10824,8 @@ function profileSetupSummary(viewports, actionCount) {
10779
10824
  .slice(-6);
10780
10825
  return {
10781
10826
  name: viewport.name,
10782
- ok: (actionCount === undefined ? results.length > 0 : results.length >= actionCount) && failed.length === 0,
10827
+ expected_action_count: expectedActionCount ?? null,
10828
+ ok: (expectedActionCount === undefined ? results.length > 0 : results.length >= expectedActionCount) && failed.length === 0,
10783
10829
  result_count: results.length,
10784
10830
  observed_path: viewport.route && viewport.route.observed || null,
10785
10831
  final_url: viewport.url || null,
@@ -10800,6 +10846,13 @@ function profileSetupSummary(viewports, actionCount) {
10800
10846
  frame_selector: result.frame_selector ?? null,
10801
10847
  reason: result.reason || result.error || null,
10802
10848
  })),
10849
+ optional_failed: optionalFailed.map((result) => ({
10850
+ ordinal: result.ordinal ?? null,
10851
+ action: profileSetupResultAction(result),
10852
+ selector: result.selector ?? null,
10853
+ frame_selector: result.frame_selector ?? null,
10854
+ reason: result.reason || result.error || null,
10855
+ })),
10803
10856
  };
10804
10857
  }),
10805
10858
  };
@@ -10902,10 +10955,24 @@ function assessProfile(profile, evidence) {
10902
10955
  if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
10903
10956
  const actionCount = profile.target.setup_actions.length;
10904
10957
  const failed = [];
10958
+ const expectedActionCountsByViewport = {};
10959
+ const capturedViewportNames = new Set(viewports.map((viewport) => viewport.name));
10960
+ for (const action of profile.target.setup_actions) {
10961
+ if (Array.isArray(action.viewports) && action.viewports.length && !action.viewports.some((name) => capturedViewportNames.has(name))) {
10962
+ failed.push({
10963
+ viewport: null,
10964
+ action: action.type,
10965
+ selector: action.selector || null,
10966
+ reason: "setup action scoped to missing viewport(s): " + action.viewports.join(", "),
10967
+ });
10968
+ }
10969
+ }
10905
10970
  for (const viewport of viewports) {
10971
+ const expectedActionCount = setupActionsForViewport(profile.target.setup_actions, viewport.name).length;
10972
+ expectedActionCountsByViewport[viewport.name] = expectedActionCount;
10906
10973
  const results = viewport.setup_action_results || [];
10907
10974
  for (const result of results) {
10908
- if (result && result.ok === false) {
10975
+ if (result && result.ok === false && result.optional !== true) {
10909
10976
  failed.push({
10910
10977
  viewport: viewport.name,
10911
10978
  action: result.action || result.type || null,
@@ -10916,12 +10983,12 @@ function assessProfile(profile, evidence) {
10916
10983
  });
10917
10984
  }
10918
10985
  }
10919
- if (results.length < actionCount && results.every((result) => !result || result.ok !== false)) {
10986
+ if (results.length < expectedActionCount && results.every((result) => !result || result.ok !== false || result.optional === true)) {
10920
10987
  failed.push({
10921
10988
  viewport: viewport.name,
10922
10989
  action: "setup_actions",
10923
10990
  selector: null,
10924
- reason: "missing setup action results: " + results.length + "/" + actionCount,
10991
+ reason: "missing setup action results: " + results.length + "/" + expectedActionCount,
10925
10992
  });
10926
10993
  }
10927
10994
  }
@@ -10933,11 +11000,12 @@ function assessProfile(profile, evidence) {
10933
11000
  action_count: actionCount,
10934
11001
  viewports: viewports.map((viewport) => ({
10935
11002
  name: viewport.name,
10936
- ok: (viewport.setup_action_results || []).length >= actionCount
10937
- && (viewport.setup_action_results || []).every((result) => !result || result.ok !== false),
11003
+ expected_action_count: expectedActionCountsByViewport[viewport.name] ?? actionCount,
11004
+ ok: (viewport.setup_action_results || []).length >= (expectedActionCountsByViewport[viewport.name] ?? actionCount)
11005
+ && (viewport.setup_action_results || []).every((result) => !result || result.ok !== false || result.optional === true),
10938
11006
  result_count: (viewport.setup_action_results || []).length,
10939
11007
  })),
10940
- setup_summary: profileSetupSummary(viewports, actionCount),
11008
+ setup_summary: profileSetupSummary(viewports, actionCount, expectedActionCountsByViewport),
10941
11009
  failed,
10942
11010
  },
10943
11011
  message: failed.length ? "Setup actions failed in " + failed.length + " viewport action(s)." : undefined,
@@ -11731,7 +11799,7 @@ async function registerNetworkMocks(mocks) {
11731
11799
  async function executeSetupAction(action, ordinal, viewport) {
11732
11800
  const type = setupActionType(action);
11733
11801
  const frameSelector = setupFrameSelector(action);
11734
- const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null };
11802
+ const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null, optional: action.optional === true };
11735
11803
  const timeout = setupNumber(action.timeout_ms, 5000);
11736
11804
  try {
11737
11805
  if (type === "wait") {
@@ -12205,7 +12273,7 @@ async function executeSetupActions(actions, viewport) {
12205
12273
  : result);
12206
12274
  const afterMs = setupNumber(action.after_ms, 0);
12207
12275
  if (afterMs) await page.waitForTimeout(afterMs);
12208
- if (result.ok === false && action.continue_on_failure !== true) {
12276
+ if (result.ok === false && action.continue_on_failure !== true && action.optional !== true) {
12209
12277
  shouldStop = true;
12210
12278
  break;
12211
12279
  }
@@ -12674,7 +12742,7 @@ async function captureViewport(viewport) {
12674
12742
  await page.waitForTimeout(profile.target.wait_ms);
12675
12743
  }
12676
12744
  const setupActionResults = (!navigationError && !waitError)
12677
- ? await executeSetupActions(profile.target.setup_actions || [], viewport)
12745
+ ? await executeSetupActions(setupActionsForViewport(profile.target.setup_actions || [], viewport.name), viewport)
12678
12746
  : [];
12679
12747
  const dom = await page.evaluate(() => {
12680
12748
  const body = document.body;
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  resolveRiddleProofProfileTimeoutSec,
59
59
  slugifyRiddleProofProfileName,
60
60
  summarizeRiddleProofProfileResult
61
- } from "./chunk-3RUIKF4C.js";
61
+ } from "./chunk-3Y35UIZO.js";
62
62
  import {
63
63
  DEFAULT_RIDDLE_API_BASE_URL,
64
64
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -269,13 +269,15 @@ function profileScreenshotLabels(viewports) {
269
269
  }
270
270
  return labels;
271
271
  }
272
- function profileSetupSummary(viewports, actionCount) {
272
+ function profileSetupSummary(viewports, actionCount, expectedActionCountByViewport) {
273
273
  return toJsonValue({
274
274
  viewport_count: viewports.length,
275
275
  action_count: actionCount ?? null,
276
276
  viewports: viewports.map((viewport) => {
277
+ const expectedActionCount = expectedActionCountByViewport?.get(viewport.name) ?? actionCount;
277
278
  const results = viewport.setup_action_results || [];
278
- const failed = results.filter((result) => result.ok === false);
279
+ const failed = results.filter((result) => result.ok === false && result.optional !== true);
280
+ const optionalFailed = results.filter((result) => result.ok === false && result.optional === true);
279
281
  const successfulClicks = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false);
280
282
  const clickCountValues = successfulClicks.map((result) => typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : void 0).filter((value) => value !== void 0);
281
283
  const clickedItems = results.filter((result) => profileSetupResultAction(result) === "click" && result.ok !== false).map((result) => {
@@ -297,7 +299,8 @@ function profileSetupSummary(viewports, actionCount) {
297
299
  })).filter((item) => item.text).slice(-6);
298
300
  return {
299
301
  name: viewport.name,
300
- ok: (actionCount === void 0 ? results.length > 0 : results.length >= actionCount) && failed.length === 0,
302
+ expected_action_count: expectedActionCount ?? null,
303
+ ok: (expectedActionCount === void 0 ? results.length > 0 : results.length >= expectedActionCount) && failed.length === 0,
301
304
  result_count: results.length,
302
305
  observed_path: viewport.route?.observed ?? null,
303
306
  final_url: viewport.url ?? null,
@@ -317,6 +320,13 @@ function profileSetupSummary(viewports, actionCount) {
317
320
  selector: result.selector ?? null,
318
321
  frame_selector: result.frame_selector ?? null,
319
322
  reason: result.reason ?? result.error ?? null
323
+ })),
324
+ optional_failed: optionalFailed.map((result) => ({
325
+ ordinal: result.ordinal ?? null,
326
+ action: profileSetupResultAction(result),
327
+ selector: result.selector ?? null,
328
+ frame_selector: result.frame_selector ?? null,
329
+ reason: result.reason ?? result.error ?? null
320
330
  }))
321
331
  };
322
332
  })
@@ -534,6 +544,8 @@ function normalizeSetupAction(input, index) {
534
544
  repeat: normalizeSetupActionRepeat(input, index),
535
545
  reload: input.reload === true,
536
546
  storage: normalizeSetupActionStorage(input.storage, index),
547
+ viewports: normalizeStringList(input.viewports ?? input.viewport_names ?? input.viewportNames, `target.setup_actions[${index}] viewports`),
548
+ optional: input.optional === true || input.required === false,
537
549
  continue_on_failure: input.continue_on_failure === true || input.continueOnFailure === true
538
550
  };
539
551
  }
@@ -1106,6 +1118,13 @@ function viewportsForCheck(check, viewports) {
1106
1118
  const names = new Set(check.viewports);
1107
1119
  return viewports.filter((viewport) => names.has(viewport.name));
1108
1120
  }
1121
+ function setupActionAppliesToViewport(action, viewportName) {
1122
+ if (!action.viewports?.length) return true;
1123
+ return Boolean(viewportName && action.viewports.includes(viewportName));
1124
+ }
1125
+ function setupActionsForViewport(actions, viewportName) {
1126
+ return (actions || []).filter((action) => setupActionAppliesToViewport(action, viewportName));
1127
+ }
1109
1128
  function assessCheckFromEvidence(check, evidence) {
1110
1129
  const viewports = viewportsForCheck(check, evidence.viewports || []);
1111
1130
  if (!viewports.length) {
@@ -1486,10 +1505,24 @@ function assessSetupActionsFromEvidence(profile, evidence) {
1486
1505
  const actionCount = profile.target.setup_actions.length;
1487
1506
  const failed = [];
1488
1507
  const viewports = evidence.viewports || [];
1508
+ const expectedActionCountByViewport = /* @__PURE__ */ new Map();
1509
+ const capturedViewportNames = new Set(viewports.map((viewport) => viewport.name));
1510
+ for (const action of profile.target.setup_actions) {
1511
+ if (action.viewports?.length && !action.viewports.some((name) => capturedViewportNames.has(name))) {
1512
+ failed.push({
1513
+ viewport: null,
1514
+ action: action.type,
1515
+ selector: action.selector ?? null,
1516
+ reason: `setup action scoped to missing viewport(s): ${action.viewports.join(", ")}`
1517
+ });
1518
+ }
1519
+ }
1489
1520
  for (const viewport of viewports) {
1521
+ const expectedActionCount = setupActionsForViewport(profile.target.setup_actions, viewport.name).length;
1522
+ expectedActionCountByViewport.set(viewport.name, expectedActionCount);
1490
1523
  const results = viewport.setup_action_results || [];
1491
1524
  for (const result of results) {
1492
- if (result.ok === false) {
1525
+ if (result.ok === false && result.optional !== true) {
1493
1526
  failed.push({
1494
1527
  viewport: viewport.name,
1495
1528
  action: result.action ?? result.type ?? null,
@@ -1500,12 +1533,12 @@ function assessSetupActionsFromEvidence(profile, evidence) {
1500
1533
  });
1501
1534
  }
1502
1535
  }
1503
- if (results.length < actionCount && results.every((result) => result.ok !== false)) {
1536
+ if (results.length < expectedActionCount && results.every((result) => result.ok !== false || result.optional === true)) {
1504
1537
  failed.push({
1505
1538
  viewport: viewport.name,
1506
1539
  action: "setup_actions",
1507
1540
  selector: null,
1508
- reason: `missing setup action results: ${results.length}/${actionCount}`
1541
+ reason: `missing setup action results: ${results.length}/${expectedActionCount}`
1509
1542
  });
1510
1543
  }
1511
1544
  }
@@ -1517,10 +1550,11 @@ function assessSetupActionsFromEvidence(profile, evidence) {
1517
1550
  action_count: actionCount,
1518
1551
  viewports: viewports.map((viewport) => ({
1519
1552
  name: viewport.name,
1520
- ok: (viewport.setup_action_results || []).length >= actionCount && (viewport.setup_action_results || []).every((result) => result.ok !== false),
1553
+ expected_action_count: expectedActionCountByViewport.get(viewport.name) ?? actionCount,
1554
+ ok: (viewport.setup_action_results || []).length >= (expectedActionCountByViewport.get(viewport.name) ?? actionCount) && (viewport.setup_action_results || []).every((result) => result.ok !== false || result.optional === true),
1521
1555
  result_count: (viewport.setup_action_results || []).length
1522
1556
  })),
1523
- setup_summary: profileSetupSummary(viewports, actionCount),
1557
+ setup_summary: profileSetupSummary(viewports, actionCount, expectedActionCountByViewport),
1524
1558
  failed
1525
1559
  },
1526
1560
  message: failed.length ? `Setup actions failed in ${failed.length} viewport action(s).` : void 0
@@ -1906,6 +1940,13 @@ function viewportsForCheck(check, viewports) {
1906
1940
  const names = new Set(check.viewports);
1907
1941
  return viewports.filter((viewport) => names.has(viewport.name));
1908
1942
  }
1943
+ function setupActionAppliesToViewport(action, viewportName) {
1944
+ if (!Array.isArray(action.viewports) || !action.viewports.length) return true;
1945
+ return Boolean(viewportName && action.viewports.includes(viewportName));
1946
+ }
1947
+ function setupActionsForViewport(actions, viewportName) {
1948
+ return (actions || []).filter((action) => setupActionAppliesToViewport(action, viewportName));
1949
+ }
1909
1950
  function summarizeRouteInventory(viewport, inventory) {
1910
1951
  const directRoutes = Array.isArray(inventory.direct_routes) ? inventory.direct_routes : [];
1911
1952
  const clickthroughs = Array.isArray(inventory.clickthroughs) ? inventory.clickthroughs : [];
@@ -2054,13 +2095,17 @@ function profileScreenshotLabels(viewports) {
2054
2095
  }
2055
2096
  return labels;
2056
2097
  }
2057
- function profileSetupSummary(viewports, actionCount) {
2098
+ function profileSetupSummary(viewports, actionCount, expectedActionCountsByViewport) {
2058
2099
  return {
2059
2100
  viewport_count: (viewports || []).length,
2060
2101
  action_count: actionCount ?? null,
2061
2102
  viewports: (viewports || []).map((viewport) => {
2103
+ const expectedActionCount = expectedActionCountsByViewport && expectedActionCountsByViewport[viewport.name] !== undefined
2104
+ ? expectedActionCountsByViewport[viewport.name]
2105
+ : actionCount;
2062
2106
  const results = viewport.setup_action_results || [];
2063
- const failed = results.filter((result) => result && result.ok === false);
2107
+ const failed = results.filter((result) => result && result.ok === false && result.optional !== true);
2108
+ const optionalFailed = results.filter((result) => result && result.ok === false && result.optional === true);
2064
2109
  const successfulClicks = results.filter((result) => result && profileSetupResultAction(result) === "click" && result.ok !== false);
2065
2110
  const clickCountValues = successfulClicks
2066
2111
  .map((result) => typeof result.click_count === "number" && Number.isFinite(result.click_count) && result.click_count > 1 ? result.click_count : undefined)
@@ -2094,7 +2139,8 @@ function profileSetupSummary(viewports, actionCount) {
2094
2139
  .slice(-6);
2095
2140
  return {
2096
2141
  name: viewport.name,
2097
- ok: (actionCount === undefined ? results.length > 0 : results.length >= actionCount) && failed.length === 0,
2142
+ expected_action_count: expectedActionCount ?? null,
2143
+ ok: (expectedActionCount === undefined ? results.length > 0 : results.length >= expectedActionCount) && failed.length === 0,
2098
2144
  result_count: results.length,
2099
2145
  observed_path: viewport.route && viewport.route.observed || null,
2100
2146
  final_url: viewport.url || null,
@@ -2115,6 +2161,13 @@ function profileSetupSummary(viewports, actionCount) {
2115
2161
  frame_selector: result.frame_selector ?? null,
2116
2162
  reason: result.reason || result.error || null,
2117
2163
  })),
2164
+ optional_failed: optionalFailed.map((result) => ({
2165
+ ordinal: result.ordinal ?? null,
2166
+ action: profileSetupResultAction(result),
2167
+ selector: result.selector ?? null,
2168
+ frame_selector: result.frame_selector ?? null,
2169
+ reason: result.reason || result.error || null,
2170
+ })),
2118
2171
  };
2119
2172
  }),
2120
2173
  };
@@ -2217,10 +2270,24 @@ function assessProfile(profile, evidence) {
2217
2270
  if (profile.target && Array.isArray(profile.target.setup_actions) && profile.target.setup_actions.length) {
2218
2271
  const actionCount = profile.target.setup_actions.length;
2219
2272
  const failed = [];
2273
+ const expectedActionCountsByViewport = {};
2274
+ const capturedViewportNames = new Set(viewports.map((viewport) => viewport.name));
2275
+ for (const action of profile.target.setup_actions) {
2276
+ if (Array.isArray(action.viewports) && action.viewports.length && !action.viewports.some((name) => capturedViewportNames.has(name))) {
2277
+ failed.push({
2278
+ viewport: null,
2279
+ action: action.type,
2280
+ selector: action.selector || null,
2281
+ reason: "setup action scoped to missing viewport(s): " + action.viewports.join(", "),
2282
+ });
2283
+ }
2284
+ }
2220
2285
  for (const viewport of viewports) {
2286
+ const expectedActionCount = setupActionsForViewport(profile.target.setup_actions, viewport.name).length;
2287
+ expectedActionCountsByViewport[viewport.name] = expectedActionCount;
2221
2288
  const results = viewport.setup_action_results || [];
2222
2289
  for (const result of results) {
2223
- if (result && result.ok === false) {
2290
+ if (result && result.ok === false && result.optional !== true) {
2224
2291
  failed.push({
2225
2292
  viewport: viewport.name,
2226
2293
  action: result.action || result.type || null,
@@ -2231,12 +2298,12 @@ function assessProfile(profile, evidence) {
2231
2298
  });
2232
2299
  }
2233
2300
  }
2234
- if (results.length < actionCount && results.every((result) => !result || result.ok !== false)) {
2301
+ if (results.length < expectedActionCount && results.every((result) => !result || result.ok !== false || result.optional === true)) {
2235
2302
  failed.push({
2236
2303
  viewport: viewport.name,
2237
2304
  action: "setup_actions",
2238
2305
  selector: null,
2239
- reason: "missing setup action results: " + results.length + "/" + actionCount,
2306
+ reason: "missing setup action results: " + results.length + "/" + expectedActionCount,
2240
2307
  });
2241
2308
  }
2242
2309
  }
@@ -2248,11 +2315,12 @@ function assessProfile(profile, evidence) {
2248
2315
  action_count: actionCount,
2249
2316
  viewports: viewports.map((viewport) => ({
2250
2317
  name: viewport.name,
2251
- ok: (viewport.setup_action_results || []).length >= actionCount
2252
- && (viewport.setup_action_results || []).every((result) => !result || result.ok !== false),
2318
+ expected_action_count: expectedActionCountsByViewport[viewport.name] ?? actionCount,
2319
+ ok: (viewport.setup_action_results || []).length >= (expectedActionCountsByViewport[viewport.name] ?? actionCount)
2320
+ && (viewport.setup_action_results || []).every((result) => !result || result.ok !== false || result.optional === true),
2253
2321
  result_count: (viewport.setup_action_results || []).length,
2254
2322
  })),
2255
- setup_summary: profileSetupSummary(viewports, actionCount),
2323
+ setup_summary: profileSetupSummary(viewports, actionCount, expectedActionCountsByViewport),
2256
2324
  failed,
2257
2325
  },
2258
2326
  message: failed.length ? "Setup actions failed in " + failed.length + " viewport action(s)." : undefined,
@@ -3046,7 +3114,7 @@ async function registerNetworkMocks(mocks) {
3046
3114
  async function executeSetupAction(action, ordinal, viewport) {
3047
3115
  const type = setupActionType(action);
3048
3116
  const frameSelector = setupFrameSelector(action);
3049
- const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null };
3117
+ const base = { ok: false, action: type || "unknown", ordinal, selector: action.selector || null, frame_selector: frameSelector || null, optional: action.optional === true };
3050
3118
  const timeout = setupNumber(action.timeout_ms, 5000);
3051
3119
  try {
3052
3120
  if (type === "wait") {
@@ -3520,7 +3588,7 @@ async function executeSetupActions(actions, viewport) {
3520
3588
  : result);
3521
3589
  const afterMs = setupNumber(action.after_ms, 0);
3522
3590
  if (afterMs) await page.waitForTimeout(afterMs);
3523
- if (result.ok === false && action.continue_on_failure !== true) {
3591
+ if (result.ok === false && action.continue_on_failure !== true && action.optional !== true) {
3524
3592
  shouldStop = true;
3525
3593
  break;
3526
3594
  }
@@ -3989,7 +4057,7 @@ async function captureViewport(viewport) {
3989
4057
  await page.waitForTimeout(profile.target.wait_ms);
3990
4058
  }
3991
4059
  const setupActionResults = (!navigationError && !waitError)
3992
- ? await executeSetupActions(profile.target.setup_actions || [], viewport)
4060
+ ? await executeSetupActions(setupActionsForViewport(profile.target.setup_actions || [], viewport.name), viewport)
3993
4061
  : [];
3994
4062
  const dom = await page.evaluate(() => {
3995
4063
  const body = document.body;
@@ -56,6 +56,8 @@ interface RiddleProofProfileSetupAction {
56
56
  repeat?: number;
57
57
  reload?: boolean;
58
58
  storage?: "local" | "session" | "both";
59
+ viewports?: string[];
60
+ optional?: boolean;
59
61
  continue_on_failure?: boolean;
60
62
  }
61
63
  interface RiddleProofProfileNetworkMockResponse {
package/dist/profile.d.ts CHANGED
@@ -56,6 +56,8 @@ interface RiddleProofProfileSetupAction {
56
56
  repeat?: number;
57
57
  reload?: boolean;
58
58
  storage?: "local" | "session" | "both";
59
+ viewports?: string[];
60
+ optional?: boolean;
59
61
  continue_on_failure?: boolean;
60
62
  }
61
63
  interface RiddleProofProfileNetworkMockResponse {
package/dist/profile.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  resolveRiddleProofProfileTimeoutSec,
20
20
  slugifyRiddleProofProfileName,
21
21
  summarizeRiddleProofProfileResult
22
- } from "./chunk-3RUIKF4C.js";
22
+ } from "./chunk-3Y35UIZO.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.83",
3
+ "version": "0.7.85",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",