diff --git a/README.md b/README.md index bef2f8f..42ae25c 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ A text-form relational graph covering: | `ai-sdk` | AI SDK v6 — text/object generation, streaming, tool calling, agents, MCP, providers, embeddings | | `auth` | Authentication integrations — Clerk, Descope, Auth0 setup for Next.js with Marketplace provisioning | | `bootstrap` | Project bootstrapping orchestrator — linking, env provisioning, db setup, first-run commands | -| `cdn-caching` | Diagnose cache hit rate, stale content, revalidation behavior, and ISR read/write cost across CDN/ISR/PPR | +| `cdn-caching` | Diagnose cache hit rate, stale content, revalidation behavior, per-request cache reasons, and ISR read/write cost across CDN/ISR/PPR | | `chat-sdk` | Multi-platform chat bots — Slack, Telegram, Teams, Discord, Google Chat, GitHub, Linear | | `deployments-cicd` | Deployment and CI/CD — deploy, promote, rollback, --prebuilt, CI workflow files | | `env-vars` | Environment variable management — .env files, vercel env commands, OIDC tokens | diff --git a/generated/build-from-skills.manifest.json b/generated/build-from-skills.manifest.json index 0bcb319..101a605 100644 --- a/generated/build-from-skills.manifest.json +++ b/generated/build-from-skills.manifest.json @@ -1,6 +1,6 @@ { "version": 1, - "generatedAt": "2026-07-01T19:14:58.838Z", + "generatedAt": "2026-07-07T17:26:07.851Z", "templates": [ { "template": "agents/ai-architect.md.tmpl", diff --git a/generated/skill-catalog.md b/generated/skill-catalog.md index a17b9c9..18f15fc 100644 --- a/generated/skill-catalog.md +++ b/generated/skill-catalog.md @@ -1,7 +1,7 @@ # Skill Catalog > Auto-generated by `scripts/generate-catalog.ts` — do not edit manually. -> Generated: 2026-07-03T20:32:55.191Z +> Generated: 2026-07-06T20:39:13.075Z > Skills: 30 ## Table of Contents diff --git a/generated/skill-manifest.json b/generated/skill-manifest.json index cc94ee5..a5fd420 100644 --- a/generated/skill-manifest.json +++ b/generated/skill-manifest.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-07-03T22:15:28.313Z", + "generatedAt": "2026-07-07T17:36:42.694Z", "version": 2, "skills": { "ai-gateway": { @@ -883,7 +883,15 @@ "isr read units", "isr write units", "stale content", - "x-vercel-cache" + "x-vercel-cache", + "cache reason", + "cacheReason", + "x-vercel-cache-reason", + "stale_time", + "stale_tag", + "stale_error", + "draft_mode", + "prerender_bypass" ], "allOf": [ [ @@ -897,15 +905,57 @@ [ "revalidation", "count" + ], + [ + "cache", + "reason" + ], + [ + "why", + "stale" + ], + [ + "why", + "bypass" + ], + [ + "cache", + "miss" ] ], "anyOf": [ "revalidate", "prerender", - "invalidate" + "invalidate", + "draft mode", + "crawler", + "cold cache", + "request collapsed" ], "noneOf": [], "minScore": 6 + }, + "retrieval": { + "aliases": [ + "cache reason", + "cache hit rate", + "stale content" + ], + "intents": [ + "why is my page stale", + "why is this request a bypass", + "why was this a cache miss" + ], + "entities": [ + "cacheReason", + "collapsed", + "draft_mode", + "prerender_bypass", + "stale_time", + "stale_tag", + "stale_error" + ], + "examples": [] } }, "chat-sdk": { diff --git a/skills/cdn-caching/SKILL.md b/skills/cdn-caching/SKILL.md index 42640fd..1665887 100644 --- a/skills/cdn-caching/SKILL.md +++ b/skills/cdn-caching/SKILL.md @@ -1,6 +1,6 @@ --- name: cdn-caching -description: Debug Vercel CDN caching — cache hit rate, stale content, revalidation behavior, ISR + PPR, and costs. +description: Debug Vercel CDN caching — cache hit rate, stale content, revalidation behavior, ISR + PPR, per-request cache reasons (cacheReason), and costs. metadata: priority: 6 docs: @@ -18,15 +18,48 @@ metadata: - 'isr write units' - 'stale content' - 'x-vercel-cache' + - 'cache reason' + - 'cacheReason' + - 'x-vercel-cache-reason' + - 'stale_time' + - 'stale_tag' + - 'stale_error' + - 'draft_mode' + - 'prerender_bypass' allOf: - [cache, debug] - [stale, cache] - [revalidation, count] + - [cache, reason] + - [why, stale] + - [why, bypass] + - [cache, miss] anyOf: - 'revalidate' - 'prerender' - 'invalidate' + - 'draft mode' + - 'crawler' + - 'cold cache' + - 'request collapsed' minScore: 6 +retrieval: + aliases: + - cache reason + - cache hit rate + - stale content + intents: + - why is my page stale + - why is this request a bypass + - why was this a cache miss + entities: + - cacheReason + - collapsed + - draft_mode + - prerender_bypass + - stale_time + - stale_tag + - stale_error chainTo: - pattern: 'use cache|cacheLife|cacheTag' @@ -65,7 +98,7 @@ Vercel caches at multiple layers between the visitor and your backend. A request - _Invalidate_ (`invalidateByTag`, Next.js `revalidateTag`/`revalidatePath`) = stale-while-revalidate. Keeps serving stale while refreshing in the background → response shows `x-vercel-cache: STALE`. - _Dangerously-delete_ (`dangerouslyDeleteByTag`, Next.js `updateTag` or a revalidate with no lifetime) = hard removal. The next request blocks in the **foreground** to regenerate → `x-vercel-cache: REVALIDATED`. - **Cache tags & blast radius** — tags group cached entries so one call can clear many. A coarse tag attached to thousands of paths has a large _blast radius_: a single write drops them all and the hit rate collapses until they re-warm. Prefer granular tags (`product-${id}`) plus a roll-up tag. -- **Cache status / cache reason** (`x-vercel-cache` response header): +- **Cache status** (`x-vercel-cache` response header) — the _outcome_: | Value | Meaning | | ------------- | ---------------------------------------------------------------- | @@ -76,6 +109,22 @@ Vercel caches at multiple layers between the visitor and your backend. A request | `REVALIDATED` | Foreground revalidation after a delete (or `Pragma: no-cache`) | | `BYPASS` | Caching skipped (`no-store`, `private`, cookies, etc.) | +- **Cache reason** (`cacheReason`) — the finer _explanation_ of that outcome for a single request. The `cache_result` metric lumps all `MISS`es (and all `STALE`s) together; the reason is the only thing that tells them apart. Nine values, three per group: + + | `cacheReason` | Refines | Meaning | + | ------------------ | -------- | ----------------------------------------------------------------------------- | + | `cold` | MISS | Cache empty for this key/variant (first request or evicted); the function ran | + | `collapsed` | MISS | Concurrent requests to one uncached path collapsed into a single invocation | + | `error` | MISS | An error prevented serving from cache | + | `draft_mode` | → BYPASS | Next.js Draft Mode active — bypassed so editors see live content | + | `prerender_bypass` | → BYPASS | Prerender-bypass cookie/token present | + | `crawler` | → BYPASS | SEO-crawler UA — full response served so bots index real content | + | `stale_time` | STALE | Time-based `revalidate` interval elapsed; regenerating in background (SWR) | + | `stale_tag` | STALE | Tag invalidated (`revalidateTag` / `invalidateByTag`); regenerating | + | `stale_error` | STALE | A revalidation attempt **failed**; serving the last-good copy (a bug signal) | + + A raw `MISS` with reason `draft_mode` / `prerender_bypass` / `crawler` is **displayed as `BYPASS`** (all usually expected). The three `stale_*` reasons separate a healthy time refresh (`stale_time`) from a broad-tag blast (`stale_tag`) from a failing regen (`stale_error`). Read `cacheReason` from `vercel logs` or the dashboard Logs "Reason" row — the `x-vercel-cache-reason` header is internal-only and not visible via `curl`. + ## Investigating cache issues Reach for the Vercel CLI. `vercel metrics` gives aggregate numbers (requires [Observability Plus](https://vercel.com/docs/observability/observability-plus)); `vercel logs` shows per-request behavior. diff --git a/vercel.md b/vercel.md index 076d13b..e760184 100644 --- a/vercel.md +++ b/vercel.md @@ -701,7 +701,7 @@ Three distinct caching systems serve different purposes. They can be used indepe ⤳ skill: runtime-cache — Per-region key-value caching with tag-based invalidation ⤳ skill: nextjs — `'use cache'`, `revalidatePath`, `revalidateTag` -⤳ skill: cdn-caching — Diagnose cache hit rate, stale content, and ISR read/write cost +⤳ skill: cdn-caching — Diagnose cache hit rate, stale content, per-request cache reasons, and ISR read/write cost ---