fix: revalidateTag with profile should not trigger client cache invalidation (#88069)
## Summary
Fixes a bug where calling `revalidateTag(tag, profile)` incorrectly
triggers client-side cache invalidation, causing read-your-own-writes
behavior that violates stale-while-revalidate semantics.
### The Problem
When `revalidateTag('tag', 'max')` is called in a server action:
1. The tag is correctly marked for stale-while-revalidate
2. BUT the `x-action-revalidated` header is incorrectly set to `1`
3. This triggers client-side cache invalidation via
`revalidateEntireCache()`
4. The client navigates and may display stale data from background
revalidation
This caused the confusing behavior where:
- Click 1: Nothing happens (correct)
- Click 2: Nothing happens (correct)
- Click 3: Data changes to a stale value from click 1 (incorrect!)
### The Fix
In `addRevalidationHeader`, change the `isTagRevalidated` calculation to
only count tags **without** a profile (from `updateTag`). Tags with a
profile (from `revalidateTag`) should follow stale-while-revalidate
semantics and not trigger immediate client-side cache invalidation.
```typescript
// Before:
const isTagRevalidated = workStore.pendingRevalidatedTags?.length ? 1 : 0
// After:
const isTagRevalidated = workStore.pendingRevalidatedTags?.some(
(item) => item.profile === undefined
) ? 1 : 0
```
### Expected Behavior After Fix
| API | Profile | Header Set | Client Behavior |
|-----|---------|------------|-----------------|
| `updateTag(tag)` | `undefined` | ✅ Yes | Immediate refresh
(read-your-own-writes) |
| `revalidateTag(tag, 'max')` | `'max'` | ❌ No | Keep stale data (SWR) |
| `revalidateTag(tag, { expire: 0 })` | `{ expire: 0 }` | ✅ Yes* |
Immediate refresh |
*For immediate expiration via `pathWasRevalidated` which is already
handled correctly.
## Test Plan
- [x] Added new test page at
`test/e2e/app-dir/use-cache/app/(partially-static)/revalidate-tag-no-refresh/page.tsx`
- [x] Added test case that verifies 3 clicks of `revalidateTag` with
profile don't change the displayed value
- [x] Verified existing `updateTag` test (`should update after
revalidateTag correctly`) still passes
- [x] Verified both tests pass with `pnpm test-start`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>