Banco de Dados — SQLite Schema

Informações Gerais

Diagrama ER (Phase 65)

erDiagram
    users ||--o{ projects : owns
    users ||--|| account_settings : has
    users ||--o{ recovery_keys : holds
    users ||--o{ subscriptions : pays
    users ||--o{ invites : creates
    users ||--o{ chat_messages : sends
    users ||--o{ trial_credits : claims
    users ||--o{ activity_log : generates

    projects ||--o{ project_phases : tracks
    projects ||--o{ pinned_notes : pins
    projects ||--o{ chat_messages : contains
    projects ||--o{ timeline_events : records
    projects ||--o{ github_links : links
    projects ||--o{ skill_benchmarks : runs
    projects ||--o{ marketplace_cache : caches

    github_links ||--o{ github_events : receives

    invites }o--|| users : "consumed by"
    subscriptions ||--o{ stripe_events : "logs idempotency"

    users {
        TEXT id PK
        TEXT email UK
        TEXT password_hash
        TEXT role "admin · user"
        TEXT google_id UK
        TEXT github_id UK
        INTEGER email_verified
        INTEGER two_factor_enabled
        TEXT last_login
    }

    projects {
        INTEGER id PK
        TEXT technical_name
        TEXT owner_id FK
        TEXT display_name
        INTEGER is_archived
    }

    chat_messages {
        INTEGER id PK
        TEXT project_name
        TEXT worker_id
        TEXT role "user · assistant · system"
        TEXT content "AES-256-GCM if encrypted=1"
        INTEGER encrypted
        TEXT timestamp
    }

    subscriptions {
        TEXT user_id PK
        TEXT plan "free · min · max · beta"
        TEXT stripe_customer_id
        TEXT status
    }

    invites {
        TEXT code PK "arc-XXXX-XXXX"
        TEXT created_by FK
        TEXT used_by FK
        TEXT parent_invite_code
        TEXT status "active · used · revoked"
    }

    recovery_keys {
        INTEGER id PK
        TEXT user_id FK
        TEXT encrypted_key
        TEXT key_hint "first 4 chars"
    }

    trial_credits {
        TEXT email PK
        INTEGER credits_used
        TEXT first_used_at
    }

23 tabelas distribuídas em 24 migrações. Auth + projetos + dados do workspace + inteligência + billing + controle de acesso beta + auditoria de exportação de contexto. Todas as constraints de FK são aplicadas.

Tabelas

users — Usuários

Coluna Tipo Descrição
id TEXT PK UUID
email TEXT UNIQUE Email (obrigatório)
password_hash TEXT Hash da senha (bcrypt)
role TEXT 'admin' ou 'user'
name TEXT Nome
avatar_url TEXT URL do avatar
google_id TEXT UNIQUE Google OAuth ID
github_id TEXT UNIQUE GitHub OAuth ID
email_verified INTEGER 0/1
two_factor_enabled INTEGER 0/1
totp_secret TEXT Segredo TOTP (2FA)
last_login TEXT ISO 8601
created_at TEXT ISO 8601

projects — Projetos

Coluna Tipo Descrição
id INTEGER PK Auto
technical_name TEXT Nome técnico (único por owner)
owner_id TEXT FK→users Proprietário
display_name TEXT Nome de exibição
description TEXT Descrição
color TEXT Cor (hex)
icon TEXT Ícone
is_archived INTEGER 0/1
sort_order INTEGER Ordem (padrão 999)
project_protocol TEXT Protocolo do projeto
created_at, updated_at TEXT Timestamps

UNIQUE(technical_name, owner_id)

account_settings — Configurações da Conta

Coluna Tipo Descrição
user_id TEXT PK FK→users
anthropic_key TEXT Chave de API Anthropic
openai_key TEXT Chave de API OpenAI
name TEXT Nome
role TEXT Função
auto_harvest INTEGER Coleta automática de skills 0/1

chat_messages — Histórico de Chats

Coluna Tipo Descrição
id INTEGER PK Auto
project_name TEXT Projeto
worker_id TEXT Worker
role TEXT 'user', 'assistant', 'system'
content TEXT Conteúdo da mensagem (criptografado com AES-256-GCM se encrypted=1)
encrypted INTEGER 0=plaintext, 1=vault-encrypted (Phase 45.3)
attachments TEXT Array JSON
timestamp TEXT ISO 8601
metadata TEXT Objeto JSON

INDEX(project_name, worker_id, timestamp DESC)

recovery_keys — Chaves de Recuperação (Phase 45.4)

Coluna Tipo Descrição
id INTEGER PK Auto
user_id TEXT FK→users Proprietário
encrypted_key TEXT Master key criptografada
key_hint TEXT Dica (primeiros 4 caracteres)
created_at TEXT ISO 8601
used_at TEXT Quando foi utilizada
revoked INTEGER 0=ativa, 1=revogada

INDEX(user_id)

github_links — Vínculos de Repositório GitHub (Phase 49.3)

Coluna Tipo Descrição
id INTEGER PK Auto
project_name TEXT Projeto Arc OS
owner TEXT Proprietário do repositório GitHub
repo TEXT Nome do repositório GitHub
webhook_secret TEXT UNIQUE Hex de 32 bytes para validação HMAC-SHA256
created_at TEXT ISO 8601
created_by TEXT FK→users Quem criou o vínculo

UNIQUE(project_name, owner, repo) — múltiplos repositórios por projeto são permitidos. INDEX(project_name), INDEX(webhook_secret)

github_events — Log de Eventos GitHub (Phase 49.3.1)

Coluna Tipo Descrição
id INTEGER PK Auto
link_id INTEGER FK→github_links CASCADE ao excluir vínculo
project_name TEXT Projeto Arc OS (desnormalizado para velocidade de consulta)
event_type TEXT push, pull_request, workflow_run, issues
action TEXT sub-ação (opened/closed/success/failure)
summary TEXT String de exibição pré-formatada
url TEXT Deep link do GitHub
actor TEXT Usuário do GitHub
created_at TEXT ISO 8601

INDEX(project_name, created_at DESC), INDEX(link_id)

skills_global — Skills Globais

Coluna Tipo Descrição
id INTEGER PK Auto
name TEXT UNIQUE Nome
description TEXT Descrição
category TEXT Categoria (padrão 'general')
content TEXT Conteúdo da skill (markdown)
triggers TEXT Array JSON de triggers
keywords TEXT Array JSON de palavras-chave
eval_rules TEXT Array JSON de regras
tool_code_ts TEXT Implementação TypeScript
version INTEGER Versão (auto-increment)
status TEXT 'active', 'draft', 'deprecated', 'archived'

skills_project_forks — Forks de Skills

Coluna Tipo Descrição
id INTEGER PK Auto
project_name TEXT Projeto
skill_id INTEGER FK→skills_global Skill pai (CASCADE)
content TEXT Conteúdo customizado
triggers, keywords, eval_rules, tool_code_ts TEXT Sobrescritas
version INTEGER Versão do fork

UNIQUE(project_name, skill_id)

skill_evolution_logs — Histórico de Alterações de Skills

Coluna Tipo Descrição
id INTEGER PK
skill_id INTEGER FK CASCADE
project_name TEXT Projeto (nullable)
action TEXT 'created', 'modified', 'applied', 'reverted'
diff_summary TEXT Descrição das alterações
author TEXT Autor (padrão 'system')
metadata TEXT JSON

skill_update_requests — PRs de Skills (Sage)

Coluna Tipo Descrição
id INTEGER PK
skill_id INTEGER FK CASCADE
proposed_by TEXT 'sage' (padrão)
status TEXT 'pending', 'approved', 'rejected', 'applied'
current_content TEXT Versão atual
proposed_content TEXT Versão proposta
reason TEXT Motivo da alteração

skill_benchmarks — Testes A/B de Skills

Coluna Tipo Descrição
id INTEGER PK
request_id INTEGER FK→skill_update_requests CASCADE
test_scenario TEXT Cenário de teste
old_output, new_output TEXT Resultados
score_old, score_new REAL Pontuações
judgment_reason TEXT Justificativa

pinned_notes — Notas Fixadas

Coluna Tipo Descrição
id INTEGER PK
project_name TEXT Projeto
worker_id TEXT Worker
title TEXT Título
body TEXT Conteúdo
source_message_id INTEGER Referência ao chat_messages

project_phases — Fases do Roadmap

Coluna Tipo Descrição
id INTEGER PK
project_name TEXT Projeto
phase_id INTEGER Número da fase
phase_title TEXT Nome
status TEXT 'PLANNED', 'ACTIVE', 'COMPLETED', 'CANCELLED'
progress REAL 0.0–1.0
deadline TEXT ISO 8601

UNIQUE(project_name, phase_id)

activity_log — Registro de Atividade

Coluna Tipo Descrição
id INTEGER PK
project_name TEXT Projeto
actor TEXT Worker ou usuário
event_type TEXT Tipo de evento
title TEXT Descrição
metadata TEXT JSON
created_at TEXT ISO 8601

INDEX(created_at DESC), INDEX(project_name, created_at DESC), INDEX(event_type, created_at DESC), INDEX(actor, created_at DESC)

system_configs — Configurações do Sistema

Coluna Tipo Descrição
key TEXT PK Chave
value TEXT Valor
description TEXT Descrição

marketplace_analysis_cache — Cache do Marketplace

Cache de análise de compatibilidade de skills do marketplace.

ephemeral_tokens — Tokens de Vida Curta (issue #27)

Armazenamento persistente para tokens OAuth de uso único, redefinição de senha e verificação de email. Antes da migração 022, esses tokens viviam em um Map em memória e eram perdidos a cada reinício do master, quebrando o login via Google/GitHub (invalid_state).

Coluna Tipo Descrição
token TEXT PK Hex de 32 bytes (randomBytes(32))
type TEXT NOT NULL oauth_state | password_reset | email_verification
payload TEXT NOT NULL JSON: {provider} para oauth_state, {email} para os demais
expires_at INTEGER NOT NULL Época em ms — TTL: 10 min (oauth) / 30 min (reset) / 24h (verify)

INDEX(type), INDEX(expires_at). Uso único: consume() em uma única transação lê o payload e exclui o registro.

project_issues — Issues por projeto (issue #53, Phase 53.14)

SQLite como SSOT para issues, substituindo o issues/issues.json por projeto. O arquivo continua em disco como export derivado (mirror), ignorado pelo git — o DB é autoritativo.

Coluna Tipo Descrição
project_name TEXT NOT NULL nome do registry; parte da PK composta com id
id INTEGER NOT NULL sequência 1-based por projeto (max+1 no insert)
title TEXT NOT NULL título da issue
body TEXT NOT NULL DEFAULT '' descrição, markdown
priority TEXT NOT NULL DEFAULT 'P2' P0 | P1 | P2 | P3
labels TEXT NOT NULL DEFAULT '[]' Array JSON de strings
status TEXT NOT NULL DEFAULT 'open' draft | open | in_progress | blocked | deferred | closed (o default SQL 'open' é mantido — novos issues são forçados a draft no nível da aplicação)
assignee TEXT NULL worker id que pegou o issue (migration 036)
created_by TEXT NOT NULL DEFAULT 'legacy' worker id de quem criou o issue (migration 046 / #327) — para auditoria (quem, quando, para quem)
created_at TEXT NOT NULL Timestamp ISO
updated_at TEXT NOT NULL Timestamp ISO
closed_at TEXT NULL Timestamp ISO quando status=closed
activity TEXT NOT NULL DEFAULT '[]' Array JSON {ts, type, author, text} — auditoria append-only

PK (project_name, id) — cada projeto tem seu próprio espaço de IDs independente. INDEX (project_name, status) — filtro mais frequente (open only). As operações ficam em issueQueries (shared/db.ts): list/get/nextId/insert/upsert/replaceAll/bulkImport. replaceAll — transação atômica; bulkImportINSERT OR IGNORE para re-seed idempotente.

Migration 036 (#187, 2026-05-23): ALTER TABLE project_issues ADD COLUMN assignee TEXT. Os novos status (in_progress, blocked, deferred) são validados no nível da aplicação (union IssueStatus em shared/db.ts), não por CHECK constraint (o SQLite não permite adicionar constraint via ALTER TABLE).

Migration 046 (#327, 2026-06-03): ALTER TABLE project_issues ADD COLUMN created_by TEXT NOT NULL DEFAULT 'legacy'. O lifecycle foi ampliado com o novo status draft. handleCreateIssue força status='draft' quando assignee=null (caso contrário, direto 'open'), exige worker_id (body ou fallback de auth). handleUpdateIssue faz auto-promote draft → open na primeira atribuição de assignee e rejeita close para issue nunca atribuído (FR-ISS-101/102/103). Linhas pré-existentes recebem backfill via DEFAULT como 'legacy'.

onboarding_progress — Estado do checklist pós-wizard (issue #56, Phase 54.1)

Cache derivado por usuário sobre o stream de eventos do activity_log (SSOT). A UI lê com uma única consulta em vez de agregar eventos. Replayável: dismiss/replay não alteram state, apenas dismissed_at.

Coluna Tipo Descrição
chat_id TEXT PRIMARY KEY id do usuário (chat id do registry)
state TEXT NOT NULL DEFAULT '{}' JSON {workers, cli, skill, bot, issue}completed | skipped (chaves ausentes = pending)
completed_count INTEGER NOT NULL DEFAULT 0 cache derivado com contagem de steps completed (0–5)
started_at TEXT NOT NULL DEFAULT (datetime('now')) criação do registro (primeiro evento/dismiss/replay)
completed_at TEXT NULL Timestamp ISO quando todos os 5 steps estão completed
dismissed_at TEXT NULL Timestamp ISO quando o usuário fechou o checklist
source TEXT NOT NULL DEFAULT 'web' web | cli — para atribuição de funil (#58 arc tour)
updated_at TEXT NOT NULL DEFAULT (datetime('now')) timestamp de mutação

INDEX em completed_at para análise de funil (#61 Phase 54.6). As operações ficam em onboardingQueries (shared/db.ts): getProgress/recordEvent/dismiss/replay. recordEvent é idempotente em (chat_id, step, status) — uma chamada idêntica repetida retorna changed=false e não escreve no activity_log. Whitelist: 5 steps × 2 statuses (completed, skipped). skipped NÃO incrementa completed_count. A transição skipped → completed adiciona ao count. Todas as mutações emitem eventos no activity_log com event_type LIKE 'onboarding_%' — verdadeiro SSOT para métricas de funil; as colunas da tabela são cache derivado.

platform_audit_log — Trilha de rotação de segredos para super-admin (Phase 57, Sentinel #103)

Registro append-only de mutações em vault platform-secrets via UI de Configurações da Plataforma. SSOT para forense pós-incidente ("qual admin rotacionou a chave Anthropic às 03:14 UTC?"). O próprio vault.json não registra isso — apenas o valor.

Coluna Tipo Descrição
id INTEGER PRIMARY KEY AUTOINCREMENT ordem temporal mesmo com clock skew
ts TEXT NOT NULL DEFAULT (datetime('now')) UTC server-side; atacante não pode retroagir
user_chat_id TEXT NOT NULL admin que executou a ação
user_email TEXT NULL snapshot da tabela users no momento da ação
action TEXT NOT NULL list | view | rotate | test | restart
key_name TEXT NOT NULL nome da entrada no vault (allowlist em platform.ts); * para list-all
ip TEXT NULL de CF-Connecting-IP / X-Real-IP / XFF tail
result TEXT NOT NULL success ou fail:<reason>
user_agent TEXT NULL string do UA, truncada em 200 chars

INDEXES: idx_platform_audit_ts (recentes por chave), idx_platform_audit_user (trilha por admin), idx_platform_audit_key (histórico de rotação por chave). Invariante append-only: sem handlers de UPDATE/DELETE; a UI expõe somente leitura via platformAuditQueries.recent + lastRotated em shared/db.ts. Cada ação (sucesso + falha) grava um registro.

auth_events — Log de eventos de autenticação de usuários finais (Migration 027, issue #130)

Registro append-only de eventos de auth para auditoria de segurança e investigação de incidentes. Separado do platform_audit_log (vault somente admin) — esta tabela cobre o fluxo de auth de usuários finais com IP.

Coluna Tipo Descrição
id INTEGER PRIMARY KEY AUTOINCREMENT ordem temporal
ts TEXT NOT NULL DEFAULT (datetime('now')) UTC server-side
event TEXT NOT NULL signup | login | oauth_login | magic_link | device_code_approve
provider TEXT NOT NULL DEFAULT 'password' password | google | github | magic_link | device_code
user_id TEXT NULL NULL para tentativas falhas em que o usuário não foi identificado
email TEXT NULL email usado na tentativa
ip TEXT NOT NULL DEFAULT 'unknown' CF-Connecting-IP → X-Real-IP → último segmento do XFF
user_agent TEXT NULL string do UA
result TEXT NOT NULL success | failed | rate_limited | invalid_invite | already_registered | 2fa_required | invalid_token
meta TEXT NULL contexto extra em JSON (p.ex. {isNew: true} para OAuth)

INDEXES: idx_auth_events_ts (mais recentes primeiro), idx_auth_events_user (histórico por usuário), idx_auth_events_ip (detecção de ameaças por IP), idx_auth_events_event (filtro event+result). authEventQueries.insert/recent em shared/db.ts. Registrado em master-bot/routes/auth.ts (register/login/oauth/magic-link) e master-bot/routes/cli.ts (device_code_approve).

Migrações

# Nome Fase O que adiciona
001 initial_schema Phase 40 users, projects, account_settings
002 project_protocol Phase 40.7 projects.project_protocol
003 chat_messages Phase 40.10 chat_messages
004 skill_system Phase 41 skills_global, skills_project_forks, skill_evolution_logs, skill_update_requests
005 skill_benchmarks Phase 40.11d skill_benchmarks
006 system_configs system_configs + FIGMA_CACHE_ROOT
007 marketplace_cache Phase 40.12.1 marketplace_analysis_cache
008 harvester Phase 40.13 +is_suggestion, +source_query, +auto_harvest
009 pinned_notes Phase 41.8 pinned_notes
010 project_phases Phase 44 project_phases
011 activity_log Phase 44.1 activity_log
012 performance_indexes Perf Audit +idx_activity_log_actor, +idx_chat_msg_role_ts
013 timeline_timestamps Phase 47 +timestamp em mensagens de worker
014 timeline_events Phase 47 tabela timeline_events
015 e2ee_chat Phase 45.3 +coluna encrypted em chat_messages
016 recovery_keys Phase 45.4 tabela recovery_keys
017 github_links Phase 49.3 tabela github_links (vínculos de webhook)
018 github_events Phase 49.3.1 tabela github_events (log de eventos para feed da UI)
019 trial_credits Phase 50.1 +users.trial_granted_at, +projects.trial_mode, +projects.trial_tokens_remaining
020 subscriptions Phase 51 tabela subscriptions (plan, stripe_customer_id, status, feature_flags) + log de idempotência stripe_events
021 invites Phase 52.1 tabela invites (cadastro beta fechado) — code, created_by, used_by, parent_invite_code, status, note
022 ephemeral_tokens issue #27 tabela ephemeral_tokens — OAuth state / redefinição de senha / verificação de email persistentes, substituindo Map em memória (corrige invalid_state no reinício do master)
023 project_issues Phase 53.14 / issue #53 tabela project_issues (PK composta project_name+id, JSON labels/activity) — migra o armazenamento de issues do issues/issues.json para o SQLite como SSOT; o JSON passa a ser mirror derivado; elimina drift de merge de texto no deploy
024 context_exports Phase 56.4 + 56.5 / issues #100 + #101 export_audit_log (id PK, project_name, owner_id, exported_at, sections JSON, findings_critical/high/medium/low INT, bytes) + export_preferences (project_name PK, always_include_emails BOOL, auto_redact_critical BOOL DEFAULT 1, notify_on_export BOOL DEFAULT 0, updated_at) — histórico de auditoria por projeto para o Project Context Export + toggles de política por projeto; alerta quando ≥3 exports/24h com notify_on_export = true
025 onboarding_progress Phase 54.1 / issue #56 onboarding_progress (chat_id PK, JSON state por 5 steps, completed_count derivado, started_at/completed_at/dismissed_at, source web|cli) — cache derivado sobre stream de eventos do activity_log; SSOT = eventos event_type LIKE 'onboarding_%'; recordEvent idempotente, dismiss/replay não-destrutivos
026 platform_audit_log Phase 57 / Sentinel #103 platform_audit_log (id PK AUTOINCREMENT, ts, user_chat_id, user_email, action, key_name, ip, result, user_agent) — trilha de auditoria append-only para mutações da UI de Configurações da Plataforma (super-admin) em vault secrets. 3 indexes: idx_platform_audit_ts/user/key. Sem UPDATE/DELETE — somente leitura via platformAuditQueries.recent + lastRotated
027 auth_events Issue #130 auth_events (id PK AUTOINCREMENT, ts, event, provider, user_id, email, ip, user_agent, result, meta JSON) — Log de eventos de autenticação + IP append-only. Eventos: signup/login/oauth_login/magic_link/device_code_approve. Resultados: success/failed/rate_limited/invalid_invite/already_registered/2fa_required/invalid_token. 4 índices: ts/user_id/ip/event+result
028 managed_containers Phase 60 #135/#136 managed_containers (id TEXT PK (nome do contêiner Docker), user_id, status (provisioning|ready|paused|suspended|deleted), server_ip, internal_port INT, claude_authed INT, github_authed INT, created_at, last_active) — Registro de contêineres Docker 1-por-usuário para Standard Cloud. managedContainerQueries.findByUser/insert/updateStatus/setAuthFlag/touch/listActive em shared/db.ts. Endpoints: POST /api/crm/cloud/provision, GET /status, POST /deprovision em shared/routes/cloud.ts
029 cloud_waitlist Phase 60 #134 cloud_waitlist (id INT PK AUTOINCREMENT, user_id TEXT UNIQUE, email, position INT, status (waiting|invited|activated|declined), notes, joined_at, invited_at, activated_at) — Lista de espera Early Access para Standard Cloud. cloudWaitlistQueries.join/findByUser/list/invite/activate/decline/stats em shared/db.ts. Endpoints: POST /api/crm/cloud/waitlist (entrar), GET /waitlist/status (próprio status), GET /waitlist (lista do admin), POST /waitlist/invite (admin ativa + faz upgrade do plano para cloud)
030 translation_feedback Phase 59.4 #122 translation_feedback (id INT PK AUTOINCREMENT, locale TEXT, message_id TEXT, original TEXT, current_translation TEXT, suggestion TEXT, user_id TEXT, status (pending|accepted|rejected), created_at, reviewed_at) — Feedback de qualidade de tradução por locale. Painel de revisão do admin em /api/crm/translations/feedback (list/stats/accept/reject).
031 token_usage_log Phase 63 #148 token_usage_log (id INT PK AUTOINCREMENT, project_name TEXT NOT NULL, owner_id TEXT NOT NULL, worker_id TEXT, input_tokens INT DEFAULT 0, output_tokens INT DEFAULT 0, cache_tokens INT DEFAULT 0, total_tokens INT DEFAULT 0, created_at INT DEFAULT (unixepoch())) — Log de uso de tokens Claude por requisição. 3 índices: owner_id, project_name, created_at. Preenchido por child-bot/claude-runner.ts via POST /api/internal/usage/log (fire-and-forget). Consultado por GET /api/crm/account/usage → retorna { rows: [...últimos 200], totals: { total, input, output } } para BillingPage + UserDropdown UsageCard.
032 arc_help_usage Phase 61 #147 arc_help_usage (id INT PK AUTOINCREMENT, user_id TEXT NOT NULL, date TEXT NOT NULL (YYYY-MM-DD), count INT NOT NULL DEFAULT 0, PRIMARY KEY (user_id, date)) — Contador de rate-limit diário para o chat de Ajuda IA no app (30 mensagens/dia). Consultado por GET /api/crm/help/usage; atualizado em POST /api/crm/help/chat.
033 arc_help_messages Phase 61 #153 arc_help_messages (id PK AUTOINCREMENT, user_id, role, text, sources JSON, created_at) — Histórico persistente do chat Arc Help por usuário (últimas 60).
034 skills_owner_project #157 ALTER TABLE skills_global ADD COLUMN owner_project TEXT — NULL=global, não-nulo=pertencente a um projeto.
035 password_version Phase 64 #174 ALTER TABLE users ADD COLUMN password_version INTEGER DEFAULT 0 — incrementado na troca de senha; claim pv do JWT validado pelo crmAuthMiddleware para invalidar tokens antigos.
036 issue_assignee #187 / 2026-05-23 ALTER TABLE project_issues ADD COLUMN assignee TEXT — worker id nullable. Habilita arc issue take <id> + status estendidos in_progress/blocked/deferred (validados no nível da aplicação).
046 issue_draft_and_created_by #327 / 2026-06-03 ALTER TABLE project_issues ADD COLUMN created_by TEXT NOT NULL DEFAULT 'legacy' — campo de auditoria "quem criou". Lifecycle += draft (union no nível da aplicação). handleCreateIssue força draft sem assignee; handleUpdateIssue faz auto-promote draft→open no assign + rejeita close de issue nunca atribuído (FR-ISS-101/102/103).
037 waitlist #197 / 2026-05-25 waitlist (id INT PK AUTOINCREMENT, email TEXT UNIQUE, message, status DEFAULT 'pending', created_at, updated_at) — formulário de pedido de early access na página de login.
038 plata_billing Phase 65 #202 / 2026-05-26 Adiciona colunas do Plata by mono em subscriptions: plata_card_token TEXT, plata_wallet_id TEXT, plata_masked_pan TEXT, next_billing_date TEXT, billing_failures INTEGER DEFAULT 0. Também cria a tabela de idempotência plata_events (invoice_id PK, status, processed_at).
039 skill_project_grants #201 / 2026-05-26 skill_project_grants (skill_id INT FK→skills_global.id CASCADE, project_name TEXT, granted_at, PK composta) — acesso many-to-many a skills entre projetos.
040 drop_stripe #205 / 2026-05-26 DROP da coluna stripe_customer_id + DROP TABLE stripe_events. NB: a coluna stripe_subscription_id sobreviveu a esta migration porque o SQLite recusa DROP COLUMN em colunas com constraint UNIQUE (capturado silenciosamente por try/catch). Ver migration 041.
041 rebuild_subscriptions #205 / 2026-05-26 Follow-up da 040 — reconstrói a tabela subscriptions sem a coluna órfã stripe_subscription_id via padrão CREATE TABLE … AS. Idempotente (pulada quando a coluna já está ausente).
042 totp_backup_codes ALTER TABLE users ADD COLUMN totp_backup_codes TEXT — códigos de recuperação TOTP.
043 worker_avatars Phase D #304 / 2026-05-30 worker_avatars (id PK AUTOINCREMENT, project_name, worker_id, file_path, mime_type, created_at; UNIQUE(project_name, worker_id)) — store de metadados para avatares de workers enviados. Arquivos salvos em data/worker-avatars/. Servidos via GET /api/crm/projects/:name/workers/:id/avatar.
044 worker_templates Phase I #304 / 2026-05-30 worker_templates (id PK AUTOINCREMENT, owner_id, name, description, config JSON, is_public, created_at; UNIQUE(owner_id, name)) — templates de workers salvos pelo usuário. Endpoints: GET/POST /api/crm/workers/templates, DELETE /api/crm/workers/templates/:id.
045 workers_runtime_state Phase F #306 / 2026-05-31 workers_runtime_state (project_name, worker_id, status, status_started_at, current_skill, current_task_id, updated_at; PK(project_name, worker_id), idx em updated_at) — estado de runtime ao vivo por worker. Escrito por handleSetActiveRole (invariante de um único role ativo por projeto). Lido por GET /api/crm/projects/:name/workers/:id/runtime com fallback de staleness: status='working' + tmux morto + updated_at > 10 min → coagido para idle (detecção de crash).
049 embeddings Phase 71.2 #359 / 2026-06-04 embeddings (id PK AUTOINCREMENT, project, doc_type wiki/issue/skill, doc_id, chunk_ix, text, created_at) + embeddings_vec (tabela virtual vec0, embedding FLOAT[1024]). Separação: vec0 não armazena metadados eficientemente; workflows de LIST/DELETE rodam como SQL puro. A extensão sqlite-vec é carregada automaticamente em initDb antes da migration rodar.
050 drop_notebook_id Phase 71.8 #365 / 2026-06-05 ALTER TABLE projects DROP COLUMN notebook_id. Descomissiona o campo de schema do NotebookLM bridge; a busca semântica agora roda via RAG self-hosted (Cohere + sqlite-vec). Idempotente via verificação PRAGMA table_info, para que DBs novos com o schema 001 atual não falhem por coluna ausente.
051 voice_usage Phase 62.4 #373 / 2026-06-05 voice_usage_log (user_id TEXT, date TEXT YYYY-MM-DD, seconds_used INTEGER DEFAULT 0, PRIMARY KEY(user_id, date)) — contador diário de quota de transcrição de voz, espelha o shape de arc_help_usage (migration 032). Segundos aproximados (estimados pelo tamanho em bytes do upload assumindo codec de voz de ~32 kbps); comparado contra o soft cap de 60 min/dia em handleVoiceTranscribe (shared/routes/voice.ts) antes de cada transcrição, para que um usuário não monopolize o pool de workers do whisper-server compartilhado no Contabo.
052 transcripts Phase 73.1 #377 / 2026-06-05 transcripts (id PK AUTOINCREMENT, project TEXT, owner_id TEXT, source_filename TEXT, source_bytes INTEGER, source_kind TEXT CHECK(audio|video), duration_seconds INTEGER, transcript_text TEXT, frames_json TEXT, summary_json TEXT, embed_to_rag INTEGER DEFAULT 1, status TEXT DEFAULT 'queued', error TEXT, created_at TEXT DEFAULT datetime('now'), completed_at TEXT) — uma linha por arquivo de reunião enviado. frames_json = array JSON de {ts_ms, description} do Claude vision (Phase 73.4). summary_json = {tldr, key_points, action_items, decisions, topics, model, generated_at} do Claude Sonnet (Phase 73.5). Arquivo de origem deletado do disco quando status='done' (decisão D4 do CEO). Índices em (project, created_at DESC) e (owner_id, created_at DESC).
056 skill_usage #319 / 2026-06-07 skill_usage (skill_name TEXT, project_name TEXT DEFAULT '_global_', trigger_count INTEGER DEFAULT 0, install_count INTEGER DEFAULT 0, session_count INTEGER DEFAULT 0, last_triggered_at TEXT, updated_at TEXT, PRIMARY KEY(skill_name, project_name)) — contadores cumulativos por skill por projeto. trigger_count incrementa cada vez que o context router injeta a skill; install_count em save/create; session_count em aparição única por sessão. Usado por GET /api/crm/projects/:name/skills/usage.
057 notes Phase 78.1 #395 / 2026-06-07 4 tabelas: notes (id PK, project_name, title, description, created_by, created_at, updated_at) — nota como contêiner de fontes. note_sources (id PK, note_id FK→notes, source_type CHECK(video|audio|youtube|web|pdf|docx|txt|image), filename, file_path, file_size, url, title, content_text, duration_seconds, status CHECK(queued|processing|done|error), error, processed_at, created_at) — uma fonte por linha, content_text = texto extraído para RAG. note_issue_links (note_id FK, issue_id, project_name, linked_at, PK(note_id,issue_id)) — vínculo nota↔issue. note_chats (id PK, note_id FK→notes, role CHECK(user|assistant), content, created_at) — histórico de chat com a nota. Índices: notes(project_name,created_at DESC), note_sources(note_id), note_sources(status), note_issue_links(project_name,issue_id), note_chats(note_id,created_at).
053 transcript_jobs Phase 73.1 #377 / 2026-06-05 transcript_jobs (job_id TEXT PK, transcript_id INTEGER, status TEXT DEFAULT 'queued', progress_pct INTEGER DEFAULT 0, step_label TEXT, error TEXT, started_at TEXT DEFAULT datetime('now'), updated_at TEXT DEFAULT datetime('now')) — rastreador de progresso de alta rotatividade para o worker em background. Isolado de transcripts para que UPDATEs frequentes (a cada poucos segundos durante ffmpeg/whisper) não desgastem os índices da tabela principal. O endpoint SSE transmite esta tabela com poll de 1s. Lifecycle de status: queued → extracting_audio → transcribing → (vídeo: extracting_frames → frames_extracted → vision_analyzing → vision_analyzed) → summarizing → summarized → embedding → done (ou failed em qualquer etapa). Índice em (status, updated_at).

Relacionamentos (Foreign Keys)

Todos com CASCADE — excluir uma skill exclui todos os seus forks, histórico, PRs e benchmarks.

Diagrama ER (textual)

users ─1:N──> projects
            ├── chat_messages
            ├── pinned_notes
            ├── project_phases
            └── skills_project_forks ──> skills_global
                                          ├── skill_evolution_logs
                                          ├── skill_update_requests
                                          │    └── skill_benchmarks
                                          └── (tabelas independentes)
                                               ├── activity_log
                                               ├── system_configs
                                               └── marketplace_analysis_cache