Skip to content

Commit d71c61c

Browse files
shinapatelclaude
andauthored
[skills] Extend cdn-caching with per-request cache reason guidance (#110)
* [skills] Add cache-reason skill for per-request cache reason debugging Adds a `cache-reason` skill that explains the per-request `cacheReason` field (cold, collapsed, error, draft_mode, prerender_bypass, crawler, stale_time, stale_tag, stale_error) — why a single request was a MISS, STALE, or BYPASS. Complements `cdn-caching`, which covers aggregate hit rate and ISR cost via `vercel metrics`. Written for the public audience: all observation paths route through the dashboard Logs "Reason" row and `vercel logs --json`. The internal-only `x-vercel-cache-reason` response header (gated behind is_timing_header_visible) is explicitly flagged as not user-visible, and the skill avoids the not-yet-published /docs/caching/cache-status anchors. - New: skills/cache-reason/SKILL.md (with promptSignals + retrieval block) - vercel.md: graph reference in the caching section - README.md: skills table row + count - cdn-caching: mutual cross-link - Regenerated skill-manifest.json, skill-catalog.md, build-from-skills.manifest.json Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [skills] Merge cache-reason guidance into cdn-caching instead of a new skill Fold the per-request cacheReason content into the existing cdn-caching skill (from #108) rather than shipping a separate cache-reason skill: - cdn-caching: add the 9-value cacheReason table (cold, collapsed, error, draft_mode, prerender_bypass, crawler, stale_time, stale_tag, stale_error) refining the x-vercel-cache status, the MISS→BYPASS displayed-status rule, and how to read cacheReason via `vercel logs --json` / the Logs "Reason" row (with the internal-only x-vercel-cache-reason header caveat). - Absorb cache-reason prompt signals + add a retrieval block so questions like "why is my page stale" / "what does stale_tag mean" route here. - Remove skills/cache-reason/ and revert its README/vercel.md wiring (back to 30 skills). - Regenerate manifest, catalog, build-from-skills manifest. Verified: 899 tests pass; the real UserPromptSubmit hook injects cdn-caching for the three cache-reason prompts and correctly ignores unrelated prompts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [skills] Tighten cdn-caching cacheReason merge (287→133 lines) Condense the skill while keeping all 6 cache statuses and all 9 cache reasons (3 MISS: cold/collapsed/error, 3 BYPASS: draft_mode/ prerender_bypass/crawler, 3 STALE: stale_time/stale_tag/stale_error). Cut verbose prose, repeated metrics examples, and the FAQ; keep the two reference tables, the core metrics/logs investigation commands, and the cacheReason observation guidance. Add [cache, miss] prompt signal so MISS questions trigger. Verified: 899 tests pass; the real UserPromptSubmit hook injects cdn-caching for MISS/STALE/BYPASS/hit-rate prompts and ignores unrelated and generic-error prompts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [skills] Complete cacheReason retrieval entities (add error, collapsed, stale_time) The retrieval.entities list included some cache reasons but omitted `error` (and `collapsed`, `stale_time`), making it inconsistent with the canonical 9-reason table. List all nine reason slugs, ordered to mirror the table. Low-risk: the "error" token was already in the lexical index via stale_error, and injection is gated on exact score — verified generic error prompts ("fix this null pointer error", etc.) still inject nothing, while MISS/STALE/BYPASS/hit-rate prompts still route to cdn-caching. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [skills] Restore docs/caching/cdn-cache link dropped during trim The cdn-cache docs URL was inadvertently removed while tightening the docs list; it's the canonical CDN-cache reference for this skill. Restore it alongside the caching overview, ISR, metrics, and logs links. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [skills] Redo cacheReason as a small additive change to cdn-caching Prior commits had rewritten (gutted) the existing cdn-caching skill. Restore the original #108 skill verbatim and add only what's needed: - A 9-row cacheReason table under Key concepts (3 MISS: cold/collapsed/ error, 3 BYPASS: draft_mode/prerender_bypass/crawler, 3 STALE: stale_time/stale_tag/stale_error), the MISS→BYPASS displayed-status rule, and how to read cacheReason (vercel logs / Logs panel; the x-vercel-cache-reason header is internal-only). - Additive promptSignals + a retrieval block so cache-reason questions route here. Net diff vs the original: +47 / -2 lines. All existing metrics/ISR/BYPASS runbook content preserved. 899 tests pass; routing verified via the real UserPromptSubmit hook. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [skills] Make cacheReason prompt signals consistent across reasons Previously only stale_tag was a phrase. Add the other distinctive reason slugs so pasting any of them from a log triggers the skill: stale_time, stale_error, draft_mode, prerender_bypass (+ existing stale_tag). Add 'cold cache' and 'request collapsed' to anyOf for MISS coverage. Bare cold/collapsed/error are intentionally NOT phrases — as ordinary English words they would false-match unrelated prose. Verified: "cacheReason: stale_error" and "prerender_bypass" fire cdn-caching, while "fix this null pointer error" / "handle the error case" inject nothing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [skills] Route the cacheReason field name to cdn-caching * [skills] Drop generic single-word cache-reason entities (cold, error, crawler) per review --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 18dd7fd commit d71c61c

6 files changed

Lines changed: 108 additions & 9 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ A text-form relational graph covering:
5454
| `ai-sdk` | AI SDK v6 — text/object generation, streaming, tool calling, agents, MCP, providers, embeddings |
5555
| `auth` | Authentication integrations — Clerk, Descope, Auth0 setup for Next.js with Marketplace provisioning |
5656
| `bootstrap` | Project bootstrapping orchestrator — linking, env provisioning, db setup, first-run commands |
57-
| `cdn-caching` | Diagnose cache hit rate, stale content, revalidation behavior, and ISR read/write cost across CDN/ISR/PPR |
57+
| `cdn-caching` | Diagnose cache hit rate, stale content, revalidation behavior, per-request cache reasons, and ISR read/write cost across CDN/ISR/PPR |
5858
| `chat-sdk` | Multi-platform chat bots — Slack, Telegram, Teams, Discord, Google Chat, GitHub, Linear |
5959
| `deployments-cicd` | Deployment and CI/CD — deploy, promote, rollback, --prebuilt, CI workflow files |
6060
| `env-vars` | Environment variable management — .env files, vercel env commands, OIDC tokens |

generated/build-from-skills.manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"version": 1,
3-
"generatedAt": "2026-07-01T19:14:58.838Z",
3+
"generatedAt": "2026-07-07T17:26:07.851Z",
44
"templates": [
55
{
66
"template": "agents/ai-architect.md.tmpl",

generated/skill-catalog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Skill Catalog
22

33
> Auto-generated by `scripts/generate-catalog.ts` — do not edit manually.
4-
> Generated: 2026-07-03T20:32:55.191Z
4+
> Generated: 2026-07-06T20:39:13.075Z
55
> Skills: 30
66
77
## Table of Contents

generated/skill-manifest.json

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"generatedAt": "2026-07-03T22:15:28.313Z",
2+
"generatedAt": "2026-07-07T17:36:42.694Z",
33
"version": 2,
44
"skills": {
55
"ai-gateway": {
@@ -883,7 +883,15 @@
883883
"isr read units",
884884
"isr write units",
885885
"stale content",
886-
"x-vercel-cache"
886+
"x-vercel-cache",
887+
"cache reason",
888+
"cacheReason",
889+
"x-vercel-cache-reason",
890+
"stale_time",
891+
"stale_tag",
892+
"stale_error",
893+
"draft_mode",
894+
"prerender_bypass"
887895
],
888896
"allOf": [
889897
[
@@ -897,15 +905,57 @@
897905
[
898906
"revalidation",
899907
"count"
908+
],
909+
[
910+
"cache",
911+
"reason"
912+
],
913+
[
914+
"why",
915+
"stale"
916+
],
917+
[
918+
"why",
919+
"bypass"
920+
],
921+
[
922+
"cache",
923+
"miss"
900924
]
901925
],
902926
"anyOf": [
903927
"revalidate",
904928
"prerender",
905-
"invalidate"
929+
"invalidate",
930+
"draft mode",
931+
"crawler",
932+
"cold cache",
933+
"request collapsed"
906934
],
907935
"noneOf": [],
908936
"minScore": 6
937+
},
938+
"retrieval": {
939+
"aliases": [
940+
"cache reason",
941+
"cache hit rate",
942+
"stale content"
943+
],
944+
"intents": [
945+
"why is my page stale",
946+
"why is this request a bypass",
947+
"why was this a cache miss"
948+
],
949+
"entities": [
950+
"cacheReason",
951+
"collapsed",
952+
"draft_mode",
953+
"prerender_bypass",
954+
"stale_time",
955+
"stale_tag",
956+
"stale_error"
957+
],
958+
"examples": []
909959
}
910960
},
911961
"chat-sdk": {

skills/cdn-caching/SKILL.md

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: cdn-caching
3-
description: Debug Vercel CDN caching — cache hit rate, stale content, revalidation behavior, ISR + PPR, and costs.
3+
description: Debug Vercel CDN caching — cache hit rate, stale content, revalidation behavior, ISR + PPR, per-request cache reasons (cacheReason), and costs.
44
metadata:
55
priority: 6
66
docs:
@@ -18,15 +18,48 @@ metadata:
1818
- 'isr write units'
1919
- 'stale content'
2020
- 'x-vercel-cache'
21+
- 'cache reason'
22+
- 'cacheReason'
23+
- 'x-vercel-cache-reason'
24+
- 'stale_time'
25+
- 'stale_tag'
26+
- 'stale_error'
27+
- 'draft_mode'
28+
- 'prerender_bypass'
2129
allOf:
2230
- [cache, debug]
2331
- [stale, cache]
2432
- [revalidation, count]
33+
- [cache, reason]
34+
- [why, stale]
35+
- [why, bypass]
36+
- [cache, miss]
2537
anyOf:
2638
- 'revalidate'
2739
- 'prerender'
2840
- 'invalidate'
41+
- 'draft mode'
42+
- 'crawler'
43+
- 'cold cache'
44+
- 'request collapsed'
2945
minScore: 6
46+
retrieval:
47+
aliases:
48+
- cache reason
49+
- cache hit rate
50+
- stale content
51+
intents:
52+
- why is my page stale
53+
- why is this request a bypass
54+
- why was this a cache miss
55+
entities:
56+
- cacheReason
57+
- collapsed
58+
- draft_mode
59+
- prerender_bypass
60+
- stale_time
61+
- stale_tag
62+
- stale_error
3063
chainTo:
3164
-
3265
pattern: 'use cache|cacheLife|cacheTag'
@@ -65,7 +98,7 @@ Vercel caches at multiple layers between the visitor and your backend. A request
6598
- _Invalidate_ (`invalidateByTag`, Next.js `revalidateTag`/`revalidatePath`) = stale-while-revalidate. Keeps serving stale while refreshing in the background → response shows `x-vercel-cache: STALE`.
6699
- _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`.
67100
- **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.
68-
- **Cache status / cache reason** (`x-vercel-cache` response header):
101+
- **Cache status** (`x-vercel-cache` response header) — the _outcome_:
69102

70103
| Value | Meaning |
71104
| ------------- | ---------------------------------------------------------------- |
@@ -76,6 +109,22 @@ Vercel caches at multiple layers between the visitor and your backend. A request
76109
| `REVALIDATED` | Foreground revalidation after a delete (or `Pragma: no-cache`) |
77110
| `BYPASS` | Caching skipped (`no-store`, `private`, cookies, etc.) |
78111

112+
- **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:
113+
114+
| `cacheReason` | Refines | Meaning |
115+
| ------------------ | -------- | ----------------------------------------------------------------------------- |
116+
| `cold` | MISS | Cache empty for this key/variant (first request or evicted); the function ran |
117+
| `collapsed` | MISS | Concurrent requests to one uncached path collapsed into a single invocation |
118+
| `error` | MISS | An error prevented serving from cache |
119+
| `draft_mode` | → BYPASS | Next.js Draft Mode active — bypassed so editors see live content |
120+
| `prerender_bypass` | → BYPASS | Prerender-bypass cookie/token present |
121+
| `crawler` | → BYPASS | SEO-crawler UA — full response served so bots index real content |
122+
| `stale_time` | STALE | Time-based `revalidate` interval elapsed; regenerating in background (SWR) |
123+
| `stale_tag` | STALE | Tag invalidated (`revalidateTag` / `invalidateByTag`); regenerating |
124+
| `stale_error` | STALE | A revalidation attempt **failed**; serving the last-good copy (a bug signal) |
125+
126+
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`.
127+
79128
## Investigating cache issues
80129

81130
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.

vercel.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ Three distinct caching systems serve different purposes. They can be used indepe
701701
702702
⤳ skill: runtime-cache — Per-region key-value caching with tag-based invalidation
703703
⤳ skill: nextjs — `'use cache'`, `revalidatePath`, `revalidateTag`
704-
⤳ skill: cdn-caching — Diagnose cache hit rate, stale content, and ISR read/write cost
704+
⤳ skill: cdn-caching — Diagnose cache hit rate, stale content, per-request cache reasons, and ISR read/write cost
705705

706706
---
707707

0 commit comments

Comments
 (0)