Caching and revalidation
How mutations refresh pages — the typed revalidate registry, why importing next/cache is a lint error, and where the generator registers new features. Use when a list is stale after a mutation, or when the next/cache restriction fires.
Generated from
.claude/skills/caching-and-revalidation/SKILL.md, which ships in every project created from this template. Your agent loads it on demand; this page is the same text. Edit the skill, not this page.
Mutations refresh pages through the typed registry in
src/lib/revalidate.ts. Importing next/cache anywhere else is a lint
error.
import { revalidate } from '@/lib/revalidate';
revalidate.projects(input.id); // list + that detail page
revalidate.files();
revalidate.everything(); // the whole shell — deliberate and bluntWhy a registry
The bug this closes is silent staleness: revalidatePath('/projcets') is a
typo the compiler shrugs at and the UI punishes — the mutation works, the
list quietly stops refreshing, and nothing anywhere says why. A named
surface turns the typo into a type error, and a route move becomes one edit.
Adding a surface
New feature → new entry, above the lq:generated-revalidate anchor.
lq generate feature inserts its own entry there — a generated slice whose
mutations cannot refresh its own pages does not compile, which is deliberate.
Keep the anchor comment.
When something is still stale
- The action calls the registry? (Every
.mutation()that changes what a page shows should.) - The entry covers the right paths? A detail page needs the id variant —
projects: (id?)revalidates both. - Reaching for
revalidate.everything()from a feature action is usually a smell — it works, but it refreshes the whole shell to hide not knowing which surface changed. Name the surface instead.
Tags, later
These pages are dynamic (session-scoped RSC reads), so path revalidation is
the whole story today. If you introduce 'use cache'/cacheTag, the tag
names live in this same registry, for the same reason the paths do.
Observability
Logging, request ids, error reporting and the health endpoint — the structured logger, why console is a lint error, requestLog/reportError, and the job-run logger. Use when adding log lines, when no-console fires, or when wiring a reporter like Sentry.
Background jobs
Defining, enqueueing and scheduling jobs — defineJob, the typed registry, cron triggers, step.run idempotency, and where withSystem is sanctioned. Use when work should not happen in the request, when adding a cron, or when a job retries strangely.