opencode-skills-collection 3.0.35 → 3.0.37

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.
Files changed (238) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +15 -1
  2. package/bundled-skills/accesslint-audit/SKILL.md +115 -0
  3. package/bundled-skills/accesslint-diff/SKILL.md +81 -0
  4. package/bundled-skills/accesslint-scan/SKILL.md +47 -0
  5. package/bundled-skills/composition-patterns/SKILL.md +87 -0
  6. package/bundled-skills/composition-patterns/rules/_sections.md +29 -0
  7. package/bundled-skills/composition-patterns/rules/_template.md +24 -0
  8. package/bundled-skills/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  9. package/bundled-skills/composition-patterns/rules/architecture-compound-components.md +112 -0
  10. package/bundled-skills/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  11. package/bundled-skills/composition-patterns/rules/patterns-explicit-variants.md +100 -0
  12. package/bundled-skills/composition-patterns/rules/react19-no-forwardref.md +42 -0
  13. package/bundled-skills/composition-patterns/rules/state-context-interface.md +191 -0
  14. package/bundled-skills/composition-patterns/rules/state-decouple-implementation.md +113 -0
  15. package/bundled-skills/composition-patterns/rules/state-lift-state.md +125 -0
  16. package/bundled-skills/debugging-toolkit/SKILL.md +35 -0
  17. package/bundled-skills/deploy-to-vercel/SKILL.md +304 -0
  18. package/bundled-skills/deploy-to-vercel/resources/deploy-codex.sh +301 -0
  19. package/bundled-skills/deploy-to-vercel/resources/deploy.sh +301 -0
  20. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  21. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  22. package/bundled-skills/docs/maintainers/backups/README-2026-06-02.md +687 -0
  23. package/bundled-skills/docs/maintainers/repo-growth-seo.md +4 -4
  24. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  25. package/bundled-skills/docs/users/bundles.md +245 -1
  26. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  27. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  28. package/bundled-skills/docs/users/getting-started.md +1 -1
  29. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  30. package/bundled-skills/docs/users/plugins.md +21 -13
  31. package/bundled-skills/docs/users/specialized-plugin-roadmap.md +95 -0
  32. package/bundled-skills/docs/users/usage.md +4 -4
  33. package/bundled-skills/docs/users/visual-guide.md +4 -4
  34. package/bundled-skills/polis-protocol/SKILL.md +93 -0
  35. package/bundled-skills/python-development/SKILL.md +35 -0
  36. package/bundled-skills/radix-ui-design-system/SKILL.md +2 -2
  37. package/bundled-skills/react-native-skills/SKILL.md +120 -0
  38. package/bundled-skills/react-native-skills/rules/_sections.md +86 -0
  39. package/bundled-skills/react-native-skills/rules/_template.md +28 -0
  40. package/bundled-skills/react-native-skills/rules/animation-derived-value.md +53 -0
  41. package/bundled-skills/react-native-skills/rules/animation-gesture-detector-press.md +95 -0
  42. package/bundled-skills/react-native-skills/rules/animation-gpu-properties.md +65 -0
  43. package/bundled-skills/react-native-skills/rules/design-system-compound-components.md +66 -0
  44. package/bundled-skills/react-native-skills/rules/fonts-config-plugin.md +71 -0
  45. package/bundled-skills/react-native-skills/rules/imports-design-system-folder.md +68 -0
  46. package/bundled-skills/react-native-skills/rules/js-hoist-intl.md +61 -0
  47. package/bundled-skills/react-native-skills/rules/list-performance-callbacks.md +44 -0
  48. package/bundled-skills/react-native-skills/rules/list-performance-function-references.md +132 -0
  49. package/bundled-skills/react-native-skills/rules/list-performance-images.md +53 -0
  50. package/bundled-skills/react-native-skills/rules/list-performance-inline-objects.md +97 -0
  51. package/bundled-skills/react-native-skills/rules/list-performance-item-expensive.md +94 -0
  52. package/bundled-skills/react-native-skills/rules/list-performance-item-memo.md +82 -0
  53. package/bundled-skills/react-native-skills/rules/list-performance-item-types.md +104 -0
  54. package/bundled-skills/react-native-skills/rules/list-performance-virtualize.md +67 -0
  55. package/bundled-skills/react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
  56. package/bundled-skills/react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
  57. package/bundled-skills/react-native-skills/rules/navigation-native-navigators.md +188 -0
  58. package/bundled-skills/react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
  59. package/bundled-skills/react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
  60. package/bundled-skills/react-native-skills/rules/react-state-dispatcher.md +91 -0
  61. package/bundled-skills/react-native-skills/rules/react-state-fallback.md +56 -0
  62. package/bundled-skills/react-native-skills/rules/react-state-minimize.md +65 -0
  63. package/bundled-skills/react-native-skills/rules/rendering-no-falsy-and.md +74 -0
  64. package/bundled-skills/react-native-skills/rules/rendering-text-in-text-component.md +36 -0
  65. package/bundled-skills/react-native-skills/rules/scroll-position-no-state.md +82 -0
  66. package/bundled-skills/react-native-skills/rules/state-ground-truth.md +80 -0
  67. package/bundled-skills/react-native-skills/rules/ui-expo-image.md +66 -0
  68. package/bundled-skills/react-native-skills/rules/ui-image-gallery.md +104 -0
  69. package/bundled-skills/react-native-skills/rules/ui-measure-views.md +78 -0
  70. package/bundled-skills/react-native-skills/rules/ui-menus.md +174 -0
  71. package/bundled-skills/react-native-skills/rules/ui-native-modals.md +77 -0
  72. package/bundled-skills/react-native-skills/rules/ui-pressable.md +61 -0
  73. package/bundled-skills/react-native-skills/rules/ui-safe-area-scroll.md +65 -0
  74. package/bundled-skills/react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
  75. package/bundled-skills/react-native-skills/rules/ui-styling.md +87 -0
  76. package/bundled-skills/skill-issue/SKILL.md +73 -0
  77. package/bundled-skills/tdd-workflows/SKILL.md +35 -0
  78. package/bundled-skills/vercel-cli-with-tokens/SKILL.md +361 -0
  79. package/bundled-skills/vercel-optimize/CONTRIBUTING.md +41 -0
  80. package/bundled-skills/vercel-optimize/SKILL.md +331 -0
  81. package/bundled-skills/vercel-optimize/lib/auth-route.mjs +23 -0
  82. package/bundled-skills/vercel-optimize/lib/budget-summary.mjs +182 -0
  83. package/bundled-skills/vercel-optimize/lib/citations.mjs +139 -0
  84. package/bundled-skills/vercel-optimize/lib/cost-coverage.mjs +143 -0
  85. package/bundled-skills/vercel-optimize/lib/dedup-recs.mjs +325 -0
  86. package/bundled-skills/vercel-optimize/lib/deep-dive.mjs +350 -0
  87. package/bundled-skills/vercel-optimize/lib/display-labels.mjs +185 -0
  88. package/bundled-skills/vercel-optimize/lib/extract-claims.mjs +550 -0
  89. package/bundled-skills/vercel-optimize/lib/framework-support.mjs +67 -0
  90. package/bundled-skills/vercel-optimize/lib/gates/build-minutes-fanout.mjs +69 -0
  91. package/bundled-skills/vercel-optimize/lib/gates/cold-start.mjs +66 -0
  92. package/bundled-skills/vercel-optimize/lib/gates/contract.mjs +79 -0
  93. package/bundled-skills/vercel-optimize/lib/gates/cwv-poor.mjs +87 -0
  94. package/bundled-skills/vercel-optimize/lib/gates/external-api-slow.mjs +55 -0
  95. package/bundled-skills/vercel-optimize/lib/gates/hard-gates.mjs +73 -0
  96. package/bundled-skills/vercel-optimize/lib/gates/index.mjs +45 -0
  97. package/bundled-skills/vercel-optimize/lib/gates/isr-overrevalidation.mjs +62 -0
  98. package/bundled-skills/vercel-optimize/lib/gates/middleware-heavy.mjs +51 -0
  99. package/bundled-skills/vercel-optimize/lib/gates/observability-events-attribution.mjs +56 -0
  100. package/bundled-skills/vercel-optimize/lib/gates/platform-bot-protection.mjs +115 -0
  101. package/bundled-skills/vercel-optimize/lib/gates/platform-fluid-compute.mjs +83 -0
  102. package/bundled-skills/vercel-optimize/lib/gates/region-misconfig.mjs +64 -0
  103. package/bundled-skills/vercel-optimize/lib/gates/route-errors.mjs +80 -0
  104. package/bundled-skills/vercel-optimize/lib/gates/scanner-driven.mjs +122 -0
  105. package/bundled-skills/vercel-optimize/lib/gates/select-candidates.mjs +134 -0
  106. package/bundled-skills/vercel-optimize/lib/gates/slow-route.mjs +88 -0
  107. package/bundled-skills/vercel-optimize/lib/gates/types.d.ts +38 -0
  108. package/bundled-skills/vercel-optimize/lib/gates/uncached-route.mjs +93 -0
  109. package/bundled-skills/vercel-optimize/lib/gates/usage-spike-triage.mjs +121 -0
  110. package/bundled-skills/vercel-optimize/lib/grade-recommendation.mjs +155 -0
  111. package/bundled-skills/vercel-optimize/lib/impact-label.mjs +126 -0
  112. package/bundled-skills/vercel-optimize/lib/impact-magnitude.mjs +60 -0
  113. package/bundled-skills/vercel-optimize/lib/investigation-brief.mjs +610 -0
  114. package/bundled-skills/vercel-optimize/lib/observation-safety.mjs +174 -0
  115. package/bundled-skills/vercel-optimize/lib/project-facts.mjs +99 -0
  116. package/bundled-skills/vercel-optimize/lib/queries.mjs +315 -0
  117. package/bundled-skills/vercel-optimize/lib/reconcile-candidates.mjs +372 -0
  118. package/bundled-skills/vercel-optimize/lib/render-report.mjs +955 -0
  119. package/bundled-skills/vercel-optimize/lib/repo-root.mjs +86 -0
  120. package/bundled-skills/vercel-optimize/lib/route-normalize.mjs +220 -0
  121. package/bundled-skills/vercel-optimize/lib/sanitizers/bot-protection-certainty.mjs +38 -0
  122. package/bundled-skills/vercel-optimize/lib/sanitizers/cache-tag-invalidation-certainty.mjs +30 -0
  123. package/bundled-skills/vercel-optimize/lib/sanitizers/count-correct.mjs +52 -0
  124. package/bundled-skills/vercel-optimize/lib/sanitizers/function-duration-invocations.mjs +38 -0
  125. package/bundled-skills/vercel-optimize/lib/sanitizers/index.mjs +79 -0
  126. package/bundled-skills/vercel-optimize/lib/sanitizers/middleware-conflict.mjs +36 -0
  127. package/bundled-skills/vercel-optimize/lib/sanitizers/missing-citation.mjs +16 -0
  128. package/bundled-skills/vercel-optimize/lib/sanitizers/pre-release.mjs +74 -0
  129. package/bundled-skills/vercel-optimize/lib/sanitizers/rate-limit.mjs +67 -0
  130. package/bundled-skills/vercel-optimize/lib/sanitizers/rendering-mode-mislabel.mjs +38 -0
  131. package/bundled-skills/vercel-optimize/lib/sanitizers/undeclared-dep.mjs +78 -0
  132. package/bundled-skills/vercel-optimize/lib/sanitizers/vercel-directive-strip.mjs +37 -0
  133. package/bundled-skills/vercel-optimize/lib/sanitizers/window-units.mjs +32 -0
  134. package/bundled-skills/vercel-optimize/lib/scanners/cache-components-suspense-dedupe.mjs +109 -0
  135. package/bundled-skills/vercel-optimize/lib/scanners/edge-heavy-import.mjs +94 -0
  136. package/bundled-skills/vercel-optimize/lib/scanners/force-dynamic.mjs +42 -0
  137. package/bundled-skills/vercel-optimize/lib/scanners/headers-in-page.mjs +44 -0
  138. package/bundled-skills/vercel-optimize/lib/scanners/index.mjs +35 -0
  139. package/bundled-skills/vercel-optimize/lib/scanners/large-static-asset.mjs +92 -0
  140. package/bundled-skills/vercel-optimize/lib/scanners/max-age-without-s-maxage.mjs +42 -0
  141. package/bundled-skills/vercel-optimize/lib/scanners/middleware-broad-matcher.mjs +55 -0
  142. package/bundled-skills/vercel-optimize/lib/scanners/missing-cache-headers.mjs +90 -0
  143. package/bundled-skills/vercel-optimize/lib/scanners/prisma-include-tree.mjs +42 -0
  144. package/bundled-skills/vercel-optimize/lib/scanners/region-pin-in-config.mjs +88 -0
  145. package/bundled-skills/vercel-optimize/lib/scanners/source-maps-production.mjs +36 -0
  146. package/bundled-skills/vercel-optimize/lib/scanners/sveltekit-prerender-missing.mjs +43 -0
  147. package/bundled-skills/vercel-optimize/lib/scanners/turbo-force-bypass.mjs +129 -0
  148. package/bundled-skills/vercel-optimize/lib/scanners/unoptimized-image.mjs +113 -0
  149. package/bundled-skills/vercel-optimize/lib/scanners/use-cache-date-stamp.mjs +106 -0
  150. package/bundled-skills/vercel-optimize/lib/support-topics.mjs +355 -0
  151. package/bundled-skills/vercel-optimize/lib/throttle.mjs +273 -0
  152. package/bundled-skills/vercel-optimize/lib/util.mjs +17 -0
  153. package/bundled-skills/vercel-optimize/lib/vercel.mjs +784 -0
  154. package/bundled-skills/vercel-optimize/lib/verify-claim.mjs +1296 -0
  155. package/bundled-skills/vercel-optimize/lib/workspace-resolver.mjs +521 -0
  156. package/bundled-skills/vercel-optimize/references/candidates.md +176 -0
  157. package/bundled-skills/vercel-optimize/references/data-collection.md +218 -0
  158. package/bundled-skills/vercel-optimize/references/docs-library.json +683 -0
  159. package/bundled-skills/vercel-optimize/references/doctrine.md +105 -0
  160. package/bundled-skills/vercel-optimize/references/observability-plus.md +108 -0
  161. package/bundled-skills/vercel-optimize/references/playbooks/README.md +53 -0
  162. package/bundled-skills/vercel-optimize/references/playbooks/ai-application.md +32 -0
  163. package/bundled-skills/vercel-optimize/references/playbooks/api-service.md +30 -0
  164. package/bundled-skills/vercel-optimize/references/playbooks/content-site.md +30 -0
  165. package/bundled-skills/vercel-optimize/references/playbooks/ecommerce.md +30 -0
  166. package/bundled-skills/vercel-optimize/references/playbooks/marketing.md +30 -0
  167. package/bundled-skills/vercel-optimize/references/playbooks/saas.md +31 -0
  168. package/bundled-skills/vercel-optimize/references/playbooks/sveltekit.md +75 -0
  169. package/bundled-skills/vercel-optimize/references/recommendations.md +203 -0
  170. package/bundled-skills/vercel-optimize/references/scanner-patterns.md +251 -0
  171. package/bundled-skills/vercel-optimize/references/scoring.md +205 -0
  172. package/bundled-skills/vercel-optimize/references/support-topics/README.md +46 -0
  173. package/bundled-skills/vercel-optimize/references/support-topics/astro-edge-middleware-scope.md +22 -0
  174. package/bundled-skills/vercel-optimize/references/support-topics/astro-output-mode-and-isr.md +22 -0
  175. package/bundled-skills/vercel-optimize/references/support-topics/auth-preserving-parallelization.md +22 -0
  176. package/bundled-skills/vercel-optimize/references/support-topics/bot-protection-product-guardrails.md +22 -0
  177. package/bundled-skills/vercel-optimize/references/support-topics/build-minutes-monorepo-fanout.md +23 -0
  178. package/bundled-skills/vercel-optimize/references/support-topics/cache-components-static-shell-boundaries.md +22 -0
  179. package/bundled-skills/vercel-optimize/references/support-topics/cache-components-suspense-dedupe-pitfall.md +23 -0
  180. package/bundled-skills/vercel-optimize/references/support-topics/cdn-cache-auth-safety.md +22 -0
  181. package/bundled-skills/vercel-optimize/references/support-topics/cold-start-initialization-bundle.md +22 -0
  182. package/bundled-skills/vercel-optimize/references/support-topics/core-web-vitals-client-bottlenecks.md +22 -0
  183. package/bundled-skills/vercel-optimize/references/support-topics/database-egress-pooling-region.md +22 -0
  184. package/bundled-skills/vercel-optimize/references/support-topics/dynamic-rendering-traps.md +22 -0
  185. package/bundled-skills/vercel-optimize/references/support-topics/external-api-critical-path-platform.md +22 -0
  186. package/bundled-skills/vercel-optimize/references/support-topics/external-api-critical-path.md +22 -0
  187. package/bundled-skills/vercel-optimize/references/support-topics/fast-data-transfer-payloads.md +22 -0
  188. package/bundled-skills/vercel-optimize/references/support-topics/fluid-compute-caveats.md +22 -0
  189. package/bundled-skills/vercel-optimize/references/support-topics/function-duration-io-and-after.md +22 -0
  190. package/bundled-skills/vercel-optimize/references/support-topics/function-invocation-reduction.md +22 -0
  191. package/bundled-skills/vercel-optimize/references/support-topics/function-region-misconfiguration-ttfb.md +23 -0
  192. package/bundled-skills/vercel-optimize/references/support-topics/image-optimization-cost-control.md +22 -0
  193. package/bundled-skills/vercel-optimize/references/support-topics/isr-revalidation-static-generation.md +22 -0
  194. package/bundled-skills/vercel-optimize/references/support-topics/middleware-proxy-edge-cost.md +22 -0
  195. package/bundled-skills/vercel-optimize/references/support-topics/next-fetch-revalidate-floor.md +22 -0
  196. package/bundled-skills/vercel-optimize/references/support-topics/next-font-cls-self-hosting.md +23 -0
  197. package/bundled-skills/vercel-optimize/references/support-topics/next-heavy-ui-lazy-load-boundaries.md +23 -0
  198. package/bundled-skills/vercel-optimize/references/support-topics/next-image-lcp-preload-sizes.md +23 -0
  199. package/bundled-skills/vercel-optimize/references/support-topics/next-route-handler-get-cache-defaults.md +22 -0
  200. package/bundled-skills/vercel-optimize/references/support-topics/next-script-third-party-strategy.md +23 -0
  201. package/bundled-skills/vercel-optimize/references/support-topics/nextjs-version-cache-semantics.md +22 -0
  202. package/bundled-skills/vercel-optimize/references/support-topics/not-found-catchall-request-waste.md +23 -0
  203. package/bundled-skills/vercel-optimize/references/support-topics/nuxt-route-rules-cache-isr.md +22 -0
  204. package/bundled-skills/vercel-optimize/references/support-topics/observability-events-cost-attribution.md +22 -0
  205. package/bundled-skills/vercel-optimize/references/support-topics/post-response-work-waituntil.md +22 -0
  206. package/bundled-skills/vercel-optimize/references/support-topics/route-error-durable-offload.md +22 -0
  207. package/bundled-skills/vercel-optimize/references/support-topics/route-error-runtime-limits.md +22 -0
  208. package/bundled-skills/vercel-optimize/references/support-topics/runtime-cache-reusable-data.md +22 -0
  209. package/bundled-skills/vercel-optimize/references/support-topics/sveltekit-isr-prerender-safety.md +22 -0
  210. package/bundled-skills/vercel-optimize/references/support-topics/sveltekit-split-cold-start-tradeoff.md +22 -0
  211. package/bundled-skills/vercel-optimize/references/support-topics/usage-spike-triage.md +22 -0
  212. package/bundled-skills/vercel-optimize/references/support-topics/use-cache-date-stamp-isr-write-amplifier.md +23 -0
  213. package/bundled-skills/vercel-optimize/references/support-topics/use-cache-remote-shared-origin-data.md +22 -0
  214. package/bundled-skills/vercel-optimize/references/support-topics/workflow-resumable-stream-routes.md +23 -0
  215. package/bundled-skills/vercel-optimize/references/verification.md +102 -0
  216. package/bundled-skills/vercel-optimize/references/voice.md +76 -0
  217. package/bundled-skills/vercel-optimize/scripts/budget-summary.mjs +56 -0
  218. package/bundled-skills/vercel-optimize/scripts/build-docs.mjs +74 -0
  219. package/bundled-skills/vercel-optimize/scripts/check-citations.mjs +81 -0
  220. package/bundled-skills/vercel-optimize/scripts/check-docs-fresh.mjs +93 -0
  221. package/bundled-skills/vercel-optimize/scripts/collect-signals.mjs +576 -0
  222. package/bundled-skills/vercel-optimize/scripts/collect-sub-agent-outputs.mjs +296 -0
  223. package/bundled-skills/vercel-optimize/scripts/deep-dive.mjs +319 -0
  224. package/bundled-skills/vercel-optimize/scripts/gate-investigations.mjs +166 -0
  225. package/bundled-skills/vercel-optimize/scripts/merge-signals.mjs +192 -0
  226. package/bundled-skills/vercel-optimize/scripts/prepare-investigation-brief.mjs +231 -0
  227. package/bundled-skills/vercel-optimize/scripts/reconcile-candidates.mjs +62 -0
  228. package/bundled-skills/vercel-optimize/scripts/render-report.mjs +437 -0
  229. package/bundled-skills/vercel-optimize/scripts/scan-codebase.mjs +313 -0
  230. package/bundled-skills/vercel-optimize/scripts/verify-and-regen.mjs +346 -0
  231. package/bundled-skills/vercel-optimize/scripts/verify-finding.mjs +19 -0
  232. package/bundled-skills/vercel-react-view-transitions/SKILL.md +327 -0
  233. package/bundled-skills/vercel-react-view-transitions/references/css-recipes.md +242 -0
  234. package/bundled-skills/vercel-react-view-transitions/references/implementation.md +182 -0
  235. package/bundled-skills/vercel-react-view-transitions/references/nextjs.md +176 -0
  236. package/bundled-skills/vercel-react-view-transitions/references/patterns.md +262 -0
  237. package/package.json +2 -2
  238. package/skills_index.json +312 -0
