stack-RFC-021 — Stack Graph: grafo de conexões gerado, glossário e JIT component-aware
Resolve a causa-raiz da análise superficial das IAs sobre a Koder Stack: não falta "um índice", falta um GRAFO completo e sempre-atual de componentes E das conexões entre eles (hoje deferido no programa #151, travado em dado esparso), e falta um glossário de termos/siglas. Define: (1) o Stack Graph é GERADO por binário (estendendo o `koder-stack-graph` existente) sobre uma SSOT de arestas DECLARADAS em `koder.toml` (sempre-atual por construção, zero drift — Code First); (2) saída dupla de um gerador único — camada de dependências no Graph Explorer (#151, humano) + `stack-graph.json` + digest por-componente (consumo da IA); (3) glossário gerado (termos/siglas × componentes × specs); (4) enforcement por extensão do JIT existente (RFC-014 fase 3) para ser COMPONENT-aware + Regra 11 apontando pro Stack Graph — SEM gate-root novo (tecnicamente impossível em hooks Claude Code e contra o design da RFC-014). Expande o #151 e a RFC-014; não cria mecanismo paralelo.
Status: Accepted — ratificado pelo owner (Rodrigo) em 2026-06-25. Origem: deliberação
/k-archde 2026-06-25 (transcrito nesta sessão). Veredictos fundamentados emcode-first.kmd(agregação determinística → binário; anti-padrão default-markdown),architecture-quality.kmd(D6 SSOT-única, D9 reversibilidade, D10 reuso, D11 debuggability) +stack-principles.kmd(#1 Meta First, #2 Quality>Speed, #3 Code First) +reuse-first.kmd.Naming: o conceito foi inicialmente proposto como "Atlas" e renomeado para Stack Graph — (a) por colisão com o componente existente
services/foundation/atlas(Koder Atlas, o produto de mapas/geo), cuja colisão é máxima porque "atlas" significa literalmente "livro de mapas"; e (b) por reuse-first: "Stack Graph" é o nome incumbente (koder-stack-graph+stack-graph.json+ cron anti-drift já existem), então esta RFC é a evolução desse gerador, não um artefato novo. (Decisão/k-go/Regra 13, 2026-06-25.)
1. Problema
À medida que a Koder Stack cresce (5 domínios, ~291 folhas, 276 sectors, 100+ componentes nomeados), as IAs desconsideram componentes por analisarem a Stack de forma superficial. A causa-raiz não é "falta um índice" — vários existem. É mais específica:
- O dado de conexão é esparso.
koder.toml/koder.manifest.jsontêm camposconsumesconsumed_bydeps, mas pouco preenchidos → nenhum grafo de conexões pode ser gerado. - Os catálogos são fragmentados e parte manuais → drift (157 deep-dives
manuais; registry de nomes ~70%, CI cronicamente RED).
- As conexões são prosa. "Primary couplings" nos deep-dives é tabela
markdown legível por humano, não parseável — a IA lê texto solto, não consulta um grafo.
O que não existe: um grafo completo e sempre-atual de componentes E conexões (deferido no programa #151, travado em popular koder.toml); e um glossário de termossiglas (KMDKVGKDBKoda confundem).
O que já existe e é reusado por esta RFC: Graph Explorer live em meta.koder.dev (containment-only — árvore de filesystem, zero arestas); binários geradores em products/dev/koder-tools (koder-stack-graph, koder-module-catalog, koder-stackdoc); RFC-014 (spec-adherence harness) com o JIT jit-spec-inject.sh (fase 3, DONE); e a Regra 11 do /k-go ("consultar a Stack antes de decidir").
2. Non-goals
- Não cria um markdown/doc novo mantido à mão (seria o 6º artefato
fragmentado e o 158º a driftar — anti-padrão "default to markdown").
- Não cria um "gate-root de leitura obrigatória" (ver §5 — tecnicamente
impossível como bloqueio em hooks Claude Code, e contra o design da RFC-014).
- Não declara à mão as arestas que o código já prova (import/path): essas são
mineradas (
provenance=mined). Só os acoplamentos NÃO-mineáveis (api/ storeipcdeploy/embed) são declarados (provenance=declared) — nunca disfarçados de provados. - Não constrói índice remissivo de termos agora (subsumido por
grafo+glossário consultáveis; regra dos 3).
- Não cria um nome-marca/produto novo — Stack Graph é artefato interno de
infraestrutura (alimenta o site e o JIT), nomeado descritivamente (reuse-first).
3. Decisão 1 (keystone) — Stack Graph GERADO; arestas HÍBRIDAS com proveniência
Veredicto /k-arch (emendado 2026-06-25): gerado, com arestas híbridas minerado+declarado, cada uma marcada com proveniência.
Emenda: a forma original ("declarar todas as arestas em koder.toml") foi corrigida ao descobrir que o
koder-stack-graphjá minera 165 arestas reais do código, e que seu autor rejeitou — com razão D1 documentada — arestas à mão ("a hand-curated edge would let the graph lie"). A reconciliação honra os dois.
- O Stack Graph (artefato) é GERADO por binário (estendendo o
koder-stack-graphexistente). Agregação input-fixo→output-fixo =code-firstmanda virar binário. Sempre-atual, zero drift — é projeção, não cópia à mão. - Cada aresta carrega
provenance:mined— provada do código (Go import, Dart·Rust path). SSOT = o código;nunca à mão (uma aresta-import curada mentiria — D1).
declared— acoplamento NÃO-mineável (apistoreipcdeployembed),declarado em
koder.toml [graph].consumes. SSOT = o koder.toml. Marcada como declarada → nunca se disfarça de provada.
- Honestidade do grafo = transparência de proveniência, não pureza de fonte.
Resolve o D1 do autor e torna visíveis as arestas de fio/deploy que a mineração não enxerga (justo as mais relevantes pro plano operador).
Rejeitadas: minerado-só (honesto mas incompleto — mente por omissão das arestas de fio); declarado-só (joga fora as 165 mineradas e reintroduz o risco D1 de driftmentira). Deliberação: `k-arch` 2026-06-25.
4. Decisão 2 — SSOT por proveniência (código × koder.toml)
Veredicto: cada proveniência tem sua SSOT única (D6), sem sobreposição.
- Mineradas → SSOT = o código (
extractEdges: imports Go + path-depsDart/Rust). Não se declaram à mão.
- Declaradas (só não-mineáveis) → SSOT =
koder.toml [graph].consumesdocomponente de origem. É onde
[self_hosted]/deps já vivem; população é o gate do #151. Lida no sector-root e nos sub-units (app/,platform/, …).
consumed_by é derivado (inverso das arestas) — nunca declarado. Rejeitado um "arquivo central de grafo" (2ª SSOT → drift, D6).
Schema implementado (flat strings "kind:target" — sem lib TOML, parse-simples):
[graph]
# Só arestas NÃO-mineáveis. import/path vêm do código, NÃO se declaram aqui.
consumes = [
"api:services/ai/gateway", # chamada HTTP (sem import)
"store:infra/data/kdb", # acesso a store over-the-wire
]kind ∈ {api, store, ipc, deploy, embed} (não-mineáveis) + import (reservado às mineradas; enum versionado). O gerador funde mineradas ∪ declaradas, dedupando por (source, target, kind), com mined vencendo em colisão exata.
5. Decisão 3 — Enforcement: JIT component-aware, sem gate-root
Veredicto: opção a — estender o JIT existente (RFC-014 fase 3) para ser COMPONENT-aware + Regra 11 apontando pro Stack Graph.
- O hook
jit-spec-inject.sh(PostToolUse, hoje path-aware) ganha um matcherpath→componente e injeta a vizinhança do grafo (consumesconsumed_by integrations + digest do componente) quando a IA toca/menciona um componente — a fatia certa no momento da decisão.
- A Regra 11 do
/k-gopassa a apontar explicitamente: "antes de decidirsobre o componente X, consultar o Stack Graph (
stack-graph.json/ digest de X)". - O dente permanece no gate pré-commit (RFC-014) — nada de bloqueio pré-ação.
Rejeitado (b) gate-root de leitura obrigatória: hooks Claude Code não bloqueiam ação (só injetam; UserPromptSubmit/PostToolUse só dão additionalContext); front-load do grafo inteiro (291+ folhas) é caro em token e contra o enxugamento em curso do CLAUDE.md; FP massivo; e fere o design deliberado da RFC-014 (guia injetado × dente no gate). Rejeitado (c) só-Regra-11: é o status quo (a regra existe sem grafo pra consultar — não muda nada).
6. Decisão 4 — Escopo: grafo + glossário gerado
Veredicto: opção b. O gerador emite grafo + glossário da mesma SSOT:
- Grafo — ataca "ignorar componentes/conexões".
- Glossário — termossiglas × componentes × specs (KMDKVGKDBKoda/…),
derivado de
component-names.md+vocabulary.md+ frontmatter das specs; ataca "ler sigla errado". Gap real e barato.
Índice remissivo de termos: deferido (YAGNI — subsumido por grafo+glossário consultáveis; construir na 3ª necessidade real).
7. Decisão 5 — Consumo: saída dupla de um gerador único
Veredicto: um gerador, uma SSOT, duas saídas:
- Visual (humano): estende o Graph Explorer (#151) com a *amada de
dependências*— fecha o item deferido do #151 (hoje containment-only).
- Máquina (IA): emite
stack-graph.json(nodes+edges completos) + umdigest terso por-componente (
<component>/.koder-digest.mdgerado, ou um índice central) — a forma canônica de consulta da IA, e a fonte de onde o JIT (§5) injeta fatias. A IA consome o JSON/digest, não o visual.
8. Plano (P0–P3)
| Fase | Entrega | Gate |
|---|---|---|
| P0 | Popular [graph].consumesintegrations nos koder.toml (começar pelos componentes do objetivo Kodeoperador da RFC-020; depois sweep dos 276 sectors). Pode ser dirigido por IA com validação. |
— |
| P1 | Estender koder-stack-graph (binário existente em koder-tools): de containment-only para agregar koder.toml+manifests → stack-graph.json com arestas (consumed_by derivado) + digests. Determinístico, idempotente. |
P0 (parcial) |
| P2 | Estender Graph Explorer (#151) com a camada de dependências + servir stack-graph.json. |
P1 |
| P3 | Glossário gerado (koder-glossary ou subcomando) + JIT component-aware (jit-spec-inject.sh) + Regra 11 apontando pro Stack Graph. |
P1 |
P0 e P1 podem rodar incrementalmente: o grafo é útil já com cobertura parcial, e a cobertura de
koder.tomlvira uma métrica auditável (advisory → warn).
9. Conformância e auditoria
- Completude como métrica:
koder-stack-graph --checkreporta % decomponentes com
[graph]populado (advisory primeiro, perpolicies/gates.kmdda RFC-014; promove a warn/block conforme cobertura sobe). - Consistência (D6):
consumed_byderivado garante que não há arestaunilateral; o gerador falha se um
targetreferencia componente inexistente nocomponent-names.md(liga as duas SSOTs).
10. Reversibilidade (D9)
- O Stack Graph é derivado: jogar fora o
stack-graph.jsone regerar é grátis. - O dado declarado (
[graph]emkoder.toml) é aditivo e versionado (kindenum). Porta de mão dupla.
11. Relação com programas/RFCs existentes
- Programa #151 (Graph Explorer) — esta RFC destrava e expande o item
deferido (dependency-edges) dele; não cria visualização paralela.
- RFC-014 (spec-adherence harness) — esta RFC estende a fase 3 (JIT) para
component-aware; não cria mecanismo de injeção paralelo (respeita a fase 5, subsunção).
- RFC-015 (Kanon) — o
applies_whendo Kanon pode futuramente expressar omatcher component-aware do JIT (hoje glob simples).
component-names.md— otargetdas arestas referencia os slugs desteregistry (liga naming ↔ grafo; pode ajudar a destravar o audit RED por uso).
12. Ratificação
Ratificado pelo owner em 2026-06-25. Sub-decisões a confirmar: keystone (Stack Graph gerado / arestas declaradas §3), SSOT em koder.toml (§4), enforcement JIT-sem-gate (§5), escopo grafo+glossário (§6). Ao ratificar, mudar status: draft → accepted e abrir os tickets de P0–P3 no backlog de products/dev/koder-tools (gerador) + meta/context (JIT/Regra 11), cruzando com o programa #151.