Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Synapse: Schema de Dados e Índice

Documento de referência para a camada de dados: o frontmatter de cada tipo de entidade e como o índice SQLite mapeia esses arquivos.

Princípios

  1. O markdown é a fonte da verdade. O SQLite é um índice descartável, reconstruível com syn reindex. Nada vive só no banco.
  2. Frontmatter para o estruturado, corpo para o livre. Campos consultáveis (status, tags, vínculos) vão no YAML; contexto, notas e histórico vão no corpo markdown.
  3. IDs estáveis e legíveis. O nome do arquivo (slug) é o ID. maria-silva.md é referenciado como maria-silva. Renomear é uma operação explícita (syn person rename) que atualiza os vínculos.
  4. Vínculos por referência, nunca por cópia. Uma pessoa que atua em três projetos existe uma vez; os projetos a referenciam pelo slug.

Convenções de campo comuns

Todo arquivo carrega um núcleo comum no frontmatter:

CampoTipoObrigatórioDescrição
typeenumsimproject, person, entity, api, task
idstring (slug)simIgual ao nome do arquivo sem extensão
titlestringsimNome legível (“Maria Silva”, “API Pagamentos”)
tagslistanãoTags livres, normalizadas em minúsculas
createddata ISO 8601simPreenchido na criação
updateddata ISO 8601simAtualizado a cada syn ... edit

Datas sempre em ISO 8601 (2026-05-23). Slugs sempre minúsculos, sem acento, hífen como separador.

Tipo: project

Arquivo: projects/<slug>/_meta.md

Um projeto é uma pasta, não um arquivo solto, porque agrega sub-documentos (stakeholders, apis, demandas específicas daquele projeto). O _meta.md é o manifesto.

---
type: project
id: projeto-alfa
title: Projeto Alfa
status: ativo            # ativo | pausado | concluido | arquivado
description: Plataforma de pagamentos B2B
owner: maria-silva       # referência a um person.id
priority: alta           # baixa | media | alta
tags: [pagamentos, b2b, prioridade]
links:
  repo: https://github.com/org/alfa
  docs: https://wiki.empresa.com/alfa
created: 2026-01-10
updated: 2026-05-20
---

## Contexto

Texto livre sobre o projeto, decisões, histórico.

## Pontos de atenção

- Migração do webhook v1 → v2 pendente

Campos específicos: status, owner, priority, links.

Tipo: person

Arquivo: people/<slug>.md

Pessoas são globais (não vivem dentro de um projeto) justamente para permitir o vínculo N:N. O campo roles lista em quais projetos a pessoa atua e com que papel: esse é o coração do modelo de grafo.

---
type: person
id: maria-silva
title: Maria Silva
email: maria@empresa.com
org: acme-corp           # referência opcional a um entity.id
roles:                   # vínculos N:N com projetos
  - project: projeto-alfa
    role: ponto-focal
  - project: projeto-beta
    role: aprovadora
tags: [pagamentos, lideranca]
created: 2026-01-12
updated: 2026-05-18
---

## Notas

- Prefere contato por e-mail, responde rápido
- Fuso: GMT-3

Campos específicos: email, org, roles (lista de {project, role}).

O comando syn person link "Maria Silva" --project gama --role revisora adiciona uma entrada em roles sem tocar no resto.

Tipo: entity

Arquivo: entities/<slug>.md

Organizações, times, fornecedores, clientes: qualquer ator que não é uma pessoa individual.

---
type: entity
id: acme-corp
title: Acme Corp
kind: fornecedor         # fornecedor | cliente | time-interno | parceiro
contact: contato@acme.com
projects: [projeto-alfa] # projetos onde aparece
tags: [pagamentos]
created: 2026-02-01
updated: 2026-04-30
---

## Notas

- SLA de resposta: 48h

Campos específicos: kind, contact, projects.

Tipo: api

Arquivo: apis/<slug>.md

APIs podem ser compartilhadas entre projetos, por isso ficam no diretório global e referenciam projetos por slug.

---
type: api
id: api-pagamentos
title: API Pagamentos
base_url: https://api.pagamentos.com/v2
auth: oauth2             # none | apikey | basic | oauth2 | jwt | mtls
status: producao         # producao | homologacao | descontinuada
owner: maria-silva
projects: [projeto-alfa, projeto-beta]
docs_url: https://docs.pagamentos.com
tags: [pagamentos, externa]
created: 2026-01-15
updated: 2026-05-10
---

## Endpoints relevantes

- `POST /charges`: cria cobrança
- `POST /webhooks`: registra webhook

## Observações

- Rate limit: 100 req/s
- Migração v1 → v2 em andamento