@@ -0,0 +1,350 @@
1
+ // Per-candidate deep-dive query specs. Runs after gate, before sub-agent reads source.
2
+ //
3
+ // CLI quirks:
4
+ // - Multi `-a` flag is NOT supported. One percentile per query.
5
+ // - External-API "calling route" dim is `origin_route` (NOT `route`).
6
+
7
+ // Same window as broad pass so rolls are comparable.
8
+ import { TIME_WINDOW } from './queries.mjs';
9
+
10
+ export { TIME_WINDOW };
11
+
12
+ // Per-query is scoped to one route/hostname, so cardinality stays small — higher than broad-pass caps.
13
+ const DEPLOYMENT_LIMIT = 10;
14
+ const ERROR_DEPLOYMENT_LIMIT = 30;
15
+ const ERROR_CODE_LIMIT = 50;
16
+ const WAF_RULE_LIMIT = 20;
17
+ const MIDDLEWARE_PATH_LIMIT = 50;
18
+ const CALLER_LIMIT = 20;
19
+
20
+ // OData escapes a literal `'` inside a string by doubling it (`it's` → `it''s`).
21
+ export function escapeODataString(s) {
22
+ if (typeof s !== 'string') return '';
23
+ return s.replace(/'/g, "''");
24
+ }
25
+
26
+ export function odataEq(dim, value) {
27
+ return `${dim} eq '${escapeODataString(value)}'`;
28
+ }
29
+
30
+ export function odataAnd(...conds) {
31
+ return conds.filter(Boolean).join(' and ');
32
+ }
33
+
34
+ export const SPEC_GENERATORS = {
35
+ slow_route(c) {
36
+ const route = c.route;
37
+ if (!route) return [];
38
+ const f = odataEq('route', route);
39
+ // cacheBreakdown/bandwidthByCache let sub-agent see miss-path cost on static routes (dynamic='error' can still show p95=900ms over millions of requests).
40
+ return [
41
+ ...latencyPercentiles('latency', 'vercel.function_invocation.function_duration_ms', f),
42
+ ...latencyPercentiles('ttfb', 'vercel.function_invocation.ttfb_ms', f),
43
+ ...latencyPercentiles('cpu', 'vercel.function_invocation.function_cpu_time_ms', f, ['p95']),
44
+ {
45
+ id: 'startTypeSplit',
46
+ metricId: 'vercel.function_invocation.count',
47
+ aggregation: 'sum',
48
+ groupBy: ['function_start_type'],
49
+ filter: f,
50
+ broadPassEquivalent: { key: 'fnStartTypeByRoute', routeFilter: route, projectDims: ['function_start_type'] },
51
+ },
52
+ // function-invocation status (5xx from function) — distinct from request-level status, can't reuse broad-pass.
53
+ {
54
+ id: 'statusDistribution',
55
+ metricId: 'vercel.function_invocation.count',
56
+ aggregation: 'sum',
57
+ groupBy: ['http_status'],
58
+ filter: f,
59
+ },
60
+ {
61
+ id: 'perDeployment',
62
+ metricId: 'vercel.function_invocation.function_duration_ms',
63
+ aggregation: 'p95',
64
+ groupBy: ['deployment_id'],
65
+ filter: f,
66
+ limit: DEPLOYMENT_LIMIT,
67
+ },
68
+ {
69
+ id: 'cacheBreakdown',
70
+ metricId: 'vercel.request.count',
71
+ aggregation: 'sum',
72
+ groupBy: ['cache_result'],
73
+ filter: f,
74
+ broadPassEquivalent: { key: 'requestsByRouteCache', routeFilter: route, projectDims: ['cache_result'] },
75
+ },
76
+ // broad-pass bandwidthByCacheResult is account-wide, so per-route still required.
77
+ {
78
+ id: 'bandwidthByCache',
79
+ metricId: 'vercel.request.fdt_total_bytes',
80
+ aggregation: 'sum',
81
+ groupBy: ['cache_result'],
82
+ filter: f,
83
+ },
84
+ ];
85
+ },
86
+
87
+ uncached_route(c) {
88
+ const route = c.route;
89
+ if (!route) return [];
90
+ const f = odataEq('route', route);
91
+ return [
92
+ {
93
+ id: 'cacheBreakdown',
94
+ metricId: 'vercel.request.count',
95
+ aggregation: 'sum',
96
+ groupBy: ['cache_result'],
97
+ filter: f,
98
+ broadPassEquivalent: { key: 'requestsByRouteCache', routeFilter: route, projectDims: ['cache_result'] },
99
+ },
100
+ {
101
+ id: 'methodDistribution',
102
+ metricId: 'vercel.request.count',
103
+ aggregation: 'sum',
104
+ groupBy: ['request_method'],
105
+ filter: f,
106
+ broadPassEquivalent: { key: 'requestsByRouteMethod', routeFilter: route, projectDims: ['request_method'] },
107
+ },
108
+ {
109
+ id: 'botShare',
110
+ metricId: 'vercel.request.fdt_total_bytes',
111
+ aggregation: 'sum',
112
+ groupBy: ['bot_category'],
113
+ filter: f,
114
+ },
115
+ {
116
+ id: 'bandwidthByCache',
117
+ metricId: 'vercel.request.fdt_total_bytes',
118
+ aggregation: 'sum',
119
+ groupBy: ['cache_result'],
120
+ filter: f,
121
+ },
122
+ ];
123
+ },
124
+
125
+ cold_start(c) {
126
+ const route = c.route;
127
+ if (!route) return [];
128
+ const f = odataEq('route', route);
129
+ return [
130
+ {
131
+ id: 'startTypeSplit',
132
+ metricId: 'vercel.function_invocation.count',
133
+ aggregation: 'sum',
134
+ groupBy: ['function_start_type'],
135
+ filter: f,
136
+ },
137
+ {
138
+ id: 'coldVsWarmLatencyP95',
139
+ metricId: 'vercel.function_invocation.function_duration_ms',
140
+ aggregation: 'p95',
141
+ groupBy: ['function_start_type'],
142
+ filter: f,
143
+ },
144
+ {
145
+ id: 'coldByDeployment',
146
+ metricId: 'vercel.function_invocation.count',
147
+ aggregation: 'sum',
148
+ groupBy: ['deployment_id'],
149
+ filter: odataAnd(f, odataEq('function_start_type', 'cold')),
150
+ limit: DEPLOYMENT_LIMIT,
151
+ },
152
+ ];
153
+ },
154
+
155
+ route_errors(c) {
156
+ const route = c.route;
157
+ if (!route) return [];
158
+ const f = odataEq('route', route);
159
+ return [
160
+ {
161
+ id: 'errorStatusPattern',
162
+ metricId: 'vercel.request.count',
163
+ aggregation: 'sum',
164
+ groupBy: ['http_status'],
165
+ filter: odataAnd(f, "http_status ge '500'"),
166
+ },
167
+ {
168
+ id: 'errorCodes',
169
+ metricId: 'vercel.function_invocation.count',
170
+ aggregation: 'sum',
171
+ groupBy: ['error_code'],
172
+ filter: f,
173
+ limit: ERROR_CODE_LIMIT,
174
+ },
175
+ {
176
+ id: 'errorsByDeployment',
177
+ metricId: 'vercel.function_invocation.count',
178
+ aggregation: 'sum',
179
+ groupBy: ['deployment_id', 'http_status'],
180
+ filter: f,
181
+ limit: ERROR_DEPLOYMENT_LIMIT,
182
+ },
183
+ ];
184
+ },
185
+
186
+ external_api_slow(c) {
187
+ const host = c.hostname;
188
+ if (!host) return [];
189
+ const f = odataEq('origin_hostname', host);
190
+ return [
191
+ ...latencyPercentiles('latency', 'vercel.external_api_request.request_duration_ms', f),
192
+ {
193
+ // "calling route" dim is origin_route (verified via metrics schema).
194
+ id: 'callersByRoute',
195
+ metricId: 'vercel.external_api_request.count',
196
+ aggregation: 'sum',
197
+ groupBy: ['origin_route'],
198
+ filter: f,
199
+ limit: CALLER_LIMIT,
200
+ },
201
+ {
202
+ id: 'transferBytes',
203
+ metricId: 'vercel.external_api_request.transfer_bytes',
204
+ aggregation: 'sum',
205
+ groupBy: [],
206
+ filter: f,
207
+ },
208
+ ];
209
+ },
210
+
211
+ isr_overrevalidation(c) {
212
+ const route = c.route;
213
+ if (!route) return [];
214
+ const f = odataEq('route', route);
215
+ return [
216
+ {
217
+ id: 'writePattern',
218
+ metricId: 'vercel.isr_operation.write_units',
219
+ aggregation: 'sum',
220
+ groupBy: ['cache_result'],
221
+ filter: f,
222
+ },
223
+ {
224
+ id: 'readPattern',
225
+ metricId: 'vercel.isr_operation.read_units',
226
+ aggregation: 'sum',
227
+ groupBy: ['cache_result'],
228
+ filter: f,
229
+ },
230
+ ];
231
+ },
232
+
233
+ cwv_poor(c) {
234
+ const route = c.route;
235
+ if (!route) return [];
236
+ const f = odataEq('route', route);
237
+ return [
238
+ ...latencyPercentiles('lcp', 'vercel.speed_insights_metric.lcp', f, ['p50', 'p75', 'p95']),
239
+ ...latencyPercentiles('inp', 'vercel.speed_insights_metric.inp', f, ['p50', 'p75', 'p95']),
240
+ ...latencyPercentiles('cls', 'vercel.speed_insights_metric.cls', f, ['p50', 'p75', 'p95']),
241
+ ];
242
+ },
243
+
244
+ middleware_heavy(_c) {
245
+ // Account-scope. Surface top middleware-paths so recommender has named targets.
246
+ return [
247
+ {
248
+ id: 'topMiddlewarePaths',
249
+ metricId: 'vercel.middleware_invocation.count',
250
+ aggregation: 'sum',
251
+ groupBy: ['request_path'],
252
+ limit: MIDDLEWARE_PATH_LIMIT,
253
+ },
254
+ ];
255
+ },
256
+
257
+ platform_fluid_compute(_c) {
258
+ // Broad-pass fnStartTypeByRoute already covers this account-scope rec; runner notes reuse.
259
+ return [];
260
+ },
261
+
262
+ platform_bot_protection(_c) {
263
+ return [
264
+ {
265
+ id: 'wafRuleFirings',
266
+ metricId: 'vercel.firewall_action.count',
267
+ aggregation: 'sum',
268
+ groupBy: ['waf_rule_id'],
269
+ limit: WAF_RULE_LIMIT,
270
+ },
271
+ ];
272
+ },
273
+
274
+ observability_events_attribution(_c) {
275
+ // Account-scope billing signal; broad-pass usage and existing route/cache/middleware metrics carry the evidence.
276
+ return [];
277
+ },
278
+
279
+ usage_spike_triage(_c) {
280
+ // Daily billing breakdown is already in the gate evidence; no per-candidate metrics query exists.
281
+ return [];
282
+ },
283
+
284
+ build_minutes_fanout(_c) {
285
+ // Account-scope billing signal + scanner findings carry the evidence; no per-candidate query.
286
+ return [];
287
+ },
288
+
289
+ region_misconfig(_c) {
290
+ // Branch 2 (scanner-only) — per-region TTFB metric unavailable today, so no deep-dive query.
291
+ return [];
292
+ },
293
+ };
294
+
295
+ // Scanner-driven kinds skip deep-dive — evidence already in scanner findings (file + line).
296
+ export const SCANNER_KINDS = new Set([
297
+ 'image_optimization',
298
+ 'cache_header_gap',
299
+ 'rendering_candidate',
300
+ 'use_cache_date_stamp',
301
+ 'cache_components_suspense_dedupe',
302
+ ]);
303
+
304
+ export function specsForCandidate(candidate) {
305
+ const kind = candidate?.kind;
306
+ if (!kind) return [];
307
+ if (SCANNER_KINDS.has(kind)) return [];
308
+ const gen = SPEC_GENERATORS[kind];
309
+ if (!gen) return [];
310
+ return gen(candidate).map((s) => ({ since: TIME_WINDOW, ...s }));
311
+ }
312
+
313
+ // One spec per percentile — CLI does not support `-a p50 -a p95` multi-aggregation.
314
+ function latencyPercentiles(idPrefix, metricId, filter, percentiles = ['p50', 'p75', 'p95', 'p99']) {
315
+ return percentiles.map((p) => ({
316
+ id: `${idPrefix}.${p}`,
317
+ metricId,
318
+ aggregation: p,
319
+ groupBy: [],
320
+ filter,
321
+ }));
322
+ }
323
+
324
+ // Dot-notation spec ids (`latency.p95`) nest under their group prefix.
325
+ export function mergeIntoEvidence(results) {
326
+ const out = {};
327
+ for (const r of results) {
328
+ const id = r?.spec?.id;
329
+ if (!id) continue;
330
+ const dot = id.indexOf('.');
331
+ if (dot > -1) {
332
+ const head = id.slice(0, dot);
333
+ const leaf = id.slice(dot + 1);
334
+ if (!out[head]) out[head] = {};
335
+ out[head][leaf] = simplify(r);
336
+ } else {
337
+ out[id] = simplify(r);
338
+ }
339
+ }
340
+ return out;
341
+ }
342
+
343
+ // Avoid leaking raw CLI payload / candidate+spec wrapper into evidence — keep summary-only.
344
+ function simplify(r) {
345
+ if (!r || r.ok === false) return { error: r?.error ?? 'unknown' };
346
+ // Check rows before value so tabular results with both stay tabular.
347
+ if (Array.isArray(r.rows)) return r.rows;
348
+ if ('value' in r) return r.value;
349
+ return null;
350
+ }
@@ -0,0 +1,185 @@
1
+ import { canonicalizeRoute } from './route-normalize.mjs';
2
+
3
+ const KIND_LABELS = new Map([
4
+ ['slow_route', 'Slow route'],
5
+ ['uncached_route', 'Low cache-hit route'],
6
+ ['cold_start', 'Cold starts'],
7
+ ['route_errors', 'Route errors'],
8
+ ['cache_header_gap', 'Missing cache headers'],
9
+ ['image_optimization', 'Image optimization'],
10
+ ['external_api_slow', 'Slow external API'],
11
+ ['isr_overrevalidation', 'ISR over-revalidation'],
12
+ ['middleware_heavy', 'Heavy middleware'],
13
+ ['cwv_poor', 'Poor Core Web Vitals'],
14
+ ['platform_fluid_compute', 'Fluid Compute usage'],
15
+ ['platform_bot_protection', 'Bot traffic'],
16
+ ['rendering_candidate', 'Rendering opportunity'],
17
+ ['missing_cache_headers', 'Missing cache headers'],
18
+ ['max_age_without_s_maxage', 'Browser-only cache header'],
19
+ ['force_dynamic', 'Forced dynamic rendering'],
20
+ ['headers_in_page', 'Dynamic API in page'],
21
+ ['unoptimized_image', 'Image optimization gap'],
22
+ ['large_static_asset', 'Large static asset'],
23
+ ['source_maps_production', 'Production source maps'],
24
+ ['edge_heavy_import', 'Heavy Edge import'],
25
+ ]);
26
+
27
+ const SIGNAL_LABELS = new Map([
28
+ ['inv', 'function invocations'],
29
+ ['runs', 'function invocations'],
30
+ ['middleware_inv', 'middleware invocations'],
31
+ ['total_req', 'total requests'],
32
+ ['requests', 'requests'],
33
+ ['p95', '95th percentile duration'],
34
+ ['p75', '75th percentile duration'],
35
+ ['5xx', '5xx error rate'],
36
+ ['errs', '5xx errors'],
37
+ ['rate', '5xx error rate'],
38
+ ['cache', 'cache hit rate'],
39
+ ['get', 'GET request share'],
40
+ ['cold', 'cold start rate'],
41
+ ['writes', 'ISR write units'],
42
+ ['reads', 'ISR read units'],
43
+ ['w/r', 'ISR writes per read'],
44
+ ['ratio', 'ratio'],
45
+ ['host', 'host'],
46
+ ['calls', 'external API calls'],
47
+ ['edge_cost', 'Edge Request cost units'],
48
+ ['bot_protection', 'Bot Protection'],
49
+ ['bot_fdt_pct', 'bot Fast Data Transfer share'],
50
+ ['LCP', 'Largest Contentful Paint (LCP)'],
51
+ ['INP', 'Interaction to Next Paint (INP)'],
52
+ ['CLS', 'Cumulative Layout Shift (CLS)'],
53
+ ]);
54
+
55
+ const REQUEST_COUNT_KINDS = new Set([
56
+ 'uncached_route',
57
+ ]);
58
+
59
+ const PUBLIC_ASSIGNMENT_LABELS = new Map([
60
+ ...SIGNAL_LABELS,
61
+ ['deepDive.latency.p95', 'deepDive latency p95'],
62
+ ['deepDive.cpu.p95', 'deepDive CPU p95'],
63
+ ['deepDive.ttfb.p95', 'deepDive TTFB p95'],
64
+ ['cpu.p95', 'CPU p95'],
65
+ ['latency.p95', 'latency p95'],
66
+ ['ttfb.p95', 'TTFB p95'],
67
+ ['cache_result', 'cache result'],
68
+ ['http_status', 'HTTP status'],
69
+ ['error_code', 'error code'],
70
+ ['status', 'status'],
71
+ ['count', 'count'],
72
+ ]);
73
+
74
+ export function formatKind(kind) {
75
+ if (!kind) return 'Candidate';
76
+ if (KIND_LABELS.has(kind)) return KIND_LABELS.get(kind);
77
+ return String(kind)
78
+ .split(/[_-]+/g)
79
+ .filter(Boolean)
80
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
81
+ .join(' ') || 'Candidate';
82
+ }
83
+
84
+ export function formatRoute(candidate) {
85
+ const route = candidate?.displayRoute ?? candidate?.route ?? candidate?.hostname ?? null;
86
+ if (route) return String(canonicalizeRoute(route));
87
+ if (Array.isArray(candidate?.files) && candidate.files.length > 0) return candidate.files[0];
88
+ return 'account-wide';
89
+ }
90
+
91
+ export function formatSignal(signal, context = {}) {
92
+ if (typeof signal !== 'string' || signal.trim() === '') return 'no signal recorded';
93
+
94
+ const parts = signal
95
+ .split(',')
96
+ .map((part) => part.trim())
97
+ .filter(Boolean)
98
+ .map((part) => formatSignalPart(part, context));
99
+
100
+ return parts.length > 0 ? parts.join('; ') : signal;
101
+ }
102
+
103
+ export function formatPublicText(value) {
104
+ if (value == null) return '';
105
+ return normalizeObservedWindowUnits(String(value))
106
+ .replace(/\bo11y\b/gi, 'observability')
107
+ .replace(/\bcache[- ]components gotcha\b/gi, 'Cache Components edge case')
108
+ .replace(/\bcache_result\b(?!\s*=)/g, 'cache result')
109
+ .replace(/\bhttp_status\b(?!\s*=)/g, 'HTTP status')
110
+ .replace(/\berror_code\b(?!\s*=)/g, 'error code')
111
+ .replace(/,(?=\s*([A-Za-z0-9][\w./-]*)=)/g, (match, key) =>
112
+ PUBLIC_ASSIGNMENT_LABELS.has(key) ? '; ' : match
113
+ )
114
+ .replace(/\b([A-Za-z0-9][\w./-]*)=([^,;\s]+)/g, (match, key, rawValue) => {
115
+ const label = PUBLIC_ASSIGNMENT_LABELS.get(key);
116
+ if (!label) return match;
117
+ return `${label}: ${formatSignalValue(key, rawValue)}`;
118
+ })
119
+ .replace(/\b(cache breakdown[^.!?\n;]{0,160}?)\b(?:function\s+)?invocations\b/gi, (match, prefix) =>
120
+ /\bstatus distribution\b/i.test(prefix) ? match : `${prefix}requests`
121
+ )
122
+ .replace(/\b(cache breakdown[^.!?\n;]{0,220}?\bout of\s+[\d,]+)\s+invocations\b/gi, '$1 requests')
123
+ .replace(/\b(cache hits over\s+[\d,.]+(?:\s?(?:K|M|B))?)\s+invocations\b/gi, '$1 requests')
124
+ .replace(/\b(?:function\s+)?invocations\b([^.!?\n;]{0,120}\b(?:empty\s+)?cache result(?: label)?\b)/gi, 'requests$1');
125
+ }
126
+
127
+ export function normalizeObservedWindowUnits(value) {
128
+ if (value == null) return '';
129
+ return String(value)
130
+ .replace(/(?<!\$)\b(\d[\d,.]*(?:\s?(?:K|M|B|KB|MB|GB|TB))?)\/mo\b/gi, '$1/window')
131
+ .replace(/\bmonthly\s+function\s+invocations\b/gi, 'function invocations/window')
132
+ .replace(/\b(requests?|invocations?|GETs|bytes|egress|bandwidth|writes?|reads?|errors?)\/mo\b/gi, '$1/window')
133
+ .replace(/\bmonthly\s+(requests?|invocations?|GETs|bytes|egress|bandwidth|writes?|reads?|errors?)\b/gi, '$1/window')
134
+ .replace(/\b(\d[\d,.]*(?:\s?(?:K|M|B|KB|MB|GB|TB))?)\s+function\s+invocations\s+per month\b/gi, '$1 function invocations/window')
135
+ .replace(/\b(\d[\d,.]*(?:\s?(?:K|M|B|KB|MB|GB|TB))?)\s+(requests?|GETs|invocations?|bytes|writes?|reads?|errors?)\s+per month\b/gi, '$1 $2/window')
136
+ .replace(/\b(\d[\d,.]*(?:\s?(?:K|M|B|KB|MB|GB|TB))?)\/window\s+(requests?|GETs|(?:function\s+)?invocations?|bytes|egress|bandwidth|writes?|reads?|errors?)\b/gi, '$1 $2 in this window')
137
+ .replace(/\b(\d[\d,.]*(?:\s?(?:K|M|B|KB|MB|GB|TB))?)\s+(requests?|GETs|(?:function\s+)?invocations?|bytes|egress|bandwidth|writes?|reads?|errors?)\/window\b/gi, '$1 $2 in this window')
138
+ .replace(/\b(\d[\d,.]*(?:\s?(?:K|M|B|KB|MB|GB|TB))?)\/window\b/gi, '$1 in this window')
139
+ .replace(/\b(requests?|invocations?|GETs|bytes|egress|bandwidth|writes?|reads?|errors?)\/window\b/gi, '$1 in this window');
140
+ }
141
+
142
+ export function formatCandidateLine(candidate) {
143
+ return `${formatKind(candidate?.kind)} on ${formatRoute(candidate)} - ${formatSignal(candidate?.o11ySignal, candidate)}`;
144
+ }
145
+
146
+ export function formatCandidateLabel(candidate) {
147
+ return `${formatKind(candidate?.kind)} on ${formatRoute(candidate)}`;
148
+ }
149
+
150
+ function formatSignalPart(part, context = {}) {
151
+ const eq = part.indexOf('=');
152
+ if (eq === -1) return part;
153
+
154
+ const key = part.slice(0, eq).trim();
155
+ const value = part.slice(eq + 1).trim();
156
+ const label = signalLabel(key, context);
157
+ return `${label}: ${formatSignalValue(key, value)}`;
158
+ }
159
+
160
+ function signalLabel(key, context = {}) {
161
+ const kind = typeof context === 'string' ? context : context?.kind;
162
+ if (key === 'inv' && REQUEST_COUNT_KINDS.has(kind)) return 'requests';
163
+ return SIGNAL_LABELS.get(key) ?? humanizeKey(key);
164
+ }
165
+
166
+ function humanizeKey(key) {
167
+ return String(key)
168
+ .replaceAll('.', ' ')
169
+ .replaceAll('_', ' ')
170
+ .replaceAll('-', ' ')
171
+ .trim();
172
+ }
173
+
174
+ function formatSignalValue(key, value) {
175
+ if (key === 'inv' || key === 'runs' || key === 'middleware_inv' || key === 'total_req' || key === 'requests' || key === 'calls' || key === 'errs' || key === 'writes' || key === 'reads') {
176
+ return formatNumberLike(value);
177
+ }
178
+ return value;
179
+ }
180
+
181
+ function formatNumberLike(value) {
182
+ const n = Number(value);
183
+ if (!Number.isFinite(n)) return value;
184
+ return new Intl.NumberFormat('en-US', { maximumFractionDigits: 2 }).format(n);
185
+ }