Delegate independent tasks to specialized agents working concurrently with isolated context.
Local-First Architecture Skill
local-first
Enforces local-first architecture principles for Breath of Now. Use this skill when working with data, state management, or sync features. Ensures IndexedDB (Dexie.js) is always the source of truth.
SKILL.md
Full skill instructions
Local-First Architecture Skill
Este skill garante que todas as operações de dados no Breath of Now seguem o princípio local-first: dados do utilizador são armazenados localmente por defeito, com cloud sync como feature premium opcional.
Arquitectura
┌─────────────────────────────────────────────────┐
│ Browser │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ IndexedDB │ │ Zustand │ │
│ │ (Dexie.js) │ │ (State) │ │
│ │ SOURCE OF │ │ UI State │ │
│ │ TRUTH │ │ Only │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ └────────┬───────┘ │
│ ▼ │
│ ┌───────────────┐ │
│ │ Sync Engine │ (Premium only) │
│ │ src/lib/sync │ │
│ └───────┬───────┘ │
└─────────────────┼───────────────────────────────┘
│ (quando online + autenticado + premium)
▼
┌───────────────┐
│ Supabase │
│ (OPCIONAL) │
└───────────────┘
Quando Usar
Aplica este skill quando:
- Criar modelos de dados ou schemas
- Implementar operações CRUD
- Construir funcionalidade de sync
- Trabalhar com preferências do utilizador
- Tratar cenários offline
Regras Fundamentais
Regra 1: IndexedDB é SEMPRE a Source of Truth
// ❌ ERRADO - Fetch directo do Supabase
const { data } = await supabase.from('expenses').select('*');
setExpenses(data);
// ✅ CORRECTO - Ler da BD local
import { db } from '@/lib/db';
const expenses = await db.expenses.toArray();
setExpenses(expenses);
Regra 2: Escrever Localmente Primeiro, Sync Depois
// ❌ ERRADO - Escrever na cloud primeiro
await supabase.from('expenses').insert(expense);
// ✅ CORRECTO - Escrever localmente, queue para sync
import { db } from '@/lib/db';
await db.expenses.add({
...expense,
localId: crypto.randomUUID(),
syncStatus: 'pending',
createdAt: new Date(),
updatedAt: new Date()
});
// O sync engine trata o push para cloud (se premium)
Regra 3: App DEVE Funcionar 100% Offline
// ❌ ERRADO - Requer network
if (!navigator.onLine) {
return <p>You need internet connection</p>;
}
// ✅ CORRECTO - Funciona offline por defeito
const expenses = await db.expenses.toArray();
// Mostrar dados independentemente do estado de conexão
// Apenas mostrar indicador de status offline
Regra 4: Sync é Premium Only
// ✅ CORRECTO - Verificar status premium antes de sync
import { usePremium } from '@/hooks/use-premium';
const { isPremium } = usePremium();
if (isPremium && navigator.onLine) {
await syncEngine.sync();
}
Schema Dexie.js
Localização: /src/lib/db/index.ts
Estrutura Actual
import Dexie, { Table } from 'dexie';
// Expenses (ExpenseFlow)
export interface Expense {
id?: number;
localId: string; // UUID para sync
amount: number;
currency: string;
category: string;
description?: string;
date: string;
tags?: string[];
isRecurring?: boolean;
// Sync metadata
syncStatus: 'synced' | 'pending' | 'conflict';
remoteId?: string; // Supabase ID
createdAt: string;
updatedAt: string;
syncedAt?: string;
}
// FitLog
// Ver src/lib/db/fitlog-db.ts
export class BreathOfNowDB extends Dexie {
expenses!: Table<Expense>;
userPreferences!: Table<UserPreferences>;
constructor() {
super('breathofnow');
this.version(1).stores({
expenses: '++id, localId, date, category, syncStatus',
userPreferences: '++id, key'
});
}
}
export const db = new BreathOfNowDB();
State Management com Zustand
Zustand é para UI state apenas, não para persistência de dados:
// ✅ CORRECTO - UI state em Zustand
interface AppStore {
// Estado de sessão
user: User | null;
theme: 'light' | 'dark' | 'system';
// UI state
isSidebarOpen: boolean;
activeApp: string | null;
isLoading: boolean;
// Actions
setTheme: (theme: Theme) => void;
toggleSidebar: () => void;
}
// ❌ ERRADO - Não guardar dados em Zustand
interface AppStore {
expenses: Expense[]; // NÃO! Usar Dexie
transactions: Transaction[]; // NÃO! Usar Dexie
}
Sync Engine
Localização: /src/lib/sync/
Estrutura
src/lib/sync/
├── index.ts # Exportações principais
├── push.ts # Push de dados locais para cloud
├── pull.ts # Pull de dados da cloud
├── conflict.ts # Resolução de conflitos
└── queue.ts # Queue de operações pendentes
Padrão de Uso
import { useSync } from '@/hooks/use-sync';
function MyComponent() {
const { syncStatus, lastSyncTime, triggerSync } = useSync();
return (
<div>
<SyncStatus status={syncStatus} />
<p>{t('lastSync', { time: lastSyncTime })}</p>
<Button onClick={triggerSync}>{t('syncNow')}</Button>
</div>
);
}
Indicador Offline
// Componente existente: src/components/pwa/connectivity-status.tsx
import { ConnectivityStatus } from '@/components/pwa/connectivity-status';
// No layout ou header
<ConnectivityStatus />
Padrões de CRUD
Create
async function createExpense(data: ExpenseInput) {
const expense = {
...data,
localId: crypto.randomUUID(),
syncStatus: 'pending' as const,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
const id = await db.expenses.add(expense);
return { ...expense, id };
}
Read
async function getExpenses() {
return await db.expenses.toArray();
}
async function getExpenseById(localId: string) {
return await db.expenses.where('localId').equals(localId).first();
}
Update
async function updateExpense(localId: string, data: Partial<ExpenseInput>) {
await db.expenses.where('localId').equals(localId).modify({
...data,
updatedAt: new Date().toISOString(),
syncStatus: 'pending'
});
}
Delete
async function deleteExpense(localId: string) {
// Soft delete para sync
await db.expenses.where('localId').equals(localId).modify({
deleted: true,
deletedAt: new Date().toISOString(),
syncStatus: 'pending'
});
}
Checklist de Verificação
Antes de completar qualquer tarefa relacionada com dados:
- Dados são lidos de IndexedDB (Dexie), não de Supabase
- Escritas vão para IndexedDB primeiro
- App funciona 100% offline
- Status de sync é tracked por registo
- Estratégia de resolução de conflitos definida
- Cloud sync está atrás de verificação premium
- Zustand contém apenas UI state, não dados
Benefícios de Privacidade
Esta arquitectura providencia:
- ✅ Data sovereignty: Utilizador é dono dos dados
- ✅ Privacy by default: Dados não saem do dispositivo a menos que optem
- ✅ Offline access: Funcionalidade completa sem internet
- ✅ Performance: Leituras locais instantâneas
- ✅ Controlo: Utilizador pode exportar/apagar todos os dados localmente
Lembra-te: Os dados do utilizador pertencem a eles. Nós estamos apenas a ajudar a organizá-los.
More Other skills
brand-guidelines
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
theme-factory
Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.
internal-comms
A set of resources to help me write all kinds of internal communications, using the formats that my company likes to use. Claude should use this skill whenever asked to write some sort of internal communications (status reports, leadership updates, 3P updates, company newsletters, FAQs, incident reports, project updates, etc.).
Fundamental design principles for writing maintainable, testable Python code.
responsive-design
Modern responsive layouts using container queries, fluid typography, CSS Grid, and mobile-first strategies.
Generate Playwright tests from scenarios using interactive browser exploration and validation.
Automated website exploration and test case generation using Playwright.
Modern React state management with Redux Toolkit, Zustand, Jotai, and React Query for every state category.
gdpr-data-handling
GDPR-compliant data handling with consent management, data subject rights, and privacy controls.
Python error handling patterns including input validation, exception hierarchies, and partial failure handling. Use when implementing validation logic, designing exception strategies, handling batch processing failures, or building robust APIs.
refactor-plan
Detailed multi-file refactor planning with safety checks and rollback guidance.
