@riddledc/riddle-proof 0.7.32 → 0.7.34

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
@@ -168,6 +168,10 @@ riddle-proof-loop run-profile \
168
168
  --output artifacts/riddle-proof/pricing
169
169
  ```
170
170
 
171
+ Hosted profile runs emit Riddle poll progress to stderr while waiting. Use
172
+ `--quiet` to suppress progress lines, or `--progress-every-ms` to tune the
173
+ heartbeat cadence for long route-inventory or workflow profiles.
174
+
171
175
  The package includes a generic starter profile at
172
176
  `examples/profiles/page-content-basic.json`; copy that shape into a repository
173
177
  profile directory and replace the selector/text checks with app-specific
@@ -216,14 +220,17 @@ both directly and through real link clicks:
216
220
  }
217
221
  ```
218
222
 
219
- The check records discovered source links, duplicate/missing/unexpected routes,
220
- direct route health, real clickthrough health, wrong-path failures, and stale
221
- source-surface failures. It runs direct/clickthrough sweeps on the first
222
- viewport by default and leaves ordinary profile overflow checks to cover the
223
- source page across all configured viewports. Set `run_direct_routes: false`,
224
- `run_clickthroughs: false`, `allow_unexpected_routes: true`, or
223
+ The check records discovered source links, unique source-link counts, duplicate
224
+ source-link counts, missing/unexpected routes, direct route health, real
225
+ clickthrough health, wrong-path failures, and stale source-surface failures. It
226
+ runs direct/clickthrough sweeps on the first viewport by default and leaves
227
+ ordinary profile overflow checks to cover the source page across all configured
228
+ viewports. Set `run_direct_routes: false`, `run_clickthroughs: false`,
229
+ `allow_unexpected_routes: true`, `require_unique_routes: false`, or
225
230
  `save_route_screenshots: true` when a profile needs a narrower or more
226
- artifact-heavy audit.
231
+ artifact-heavy audit. `require_unique_routes: false` is useful when a navigation
232
+ surface intentionally links to the same route from multiple cards or anchors,
233
+ while still proving the unique expected route set.
227
234
 
228
235
  The result uses `riddle-proof.profile-result.v1` and separates product failures
229
236
  from weak proof and environment blockers:
