You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[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>
Copy file name to clipboardExpand all lines: skills/cdn-caching/SKILL.md
+51-2Lines changed: 51 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
---
2
2
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.
4
4
metadata:
5
5
priority: 6
6
6
docs:
@@ -18,15 +18,48 @@ metadata:
18
18
- 'isr write units'
19
19
- 'stale content'
20
20
- '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'
21
29
allOf:
22
30
- [cache, debug]
23
31
- [stale, cache]
24
32
- [revalidation, count]
33
+
- [cache, reason]
34
+
- [why, stale]
35
+
- [why, bypass]
36
+
- [cache, miss]
25
37
anyOf:
26
38
- 'revalidate'
27
39
- 'prerender'
28
40
- 'invalidate'
41
+
- 'draft mode'
42
+
- 'crawler'
43
+
- 'cold cache'
44
+
- 'request collapsed'
29
45
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
30
63
chainTo:
31
64
-
32
65
pattern: 'use cache|cacheLife|cacheTag'
@@ -65,7 +98,7 @@ Vercel caches at multiple layers between the visitor and your backend. A request
65
98
-_Invalidate_ (`invalidateByTag`, Next.js `revalidateTag`/`revalidatePath`) = stale-while-revalidate. Keeps serving stale while refreshing in the background → response shows `x-vercel-cache: STALE`.
66
99
-_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`.
67
100
-**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_:
-**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:
|`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
+
79
128
## Investigating cache issues
80
129
81
130
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.
0 commit comments