Imported from igor-furtado/grana-app (
AGENTS.md). Install upstream withnpx skills add igor-furtado/grana-app. Copyright stays with the author.
AGENTS.md
Guia operacional e técnico para agentes neste repositório.
Antes de alterar código
- Leia este arquivo e
CONTEXT.md. Consulte ADRs relacionados emdocs/adr/, quando existirem. - Inspecione
git status --shorte preserve mudanças do usuário. Não reverta, reformate nem inclua arquivos alheios. - Use
ROADMAP.mdapenas como contexto de planejamento; ele não define regras nem limita pedidos explícitos. - Confirme a implementação atual no código, testes,
project.pbxproj,.swiftformate.swiftlint.yml. - Antes de alterar UI, leia
docs/design-system.mdedocs/agents/design-system.md.
CONTEXT.md define o vocabulário de domínio. Este arquivo define convenções de implementação. Código e configurações
mostram o estado atual. Em divergências, não normalize silenciosamente: corrija a fonte obsoleta ou sinalize o conflito.
Stack e escopo técnico
- App exclusivo para macOS, com SwiftUI, Observation (
@Observable) e Swift Charts. - Target macOS
26.1, isolamento padrãoMainActor. - Direção aceita: app online-only estrito com Supabase Postgres como fonte única de verdade; veja
docs/adr/0004-app-online-only-com-supabase-como-fonte-da-verdade.md. - PowerSync, SQLite, schema local e fluxos offline-first foram removidos. Não reintroduza persistência financeira local.
- Testes com Swift Testing (
import Testing,@Suite,@Test,#expect). - O GranaApp não chama provedores externos de IA nem Edge Functions de categorização. Veja
docs/adr/0006-granaapp-sem-ia-remota.md. - App Sandbox permanece desativado para permitir
Process. - Não adicione dependências nem troque a stack sem pedido explícito.
Arquitetura
Fluxo obrigatório:
SwiftUI View -> @Observable Store -> Repository -> Supabase backend
- Views não executam SQL, não chamam Supabase diretamente nem instanciam repositories.
- Stores recebem
AppContainer; coordenam estado e operações. - Repositories concentram chamadas remotas, DTOs e mapeamento entre contratos backend e models.
AppContaineré o composition root e expõe repositories e serviços.- Features stateful novas ou migradas, especialmente com fluxo multi-etapa, devem preferir TCA (
@Reducer,StoreOf,DependencyValues) com clients explícitos para efeitos. - Reducers não conhecem
AppContainer; a composiçãoAppContainer -> client live -> dependencyacontece na borda SwiftUI/composition root. - O app não persiste dados financeiros localmente. Supabase Auth pode manter sessão/token local; dados financeiros só em memória durante sessão válida.
- Use
load()erefresh()explícitos por tela. Não introduzawatch()/Realtime sem decisão específica. - Telas compostas consomem read models ou RPCs versionadas do backend; não busque histórico inteiro para agregar no Swift.
- Operações consistentes com múltiplas etapas usam RPCs transacionais no backend.
- Tabelas financeiras não recebem escrita direta do app.
INSERT,UPDATEeDELETEfinanceiros passam por funções controladas.
Invariantes de implementação
- Toda transação referencia exatamente uma conta e uma categoria; subcategoria é opcional.
- Dinheiro usa
Decimalno Swift eInt64em centavos no banco. Nunca useDouble; converta comConverters. Transaction.amounté sempre magnitude positiva;CategoryKinddetermina receita, despesa ou transferência.- Instantes usam
timestamptzno backend eDateno Swift; datas civis de fatura usamdate. - Transferências não entram em cards nem gráficos de receitas e despesas.
- IDs financeiros finais são gerados pelo backend. O app pode enviar IDs temporários ou chaves de idempotência.
- Moeda padrão: BRL.
accountscontém apenas campos universais. Dados específicos ficam embank_accountsecredit_cards, escritos atomicamente pelo backend.- Toda transação de cartão exige fatura. Mudança de conta ou data re-resolve o ciclo.
- Datas de fechamento e vencimento de uma fatura são snapshots; mudanças futuras no cartão não as alteram.
- Compra se vincula à fatura por
transactions.statement_id; pagamento se vincula porstatement_payments. - Escritas que afetam compras ou pagamentos recalculam total e status da fatura na mesma transação backend.
- Categorias são hierárquicas. Apenas raízes têm ícone; subcategorias herdam o ícone na UI.
- Categorias e instituições são catálogos globais somente leitura no MVP. O app resolve catálogos por slug/código, não por IDs conhecidos.
- Instituições financeiras fora do catálogo suportado bloqueiam criação de conta.
Convenções Swift e UI
- Use
@Observable; não introduzaObservableObject,@Publishednem Combine. - Em features TCA, prefira
@ObservableState,BindableActione subfeatures explícitas para fluxos complexos; evite concentrar histórico, wizard, parsing e commit em um único reducer. - Use
async/await. - Estado apenas visual fica em
@State; dados persistidos ou compartilhados ficam no Store. - Dados financeiros não podem ser persistidos em
UserDefaults, arquivos, SQLite, banco local ou caches em disco. - Mantenha Views pequenas e extraia subviews quando acumularem responsabilidades.
#Previewso e permitido no targetAppUI. Fora dele, valide UI executando o app.- Ícones de UI vêm de
AppIcon; ícones de categoria passam porCategoryIcone seus mappings. - Tokens visuais do tema ficam em
GranaTheme. Assets de cor só são necessários quando a cor precisar ser referenciada pelo asset catalog/Xcode; enquanto o app for light-only, novas cores visuais não exigem variante dark. - Erros de domínio são enums
LocalizedError, com mensagens em PT-BR. - Comentários explicam decisões e motivos, não narram o código.
- Arquivos com interpolação de
LoggerimportamOSLog. - Tipos e arquivos usam
PascalCase; funções e propriedades,camelCase; tabelas e colunas,snake_case. - TODOs usam
// TODO(fase-N): .... - Siga
.swiftformate.swiftlint.yml; não afrouxe regras customizadas para acomodar uma mudança.
Feedback e logs
- Toda mensagem visível passa por
NoticeCenter. - Relate erros onde forem consumidos.
catchque relança ou transforma não gera toast. CancellationErroré esperado e permanece silencioso.- Não faça
log.errorantes deNoticeCenter.report; o centro já registra o notice. - Use as categorias de
Core/Logging/Logger.swift; não useprint. - Nunca registre valores de transações, credenciais ou dados financeiros sensíveis.
Importação e classificação
ImportFeatureConfiguration.supportedExtensionsé a fonte dos formatos aceitos.- Importadores aplicam
abs()antes de persistir valores. - Preserve as regras existentes de deduplicação por formato.
- Cada
STMTRSOFX gera umImportBatch; múltiplos extratos são enviados em payload estruturado para commit atômico no backend. ImportBatchpermanece reversível, sem transações órfãs.- A revisão de classificação ocorre antes do commit final. Enquanto o projeto local de IA não existir, o app gera fallback em Não Classificado para revisão manual.
- Deduplicação de importação é garantia backend com função e constraint; duplicatas são puladas com relatório.
- Não introduza chamadas diretas do GranaApp a APIs públicas de IA. Qualquer integração inteligente futura deve passar por contrato local/processo dedicado aprovado.
Onde alterar
| Necessidade | Local principal |
|---|---|
| Nova tabela, RLS, função ou seed global | migrations Supabase versionadas no repo |
| Novo read model ou mutação financeira | schema api/RPC versionada e repository remoto |
| Nova categoria padrão | seed/migration Supabase de catálogo global |
| Novo formato de importação | GranaApp/Core/Import/, GranaApp/Features/Import/ e step de revisão |
| Novo repository ou serviço | Registro no AppContainer |
| Novo ícone de UI | GranaApp/Shared/Components/AppIcon.swift |
| Novo ícone de categoria | GranaApp/Models/Category.swift e extensions de CategoryIcon |
| Feedback ao usuário | NoticeCenter |
| Nova categoria de log | GranaApp/Core/Logging/Logger.swift |
Configuração e validação
Quando necessário, crie a configuração local a partir do template:
cp GranaApp/Config.example.swift GranaApp/Config.swift
Config.swift permanece ignorado pelo Git.
xcodebuild \
-project GranaApp.xcodeproj \
-scheme GranaApp \
-destination 'platform=macOS' \
-derivedDataPath /tmp/GranaAppDerivedData \
build
xcodebuild \
-project GranaApp.xcodeproj \
-scheme GranaApp \
-destination 'platform=macOS' \
-derivedDataPath /tmp/GranaAppDerivedData \
test
swiftformat --lint .
swiftlint
- Rode primeiro a validação mais estreita que cobre a mudança; amplie para build e testes completos quando o impacto for transversal.
- Teste regras de domínio, parsers, conversões, queries e regressões.
- Para repositories remotos, use clients fake em testes Swift. Testes backend, scripts SQL e verificações formais de backend não são requisito nesta refatoração; mudanças backend dependem de revisão de código/contrato e dos testes do app.
- Migrations que alteram RLS, grants, RPCs financeiras ou schema financeiro devem ser pequenas, revisáveis e acompanhadas de plano de rollback manual no PR/issue. Sem testes backend, revisão humana é o gate principal dessas mudanças.
- Injete
Calendarem comportamento dependente de dia ou fuso. - Informe validações não executadas.
- Não faça stage, commit, push, mudanças destrutivas de banco nem alterações de dependências sem pedido explícito.
Agent skills
Issue tracker
Issues e PRDs são rastreados no GitHub Issues via gh. Veja docs/agents/issue-tracker.md.
Triage labels
Usa os cinco rótulos canônicos no GitHub Issues. Veja docs/agents/triage-labels.md.
Domain docs
Layout single-context. Veja docs/agents/domain.md.