O que c4 faz?
C4 responde "o que é este sistema, do que ele é feito e quem chama quem"
em três níveis de zoom. A superfície é o motor visualize: um canvas rataflow,
uma seção descrita por nível, caixas containers para propriedade. Leia
agents/.agents/skills/visualize/SKILL.md para o renderizador em si — este
skill cobre apenas o que C4 adiciona.
C4 adiciona três coisas que visualize não faz:
- Uma escada de níveis. Contexto L0, depois containers L1/L1b, depois componentes L2/L2b. Cada nível existe para motivar o próximo, e o nível em que um leitor está é visível pela caixa, não por uma legenda.
- Coleta de fatos. C4 deve ser completo sobre quem chama o que e por quê. Scouts leem a base de código e produzem um inventário de elementos e relações baseado em evidências antes que qualquer especificação exista.
- Contenção como uma afirmação. Uma caixa de container contém as partes próprias do componente e nada mais. Isso é uma afirmação de propriedade, então precisa de fontes e deve sobreviver a um leitor verificando a caixa contra o código.
Não invente um elemento, uma relação ou um limite. Se os scouts não puderem citá-lo, ele não será desenhado.
Roteamento
| Pergunta | Use |
|---|---|
| Níveis C4 de um componente em uma base de código real | este skill |
| Um grafo de relações a partir de fatos que você já tem | visualize |
| Diagrama dentro da resposta do chat | ferramenta mermaid |
| ANSI / PNG estático | skill mermaid |
| Página ou deck de prosa de arquitetura | skill artifact |
/c4 <componente> ou "me dê uma visão C4 de X" é a invocação. O skill
funciona em um repositório que nunca viu: os scouts são a única maneira de
aprender qualquer coisa, então nada aqui depende de conhecimento prévio do código.
Passo 0 — Fixe o componente
Declare, antes de qualquer scout executar:
componente: gerenciador de máquinas
raiz do repositório: /Users/aviral/modal
âncora: machine_manager/ (um pacote, um implantável)
- Componente é uma coisa que um leitor pode apontar no código: um serviço, um pacote, uma unidade implantável. Se a pergunta nomear um subsistema que abrange vários implantáveis, escolha o implantável e diga quais vizinhos ele puxa como externos.
- Raiz do repositório é onde os scouts começam. Confirme que o checkout existe; não assuma um caminho.
- Âncora é a peça única de evidência mais forte de que o componente é uma
coisa (manifesto de pacote, chart Helm, módulo Terraform, serviço proto,
main.go). Se você não puder nomear uma, o componente provavelmente são dois componentes — pergunte ao humano em vez de adivinhar.
Faça no máximo uma pergunta esclarecedora (a fronteira do componente é a única que vale a pena perguntar). Caso contrário, decida e registre.
Passo 1 — Lance os scouts
Quatro prompts vivem em prompts/; cada um é uma tarefa completa de subagente.
Lance-os como chamadas subagent com agent: "scout" (reconhecimento
somente leitura — scouts não devem editar o repositório), todos os quatro em
uma única rodada para que executem em paralelo, cwd: a raiz do repositório
resolvida no Passo 0, e o nome do componente substituído por <componente>:
| Scout | Prompt | Retorna |
|---|---|---|
| Escopo | prompts/scope.md | inventário de elementos: id, função de uma linha, pontos de entrada, implantáveis, armazenamentos, serviços externos, atores, caminhos de arquivo |
| Relações | prompts/relationships.md | linhas de → para : verbo + transporte, cada uma com arquivo:linha |
| Fronteira | prompts/boundary.md | membros vs externos + a fronteira de módulo/implantação que justifica cada associação |
| Níveis | prompts/levels.md | atribuição de nível por elemento e quais níveis devem ser divididos em L1/L1b (L2/L2b) |
Cada scout retorna evidência com referências arquivo:linha. Uma linha
sem uma é um boato; descarte-a ou peça novamente a esse scout por uma fonte.
Lance os scouts da raiz do repositório — execute a sessão lá (ou faça
cwd: o mesmo diretório em que a sessão já está). Um scout cujo cwd
difere do da sessão de criação pode morrer antes de seu processo iniciar, e
a falha parece "nenhum processo pi apareceu em seu painel".
Não consulte os subagentes. Os resultados chegam por conta própria; continue quando todos os quatro estiverem de volta. Se um falhar, diga qual, e ou o repita ou registre a lacuna no registro de geração — nunca preencha o buraco de memória.
Passo 2 — Reconcilie em um único livro-razão
Mescle os quatro retornos em três tabelas no registro de geração. Esta é toda a base de fatos; a especificação não pode conter nada que não esteja aqui.
elementos: id | rótulo | função | classe (ator|externo|membro|armazenamento) | evidência
relações: id | de → para | verbo + transporte | nível | evidência
fronteira: id | dentro/fora | fronteira justificada | evidência
- Conflitos são resolvidos citando a fonte mais forte (um manifesto ou chart de implantação supera um comentário; um local de chamada supera um docstring), e a reivindicação perdedora é registrada, não deletada.
- Conflitos não resolvidos e relações sem fonte sobrevivente são registrados como perguntas em aberto. Silêncio é um defeito; uma dúvida visível não é.
- Mantenha um id por elemento e reutilize-o entre os níveis, para que um
leitor rastreando
q → regveja as mesmas duas caixas em L1 e L2.
Passo 3 — O que desenhar (priorização)
Uma visão C4 é um julgamento sobre importância, não um despejo do grafo de chamadas. O livro-razão contém todas as relações que um scout encontrou; o diagrama contém aquelas que um leitor precisa, e diz em voz alta o que deixou de fora.
Uma relação ganha um lugar quando atende a pelo menos um destes:
| id | teste | exemplo |
|---|---|---|
| I1 | atravessa a fronteira do componente (membro ↔ externo, externo ↔ externo) | webapi → API Latitude, máquinas → sshx.io |
| I2 | é um caminho primário de solicitação, controle ou dados que a fonte chama (provisionamento, batimento cardíaco, auditoria, reconciliação, inicialização) | plano de controle → gRPC privado, fila River → trabalhadores |
| I3 | carrega um protocolo ou qualificador que o leitor precisa: verbo RPC, transporte, porta, bucket, intervalo, timeout | registrar + batimento cardíaco a cada 1 min, sincronização gcore a cada 1 min |
| I4 | explica um armazenamento de dados ou um ator que o leitor não pode inferir das caixas | armazenamento → Postgres, ops + mmctl → webapi |
| I5 | é uma perna de um caminho redundante ou paralelo que deve permanecer contável | máquinas → inicialização e inicialização → máquinas; duas puxadas independentes |
Classes de descarte — uma relação deixa o diagrama apenas como um destes:
| id | classe | o que é |
|---|---|---|
| C1 | detalhe interno | totalmente dentro de um container, não adiciona fronteira, protocolo ou informação de ator (trabalhador → armazenamento linha por consulta quando os escritores do armazenamento já estão desenhados) |
| C2 | local de chamada duplicado | o mesmo salto de uma relação desenhada, diferindo apenas em tipo de trabalho ou argumento, que o rótulo desenhado já carrega |
| C3 | auxiliar | um elemento cujo único trabalho é reentrar no mesmo container (um wrapper de cliente entre duas caixas desenhadas) |
| C4 | perna redundante | uma perna paralela adicional de um caminho cuja contabilidade o leitor não precisa (um segundo artefato do mesmo bucket, mesmo transporte) |
Regras que mantêm o julgamento honesto:
- Uma seta pode carregar vários locais de chamada quando o rótulo carrega
seus verbos (
criar / terminar / reparar). Isso é uma dobra C2, não uma exclusão. - Atravessar fronteira ganha um empate (I1 antes de I3 antes de I2). Um diagrama que descarta um salto externo para abrir espaço para um interno inverteu suas próprias prioridades.
- Quando em dúvida, desenhe-o e deixe a descrição do nível dizer por que
está lá. Dúvida não é um motivo para descarte;
C1–C4são. - Nunca descarte silenciosamente. Cada linha do livro-razão termina como
uma de: desenhada,
dobrada em <relação desenhada>(com a dobra justificada como C2) ouomitida — C1|C2|C3|C4com uma linha do porquê. O mapa de cobertura (Passo 7) declara as quatro contagens: desenhada, dobrada, omitida por classe, não contabilizada. - Redução de escopo é uma coisa diferente e precisa das palavras do
solicitante. "Apenas a visão do plano de controle" ou "20 relações no
máximo" é uma solicitação de escopo: registre-a como base (
base: escopo do solicitante, N relações) junto com o livro-razão contado. A priorização é o padrão; um subconjunto é uma decisão que o solicitante toma, não uma que o gerador infere.
Passo 4 — A escada de níveis
A forma alvo são cinco seções, de cima para baixo:
L0 · Contexto do sistema um nó para o componente + seus vizinhos
L1 · O componente o componente aberto uma vez, container em suas partes
L1b · <segundo lado> a mesma profundidade, o outro lado do sistema
L2 · Dentro — <fatia A> relações entre membros, container neles
L2b · Dentro — <fatia B> a mesma profundidade, a outra fatia
Essa é a escada: um canvas de cinco seções é o entregável. Mais seções é um defeito, a menos que a disciplina de divisão abaixo seja satisfeita, com números.
Desenhe cada nível somente depois que o anterior existir e dê a cada um sua descrição antes de prosseguir.
| Nível | Desenha | Comportamento da caixa |
|---|---|---|
| L0 contexto do sistema | o componente como UM nó simples, mais todos os atores e sistemas externos ao redor, mais as setas grossas entre eles | sem container, sem focus, sem borda dupla — o ponto é o mundo ao redor, então o componente se parece com qualquer outra caixa |
| L1 containers | o componente aberto um nível: suas próprias unidades de tempo de execução/implantáveis dentro de um container; os externos L0 ficam fora | caixa de container nos membros |
| L1b visão lateral | a segunda fatia opcional quando um L1 genuinamente não pode segurar o fluxo (o lado da frota, o plano de dados, o caminho em lote) — medido, nunca um novo tópico | container, mesmas regras |
| L2 componentes | as relações dentro de um container — os membros de L1, agora chamando uns aos outros | caixa de container ao redor deles |
| L2b | segunda fatia opcional da mesma profundidade quando um L2 não pode rotear suas relações, ou duas histórias precisam de classificações separadas | container, mesmas regras |
Regras de nível que não são negociáveis:
- L0 é o componente como UM nó simples. Uma caixa para o componente
inteiro, sua borda normal, mais os atores e sistemas externos ao redor.
Não suas partes, não seus ouvintes, não suas tabelas — esses são o próximo
nível. Se L0 tiver mais de um nó que pertence dentro do componente, não é
uma visão de contexto. Contenção em L0 reivindicaria internos que o leitor
ainda não viu, e
focusapontaria para um nível que ainda não existe. - L1/L1b abrem a caixa de L0. Cada membro fica dentro do container; cada elemento que era um externo em L0 fica fora dele. Um membro que não pode ser desenhado fora da caixa delimitadora do container ganha sua própria entrada de container (veja Contiguidade) — nunca uma ajustada.
- L2/L2b ficam dentro de uma caixa. Ambos os endpoints de cada relação L2 são membros do container. Se uma relação sair da caixa, ela pertence a L1/L1b.
- Um nível se descreve. Título é curto (
L0 · Contexto do sistema); a descrição declara o que está dentro da caixa, o que está fora dela e como se relaciona com seu nível vizinho. Mire em 2–3 linhas encapsuladas. - Movimento não é uma propriedade C4. Níveis 0–1b são estáticos
(
pontos: 0). Um nível interno cujas relações são fluxos ao vivo pode carregar exatamente um ponto por rota (fluxo: true, pontos: 1); registre a escolha. Nunca anime uma seta externa para decorar a escada.
Disciplina de divisão — um nível é uma PROFUNDIDADE, não um tópico
Um nível C4 responde a uma pergunta em um zoom. Dois níveis da mesma profundidade existem apenas porque uma seção não poderia segurar o conteúdo; um novo tópico não é uma razão para adicionar uma seção.
- Nunca adicione L1c, L1d, L1e ou um quinto lado. A escada tem três profundidades (contexto, containers, componentes) e no máximo duas fatias em cada profundidade abaixo da primeira. Um canvas que cresce uma fatia por subsistema ou por menção é uma lista de tópicos usando títulos de nível.
- Uma segunda fatia deve nomear sua medição: as rotas que
--checkrecusa, o corredor compartilhado que relata, ou as classificações que colidem (dois fins externos sem externo em comum). "Parecia apertado" não é uma. - Fatias da mesma profundidade compartilham a caixa, os rótulos e os ids. L1 e L1b desenham o mesmo componente com os mesmos ids de membro; L2 e L2b desenham os mesmos membros. Apenas o conjunto de arestas e as classificações diferem, e cada descrição diz qual fatia é.
- Uma fatia é nomeada
b(l1b,l2b); nunca invente uma profundidade. - Profundidade além de L2 não é desenhada. Se os internos do componente ainda se decompõem, diga no registro e pare: classes e funções de código não são um nível C4 que um leitor pediu. A escada termina onde termina a pergunta.
- Tudo o que o livro-razão contém e a escada não desenha é uma decisão de escopo, escrita (Passo 6), não uma exclusão silenciosa e não uma nova seção.
Passo 5 — Mecânica da especificação
Um canvas, uma direção de leitura, uma seção por nível:
{
"direction": "lr",
"theme": "gruvbox",
"diagrams": [
{
"id": "l0",
"title": "L0 · Contexto do sistema",
"description": "Contexto do sistema: o gerenciador é uma caixa. ...",
"nodes": [ ... ],
"edges": [ ... ]
},
{
"id": "l1",
"title": "L1 · O gerenciador",
"description": "Caixa de L0 aberta um nível: ...",
"containers": [ { "label": "Gerenciador de máquinas", "members": ["api", "q", "db"] } ],
"layers": [ ["ator"], ["api", "q"], ["db"], ["prov"] ],
"nodes": [ ... ],
"edges": [ ... ]
}
]
}
direction: "lr"uma vez, no canvas. Nunca uma substituição por diagrama: um leitor que reorienta a cada seção perdeu a escada.- Cinco seções.
l0,l1,l1b,l2,l2b, nessa ordem. Uma sexta seção precisa da medição do Passo 3 registrada; um canvas de dez ou vinte seções é uma lista de tópicos, não uma escada — dobre-a de volta para três profundidades e duas fatias cada. containersé uma lista por diagrama de{label, members}. A caixa é um retângulo real de borda dupla crescido ao redor dos membros, com o rótulo inserido em sua borda superior. Várias caixas por diagrama são permitidas.- Ids de membros devem existir nos
nodesdaquele diagrama. Um id de membro que não nomeia nenhum nó é silenciosamente descartado pelo parser — a caixa então desenha ao redor de menos nós do que você declarou e--checknão avisa. Conte a caixa desenhada contra os membros que você declarou. - Regra de contiguidade. Um container é um retângulo que envolve seus membros visualmente. Se um membro estiver visualmente separado (por exemplo, devido a um layout de camadas), o container ainda deve parecer unificado. Membros não contíguos (separados por um externo) não podem compartilhar a mesma declaração de container.
- Layout de camadas. Use a chave `