claude-code - 💡(How to fix) Fix [BUG] [#40175 extension] Virus-like propagation of .claude.json config rewrite: Dispatch persistence + session-chain contamination + auto-compact re-injection [2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#48560Fetched 2026-04-16 06:56:43
View on GitHub
Comments
2
Participants
2
Timeline
5
Reactions
0
Timeline (top)
labeled ×3commented ×2

Error Message

Calling this a "virus" is not rhetorical flourish — the topology matches: a single carrier, a propagation mechanism (Dispatch persistence, cache-at-launch, and conjectured auto-compact re-read), and a growing population of carriers. A fix targeting the write-path race only (e.g., abort-on-stale instead of warn-on-stale) patches the collision; it does not change the fact that the population of stale holders is non-decreasing over time. | .claude.json uses stale-write with warn-only abort | #40175, #27941 | Published |

  1. Abort, don't warn, on stale write. saveConfigWithLock already records tengu_config_stale_write telemetry when mtime/size drift is detected; the current behavior is to log and proceed. An abort (with the session re-reading the current host file before retrying) would make the stale-write race self-correcting rather than self-reinforcing.

Error Messages/Logs

No user-visible error. Failure is silent — Global Instructions in Settings UI revert to an older version without any warning, log entry, or notification. This is part of what makes the bug hard to diagnose.

Root Cause

The practical consequence, reproduced on 2026-04-15 on a Windows 11 MSIX build: a user who edits Global Instructions in Settings, sees the edit persist briefly, and then finds it silently reverted — not once but repeatedly over days — because the set of stale writers keeps growing rather than shrinking.

Fix Action

Fix / Workaround

.claude.json stale-write (#40175 / #27941) has a second-order behavior that has not been documented: contamination spreads. Once any single session holds a stale in-memory copy of .claude.json, every session the user subsequently talks to, every session that Dispatch spawns from that contaminated orchestrator, and (under a conjectured auto-compact re-injection path) every session that undergoes an auto-compact while the host file happens to be stale, can become new sources of stale writes.

This is not a new root cause. The stale-write race is the same one documented in #40175. But the propagation pattern means the blast radius is larger than the existing issues suggest, and a fix that targets only the stale-write telemetry without addressing Dispatch permanence or auto-compact behavior will leave the spreading intact.

  • #24460 — request that /compact re-inject CLAUDE.md from disk. Closed NOT_PLANNED 2026-03-11. The close comment does not specify whether re-injection already happens from disk or from a frozen in-memory snapshot.
  • #21925 [DESIGN FLAW] Context compaction destroys workflow — no CLAUDE.md reload — asserts the opposite: that post-compact sessions do not reload CLAUDE.md.
  • #45937 — Dispatch main conversation permanently offline with no UI to start a fresh thread.
  • #40070 — Dispatch session collisions; author states "server-side bug, no client-side workaround fully resolves it."
  • #37713 — Dispatch isolation caused by stale session directories.
  • #37473 — Dispatch spawn failures (related).
  • #25579, #26073, #40177, #43534 — Windows-specific MSIX path / VM lifecycle issues that change the surface on which the above mechanisms operate.

Code Example

# 0) Stop all Claude processes and the Cowork VM
Stop-Process -Name "Claude" -Force -ErrorAction SilentlyContinue
Stop-Process -Name "Claude Helper*" -Force -ErrorAction SilentlyContinue
Stop-Service -Name "CoworkVMService" -Force -ErrorAction SilentlyContinue
Get-VM | Where-Object { $_.Name -like "*Cowork*" -or $_.Name -like "*Claude*" } |
    Stop-VM -TurnOff -Force -ErrorAction SilentlyContinue

# 1) Dynamically locate the MSIX package directory
$claudePkg = Get-ChildItem "$env:LOCALAPPDATA\Packages\Claude_*" -Directory | Select-Object -First 1
$base = "$($claudePkg.FullName)\LocalCache\Roaming\Claude"

# 2) Back up .claude.json (note: it lives one level above $base)
$claudeJson = "$($claudePkg.FullName)\LocalCache\Roaming\.claude.json"
if (Test-Path $claudeJson) {
    Copy-Item $claudeJson "$claudeJson.backup-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
}

# 3) Clear the server-side Dispatch session remnants (per #45937)
Remove-Item -Recurse -Force "$base\local-agent-mode-sessions" -ErrorAction SilentlyContinue
Remove-Item -Force "$base\bridge-state.json" -ErrorAction SilentlyContinue

# 4) Restart
Start-Service -Name "CoworkVMService"
Start-Process "shell:AppsFolder\$($claudePkg.Name)!App"

---

No user-visible error. Failure is silent — Global Instructions in Settings UI revert to an older version without any warning, log entry, or notification. This is part of what makes the bug hard to diagnose.
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

1. TL;DR

.claude.json stale-write (#40175 / #27941) has a second-order behavior that has not been documented: contamination spreads. Once any single session holds a stale in-memory copy of .claude.json, every session the user subsequently talks to, every session that Dispatch spawns from that contaminated orchestrator, and (under a conjectured auto-compact re-injection path) every session that undergoes an auto-compact while the host file happens to be stale, can become new sources of stale writes.

The practical consequence, reproduced on 2026-04-15 on a Windows 11 MSIX build: a user who edits Global Instructions in Settings, sees the edit persist briefly, and then finds it silently reverted — not once but repeatedly over days — because the set of stale writers keeps growing rather than shrinking.

This is not a new root cause. The stale-write race is the same one documented in #40175. But the propagation pattern means the blast radius is larger than the existing issues suggest, and a fix that targets only the stale-write telemetry without addressing Dispatch permanence or auto-compact behavior will leave the spreading intact.

2. Context and prior art

Two closed/open issues already establish the core race:

  • #40175 tengu_config_stale_write — Cowork Global Instructions silently revert. Most recent comment 2026-04-14 (user ed-ovando) reports a two-week pattern of edits being rolled back. The issue as written describes the mechanism as a single-writer-loses race.
  • #27941 — twin stale-write issue against .claude.json, closed as stale.

Neither issue discusses (a) what makes the race trigger permanently rather than intermittently, (b) how one contaminated session causes additional sessions to become contaminated, or (c) what happens when an auto-compact coincides with a stale host file.

Related but orthogonal issues:

  • #24460 — request that /compact re-inject CLAUDE.md from disk. Closed NOT_PLANNED 2026-03-11. The close comment does not specify whether re-injection already happens from disk or from a frozen in-memory snapshot.
  • #21925 [DESIGN FLAW] Context compaction destroys workflow — no CLAUDE.md reload — asserts the opposite: that post-compact sessions do not reload CLAUDE.md.
  • #45937 — Dispatch main conversation permanently offline with no UI to start a fresh thread.
  • #40070 — Dispatch session collisions; author states "server-side bug, no client-side workaround fully resolves it."
  • #37713 — Dispatch isolation caused by stale session directories.
  • #37473 — Dispatch spawn failures (related).
  • #25579, #26073, #40177, #43534 — Windows-specific MSIX path / VM lifecycle issues that change the surface on which the above mechanisms operate.

The observations in this issue sit at the intersection of the above. They are derivable by reading five issues in parallel; they are not in any single issue.

3. Environment

  • OS: Windows 11 (MSIX package install of Claude Desktop + Cowork mode)
  • Host config path: %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\.claude.json (note: directly under Roaming\, not Roaming\Claude\; per #25579 the documented path is wrong for MSIX)
  • VM: VirtioFS mount exposing host .claude.json as /mnt/.claude/CLAUDE.md inside each Cowork session VM
  • Build: Claude 1.2581.0 (f10398, 2026-04-14T17:16:40Z)
  • Date reproduced: 2026-04-15
  • Dispatch in use: yes, first spawned 2026-03-18, continuously alive since

4. The virus analogy (why "propagation" is the right frame)

Existing discussion treats the stale-write race as a collision — two writers, last one wins, user's edit loses. A collision is a one-shot event.

What the user actually observes is not a collision but a spread:

  • Day 0: user edits Global Instructions → saved successfully.
  • Day 0 + hours: Dispatch flushes its stale in-memory copy → host reverts to old version.
  • Day 0 + more hours: a second session, which had started with a clean copy, undergoes auto-compact. If auto-compact re-reads CLAUDE.md from the now-stale host file (hypothesis — see §7), this second session is now holding the stale copy too.
  • Day 0 + more hours: user starts a new session today. That session's VirtioFS snapshot reads the host-side stale file at launch → VM sees stale content → any further flush from this session also writes stale. The new session is contaminated before the user has typed anything.
  • Day N: the set of sessions that could overwrite the host with stale content has grown. Some were born contaminated (started after the host went stale), some became contaminated mid-life (auto-compact + Layer 5), some are the original contaminator (Dispatch). The user's correct edit in Settings now has to win against all of them simultaneously.

Calling this a "virus" is not rhetorical flourish — the topology matches: a single carrier, a propagation mechanism (Dispatch persistence, cache-at-launch, and conjectured auto-compact re-read), and a growing population of carriers. A fix targeting the write-path race only (e.g., abort-on-stale instead of warn-on-stale) patches the collision; it does not change the fact that the population of stale holders is non-decreasing over time.

5. Observation 1: Dispatch is a permanent trigger, not an intermittent one

#40175 states stale-writes become "more reliably reproduced when multiple Cowork threads/sessions exist, as each holds its own stale copy." What the existing issue does not say: Dispatch is a session that cannot be ended from the client.

Supporting evidence, all already on file:

  • Anthropic's own help doc (support.claude.com/articles/13947068) states: "There's no way to start a new thread or manage multiple threads."
  • #45937 — main Dispatch conversation gets permanently stuck offline, no recovery UI.
  • #40070 — server-side local_ditto_* parent session ID tied to org ID; survives reinstall. Quote: "This is a server-side bug — no client-side workaround fully resolves it."
  • #37713 / #37473 — Dispatch spawn paths hold stale session directories.

Combined with #40175's stale-write mechanism, this means:

Once a user has opened Dispatch even once, their Global Instructions cannot be reliably edited through Settings UI anymore, because Dispatch's in-memory copy (frozen at the session's first launch date) will eventually flush and overwrite whatever the user just saved. Dispatch is the stale writer that never dies.

This is not present as a standalone ticket in any of #40175, #40070, #45937, #37713, #37473. Each of the five provides one piece of the chain.

Concrete measurements (user-reproduced 2026-04-15):

SessionLaunchedVirtioFS view of /mnt/.claude/CLAUDE.mdmtime observed
Dispatch (persistent)2026-03-187 lines / 651 bytes3/18 10:38
Local session local_53be7a6d~2026-04-14 pm98 lines / 22,536 bytes4/14 23:41

Both VMs were reading the same Windows host file via VirtioFS at the same wall-clock moment. They saw different content because each held its own startup snapshot.

6. Observation 2: conversation across sessions re-triggers host overwrite (directly reproduced 2026-04-15)

This is the observation that elevates the bug from "stale writer exists" to "stale writers multiply."

On 2026-04-15 the user had already:

  1. Recovered the 98-line version of Global Instructions from a late-launched session's frozen VM view.
  2. Pasted that 98-line version into Settings UI. Edit persisted, verified via Settings reload.
  3. Then conversed with (a) a Dispatch session that was still running from 3/18, and (b) several sessions that Dispatch had spawned (which inherited Dispatch's stale cache at their launch moment, because the host was stale when they started).

Within minutes of those conversations, .claude.json was again rewritten back to the 7-line version.

Causal chain:

  • Dispatch holds stale copy A (7 lines, mtime 3/18).
  • Sessions spawned by Dispatch were launched while host was stale → they inherit copy A.
  • User talks to any of these sessions → session's background flush writes A → host overwritten.
  • From the user's perspective: Settings UI edit vanishes again, even though they never opened the original Dispatch tab.

Key point: the user did not need to interact with Dispatch directly. Merely conversing with Dispatch-spawned children — which from the UI look like normal local sessions — was sufficient to re-trigger the overwrite. This explains why #40175 users report that the revert "keeps happening" over weeks despite repeated save attempts.

Equivalently, from a security-engineering framing: the contamination surface is not "Dispatch's tab" but "anything reachable from Dispatch's session graph." The user has no UI cue that a given local session is Dispatch-descended.

7. Observation 3 (hypothesis): auto-compact may be a secondary propagation vector

This section is explicitly marked as a hypothesis. The key premise has not been confirmed by Anthropic and the conflicting evidence is documented below.

If premise C below holds, auto-compact turns previously-clean sessions into new stale writers.

Premises:

  1. A (verified): each Cowork session's in-memory copy of .claude.json is frozen at session launch (see §5 evidence table).
  2. B (verified): Dispatch's background flush can overwrite the host file with its stale copy at any moment (§5, §6).
  3. C (not verified by Anthropic): when auto-compact fires inside a session, the system re-reads CLAUDE.md / system prompt content from the host file, replacing the session's in-memory snapshot.

If A and B and C all hold:

  • A session X starts clean (host was correct at X's launch).
  • Mid-conversation, Dispatch flushes → host goes stale.
  • X undergoes auto-compact → X's in-memory copy is updated to host's current (stale) content.
  • X's next background flush writes stale content to host.
  • X is now a contaminator, even though it started clean.

Evidence for and against C:

  • For: morphllm.com/claude-code-auto-compact (community blog) asserts "CLAUDE.md files are re-injected fresh from disk" during compact, but does not cite a source.
  • For: #24460 was closed NOT_PLANNED — the close implicitly frames re-injection from disk as already the behavior, though the comment does not spell this out.
  • Against: #21925 asserts that post-compact sessions do not reload CLAUDE.md at all.
  • Unclear: product-line difference — Claude Code CLI and Cowork may have different compact behaviors. Existing issues don't always specify.

Proposed verification experiment (six steps; any user or Anthropic QA can run):

  1. Ensure Dispatch is terminated; confirm host .claude.json holds known-good version A.
  2. Open a new Cowork session X; confirm X's /mnt/.claude/CLAUDE.md = A.
  3. In the background, launch a second session Y whose startup snapshot is a known-stale version B; allow Y to flush → host now holds B.
  4. In session X, inject sufficient input to trigger auto-compact.
  5. Immediately after compact completes, ask X to print /mnt/.claude/CLAUDE.md.
  6. Result = A → premise C is false, Layer 5 propagation does not exist. Result = B → premise C is true, propagation chain confirmed.

Whichever result obtains should be documented. A = A result is not benign — it would mean post-compact sessions are stuck on stale CLAUDE.md content, which is its own failure mode (the user-visible complaint of #21925).

8. Evidence summary

All observations in §§5–7 reduce to cross-references of existing issues plus one directly-reproduced event:

ClaimEvidenceStatus
.claude.json uses stale-write with warn-only abort#40175, #27941Published
Session in-memory copy is frozen at launchDirect measurement 2026-04-15 (§5 table)Reproduced
Dispatch cannot be terminated from clientsupport.claude.com/articles/13947068, #45937Published
Dispatch session ID persists server-side across reinstall#40070 (author quote)Published
"Dispatch = permanent stale-writer" as composite claimNo single issueNot on file
Talking to Dispatch-spawned sessions re-triggers overwriteUser reproduction 2026-04-15 (§6)Reproduced, novel
/compact re-injects CLAUDE.md from disk (premise C)morphllm blog (unsourced); #24460 close (implicit); #21925 contradictsUnconfirmed
Windows MSIX documented path is wrong#25579 (NOT_PLANNED)Published

9. Impact: contamination surface is monotonically non-decreasing

Under current behavior, the set of sessions that could stale-write to host .claude.json at any given moment cannot shrink through user action short of a full Dispatch server-side reset + killing every local session + restart.

Specifically:

  • Every local Cowork session the user opens while the host is stale is born contaminated.
  • If premise C holds, every session that undergoes auto-compact during a stale window becomes contaminated.
  • Dispatch persists, so the window is never fully closed.
  • The PowerShell cleanup in §11 can restore a clean baseline, but any subsequent Dispatch interaction rearms the loop.

From the user's point of view this manifests as: "I fixed my Global Instructions, and a day later they were wrong again. I fixed them again, and a week later they were wrong again." The existing #40175 report frames this as "sometimes my edit is lost." The actual pattern is that the set of potential overwriters is growing, so the fix window keeps closing faster than the user can re-save.

This is why we believe the fix priority for #40175 warrants escalation relative to how it currently reads as a "sometimes" bug. It is an always-bug whose apparent intermittency is an artifact of small session graphs.

10. Suggested directions (non-prescriptive)

Offered as starting points, not as a demand for a specific implementation:

  1. Abort, don't warn, on stale write. saveConfigWithLock already records tengu_config_stale_write telemetry when mtime/size drift is detected; the current behavior is to log and proceed. An abort (with the session re-reading the current host file before retrying) would make the stale-write race self-correcting rather than self-reinforcing.
  2. Surface a Dispatch reset control. #45937 already requests a "start new thread" UI; independently of that, a /reset-dispatch command or Settings toggle that kills the server-side local_ditto_* session would let users exit the propagation loop without the PowerShell gymnastics of §11.
  3. Document auto-compact behavior re: CLAUDE.md re-injection. Whichever of premise C's two possible values is correct, both have user-visible consequences. If re-inject happens, document the staleness risk. If it doesn't, document the CLAUDE.md-reload failure (#21925). The current ambiguity forces users to guess.
  4. Store Global Instructions in a dedicated file. The root-cause enabler of all the above is that Global Instructions share .claude.json with high-frequency internal state writes. Splitting the storage (Global Instructions → dedicated small file, internal state → .claude.json) reduces write contention by orders of magnitude and removes the shared-fate coupling between user content and app telemetry.

(4) is architectural and slow; (1), (2), (3) are independently shippable and each partially defuses the propagation.

11. Current user workaround (Windows 11 MSIX, administrator PowerShell)

Posted for completeness. This is a workaround, not a fix — it restores a clean baseline but leaves the mechanism intact.

# 0) Stop all Claude processes and the Cowork VM
Stop-Process -Name "Claude" -Force -ErrorAction SilentlyContinue
Stop-Process -Name "Claude Helper*" -Force -ErrorAction SilentlyContinue
Stop-Service -Name "CoworkVMService" -Force -ErrorAction SilentlyContinue
Get-VM | Where-Object { $_.Name -like "*Cowork*" -or $_.Name -like "*Claude*" } |
    Stop-VM -TurnOff -Force -ErrorAction SilentlyContinue

# 1) Dynamically locate the MSIX package directory
$claudePkg = Get-ChildItem "$env:LOCALAPPDATA\Packages\Claude_*" -Directory | Select-Object -First 1
$base = "$($claudePkg.FullName)\LocalCache\Roaming\Claude"

# 2) Back up .claude.json (note: it lives one level above $base)
$claudeJson = "$($claudePkg.FullName)\LocalCache\Roaming\.claude.json"
if (Test-Path $claudeJson) {
    Copy-Item $claudeJson "$claudeJson.backup-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
}

# 3) Clear the server-side Dispatch session remnants (per #45937)
Remove-Item -Recurse -Force "$base\local-agent-mode-sessions" -ErrorAction SilentlyContinue
Remove-Item -Force "$base\bridge-state.json" -ErrorAction SilentlyContinue

# 4) Restart
Start-Service -Name "CoworkVMService"
Start-Process "shell:AppsFolder\$($claudePkg.Name)!App"

Caveats:

  • Must run from an elevated PowerShell (Stop-Service / Stop-VM require it).
  • MSIX virtualization does not apply to PowerShell. Editing %APPDATA%\Claude\ from PowerShell does not redirect; you will modify the wrong location. Use the dynamic $claudePkg.FullName path above. (#25579 documents the path, marked NOT_PLANNED.)
  • Dispatch's old conversation history is stored server-side and is not cleared by this script; newly spawned task sessions will re-read CLAUDE.md correctly once the host file is clean, but historical Dispatch turns retain their original embedded system prompt.

12. Related issues (grouped)

  • Core race condition: #40175, #27941, #40731
  • Compact behavior: #24460, #21925
  • Dispatch lifecycle: #45937, #40070, #37713, #37473
  • Windows-specific surface: #25579, #26073, #40177, #43534

Filed as a structured extension of #40175, not a duplicate. If maintainers prefer, §§5–6 (Dispatch permanence + conversational re-triggering) and §7 (auto-compact hypothesis) can be split into two issues; they are presented together here because the causal chain runs through both and readers evaluating the propagation argument benefit from having them adjacent.

What Should Happen?

Global Instructions edited in Settings should persist after Save. Specifically, the content written to .claude.json should not be subsequently overwritten by stale in-memory copies held by background sessions (in particular Dispatch, which cannot be terminated from the client). See §10 of "What's Wrong?" for suggested fix directions.

Error Messages/Logs

No user-visible error. Failure is silent — Global Instructions in Settings UI revert to an older version without any warning, log entry, or notification. This is part of what makes the bug hard to diagnose.

Steps to Reproduce

Full reproduction detailed in §6 of "What's Wrong?" above. Summary:

  1. Have a Dispatch session alive (spawned at any prior date — in our repro, 2026-03-18).
  2. In Settings, edit Global Instructions; Save; verify via Settings reload that the edit persists.
  3. Converse with any Cowork session — especially sessions spawned by Dispatch, which are UI-indistinguishable from local sessions.
  4. Within minutes, re-open Settings: Global Instructions have reverted to a pre-edit version.

Reproduced 2026-04-15 on Claude 1.2581.0 (f10398). Direct measurement evidence (VirtioFS view divergence between two live sessions) in §5 table of "What's Wrong?".

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

No response

Claude Code Version

N/A — this is a Claude Desktop (Cowork mode) bug, not Claude Code CLI. Claude Desktop build: 1.2581.0 (f10398, 2026-04-14T17:16:40Z).

Platform

Other

Operating System

Windows

Terminal/Shell

Other

Additional Information

Template note: this repo's Bug Report template is CLI-oriented (Claude Code Version, Terminal/Shell, Platform=API/Bedrock/Vertex). This report is for Claude Desktop + Cowork mode on Windows MSIX, so several fields are marked N/A or Other. The precedent #40175 is also a Cowork mode bug in this repo. If maintainers prefer a different filing location, happy to re-file — please advise.

extent analysis

TL;DR

The most likely fix for the stale-write issue in Claude Desktop's Cowork mode is to implement an abort-on-stale-write mechanism instead of the current warn-on-stale-write behavior.

Guidance

  1. Investigate and confirm the auto-compact behavior: Determine whether the system re-reads CLAUDE.md from the host file during auto-compact, as this is a crucial factor in the propagation of stale writes.
  2. Implement abort-on-stale-write: Modify the saveConfigWithLock function to abort the write operation when a stale write is detected, and then re-read the current host file before retrying.
  3. Surface a Dispatch reset control: Provide a UI control or command to reset the Dispatch session, allowing users to exit the propagation loop without relying on workarounds.
  4. Consider storing Global Instructions in a dedicated file: Splitting the storage of Global Instructions from internal state writes could reduce write contention and remove the shared-fate coupling between user content and app telemetry.

Example

No specific code example is provided, as the necessary changes depend on the internal implementation details of Claude Desktop's Cowork mode. However, the saveConfigWithLock function should be modified to include an abort-on-stale-write mechanism.

Notes

The provided workaround using PowerShell can restore a clean baseline but does not address the underlying issue. The auto-compact behavior and its impact on stale writes need to be confirmed and addressed.

Recommendation

Apply the workaround provided in the issue body as a temporary solution, and prioritize implementing the abort-on-stale-write mechanism to prevent further propagation of stale writes.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING