Metadata Schema — ContextStore (v1)
Spec de produto pré-implementação · mindflow v0.3 · 2026-06-21
Contexto
O ContextStore usa SQLite com a tabela events:
CREATE TABLE events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
source TEXT NOT NULL, -- "mindflow", "mural", "web", "telegram"
kind TEXT NOT NULL, -- "nota", "ideia", "gratidao", "briefing", "setup" ...
payload TEXT NOT NULL, -- JSON livre — aqui mora o problema
created_at TEXT NOT NULL
);
Antes da v0.3, o payload era livre: cada chamador salvava o que queria. Resultado:
| Origem | Payload real salvo |
|---|---|
POST /api/event (web/telegram) |
{"text": "..."} |
briefing_routine |
{"text": "...", "actions": [...]} |
| Nenhum evento de setup existia ainda | — |
Isso impede filtros consistentes, renderização uniforme no histórico e rastreamento de canal.
Schema unificado (v1)
Todo evento novo (a partir da v0.3) deve ter o payload no seguinte formato:
{
# OBRIGATÓRIO
"text": str,
# METADADOS SEMÂNTICOS
"type": "capture" | "routine_result" | "system",
"routine_id": str | None, # "briefing", "brisa", "setup" ou None para captures
"source": "web" | "telegram",
# IMPORTÂNCIA E TAGS
"importance": 1 | 2 | 3, # 1=normal, 2=marcante, 3=épico
"tags": list[str], # ex: ["ideia", "decisão", "gratidão"]
# VERSIONAMENTO
"version": "1"
}
Campos explicados
text — conteúdo principal. Sempre presente. É o campo lido por briefing_routine, build_timeline e api_chat para montar contexto.
type — categoria semântica do evento:
- "capture" — entrada direta do usuário (nota, ideia, gratidão, brisei)
- "routine_result" — output de uma rotina (briefing, brisa)
- "system" — evento de sistema (setup completo, reindex, etc.)
routine_id — qual rotina gerou o evento. None para captures manuais. Permite filtrar histórico por rotina (api_routine_history já faz isso via kind, mas routine_id no payload é mais explícito e tolerante a colisões de nomenclatura).
source — canal de entrada do usuário. Duplica a coluna SQL para que leitores do payload não precisem do dict externo.
importance — escala 1-3, substituindo a inferência atual do timeline/builder.py via _KIND_IMPORTANCE. Com o campo explícito, o frontend pode filtrar sem lógica derivada.
tags — lista aberta de tags semânticas. Pode ser vazia ([]). Não há taxonomia fechada na v0.3 — tags são livres e geradas pelo chamador.
version — versão do schema para facilitar migrations futuras. Sempre "1" na v0.3.
Defaults por chamador
| Chamador | type |
routine_id |
source |
importance |
tags |
|---|---|---|---|---|---|
POST /api/event kind=nota |
"capture" |
None |
"web" ou "telegram" |
1 |
[] |
POST /api/event kind=ideia |
"capture" |
None |
"web" ou "telegram" |
2 |
["ideia"] |
POST /api/event kind=gratidao |
"capture" |
None |
"web" ou "telegram" |
3 |
["gratidão"] |
POST /api/event kind=brisei |
"capture" |
None |
"web" ou "telegram" |
1 |
[] |
briefing_routine |
"routine_result" |
"briefing" |
"web" |
1 |
[] |
| rotina "brisa" (v0.3) | "routine_result" |
"brisa" |
"web" |
1 |
[] |
| setup completo | "system" |
"setup" |
"web" |
2 |
["setup", "perfil"] |
A importância por kind de capture replica e torna explícita a lógica atual do _KIND_IMPORTANCE em timeline/builder.py.
Payload completo do evento de setup
store.log_event("web", "setup", {
"text": "Perfil configurado",
"type": "system",
"routine_id": "setup",
"source": "web",
"importance": 2,
"tags": ["setup", "perfil"],
"version": "1",
"profile": {
"name": str,
"areas": list[str], # ["saúde", "trabalho", "relacionamentos", ...]
"tone": "direto" | "amigável",
"context": str # texto livre — contexto inicial do usuário
}
})
O campo profile é específico de eventos kind="setup" — não é parte do schema base.
Compatibilidade retroativa
Eventos existentes no banco (antes da v0.3) não são migrados. A lógica de leitura deve tolerar payloads sem os novos campos:
# Padrão de leitura retrocompatível
payload = event.get("payload", {})
text = payload.get("text", "")
event_type = payload.get("type", "capture")
source = payload.get("source") or event.get("source", "web")
importance = payload.get(
"importance",
_KIND_IMPORTANCE.get(event.get("kind", "nota"), 1) # fallback para lógica antiga
)
tags = payload.get("tags", [])
version = payload.get("version", "0") # "0" = formato pré-v0.3
Regra: se version está ausente ou é "0", o código trata o evento como legacy e usa os fallbacks acima. Nenhuma query SQL nova é necessária — a retrocompatibilidade é tratada em Python na camada de leitura.
Arquivos afetados na implementação
| Arquivo | O que muda |
|---|---|
mindflow/api/server.py — api_event() |
Enriquecer payload com campos do schema v1 |
mindflow/routines/briefing.py — briefing_routine() |
Adicionar type, routine_id, source, importance, version ao log_event |
mindflow/timeline/builder.py — build_timeline() |
Leitura retrocompatível; remover dependência de _KIND_IMPORTANCE para eventos v1 |
mindflow/api/server.py — novo endpoint |
GET /api/context/setup-status |
mindflow/routines/ — nova rotina setup |
Salvar evento kind="setup" com payload completo |
O que NÃO muda
- Schema SQL da tabela
events— nenhuma coluna nova - A coluna
kindSQL — permanece como identificador primário de tipo para queries ContextStore.log_event()eContextStore.recent_events()— assinatura inalterada- Eventos existentes no
/data/brisa.db— lidos com fallback, não migrados