Aplicação web (PWA) de registro de horas. Documentos de produto, escopo e decisões (ADRs) ficam no Projeto "Compasso" no claude.ai: escopo-mvp-v1.md e plano-implementacao.md.
Next.js (App Router, TS estrito), Tailwind, shadcn/ui, Drizzle ORM + Neon (PostgreSQL), Better Auth, Zod, Vitest. Ler AGENTS.md e a documentação em node_modules/next/dist/docs/ antes de usar APIs do Next.js.
Workspace (tenant, 1:1 com organização do Better Auth) -> Organização (empresa própria ou cliente) -> Projeto -> Tarefa. Tags são do workspace. Papéis por workspace: admin | member (membro vê só as próprias horas).
Tokens, componentes e convenções estão em design-system.md; a referência viva fica em /design (oculta em produção). Usar apenas tokens (bg-background, text-muted-foreground...), nunca hex; vermelho só para cronômetro em andamento, ações destrutivas e alertas; horas sempre em tabular-nums.
- Toda query operacional filtra por
workspace_id; nunca confiar em IDs do cliente sem checar o tenant. - Autorização por recurso, não por caminho: toda página, route handler e Server Action protegido chama
requireWorkspaceContext(). - Dados do workspace (catálogo e, depois, registros de tempo) só pelo
getTenant()(src/server/get-tenant.ts) /createTenant()(src/server/tenant.ts), nunca comgetDb()direto em páginas, route handlers ou Server Actions. A camada carimbaworkspace_idda sessão, valida que IDs de pais pertencem ao workspace, trata IDs de outro workspace como inexistentes e restringe escrita do catálogo a admin (ADR-026). Nova tabela comworkspace_idganha repositório e teste de isolamento emsrc/server/tenant.test.ts(PGlite com as migrações reais). - Remoção sempre lógica (ADR-025): nada de DELETE físico em users/workspaces/membros.
users.deleted_at(anonimizado, LGPD),workspaces.archived_at,workspace_members.removed_at; consultas de acesso filtram esses campos. Anonimização emsrc/server/auth/anonymize.ts(ADR-034; scriptscripts/anonymize-user.ts); provisionamento emsrc/server/auth/workspace-context.ts. - Service worker (
public/sw.js, ADR-012): só serve/offlinecomo fallback de navegação e cacheia ativos estáticos imutáveis; nunca cachear HTML de páginas do app, RSC,/apinem dados autenticados. Só registra em produção (para testar:npm run build && npm start). - Instantes em UTC (timestamptz); fuso IANA no usuário e no registro. Duração nunca é armazenada: derivar de
ended_at - started_at(versrc/lib/time.ts). - Um único timer ativo por usuário (índice único parcial).
- Valores monetários em Decimal (fora do MVP). Exportações escrevem números reais, nunca texto.
- Código em inglês, UI em português do Brasil. Validar entradas com Zod. Testes para regras de negócio.
- Migrações versionadas (
npm run db:generate); explicar o impacto antes de alterar o schema.
npm run dev | build | lint | typecheck | test | db:generate | db:migrate | db:studio
main= produção (só receberelease/*ehotfix/*);develop= integração.- Trabalho novo:
feature/<nome>a partir dedevelop, sempre via PR paradevelop. Nunca commitar direto emmainoudevelop. - Release:
release/x.y.za partir dedevelop→ PR paramain+ tag + merge de volta emdevelop. Correção urgente:hotfix/*a partir demain. - Versões 0.x (enquanto a Vercel publica a
develop): PRchore/release-x.y.zcom a versão empackage.jsone a entrada noCHANGELOG.md; depois do merge, a tagvx.y.zno commit dadevelop. O fluxorelease/*→mainacima passa a valer na 1.0.0 (ver ADR-031). - Commits em Conventional Commits (
feat:,fix:,chore:,docs:,test:). - Antes de abrir o PR:
npm run typecheck && npm test && npm run build; depois revisão (code-review → simplify → security-review).