@@ -578,14 +578,23 @@ function assessCheckFromEvidence(check, evidence) {
578
578
  const first = inventories[0]?.inventory;
579
579
  const directRoutes = Array.isArray(first?.direct_routes) ? first.direct_routes : [];
580
580
  const clickthroughs = Array.isArray(first?.clickthroughs) ? first.clickthroughs : [];
581
+ const sourceLinkCount = numberValue(first?.source_link_count) ?? numberValue(first?.home_game_link_count) ?? null;
582
+ const sourceUniqueLinkCount = numberValue(first?.source_unique_link_count) ?? numberValue(first?.home_unique_game_link_count) ?? null;
583
+ const duplicateSourceLinks = Array.isArray(first?.duplicate_source_link_paths) ? first.duplicate_source_link_paths.map((path) => String(path)) : [];
584
+ const duplicateSourceLinkCount = numberValue(first?.duplicate_source_link_count) ?? (sourceLinkCount !== null && sourceUniqueLinkCount !== null ? Math.max(0, sourceLinkCount - sourceUniqueLinkCount) : null);
581
585
  return {
582
586
  type: check.type,
583
587
  label: checkLabel(check),
584
588
  status: failures.length ? "failed" : "passed",
585
589
  evidence: {
586
590
  expected_count: check.expected_routes?.length || 0,
587
- homepage_link_count: numberValue(first?.home_game_link_count) ?? null,
588
- homepage_unique_link_count: numberValue(first?.home_unique_game_link_count) ?? null,
591
+ source_link_count: sourceLinkCount,
592
+ source_unique_link_count: sourceUniqueLinkCount,
593
+ duplicate_source_link_count: duplicateSourceLinkCount,
594
+ duplicate_source_links: duplicateSourceLinks,
595
+ duplicates_allowed: check.require_unique_routes === false,
596
+ homepage_link_count: numberValue(first?.home_game_link_count) ?? sourceLinkCount,
597
+ homepage_unique_link_count: numberValue(first?.home_unique_game_link_count) ?? sourceUniqueLinkCount,
589
598
  direct_route_count: directRoutes.length,
590
599
  clickthrough_count: clickthroughs.length,
591
600
  failures
@@ -1125,14 +1134,23 @@ function assessProfile(profile, evidence) {
1125
1134
  const first = inventories[0].inventory || {};
1126
1135
  const directRoutes = Array.isArray(first.direct_routes) ? first.direct_routes : [];
1127
1136
  const clickthroughs = Array.isArray(first.clickthroughs) ? first.clickthroughs : [];
1137
+ const sourceLinkCount = typeof first.source_link_count === "number" ? first.source_link_count : typeof first.home_game_link_count === "number" ? first.home_game_link_count : null;
1138
+ const sourceUniqueLinkCount = typeof first.source_unique_link_count === "number" ? first.source_unique_link_count : typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : null;
1139
+ const duplicateSourceLinks = Array.isArray(first.duplicate_source_link_paths) ? first.duplicate_source_link_paths.map((path) => String(path)) : [];
1140
+ const duplicateSourceLinkCount = typeof first.duplicate_source_link_count === "number" ? first.duplicate_source_link_count : sourceLinkCount !== null && sourceUniqueLinkCount !== null ? Math.max(0, sourceLinkCount - sourceUniqueLinkCount) : null;
1128
1141
  checks.push({
1129
1142
  type: check.type,
1130
1143
  label: check.label || check.type,
1131
1144
  status: failures.length ? "failed" : "passed",
1132
1145
  evidence: {
1133
1146
  expected_count: (check.expected_routes || []).length,
1134
- homepage_link_count: typeof first.home_game_link_count === "number" ? first.home_game_link_count : null,
1135
- homepage_unique_link_count: typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : null,
1147
+ source_link_count: sourceLinkCount,
1148
+ source_unique_link_count: sourceUniqueLinkCount,
1149
+ duplicate_source_link_count: duplicateSourceLinkCount,
1150
+ duplicate_source_links: duplicateSourceLinks,
1151
+ duplicates_allowed: check.require_unique_routes === false,
1152
+ homepage_link_count: typeof first.home_game_link_count === "number" ? first.home_game_link_count : sourceLinkCount,
1153
+ homepage_unique_link_count: typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : sourceUniqueLinkCount,
1136
1154
  direct_route_count: directRoutes.length,
1137
1155
  clickthrough_count: clickthroughs.length,
1138
1156
  failures,
@@ -1651,8 +1669,9 @@ async function collectRouteInventory(check, viewport) {
1651
1669
  const homeLinkPaths = homeLinks.map((link) => link.app_path);
1652
1670
  const uniqueHomeLinkPaths = Array.from(new Set(homeLinkPaths));
1653
1671
  const duplicateHomeLinkPaths = homeLinkPaths.filter((path, index) => homeLinkPaths.indexOf(path) !== index);
1672
+ const duplicateHomeLinkPathSet = Array.from(new Set(duplicateHomeLinkPaths));
1654
1673
  if (check.require_unique_routes !== false && duplicateHomeLinkPaths.length) {
1655
- failures.push({ code: "duplicate_source_links", paths: Array.from(new Set(duplicateHomeLinkPaths)) });
1674
+ failures.push({ code: "duplicate_source_links", paths: duplicateHomeLinkPathSet });
1656
1675
  }
1657
1676
  for (const route of expectedRoutes) {
1658
1677
  if (!homeLinkPaths.includes(normalizeRoutePath(route.path))) {
@@ -1728,6 +1747,11 @@ async function collectRouteInventory(check, viewport) {
1728
1747
  expected_routes: expectedRoutes,
1729
1748
  link_selector: check.link_selector || "a[href]",
1730
1749
  source_selector: check.source_selector || null,
1750
+ source_link_count: homeLinkPaths.length,
1751
+ source_unique_link_count: uniqueHomeLinkPaths.length,
1752
+ duplicate_source_link_count: duplicateHomeLinkPaths.length,
1753
+ duplicate_source_link_paths: duplicateHomeLinkPathSet,
1754
+ duplicates_allowed: check.require_unique_routes === false,
1731
1755
  home_game_link_count: homeLinkPaths.length,
1732
1756
  home_unique_game_link_count: uniqueHomeLinkPaths.length,
1733
1757
  home_links: homeLinks,
@@ -1871,6 +1895,11 @@ async function captureViewport(viewport) {
1871
1895
  expected_routes: routeInventoryCheck.expected_routes || [],
1872
1896
  link_selector: routeInventoryCheck.link_selector || "a[href]",
1873
1897
  source_selector: routeInventoryCheck.source_selector || null,
1898
+ source_link_count: 0,
1899
+ source_unique_link_count: 0,
1900
+ duplicate_source_link_count: 0,
1901
+ duplicate_source_link_paths: [],
1902
+ duplicates_allowed: routeInventoryCheck.require_unique_routes === false,
1874
1903
  home_game_link_count: 0,
1875
1904
  home_unique_game_link_count: 0,
1876
1905
  home_links: [],
@@ -1945,6 +1974,9 @@ function buildProfileEvidence(currentViewports) {
1945
1974
  .map((viewport) => ({
1946
1975
  viewport: viewport.name,
1947
1976
  expected_count: (viewport.route_inventory.expected_routes || []).length,
1977
+ source_link_count: viewport.route_inventory.source_link_count == null ? viewport.route_inventory.home_game_link_count : viewport.route_inventory.source_link_count,
1978
+ source_unique_link_count: viewport.route_inventory.source_unique_link_count == null ? viewport.route_inventory.home_unique_game_link_count : viewport.route_inventory.source_unique_link_count,
1979
+ duplicate_source_link_count: viewport.route_inventory.duplicate_source_link_count,
1948
1980
  home_unique_game_link_count: viewport.route_inventory.home_unique_game_link_count,
1949
1981
  direct_route_count: (viewport.route_inventory.direct_routes || []).length,
1950
1982
  clickthrough_count: (viewport.route_inventory.clickthroughs || []).length,
package/dist/cli.cjs CHANGED
@@ -7451,14 +7451,23 @@ function assessCheckFromEvidence(check, evidence) {
7451
7451
  const first = inventories[0]?.inventory;
7452
7452
  const directRoutes = Array.isArray(first?.direct_routes) ? first.direct_routes : [];
7453
7453
  const clickthroughs = Array.isArray(first?.clickthroughs) ? first.clickthroughs : [];
7454
+ const sourceLinkCount = numberValue(first?.source_link_count) ?? numberValue(first?.home_game_link_count) ?? null;
7455
+ const sourceUniqueLinkCount = numberValue(first?.source_unique_link_count) ?? numberValue(first?.home_unique_game_link_count) ?? null;
7456
+ const duplicateSourceLinks = Array.isArray(first?.duplicate_source_link_paths) ? first.duplicate_source_link_paths.map((path7) => String(path7)) : [];
7457
+ const duplicateSourceLinkCount = numberValue(first?.duplicate_source_link_count) ?? (sourceLinkCount !== null && sourceUniqueLinkCount !== null ? Math.max(0, sourceLinkCount - sourceUniqueLinkCount) : null);
7454
7458
  return {
7455
7459
  type: check.type,
7456
7460
  label: checkLabel(check),
7457
7461
  status: failures.length ? "failed" : "passed",
7458
7462
  evidence: {
7459
7463
  expected_count: check.expected_routes?.length || 0,
7460
- homepage_link_count: numberValue(first?.home_game_link_count) ?? null,
7461
- homepage_unique_link_count: numberValue(first?.home_unique_game_link_count) ?? null,
7464
+ source_link_count: sourceLinkCount,
7465
+ source_unique_link_count: sourceUniqueLinkCount,
7466
+ duplicate_source_link_count: duplicateSourceLinkCount,
7467
+ duplicate_source_links: duplicateSourceLinks,
7468
+ duplicates_allowed: check.require_unique_routes === false,
7469
+ homepage_link_count: numberValue(first?.home_game_link_count) ?? sourceLinkCount,
7470
+ homepage_unique_link_count: numberValue(first?.home_unique_game_link_count) ?? sourceUniqueLinkCount,
7462
7471
  direct_route_count: directRoutes.length,
7463
7472
  clickthrough_count: clickthroughs.length,
7464
7473
  failures
@@ -7982,14 +7991,23 @@ function assessProfile(profile, evidence) {
7982
7991
  const first = inventories[0].inventory || {};
7983
7992
  const directRoutes = Array.isArray(first.direct_routes) ? first.direct_routes : [];
7984
7993
  const clickthroughs = Array.isArray(first.clickthroughs) ? first.clickthroughs : [];
7994
+ const sourceLinkCount = typeof first.source_link_count === "number" ? first.source_link_count : typeof first.home_game_link_count === "number" ? first.home_game_link_count : null;
7995
+ const sourceUniqueLinkCount = typeof first.source_unique_link_count === "number" ? first.source_unique_link_count : typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : null;
7996
+ const duplicateSourceLinks = Array.isArray(first.duplicate_source_link_paths) ? first.duplicate_source_link_paths.map((path) => String(path)) : [];
7997
+ const duplicateSourceLinkCount = typeof first.duplicate_source_link_count === "number" ? first.duplicate_source_link_count : sourceLinkCount !== null && sourceUniqueLinkCount !== null ? Math.max(0, sourceLinkCount - sourceUniqueLinkCount) : null;
7985
7998
  checks.push({
7986
7999
  type: check.type,
7987
8000
  label: check.label || check.type,
7988
8001
  status: failures.length ? "failed" : "passed",
7989
8002
  evidence: {
7990
8003
  expected_count: (check.expected_routes || []).length,
7991
- homepage_link_count: typeof first.home_game_link_count === "number" ? first.home_game_link_count : null,
7992
- homepage_unique_link_count: typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : null,
8004
+ source_link_count: sourceLinkCount,
8005
+ source_unique_link_count: sourceUniqueLinkCount,
8006
+ duplicate_source_link_count: duplicateSourceLinkCount,
8007
+ duplicate_source_links: duplicateSourceLinks,
8008
+ duplicates_allowed: check.require_unique_routes === false,
8009
+ homepage_link_count: typeof first.home_game_link_count === "number" ? first.home_game_link_count : sourceLinkCount,
8010
+ homepage_unique_link_count: typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : sourceUniqueLinkCount,
7993
8011
  direct_route_count: directRoutes.length,
7994
8012
  clickthrough_count: clickthroughs.length,
7995
8013
  failures,
@@ -8508,8 +8526,9 @@ async function collectRouteInventory(check, viewport) {
8508
8526
  const homeLinkPaths = homeLinks.map((link) => link.app_path);
8509
8527
  const uniqueHomeLinkPaths = Array.from(new Set(homeLinkPaths));
8510
8528
  const duplicateHomeLinkPaths = homeLinkPaths.filter((path, index) => homeLinkPaths.indexOf(path) !== index);
8529
+ const duplicateHomeLinkPathSet = Array.from(new Set(duplicateHomeLinkPaths));
8511
8530
  if (check.require_unique_routes !== false && duplicateHomeLinkPaths.length) {
8512
- failures.push({ code: "duplicate_source_links", paths: Array.from(new Set(duplicateHomeLinkPaths)) });
8531
+ failures.push({ code: "duplicate_source_links", paths: duplicateHomeLinkPathSet });
8513
8532
  }
8514
8533
  for (const route of expectedRoutes) {
8515
8534
  if (!homeLinkPaths.includes(normalizeRoutePath(route.path))) {
@@ -8585,6 +8604,11 @@ async function collectRouteInventory(check, viewport) {
8585
8604
  expected_routes: expectedRoutes,
8586
8605
  link_selector: check.link_selector || "a[href]",
8587
8606
  source_selector: check.source_selector || null,
8607
+ source_link_count: homeLinkPaths.length,
8608
+ source_unique_link_count: uniqueHomeLinkPaths.length,
8609
+ duplicate_source_link_count: duplicateHomeLinkPaths.length,
8610
+ duplicate_source_link_paths: duplicateHomeLinkPathSet,
8611
+ duplicates_allowed: check.require_unique_routes === false,
8588
8612
  home_game_link_count: homeLinkPaths.length,
8589
8613
  home_unique_game_link_count: uniqueHomeLinkPaths.length,
8590
8614
  home_links: homeLinks,
@@ -8728,6 +8752,11 @@ async function captureViewport(viewport) {
8728
8752
  expected_routes: routeInventoryCheck.expected_routes || [],
8729
8753
  link_selector: routeInventoryCheck.link_selector || "a[href]",
8730
8754
  source_selector: routeInventoryCheck.source_selector || null,
8755
+ source_link_count: 0,
8756
+ source_unique_link_count: 0,
8757
+ duplicate_source_link_count: 0,
8758
+ duplicate_source_link_paths: [],
8759
+ duplicates_allowed: routeInventoryCheck.require_unique_routes === false,
8731
8760
  home_game_link_count: 0,
8732
8761
  home_unique_game_link_count: 0,
8733
8762
  home_links: [],
@@ -8802,6 +8831,9 @@ function buildProfileEvidence(currentViewports) {
8802
8831
  .map((viewport) => ({
8803
8832
  viewport: viewport.name,
8804
8833
  expected_count: (viewport.route_inventory.expected_routes || []).length,
8834
+ source_link_count: viewport.route_inventory.source_link_count == null ? viewport.route_inventory.home_game_link_count : viewport.route_inventory.source_link_count,
8835
+ source_unique_link_count: viewport.route_inventory.source_unique_link_count == null ? viewport.route_inventory.home_unique_game_link_count : viewport.route_inventory.source_unique_link_count,
8836
+ duplicate_source_link_count: viewport.route_inventory.duplicate_source_link_count,
8805
8837
  home_unique_game_link_count: viewport.route_inventory.home_unique_game_link_count,
8806
8838
  direct_route_count: (viewport.route_inventory.direct_routes || []).length,
8807
8839
  clickthrough_count: (viewport.route_inventory.clickthroughs || []).length,
@@ -8934,7 +8966,7 @@ function usage() {
8934
8966
  " riddle-proof-loop respond --state-path <path> --response-json <file|json|->",
8935
8967
  " riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
8936
8968
  " riddle-proof-loop status --state-path <path>",
8937
- " riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--output <dir>]",
8969
+ " riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--output <dir>] [--quiet]",
8938
8970
  " riddle-proof-loop riddle-preview-deploy <build-dir> <label>",
8939
8971
  " riddle-proof-loop riddle-server-preview <directory> --script-file <file> [--path /route] [--wait-for-selector selector]",
8940
8972
  " riddle-proof-loop riddle-run-script --url <url> --script-file <file> [--viewport 1280x720] [--strict true|false]",
@@ -9305,7 +9337,12 @@ async function runProfileForCli(profile, options) {
9305
9337
  const poll = await client.pollJob(jobId, {
9306
9338
  wait: true,
9307
9339
  attempts: optionString(options, "attempts") ? Number(optionString(options, "attempts")) : void 0,
9308
- intervalMs: optionString(options, "intervalMs") ? Number(optionString(options, "intervalMs")) : void 0
9340
+ intervalMs: optionString(options, "intervalMs") ? Number(optionString(options, "intervalMs")) : void 0,
9341
+ progressEveryMs: optionString(options, "progressEveryMs") ? Number(optionString(options, "progressEveryMs")) : void 0,
9342
+ onProgress: options.quiet !== true ? (snapshot) => {
9343
+ process.stderr.write(`${riddlePollProgressLine(snapshot)}
9344
+ `);
9345
+ } : void 0
9309
9346
  });
9310
9347
  const artifacts = collectRiddleProfileArtifactRefs(poll.artifacts);
9311
9348
  if (!poll.ok || !poll.terminal) {
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  profileStatusExitCode,
11
11
  resolveRiddleProofProfileTargetUrl,
12
12
  resolveRiddleProofProfileTimeoutSec
13
- } from "./chunk-COUZXKGM.js";
13
+ } from "./chunk-PWGJQLOS.js";
14
14
  import {
15
15
  createRiddleApiClient,
16
16
  parseRiddleViewport
@@ -44,7 +44,7 @@ function usage() {
44
44
  " riddle-proof-loop respond --state-path <path> --response-json <file|json|->",
45
45
  " riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
46
46
  " riddle-proof-loop status --state-path <path>",
47
- " riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--output <dir>]",
47
+ " riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--output <dir>] [--quiet]",
48
48
  " riddle-proof-loop riddle-preview-deploy <build-dir> <label>",
49
49
  " riddle-proof-loop riddle-server-preview <directory> --script-file <file> [--path /route] [--wait-for-selector selector]",
50
50
  " riddle-proof-loop riddle-run-script --url <url> --script-file <file> [--viewport 1280x720] [--strict true|false]",
@@ -415,7 +415,12 @@ async function runProfileForCli(profile, options) {
415
415
  const poll = await client.pollJob(jobId, {
416
416
  wait: true,
417
417
  attempts: optionString(options, "attempts") ? Number(optionString(options, "attempts")) : void 0,
418
- intervalMs: optionString(options, "intervalMs") ? Number(optionString(options, "intervalMs")) : void 0
418
+ intervalMs: optionString(options, "intervalMs") ? Number(optionString(options, "intervalMs")) : void 0,
419
+ progressEveryMs: optionString(options, "progressEveryMs") ? Number(optionString(options, "progressEveryMs")) : void 0,
420
+ onProgress: options.quiet !== true ? (snapshot) => {
421
+ process.stderr.write(`${riddlePollProgressLine(snapshot)}
422
+ `);
423
+ } : void 0
419
424
  });
420
425
  const artifacts = collectRiddleProfileArtifactRefs(poll.artifacts);
421
426
  if (!poll.ok || !poll.terminal) {
package/dist/index.cjs CHANGED
@@ -9292,14 +9292,23 @@ function assessCheckFromEvidence(check, evidence) {
9292
9292
  const first = inventories[0]?.inventory;
9293
9293
  const directRoutes = Array.isArray(first?.direct_routes) ? first.direct_routes : [];
9294
9294
  const clickthroughs = Array.isArray(first?.clickthroughs) ? first.clickthroughs : [];
9295
+ const sourceLinkCount = numberValue3(first?.source_link_count) ?? numberValue3(first?.home_game_link_count) ?? null;
9296
+ const sourceUniqueLinkCount = numberValue3(first?.source_unique_link_count) ?? numberValue3(first?.home_unique_game_link_count) ?? null;
9297
+ const duplicateSourceLinks = Array.isArray(first?.duplicate_source_link_paths) ? first.duplicate_source_link_paths.map((path6) => String(path6)) : [];
9298
+ const duplicateSourceLinkCount = numberValue3(first?.duplicate_source_link_count) ?? (sourceLinkCount !== null && sourceUniqueLinkCount !== null ? Math.max(0, sourceLinkCount - sourceUniqueLinkCount) : null);
9295
9299
  return {
9296
9300
  type: check.type,
9297
9301
  label: checkLabel(check),
9298
9302
  status: failures.length ? "failed" : "passed",
9299
9303
  evidence: {
9300
9304
  expected_count: check.expected_routes?.length || 0,
9301
- homepage_link_count: numberValue3(first?.home_game_link_count) ?? null,
9302
- homepage_unique_link_count: numberValue3(first?.home_unique_game_link_count) ?? null,
9305
+ source_link_count: sourceLinkCount,
9306
+ source_unique_link_count: sourceUniqueLinkCount,
9307
+ duplicate_source_link_count: duplicateSourceLinkCount,
9308
+ duplicate_source_links: duplicateSourceLinks,
9309
+ duplicates_allowed: check.require_unique_routes === false,
9310
+ homepage_link_count: numberValue3(first?.home_game_link_count) ?? sourceLinkCount,
9311
+ homepage_unique_link_count: numberValue3(first?.home_unique_game_link_count) ?? sourceUniqueLinkCount,
9303
9312
  direct_route_count: directRoutes.length,
9304
9313
  clickthrough_count: clickthroughs.length,
9305
9314
  failures
@@ -9839,14 +9848,23 @@ function assessProfile(profile, evidence) {
9839
9848
  const first = inventories[0].inventory || {};
9840
9849
  const directRoutes = Array.isArray(first.direct_routes) ? first.direct_routes : [];
9841
9850
  const clickthroughs = Array.isArray(first.clickthroughs) ? first.clickthroughs : [];
9851
+ const sourceLinkCount = typeof first.source_link_count === "number" ? first.source_link_count : typeof first.home_game_link_count === "number" ? first.home_game_link_count : null;
9852
+ const sourceUniqueLinkCount = typeof first.source_unique_link_count === "number" ? first.source_unique_link_count : typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : null;
9853
+ const duplicateSourceLinks = Array.isArray(first.duplicate_source_link_paths) ? first.duplicate_source_link_paths.map((path) => String(path)) : [];
9854
+ const duplicateSourceLinkCount = typeof first.duplicate_source_link_count === "number" ? first.duplicate_source_link_count : sourceLinkCount !== null && sourceUniqueLinkCount !== null ? Math.max(0, sourceLinkCount - sourceUniqueLinkCount) : null;
9842
9855
  checks.push({
9843
9856
  type: check.type,
9844
9857
  label: check.label || check.type,
9845
9858
  status: failures.length ? "failed" : "passed",
9846
9859
  evidence: {
9847
9860
  expected_count: (check.expected_routes || []).length,
9848
- homepage_link_count: typeof first.home_game_link_count === "number" ? first.home_game_link_count : null,
9849
- homepage_unique_link_count: typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : null,
9861
+ source_link_count: sourceLinkCount,
9862
+ source_unique_link_count: sourceUniqueLinkCount,
9863
+ duplicate_source_link_count: duplicateSourceLinkCount,
9864
+ duplicate_source_links: duplicateSourceLinks,
9865
+ duplicates_allowed: check.require_unique_routes === false,
9866
+ homepage_link_count: typeof first.home_game_link_count === "number" ? first.home_game_link_count : sourceLinkCount,
9867
+ homepage_unique_link_count: typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : sourceUniqueLinkCount,
9850
9868
  direct_route_count: directRoutes.length,
9851
9869
  clickthrough_count: clickthroughs.length,
9852
9870
  failures,
@@ -10365,8 +10383,9 @@ async function collectRouteInventory(check, viewport) {
10365
10383
  const homeLinkPaths = homeLinks.map((link) => link.app_path);
10366
10384
  const uniqueHomeLinkPaths = Array.from(new Set(homeLinkPaths));
10367
10385
  const duplicateHomeLinkPaths = homeLinkPaths.filter((path, index) => homeLinkPaths.indexOf(path) !== index);
10386
+ const duplicateHomeLinkPathSet = Array.from(new Set(duplicateHomeLinkPaths));
10368
10387
  if (check.require_unique_routes !== false && duplicateHomeLinkPaths.length) {
10369
- failures.push({ code: "duplicate_source_links", paths: Array.from(new Set(duplicateHomeLinkPaths)) });
10388
+ failures.push({ code: "duplicate_source_links", paths: duplicateHomeLinkPathSet });
10370
10389
  }
10371
10390
  for (const route of expectedRoutes) {
10372
10391
  if (!homeLinkPaths.includes(normalizeRoutePath(route.path))) {
@@ -10442,6 +10461,11 @@ async function collectRouteInventory(check, viewport) {
10442
10461
  expected_routes: expectedRoutes,
10443
10462
  link_selector: check.link_selector || "a[href]",
10444
10463
  source_selector: check.source_selector || null,
10464
+ source_link_count: homeLinkPaths.length,
10465
+ source_unique_link_count: uniqueHomeLinkPaths.length,
10466
+ duplicate_source_link_count: duplicateHomeLinkPaths.length,
10467
+ duplicate_source_link_paths: duplicateHomeLinkPathSet,
10468
+ duplicates_allowed: check.require_unique_routes === false,
10445
10469
  home_game_link_count: homeLinkPaths.length,
10446
10470
  home_unique_game_link_count: uniqueHomeLinkPaths.length,
10447
10471
  home_links: homeLinks,
@@ -10585,6 +10609,11 @@ async function captureViewport(viewport) {
10585
10609
  expected_routes: routeInventoryCheck.expected_routes || [],
10586
10610
  link_selector: routeInventoryCheck.link_selector || "a[href]",
10587
10611
  source_selector: routeInventoryCheck.source_selector || null,
10612
+ source_link_count: 0,
10613
+ source_unique_link_count: 0,
10614
+ duplicate_source_link_count: 0,
10615
+ duplicate_source_link_paths: [],
10616
+ duplicates_allowed: routeInventoryCheck.require_unique_routes === false,
10588
10617
  home_game_link_count: 0,
10589
10618
  home_unique_game_link_count: 0,
10590
10619
  home_links: [],
@@ -10659,6 +10688,9 @@ function buildProfileEvidence(currentViewports) {
10659
10688
  .map((viewport) => ({
10660
10689
  viewport: viewport.name,
10661
10690
  expected_count: (viewport.route_inventory.expected_routes || []).length,
10691
+ source_link_count: viewport.route_inventory.source_link_count == null ? viewport.route_inventory.home_game_link_count : viewport.route_inventory.source_link_count,
10692
+ source_unique_link_count: viewport.route_inventory.source_unique_link_count == null ? viewport.route_inventory.home_unique_game_link_count : viewport.route_inventory.source_unique_link_count,
10693
+ duplicate_source_link_count: viewport.route_inventory.duplicate_source_link_count,
10662
10694
  home_unique_game_link_count: viewport.route_inventory.home_unique_game_link_count,
10663
10695
  direct_route_count: (viewport.route_inventory.direct_routes || []).length,
10664
10696
  clickthrough_count: (viewport.route_inventory.clickthroughs || []).length,
package/dist/index.js CHANGED
@@ -58,7 +58,7 @@ import {
58
58
  resolveRiddleProofProfileTimeoutSec,
59
59
  slugifyRiddleProofProfileName,
60
60
  summarizeRiddleProofProfileResult
61
- } from "./chunk-COUZXKGM.js";
61
+ } from "./chunk-PWGJQLOS.js";
62
62
  import {
63
63
  DEFAULT_RIDDLE_API_BASE_URL,
64
64
  DEFAULT_RIDDLE_API_KEY_FILE,
package/dist/profile.cjs CHANGED
@@ -621,14 +621,23 @@ function assessCheckFromEvidence(check, evidence) {
621
621
  const first = inventories[0]?.inventory;
622
622
  const directRoutes = Array.isArray(first?.direct_routes) ? first.direct_routes : [];
623
623
  const clickthroughs = Array.isArray(first?.clickthroughs) ? first.clickthroughs : [];
624
+ const sourceLinkCount = numberValue(first?.source_link_count) ?? numberValue(first?.home_game_link_count) ?? null;
625
+ const sourceUniqueLinkCount = numberValue(first?.source_unique_link_count) ?? numberValue(first?.home_unique_game_link_count) ?? null;
626
+ const duplicateSourceLinks = Array.isArray(first?.duplicate_source_link_paths) ? first.duplicate_source_link_paths.map((path) => String(path)) : [];
627
+ const duplicateSourceLinkCount = numberValue(first?.duplicate_source_link_count) ?? (sourceLinkCount !== null && sourceUniqueLinkCount !== null ? Math.max(0, sourceLinkCount - sourceUniqueLinkCount) : null);
624
628
  return {
625
629
  type: check.type,
626
630
  label: checkLabel(check),
627
631
  status: failures.length ? "failed" : "passed",
628
632
  evidence: {
629
633
  expected_count: check.expected_routes?.length || 0,
630
- homepage_link_count: numberValue(first?.home_game_link_count) ?? null,
631
- homepage_unique_link_count: numberValue(first?.home_unique_game_link_count) ?? null,
634
+ source_link_count: sourceLinkCount,
635
+ source_unique_link_count: sourceUniqueLinkCount,
636
+ duplicate_source_link_count: duplicateSourceLinkCount,
637
+ duplicate_source_links: duplicateSourceLinks,
638
+ duplicates_allowed: check.require_unique_routes === false,
639
+ homepage_link_count: numberValue(first?.home_game_link_count) ?? sourceLinkCount,
640
+ homepage_unique_link_count: numberValue(first?.home_unique_game_link_count) ?? sourceUniqueLinkCount,
632
641
  direct_route_count: directRoutes.length,
633
642
  clickthrough_count: clickthroughs.length,
634
643
  failures
@@ -1168,14 +1177,23 @@ function assessProfile(profile, evidence) {
1168
1177
  const first = inventories[0].inventory || {};
1169
1178
  const directRoutes = Array.isArray(first.direct_routes) ? first.direct_routes : [];
1170
1179
  const clickthroughs = Array.isArray(first.clickthroughs) ? first.clickthroughs : [];
1180
+ const sourceLinkCount = typeof first.source_link_count === "number" ? first.source_link_count : typeof first.home_game_link_count === "number" ? first.home_game_link_count : null;
1181
+ const sourceUniqueLinkCount = typeof first.source_unique_link_count === "number" ? first.source_unique_link_count : typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : null;
1182
+ const duplicateSourceLinks = Array.isArray(first.duplicate_source_link_paths) ? first.duplicate_source_link_paths.map((path) => String(path)) : [];
1183
+ const duplicateSourceLinkCount = typeof first.duplicate_source_link_count === "number" ? first.duplicate_source_link_count : sourceLinkCount !== null && sourceUniqueLinkCount !== null ? Math.max(0, sourceLinkCount - sourceUniqueLinkCount) : null;
1171
1184
  checks.push({
1172
1185
  type: check.type,
1173
1186
  label: check.label || check.type,
1174
1187
  status: failures.length ? "failed" : "passed",
1175
1188
  evidence: {
1176
1189
  expected_count: (check.expected_routes || []).length,
1177
- homepage_link_count: typeof first.home_game_link_count === "number" ? first.home_game_link_count : null,
1178
- homepage_unique_link_count: typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : null,
1190
+ source_link_count: sourceLinkCount,
1191
+ source_unique_link_count: sourceUniqueLinkCount,
1192
+ duplicate_source_link_count: duplicateSourceLinkCount,
1193
+ duplicate_source_links: duplicateSourceLinks,
1194
+ duplicates_allowed: check.require_unique_routes === false,
1195
+ homepage_link_count: typeof first.home_game_link_count === "number" ? first.home_game_link_count : sourceLinkCount,
1196
+ homepage_unique_link_count: typeof first.home_unique_game_link_count === "number" ? first.home_unique_game_link_count : sourceUniqueLinkCount,
1179
1197
  direct_route_count: directRoutes.length,
1180
1198
  clickthrough_count: clickthroughs.length,
1181
1199
  failures,
@@ -1694,8 +1712,9 @@ async function collectRouteInventory(check, viewport) {
1694
1712
  const homeLinkPaths = homeLinks.map((link) => link.app_path);
1695
1713
  const uniqueHomeLinkPaths = Array.from(new Set(homeLinkPaths));
1696
1714
  const duplicateHomeLinkPaths = homeLinkPaths.filter((path, index) => homeLinkPaths.indexOf(path) !== index);
1715
+ const duplicateHomeLinkPathSet = Array.from(new Set(duplicateHomeLinkPaths));
1697
1716
  if (check.require_unique_routes !== false && duplicateHomeLinkPaths.length) {
1698
- failures.push({ code: "duplicate_source_links", paths: Array.from(new Set(duplicateHomeLinkPaths)) });
1717
+ failures.push({ code: "duplicate_source_links", paths: duplicateHomeLinkPathSet });
1699
1718
  }
1700
1719
  for (const route of expectedRoutes) {
1701
1720
  if (!homeLinkPaths.includes(normalizeRoutePath(route.path))) {
@@ -1771,6 +1790,11 @@ async function collectRouteInventory(check, viewport) {
1771
1790
  expected_routes: expectedRoutes,
1772
1791
  link_selector: check.link_selector || "a[href]",
1773
1792
  source_selector: check.source_selector || null,
1793
+ source_link_count: homeLinkPaths.length,
1794
+ source_unique_link_count: uniqueHomeLinkPaths.length,
1795
+ duplicate_source_link_count: duplicateHomeLinkPaths.length,
1796
+ duplicate_source_link_paths: duplicateHomeLinkPathSet,
1797
+ duplicates_allowed: check.require_unique_routes === false,
1774
1798
  home_game_link_count: homeLinkPaths.length,
1775
1799
  home_unique_game_link_count: uniqueHomeLinkPaths.length,
1776
1800
  home_links: homeLinks,
@@ -1914,6 +1938,11 @@ async function captureViewport(viewport) {
1914
1938
  expected_routes: routeInventoryCheck.expected_routes || [],
1915
1939
  link_selector: routeInventoryCheck.link_selector || "a[href]",
1916
1940
  source_selector: routeInventoryCheck.source_selector || null,
1941
+ source_link_count: 0,
1942
+ source_unique_link_count: 0,
1943
+ duplicate_source_link_count: 0,
1944
+ duplicate_source_link_paths: [],
1945
+ duplicates_allowed: routeInventoryCheck.require_unique_routes === false,
1917
1946
  home_game_link_count: 0,
1918
1947
  home_unique_game_link_count: 0,
1919
1948
  home_links: [],
@@ -1988,6 +2017,9 @@ function buildProfileEvidence(currentViewports) {
1988
2017
  .map((viewport) => ({
1989
2018
  viewport: viewport.name,
1990
2019
  expected_count: (viewport.route_inventory.expected_routes || []).length,
2020
+ source_link_count: viewport.route_inventory.source_link_count == null ? viewport.route_inventory.home_game_link_count : viewport.route_inventory.source_link_count,
2021
+ source_unique_link_count: viewport.route_inventory.source_unique_link_count == null ? viewport.route_inventory.home_unique_game_link_count : viewport.route_inventory.source_unique_link_count,
2022
+ duplicate_source_link_count: viewport.route_inventory.duplicate_source_link_count,
1991
2023
  home_unique_game_link_count: viewport.route_inventory.home_unique_game_link_count,
1992
2024
  direct_route_count: (viewport.route_inventory.direct_routes || []).length,
1993
2025
  clickthrough_count: (viewport.route_inventory.clickthroughs || []).length,
package/dist/profile.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  resolveRiddleProofProfileTimeoutSec,
20
20
  slugifyRiddleProofProfileName,
21
21
  summarizeRiddleProofProfileResult
22
- } from "./chunk-COUZXKGM.js";
22
+ } from "./chunk-PWGJQLOS.js";
23
23
  export {
24
24
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
25
25
  RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.32",
3
+ "version": "0.7.34",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",