Campos específicos: base_url, auth, status, owner, projects, docs_url.

Tipo: task (demanda)

Arquivo: projects/<projeto>/tasks/<id>.md ou tasks/<id>.md (global)

Demandas são curtas e numerosas, então o id é numérico/sequencial gerado pelo CLI, e o title carrega a descrição.

---
type: task
id: alfa-042
title: Migrar webhook para v2
project: projeto-alfa
status: aberta           # aberta | em-andamento | bloqueada | concluida
owner: maria-silva
api: api-pagamentos      # referência opcional
due: 2026-06-15
tags: [migracao, prioridade]
created: 2026-05-20
updated: 2026-05-22
---

## Detalhes

Contexto da demanda, links, critérios de aceite.

Campos específicos: project, status, owner, api, due.

O índice SQLite

O índice existe para uma coisa: busca rápida e filtragem. Ele nunca é a fonte da verdade. Estrutura mínima (fase 1, sem vetores):

Tabela entities (espelho do frontmatter)

CREATE TABLE entities (
    id          TEXT PRIMARY KEY,   -- slug
    type        TEXT NOT NULL,      -- project|person|entity|api|task
    title       TEXT NOT NULL,
    status      TEXT,
    owner       TEXT,               -- person.id
    path        TEXT NOT NULL,      -- caminho do .md no disco
    frontmatter TEXT NOT NULL,      -- JSON do YAML completo (fonte p/ filtros)
    body        TEXT NOT NULL,      -- corpo markdown
    created     TEXT,
    updated     TEXT
);

CREATE INDEX idx_entities_type   ON entities(type);
CREATE INDEX idx_entities_status ON entities(status);
CREATE INDEX idx_entities_owner  ON entities(owner);

O frontmatter guardado como JSON permite filtros arbitrários sem precisar de uma coluna por campo: você consulta com json_extract().

Tabela tags (relação N:N)

CREATE TABLE tags (
    entity_id TEXT NOT NULL REFERENCES entities(id),
    tag       TEXT NOT NULL,
    PRIMARY KEY (entity_id, tag)
);

CREATE INDEX idx_tags_tag ON tags(tag);

Materializa todos os vínculos entre entidades, vindos de roles, projects, owner, api, etc. É isto que torna possível perguntar “quem são os pontos focais do projeto alfa” ou “quais tasks dependem da API de pagamentos”.

CREATE TABLE links (
    from_id   TEXT NOT NULL,   -- ex: maria-silva
    to_id     TEXT NOT NULL,   -- ex: projeto-alfa
    rel       TEXT NOT NULL,   -- ex: ponto-focal, owner, uses-api
    PRIMARY KEY (from_id, to_id, rel)
);

CREATE INDEX idx_links_to  ON links(to_id, rel);
CREATE INDEX idx_links_from ON links(from_id, rel);

Tabela virtual FTS5 (busca textual)

CREATE VIRTUAL TABLE entities_fts USING fts5(
    id UNINDEXED,
    title,
    body,
    tags,
    content=''            -- índice externo, não duplica conteúdo
);

Consulta de busca típica que o syn search executa:

SELECT e.id, e.type, e.title
FROM entities_fts f
JOIN entities e ON e.id = f.id
WHERE entities_fts MATCH 'oauth'
  AND e.type = 'api'              -- filtro --type
  AND e.id IN (SELECT entity_id FROM tags WHERE tag = 'prioridade')
ORDER BY rank;

Fase 2: busca semântica (sqlite-vec)

Quando o FTS não bastar, adiciona-se uma tabela virtual de vetores no mesmo banco:

CREATE VIRTUAL TABLE entities_vec USING vec0(
    id TEXT PRIMARY KEY,
    embedding FLOAT[384]   -- dimensão do modelo de embedding escolhido
);

A busca híbrida combina o rank do FTS5 com a distância vetorial, fundindo os dois sinais (reciprocal rank fusion é o método simples e robusto). Mas isso só entra quando a busca textual demonstrar ser insuficiente, não antes.

Fluxo de reindexação

syn reindex faz, de forma idempotente:

  1. Apaga e recria as tabelas (ou faz upsert por updated).
  2. Varre projects/, people/, entities/, apis/, tasks/.
  3. Para cada .md: faz parse do frontmatter (YAML → JSON), extrai corpo.
  4. Popula entities, tags, links, entities_fts.
  5. Reporta contagens e inconsistências (ex: owner apontando para pessoa inexistente): esse relatório é o que o syn doctor consome.

A reindexação completa de alguns milhares de arquivos leva milissegundos, então ela pode rodar automaticamente após qualquer comando de escrita (add, rm, edit) ou ser disparada por um watcher de filesystem opcional.