Back to Skills

fvtt-performance-safe-updates

verified

This skill should be used when adding features that update actors or items, implementing hook handlers, modifying update logic, or replacing embedded documents. Covers ownership guards, no-op checks, batched updates, queueUpdate wrapper, atomic document operations, and letting Foundry handle renders automatically for multi-client sync.

View on GitHub

Marketplace

hh-agentics

ImproperSubset/hh-agentics

Plugin

fvtt-dev

development

Repository

ImproperSubset/hh-agentics

fvtt-dev/skills/fvtt-performance-safe-updates/SKILL.md

Last Verified

January 21, 2026

Install Skill

Select agents to install to:

Scope:
npx add-skill https://github.com/ImproperSubset/hh-agentics/blob/main/fvtt-dev/skills/fvtt-performance-safe-updates/SKILL.md -a claude-code --skill fvtt-performance-safe-updates

Installation paths:

Claude
.claude/skills/fvtt-performance-safe-updates/
Powered by add-skill CLI

Instructions

# Foundry VTT Performance-Safe Updates

Ensure document updates in Foundry VTT modules don't cause multi-client update storms or render cascades.

## When to Use This Skill

Invoke this skill when implementing ANY of the following in a Foundry VTT module:
- Adding a new feature that updates actors or items
- Modifying existing update logic
- Adding UI elements that trigger document changes
- Implementing hook handlers that respond to document changes
- Replacing or swapping embedded documents (abilities, items, effects)

## Core Problem

Foundry VTT runs in multi-client sessions where hooks fire on ALL connected clients. Without proper guards:
- Every client triggers duplicate updates (2-10x redundant database writes)
- Update storms occur when updates trigger more updates across clients
- UI flickers when delete+create patterns cause "empty state" renders between operations
- Performance degrades exponentially with number of connected clients

## The Performance-Safe Pattern

### Step 1: Ownership Guards

**Before any document update, ask: "Should this run on every client?"**

```javascript
// ❌ BAD: Runs on every connected client
Hooks.on("deleteItem", (item, options, userId) => {
  item.parent.update({ "system.someField": newValue });
});

// ✅ GOOD: Only owner/GM performs the update
Hooks.on("deleteItem", (item, options, userId) => {
  if (!item.parent?.isOwner) return;
  item.parent.update({ "system.someField": newValue });
});
```

**Common ownership checks:**
- `item.isOwner` - Current user owns this item
- `item.parent?.isOwner` - Current user owns the parent (actor/container)
- `actor.isOwner` - Current user owns this actor
- `game.user.isGM` - Current user is the GM

**Use GM-only guards for:**
- World-level changes
- Compendium updates
- Global settings modifications

### Step 2: Skip No-Op Updates

**Before calling update, check if the value actually changes:**

```javascript
// ❌ BAD: Always updates, even if value unchanged
await actor.update({ "system.

Validation Details

Front Matter
Required Fields
Valid Name Format
Valid Description
Has Sections
Allowed Tools
Instruction Length:
12877 chars