Setting the file. One moment.
Extract Claims · Vercel Optimize · vercel-labs/agent-skills · Skills Docs
ContentsBack to the top of the page — line 382
This file
Number 7.86
Position 86 of 155
Type JavaScript
Size 19 KB
Lines 550 lib/ extract-claims.mjs
JavaScript · 550 lines · 19 KB
=
ctx.version;
9 const cacheComponents = ctx.cacheComponents;
10 const signals = ctx.signals;
11 const projectFacts = Array. isArray (ctx.projectFacts) ? ctx.projectFacts : [];
12
13 // One synthetic claim asserts rec doesn't contradict any already-on project fact (Fluid, in-function concurrency, …).
14 if (projectFacts. length > 0 ) {
15 claims. push ({
16 type: 'does_not_contradict_project_config' ,
17 rec,
18 projectFacts,
19 sourceField: 'projectFacts' ,
20 });
21 }
22
23 for ( const cite of asArray (rec.citations)) {
24 // Skill-rule refs are filtered upstream — skip version check.
25 if ( / ^ [\w-] + : [\w-] +$ / . test (cite)) {
26 claims. push ({ type: 'citation_in_library' , url: cite, sourceField: 'citations' });
27 continue ;
28 }
29 claims. push ({ type: 'citation_in_library' , url: cite, sourceField: 'citations' });
30 if (framework && frameworkVersion) {
31 claims. push ({
32 type: 'citation_applies_to_version' ,
33 url: cite,
34 framework,
35 frameworkVersion,
36 sourceField: 'citations' ,
37 });
38 }
39 }
40
41 for ( const f of asArray (rec.affectedFiles)) {
42 claims. push ({ type: 'file_exists' , file: f, repoRoot, projectRootDirectory, sourceField: 'affectedFiles' });
43 }
44
45 // findingRefs lack a pattern, so we only check file existence.
46 for ( const ref of asArray (rec.findingRefs)) {
47 const m = String (ref). match ( / ^ ( . +? ): \d +$ / );
48 if (m && ! claims. some (( c ) => c.type === 'file_exists' && c.file === m[ 1 ])) {
49 claims. push ({ type: 'file_exists' , file: m[ 1 ], repoRoot, projectRootDirectory, sourceField: 'findingRefs' });
50 }
51 }
52
53 const cacheFiles = cacheRecommendationFiles (rec);
54 if ( isCacheCandidate (rec)) {
55 claims. push ({
56 type: 'cache_policy_positive_or_no_ready_rec' ,
57 rec,
58 sourceField: 'cache-policy' ,
59 });
60 }
61 if (cacheFiles. length > 0 ) {
62 claims. push ({
63 type: 'cache_vary_matches_dynamic_inputs' ,
64 rec,
65 files: cacheFiles,
66 repoRoot,
67 projectRootDirectory,
68 sourceField: 'cache-safety' ,
69 });
70 if ( mentionsVaryHeader (rec)) {
71 claims. push ({
72 type: 'cache_vary_cardinality_safe' ,
73 rec,
74 sourceField: 'cache-vary-cardinality' ,
75 });
76 }
77 claims. push ({
78 type: 'cache_rec_not_error_dominated_or_acknowledged' ,
79 rec,
80 signals,
81 sourceField: 'cache-error-safety' ,
82 });
83 claims. push ({
84 type: 'cache_control_header_syntax' ,
85 rec,
86 sourceField: 'cache-header-syntax' ,
87 });
88 claims. push ({
89 type: 'cache_control_headers_citation' ,
90 rec,
91 sourceField: 'cache-header-citation' ,
92 });
93 if ( mentionsCachedNotFoundOr404 (rec)) {
94 claims. push ({
95 type: 'cache_404_long_ttl_safety' ,
96 rec,
97 sourceField: 'cache-404-safety' ,
98 });
99 }
100 }
101
102 if ( mentionsNextCachedNotFound (rec)) {
103 claims. push ({
104 type: 'next_cached_not_found_causal_support' ,
105 rec,
106 framework,
107 frameworkVersion,
108 sourceField: 'next-cache-not-found' ,
109 });
110 }
111
112 if ( mentionsNextStableCacheApi (rec)) {
113 claims. push ({
114 type: 'next_stable_cache_api_for_version' ,
115 rec,
116 framework,
117 frameworkVersion,
118 sourceField: 'next-cache-api-version' ,
119 });
120 }
121
122 if ( mentionsNext16RuntimeCacheApiMismatch (rec)) {
123 claims. push ({
124 type: 'next_runtime_cache_api_for_version' ,
125 rec,
126 framework,
127 frameworkVersion,
128 sourceField: 'next-runtime-cache-api-version' ,
129 });
130 }
131
132 if ( mentionsRuntimeCacheWhenCacheComponents (rec)) {
133 claims. push ({
134 type: 'next_cache_components_runtime_cache_preference' ,
135 rec,
136 framework,
137 frameworkVersion,
138 cacheComponents,
139 sourceField: 'next-cache-components-runtime-cache-preference' ,
140 });
141 }
142
143 if ( mentionsMultipleCacheLifeCalls (rec)) {
144 claims. push ({
145 type: 'next_cache_life_single_execution' ,
146 rec,
147 framework,
148 frameworkVersion,
149 sourceField: 'next-cache-life-single-execution' ,
150 });
151 }
152
153 if ( mentionsCacheLifetimeChange (rec)) {
154 claims. push ({
155 type: 'next_cache_lifetime_freshness_supported' ,
156 rec,
157 files: recommendationFiles (rec),
158 repoRoot,
159 projectRootDirectory,
160 sourceField: 'next-cache-lifetime-freshness' ,
161 });
162 }
163
164 if ( mentionsNextCacheComponentsStaticShellTarget (rec)) {
165 claims. push ({
166 type: 'next_cache_components_route_chain_file' ,
167 rec,
168 framework,
169 frameworkVersion,
170 cacheComponents,
171 signals,
172 sourceField: 'next-cache-components-route-chain' ,
173 });
174 }
175
176 if ( mentionsCacheLifeCdnHeaderClaim (rec)) {
177 claims. push ({
178 type: 'next_cache_life_cdn_header_semantics' ,
179 rec,
180 framework,
181 frameworkVersion,
182 sourceField: 'next-cache-life-cdn-header-semantics' ,
183 });
184 }
185
186 if ( mentionsImageResponseHeaders (rec)) {
187 claims. push ({
188 type: 'image_response_headers_citation' ,
189 rec,
190 framework,
191 frameworkVersion,
192 sourceField: 'image-response-headers' ,
193 });
194 }
195
196 if ( mentionsNextImagePriorityRecommendation (rec)) {
197 claims. push ({
198 type: 'next_image_priority_api_for_version' ,
199 rec,
200 framework,
201 frameworkVersion,
202 sourceField: 'next-image-priority-api' ,
203 });
204 }
205
206 if ( mentionsNextCacheComponentsRouteSegmentConfig (rec)) {
207 claims. push ({
208 type: 'next_cache_components_route_segment_config' ,
209 rec,
210 framework,
211 frameworkVersion,
212 cacheComponents,
213 sourceField: 'next-route-segment-config' ,
214 });
215 }
216
217 if ( mentionsRouteLevelRevalidate (rec)) {
218 claims. push ({
219 type: 'next_route_revalidate_static_prereq' ,
220 rec,
221 framework,
222 frameworkVersion,
223 cacheComponents,
224 repoRoot,
225 projectRootDirectory,
226 sourceField: 'next-route-revalidate-static-prereq' ,
227 });
228 }
229
230 if ( mentionsExistingCacheTagInvalidation (rec)) {
231 claims. push ({
232 type: 'next_cache_tag_invalidation_supported' ,
233 rec,
234 repoRoot,
235 projectRootDirectory,
236 sourceField: 'next-cache-tag-invalidation' ,
237 });
238 }
239
240 if ( mentionsUnsafeImmutableDynamicRoute (rec)) {
241 claims. push ({
242 type: 'immutable_dynamic_route_safety' ,
243 rec,
244 sourceField: 'immutable-dynamic-route' ,
245 });
246 }
247
248 if ( mentionsAuthSensitiveParallelization (rec)) {
249 claims. push ({
250 type: 'auth_guard_parallelization_safety' ,
251 rec,
252 sourceField: 'auth-parallelization' ,
253 });
254 }
255
256 if ( mentionsParallelizationImpactOverclaim (rec)) {
257 claims. push ({
258 type: 'parallelization_impact_not_overclaimed' ,
259 rec,
260 sourceField: 'parallelization-impact' ,
261 });
262 }
263
264 if ( mentionsCpuBoundParallelization (rec)) {
265 claims. push ({
266 type: 'parallelization_not_cpu_bound_work' ,
267 rec,
268 sourceField: 'parallelization-cpu-bound' ,
269 });
270 }
271
272 if ( mentionsRuntimeErrorCause (rec)) {
273 claims. push ({
274 type: 'runtime_error_cause_supported' ,
275 rec,
276 sourceField: 'runtime-error-cause' ,
277 });
278 }
279
280 if ( mentionsCatchToNotFound (rec)) {
281 claims. push ({
282 type: 'route_error_not_found_status_and_scope' ,
283 rec,
284 sourceField: 'route-error-catch-safety' ,
285 });
286 }
287
288 if ( mentionsIgnoredBuildStepRecommendation (rec)) {
289 claims. push ({
290 type: 'vercel_ignore_command_project_state' ,
291 rec,
292 signals,
293 sourceField: 'ignored-build-step-state' ,
294 });
295 }
296
297 if ( mentionsTurboBuildCacheRecommendation (rec)) {
298 claims. push ({
299 type: 'turbo_build_cache_safety' ,
300 rec,
301 files: recommendationFiles (rec),
302 repoRoot,
303 projectRootDirectory,
304 framework,
305 sourceField: 'turbo-build-cache-safety' ,
306 });
307 }
308
309 for ( const c of asArray (rec.verifiableClaims)) {
310 if (c && typeof c === 'object' && typeof c.type === 'string' ) {
311 claims. push ({
312 ... c,
313 repoRoot: c.repoRoot ?? repoRoot,
314 projectRootDirectory: c.projectRootDirectory ?? projectRootDirectory,
315 sourceField: 'verifiableClaims' ,
316 });
317 }
318 }
319
320 return claims;
321 }
322
323 function normalizeProjectRootDirectory ( value ) {
324 if ( typeof value !== 'string' || value. trim () === '' ) return null ;
325 return value. replace ( / \\ / g , '/' ). replace ( / ^ \.\/ + / , '' ). replace ( / \/ +$ / , '' );
326 }
327
328 function cacheRecommendationFiles ( rec ) {
329 if ( ! recommendsSharedCache (rec)) return [];
330 return recommendationFiles (rec);
331 }
332
333 function isCacheCandidate ( rec ) {
334 return / ^ (?:uncached_route | cache_header_gap):/ . test ( String (rec?.candidateRef ?? '' ));
335 }
336
337 function recommendationFiles ( rec ) {
338 const files = [
339 ... asArray (rec.affectedFiles),
340 ... asArray (rec.findingRefs)
341 . map (( ref ) => String (ref). match ( / ^ ( . +? ): \d +$ / )?.[ 1 ])
342 . filter (Boolean),
343 ];
344 return Array. from ( new Set (files));
345 }
346
347 function recommendsSharedCache ( rec ) {
348 const haystack = [
349 rec?.what,
350 rec?.why,
351 rec?.fix,
352 rec?.desiredBehavior,
353 rec?.verify,
354 ]. filter (Boolean). join ( ' \n ' );
355 return / \b (?:s-maxage | CDN-Cache-Control | Vercel-CDN-Cache-Control | Cache-Control) \b / i . test (haystack);
356 }
357
358 function mentionsVaryHeader ( rec ) {
359 return / \b Vary \b / i . test ( recText (rec));
360 }
361
362 function mentionsNextCachedNotFound ( rec ) {
363 const haystack = recText (rec);
364 return / \b notFound \b / . test (haystack) &&
365 / ['"`] use cache ['"`] |\b use cache \b / i . test (haystack) &&
366 / \b (?:500 | 5xx | error rate | errors ? ) \b / i . test (haystack);
367 }
368
369 function mentionsNextStableCacheApi ( rec ) {
370 const haystack = recText (rec);
371 return / \b unstable_(?:cacheLife | cacheTag) \b / . test (haystack) ||
372 / \b revalidateTag \s * \( [ ^ )] * ['"`][ ^ '"`] + ['"`]\s * \) / . test (haystack);
373 }
374
375 function mentionsNext16RuntimeCacheApiMismatch ( rec ) {
376 const haystack = recText (rec);
377 const citations = asArray (rec?.citations). join ( ' \n ' );
378 return / \b unstable_cache \b / . test (haystack) &&
379 ( / \b Runtime Cache \b / i . test (haystack) || /vercel \. com \/ docs \/ caching \/ runtime-cache/ i . test (citations));
380 }
381
382 function mentionsRuntimeCacheWhenCacheComponents ( rec ) {
383 const haystack = recText (rec);
384 const citations = asArray (rec?.citations). join ( ' \n ' );
385 return / \b (?:Runtime Cache | @vercel \/ functions | getCache \s * \( | setCache \s * \( ) \b / i . test (haystack) ||
386 /vercel \. com \/ docs \/ caching \/ runtime-cache/ i . test (citations);
387 }
388
389 function mentionsMultipleCacheLifeCalls ( rec ) {
390 const haystack = recText (rec);
391 const matches = haystack. match ( / \b cacheLife \s * \( / g ) ?? [];
392 return matches. length > 1 ;
393 }
394
395 function mentionsCacheLifetimeChange ( rec ) {
396 return / \b cacheLife \s * \( / . test ( recText (rec));
397 }
398
399 function mentionsCacheLifeCdnHeaderClaim ( rec ) {
400 const haystack = recText (rec);
401 if ( ! / \b cacheLife \b / . test (haystack)) return false ;
402 return / \b cacheLife \b [ ^ .\n] {0,240}\b (?:Cache-Control | s-maxage | CDN | edge cache | cache breakdown | x-vercel-cache | HIT | MISS | function (?:still ) ? runs per request | every request invokes the function) \b / i . test (haystack) ||
403 / \b (?:Cache-Control | s-maxage | CDN | edge cache | cache breakdown | x-vercel-cache | HIT | MISS | function (?:still ) ? runs per request | every request invokes the function) \b [ ^ .\n] {0,240}\b cacheLife \b / i . test (haystack) ||
404 / \b (?:no | never | without | missing) \s + cacheLife \b [ ^ .\n] {0,240}\b (?:no | not | never | 0% | every | per request | function) \b [ ^ .\n] {0,120}\b (?:cache | cached | hit | runs ?| invoke)/ i . test (haystack);
405 }
406
407 function mentionsNextCacheComponentsStaticShellTarget ( rec ) {
408 const haystack = recText (rec);
409 if ( ! / \b (?:cacheComponents | Cache Components | cacheLife | cacheTag | ['"`] use cache ['"`] | use cache | static shell | pre [- ] ? render | prerender) \b / i . test (haystack)) {
410 return false ;
411 }
412 const files = [
413 ... asArray (rec?.affectedFiles),
414 ... asArray (rec?.findingRefs). map (( ref ) => String (ref). match ( / ^ ( . +? ): \d +$ / )?.[ 1 ]). filter (Boolean),
415 ];
416 return files. some (( file ) => /( ^| \/ )layout \. (?:tsx ?| jsx ? ) $ / . test ( String (file)));
417 }
418
419 function mentionsImageResponseHeaders ( rec ) {
420 const haystack = recText (rec);
421 return / \b ImageResponse \b / . test (haystack) &&
422 / \b headers ?\b [\s\S] {0,200}\b (?:Cache-Control | s-maxage | CDN | response) \b|\b (?:Cache-Control | s-maxage | CDN) \b [\s\S] {0,200}\b headers ?\b / i . test (haystack);
423 }
424
425 function mentionsNextImagePriorityRecommendation ( rec ) {
426 const haystack = recText (rec);
427 if ( ! / \b (?:next \/ image | <Image \b| Image component | image) \b / i . test (haystack)) return false ;
428 if ( ! / \b priority \b / i . test (haystack)) return false ;
429 if ( / \b (?:deprecated | replace | remove | avoid) \b [ ^ .\n] {0,120}\b priority \b / i . test (haystack) ||
430 / \b priority \b [ ^ .\n] {0,120}\b (?:deprecated | replace | remove | avoid) \b / i . test (haystack)) {
431 return false ;
432 }
433 return / \b (?:set | add | use | enable | mark | make | turn on | with) \b [ ^ .\n] {0,120}\b priority \b / i . test (haystack) ||
434 /<Image \b [ ^ >] *\b priority(?: \s | = | >)/ i . test (haystack);
435 }
436
437 function mentionsNextCacheComponentsRouteSegmentConfig ( rec ) {
438 const haystack = recText (rec);
439 return / \b (?:export \s + const \s + ) ? (?:dynamicParams | fetchCache) \s * =/ . test (haystack) ||
440 / \b export \s + const \s + (?:dynamic | revalidate) \b / . test (haystack) ||
441 / \b (?:set | add | configure | use) \s + [ ^ .\n] {0,80}\b (?:dynamicParams | fetchCache) \b / i . test (haystack) ||
442 / \b route segment config options ?\b [ ^ .\n] {0,120}\b (?:Route Handlers ?| handlers ? ) \b [ ^ .\n] {0,120}\b (?:no longer apply | do not apply | removed) \b / i . test (haystack) ||
443 / \b (?:revalidate | dynamic | fetchCache) \b [ ^ .\n] {0,80}\b route segment (?:config | export) \b / i . test (haystack);
444 }
445
446 function mentionsRouteLevelRevalidate ( rec ) {
447 const haystack = recText (rec);
448 return / \b export \s + const \s + revalidate \b / . test (haystack) ||
449 / \b route [- ] level \s + revalidate \b / i . test (haystack) ||
450 / \b revalidate \s * (?:= | :) \s * \d +\b [ ^ .\n] {0,120}\b (?:page | layout | route segment | segment export) \b / i . test (haystack);
451 }
452
453 function mentionsExistingCacheTagInvalidation ( rec ) {
454 const haystack = recText (rec);
455 if ( ! / \b cacheTag \s * \( / . test (haystack)) return false ;
456 if ( ! / \b (?:revalidateTag | updateTag | invalidate | invalidation | revalidation | webhook | CMS | content-sync | content sync | publish | deploy) \b / i . test (haystack)) {
457 return false ;
458 }
459 return / \b (?:existing | current | already | keep | keeps | preserve | preserves | continue | continues | maintain | maintains | via) \b [\s\S] {0,180}\b (?:revalidateTag | updateTag | invalidate | invalidation | revalidation | event-driven | webhook | CMS | content-sync | content sync | publish | deploy | tags ? ) \b / i . test (haystack) ||
460 / \b (?:invalidation | revalidation) \s + is \s + already \b / i . test (haystack) ||
461 / \b already \s + event-driven \b / i . test (haystack);
462 }
463
464 function mentionsUnsafeImmutableDynamicRoute ( rec ) {
465 const haystack = recText (rec);
466 if ( ! / \b immutable \b / i . test (haystack)) return false ;
467 const files = [
468 ... asArray (rec?.affectedFiles),
469 ... asArray (rec?.findingRefs). map (( ref ) => String (ref). match ( / ^ ( . +? ): \d +$ / )?.[ 1 ]). filter (Boolean),
470 ];
471 const routeHandler = files. some (( file ) => /(?: ^| \/ )route \. [cm] ? [jt] sx ?$ / . test ( String (file)));
472 const apiRoute = / ^ cache_header_gap: \/ api \/ / . test ( String (rec?.candidateRef ?? '' ));
473 return routeHandler || apiRoute;
474 }
475
476 function mentionsAuthSensitiveParallelization ( rec ) {
477 const haystack = recText (rec);
478 if ( ! / \b (?:parallelize | Promise \. all | run concurrently | start . * early) \b / i . test (haystack)) return false ;
479 if ( ! / \b (?:auth | authorize | authorization | ownership | owns | owner | private | session | permission | access) \b / i . test (haystack)) return false ;
480 return / \b (?:private | secret | token | registrant | account | user | ticket | payment | session) \w *\b / i . test (haystack);
481 }
482
483 function mentionsParallelizationImpactOverclaim ( rec ) {
484 const haystack = recText (rec);
485 if ( ! / \b (?:parallelize | Promise \. all | run concurrently | start . * early) \b / i . test (haystack)) return false ;
486 return / \b (?:drop | drops | reduce | reduces | reduction | save | saves | shave | shaves) \b [ ^ .\n] {0,200}\b (?:roughly | approximately | about | around | equal \s + to) ? \s * (?:the \s + ) ? (?:duration \s + of \s + [A-Za-z_$][\w$] * \s * \( \s * \) | min \s * \( [ ^ )] * duration [ ^ )] * \) | one \s + [\w-] + \s + round [- ] trip | one \s + await | one \s + network \s + call | one \s + database \s + query)/ i . test (haystack);
487 }
488
489 function mentionsCpuBoundParallelization ( rec ) {
490 const haystack = recText (rec);
491 if ( ! / \b (?:parallelize | Promise \. all | run concurrently | start . * early) \b / i . test (haystack)) return false ;
492 return / \b (?:cpu \. p95 | CPU p95 | cpu p95 | CPU-bound | compute-bound | in-process compute | compileMDX | MDX compilation | compilation | render compute) \b / i . test (haystack);
493 }
494
495 function mentionsCachedNotFoundOr404 ( rec ) {
496 const haystack = recText (rec);
497 if ( ! / \b (?:s-maxage | CDN-Cache-Control | Vercel-CDN-Cache-Control | Cache-Control) \b / i . test (haystack)) return false ;
498 return / \b (?:404 | not [- ] found | notFound | not found branch | not-found branch) \b / i . test (haystack);
499 }
500
501 function mentionsRuntimeErrorCause ( rec ) {
502 if ( ! / ^ route_errors:/ . test ( String (rec?.candidateRef ?? '' ))) return false ;
503 const haystack = recText (rec);
504 return / \b (?:ENOENT | ETIMEDOUT | ECONNRESET | outputFileTracing | missing \s + (?:file | mdx | module) | no \s + (?:matching | corresponding) \s + (?:file | mdx | post) | does \s + not \s + exist | signature \s + of | root cause | caused by | unhandled \s + exceptions ?| uncaught(?:-exception) ?| throws ?| bubbles ? \s + to \s + the \s + runtime | reads ? \s + [ ^ .] {0,80} (?:filePath | filesystem | file system | disk) | readFile) \b / i . test (haystack);
505 }
506
507 function mentionsCatchToNotFound ( rec ) {
508 if ( ! / ^ route_errors:/ . test ( String (rec?.candidateRef ?? '' ))) return false ;
509 const haystack = recText (rec);
510 return / \b catch \b / i . test (haystack) &&
511 / \b (?:404 | not [- ] found | not found | notFound) \b / i . test (haystack);
512 }
513
514 function mentionsIgnoredBuildStepRecommendation ( rec ) {
515 const haystack = recText (rec);
516 return / \b (?:Ignored Build Step | ignoreCommand | turbo-ignore | skip unaffected | unaffected projects ? ) \b / i . test (haystack) &&
517 / \b (?:add | set | configure | enable | use | introduce | wire | adopt | turn on) \b [ ^ .\n] {0,180}\b (?:Ignored Build Step | ignoreCommand | turbo-ignore | skip unaffected | unaffected projects ? ) \b / i . test (haystack);
518 }
519
520 function mentionsTurboBuildCacheRecommendation ( rec ) {
521 const haystack = recText (rec);
522 if ( ! / \b (?:Turbo | Turborepo | turbo \. json | tasks \. build | build cache | build caching) \b / i . test (haystack)) return false ;
523 return / \b (?:enable | re-enable | restore | turn on | set | remove) \b [ ^ .\n] {0,220}\b (?:cache \s * : \s * false | tasks \. build \. cache | build cache | build caching | Turbo cache | Turborepo cache) \b / i . test (haystack) ||
524 / \b (?:cache \s * : \s * false | tasks \. build \. cache | build cache | build caching | Turbo cache | Turborepo cache) \b [ ^ .\n] {0,220}\b (?:enable | re-enable | restore | turn on | set | remove) \b / i . test (haystack);
525 }
526
527 function recText ( rec ) {
528 return [
529 rec?.what,
530 rec?.why,
531 rec?.fix,
532 rec?.currentBehavior,
533 rec?.desiredBehavior,
534 rec?.verify,
535 ]. filter (Boolean). join ( ' \n ' );
536 }
537
538 function asArray ( v ) {
539 return Array. isArray (v) ? v : [];
540 }
541
542 export function summarizeClaimResults ( results ) {
543 const counts = { verified: 0 , failed: 0 , unsupported: 0 , unverifiable: 0 };
544 for ( const r of results) {
545 if (r?.disposition && counts[r.disposition] !== undefined ) counts[r.disposition] ++ ;
546 }
547 const verifiable = counts.verified + counts.failed;
548 const passRate = verifiable > 0 ? counts.verified / verifiable : 1 ;
549 return { ... counts, verifiable, passRate, total: results. length };
550 }