aviralmansingka/c4

Visões de arquitetura C4 — níveis de contexto de sistema, containers e componentes — reunidas a partir de uma base de código com subagentes scout. Constrói uma escada de níveis (L0 contexto de sistema, L1/L1b containers, L2/L2b componentes) como caixas de container na tela do visualize, com sistemas externos fora de cada caixa e uma descrição encapsulada por nível.

O que é c4?

Este skill implementa a modelagem de arquitetura C4 dentro do agente de IA, permitindo que ele analise uma base de código e construa visualizações de arquitetura em três níveis de zoom: contexto do sistema (L0), containers (L1/L1b) e componentes (L2/L2b). Ele utiliza subagentes scout para reunir fatos sobre elementos do sistema, suas relações e fronteiras, garantindo que todos os dados sejam baseados em evidências reais do código-fonte, em vez de suposições. O skill organiza a saída em uma escada de níveis no canvas do visualize, com uma seção por nível, onde containers agrupam elementos internos e sistemas externos são mostrados fora das caixas. Para desenvolvedores, ele é útil para gerar documentação de arquitetura precisa e auditável, suportando a compreensão de sistemas complexos, especialmente em repositórios desconhecidos, onde os scouts são a única fonte de conhecimento.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/aviralmansingka/dotfiles/tree/HEAD/agents/.agents/skills/c4

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

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:

  1. 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.
  2. 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.
  3. 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

PerguntaUse
Níveis C4 de um componente em uma base de código realeste skill
Um grafo de relações a partir de fatos que você já temvisualize
Diagrama dentro da resposta do chatferramenta mermaid
ANSI / PNG estáticoskill mermaid
Página ou deck de prosa de arquiteturaskill 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>:

ScoutPromptRetorna
Escopoprompts/scope.mdinventário de elementos: id, função de uma linha, pontos de entrada, implantáveis, armazenamentos, serviços externos, atores, caminhos de arquivo
Relaçõesprompts/relationships.mdlinhas de → para : verbo + transporte, cada uma com arquivo:linha
Fronteiraprompts/boundary.mdmembros vs externos + a fronteira de módulo/implantação que justifica cada associação
Níveisprompts/levels.mdatribuiçã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 → reg veja 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:

idtesteexemplo
I1atravessa 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
I3carrega um protocolo ou qualificador que o leitor precisa: verbo RPC, transporte, porta, bucket, intervalo, timeoutregistrar + batimento cardíaco a cada 1 min, sincronização gcore a cada 1 min
I4explica um armazenamento de dados ou um ator que o leitor não pode inferir das caixasarmazenamento → Postgres, ops + mmctl → webapi
I5é uma perna de um caminho redundante ou paralelo que deve permanecer contávelmáquinas → inicialização e inicialização → máquinas; duas puxadas independentes

Classes de descarte — uma relação deixa o diagrama apenas como um destes:

idclasseo que é
C1detalhe internototalmente 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)
C2local de chamada duplicadoo mesmo salto de uma relação desenhada, diferindo apenas em tipo de trabalho ou argumento, que o rótulo desenhado já carrega
C3auxiliarum elemento cujo único trabalho é reentrar no mesmo container (um wrapper de cliente entre duas caixas desenhadas)
C4perna redundanteuma 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–C4 sã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) ou omitida — C1|C2|C3|C4 com 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ívelDesenhaComportamento da caixa
L0 contexto do sistemao componente como UM nó simples, mais todos os atores e sistemas externos ao redor, mais as setas grossas entre elessem container, sem focus, sem borda dupla — o ponto é o mundo ao redor, então o componente se parece com qualquer outra caixa
L1 containerso componente aberto um nível: suas próprias unidades de tempo de execução/implantáveis dentro de um container; os externos L0 ficam foracaixa de container nos membros
L1b visão laterala 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ópicocontainer, mesmas regras
L2 componentesas relações dentro de um container — os membros de L1, agora chamando uns aos outroscaixa de container ao redor deles
L2bsegunda fatia opcional da mesma profundidade quando um L2 não pode rotear suas relações, ou duas histórias precisam de classificações separadascontainer, 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 focus apontaria 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 --check recusa, 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 nodes daquele 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 --check nã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 `

Habilidades Relacionadas