6. Adoção e CI¶
English | Português (Brasil)
Tradução do original em inglês; quando os dois divergirem, vale o inglês.
← Ferramentas · Índice · Conformidade e leitores →
Este capítulo é o mapa. O guia completo, com cada caso de borda, é o ADOPTING.pt-BR.md.
O caminho em quatro passos¶
flowchart LR
A["1. vendor update<br/>traz a tag"] --> B["2. git add vendor/<br/>+ gate --update"]
B --> C["3. commit<br/>vendor + base"]
C --> D["4. CI: check + gate<br/>em todo PR"]
Rode isto na raiz do seu repositório, com Python 3.12 ou mais (o piso desde a 4.35.0; o CI roda 3.12, 3.14 e 3.15, e o extra opcional
vector vai até o 3.14 enquanto o onnxruntime não sai para o 3.15):
# 1. trazer a release, por tag, do repositório público
git clone https://github.com/marciocar/kg-ssot ../kg-ssot
python3 -I -B ../kg-ssot/tools/kg_vendor.py update --tag kg-ssot-v4.45.0 --dest vendor/kg-ssot
# 2. as dependências num venv, e a linha de base (o gate só mede arquivo rastreado: git add antes)
python3 -m venv .venv && .venv/bin/pip install --require-hashes -r vendor/kg-ssot/tools/requirements.txt
echo .venv/ >> .gitignore
git add vendor/kg-ssot .gitignore docs/ # e os seus .kg.yaml, onde estiverem
.venv/bin/python3 -I -B vendor/kg-ssot/tools/kg_gate.py --update
# 3. commitar o vendor, com o carimbo, e a base
git add .kg-ssot/gate && git commit -m "adota o contrato KG-SSOT"
Os grafos que já reprovam viram dívida herdada, nomeada por caminho. O passo 4 é o CI, em todo PR. O workflow
completo do GitHub Actions está na seção 3 do ADOPTING.pt-BR.md, o passo de CI. O passo
em si é:
- name: Contrato KG-SSOT (vendor íntegro + gate por grafo)
run: |
pip install --require-hashes -r vendor/kg-ssot/tools/requirements.txt
python3 -I -B vendor/kg-ssot/tools/kg_vendor.py check --dest vendor/kg-ssot
python3 -I -B vendor/kg-ssot/tools/kg_gate.py
O que o gate garante¶
- Nenhum grafo novo entra reprovando no MUST.
- Nenhum grafo herdado ganha um código MUST que não tinha.
- A dívida SHOULD, por código, não sobe.
- Uma troca de contrato aparece no PR: a base guarda o nome e o sha256 dos schemas.
Consertar um grafo não compensa quebrar outro: o gate compara o MUST grafo a grafo.
O gate mede só arquivos .kg.yaml. Um .kg.json no repositório fica fora dele. Se você quer o CI cobrando esses
arquivos, rode kg_validate.py neles num passo próprio.
A língua da saída no CI¶
Desde a 4.18.0, as ferramentas imprimem a saída humana em inglês por padrão. Elas a imprimem em português do Brasil
quando há KG_SSOT_LANG=pt-BR ou o locale é pt_BR. As ferramentas leem a língua na ordem do gettext:
KG_SSOT_LANG, LANGUAGE, LC_ALL, LC_MESSAGES, LANG. Decide a primeira variável definida; uma vazia é pulada.
C, POSIX e uma língua sem catálogo são o inglês.
Decida pelo código de retorno, nunca pelo texto. O gate devolve rc 0 quando nada piorou, rc 1 quando algo piorou e rc 2 em entrada quebrada. Eis um gate reprovando, em português, depois que entrou no PR um grafo novo que reprova no MUST (rc 1):
✗ PIOROU docs/new.kg.yaml falha no MUST: form.required.node.label
· 3 grafos; 1 passam no MUST; 1 na dívida herdada
MUST docs/legacy.kg.yaml (herdado): form.required.node.plane
MUST docs/new.kg.yaml (novo): form.required.node.label
Sem KG_SSOT_LANG e com um locale que não é pt_BR, a mesma execução imprime ✗ WORSE no lugar de ✗ PIOROU. Se
um passo do CI procura texto na saída (✗ PIOROU ou GATE QUEBRADO do gate, PASSA ou ENTRADA QUEBRADA do
kg_validate.py), fixe KG_SSOT_LANG nesse passo ou case as duas línguas. Os dados não mudam de língua: as chaves
do JSON, os códigos e o lugar de um veredito (nó X, aresta #n) ficam iguais; só o texto de message e hint
segue a língua.
Atualizar a tag¶
Faça tudo num PR só:
- Rode
kg_vendor.py updatecom a tag nova. De dentro do vendor, passe--source https://github.com/marciocar/kg-ssot. - Rode
git add vendor/kg-ssot. - Rode
kg_vendor.py check --dest vendor/kg-ssot --indexpara conferir o que o commit vai levar. Se você sai de uma tag anterior à 4.8.1, rode oupdateduas vezes: o kit antigo não grava o.gitattributesdo vendor, e o da 4.8.1 grava. - Rode
kg_gate.pypara ver a diferença. - Rode
kg_gate.py --updatepara gravar a identidade nova.
Se uma versão menor trouxe um aviso SHOULD que o seu corpus já tinha, a dívida sobe por desenho. Nesse caso, rode o
--update com --accept-regression "<motivo>".
Testar um commit antes da tag (desde a 4.24)¶
Para testar o que vai virar release, antes de a tag existir, traga um commit com
kg_vendor.py update --ref <commit> --source <clone ou URL>. Você precisa de um repositório onde esse commit se
alcança: o espelho público guarda só os commits com tag, então isto é para quem chega à branch (um clone do produto,
um fork, um CI que faz checkout do PR). Ele leva a árvore desse commit com o spec/release.json
que ela tiver (a versão ali pode ainda ser a anterior, que o --tag recusaria como release mal rotulada). O carimbo
diz unreleased no lugar da tag e guarda o commit. O kg_vendor.py check então reprova com VENDOR FORA DE RELEASE, a
não ser que você passe --allow-unreleased, para um vendor de bancada nunca passar por release no seu CI. O mesmo vale
para o kg_gate.py, que também reprova o vendor de bancada sem --allow-unreleased e nunca grava a base a partir de
um: deixe a opção no comando da bancada, nunca no CI que guarda as suas releases. Quando a tag sair, o update --tag o
substitui sem --force.
Subir de uma versão maior (v3 → v4)¶
O v4 reprova o que o v3 só alertava. A subida é pela rampa: os grafos que passam a reprovar entram na base como
dívida herdada, e daí em diante a dívida só encolhe. Não há truque de data no schema, e você não conserta o corpus
inteiro de uma vez. O passo a passo, com os comandos, está na seção 5 do
ADOPTING.pt-BR.md, do v3 para o v4.
Nota de ambiente¶
Em sistemas com PEP 668 (Ubuntu 24.04 em diante), o pip install fora de um venv é recusado. Use um venv e rode o
kit com o Python dele. No GitHub Actions com actions/setup-python, o pip install funciona direto.
Num app¶
O KG-SSOT guarda conhecimento sobre o produto: decisões, regras de negócio, afirmações e a evidência de cada uma.
Não é banco de dados de runtime. O progresso, os pontos e a sequência de um usuário continuam no banco do app. O que
vai para o grafo é, por exemplo, "a sequência zera depois de um dia sem registro", com a fonte da decisão, a data em
que foi conferida e uma aresta TRACES_TO até o código ou o teste que a implementa. A receita está no
capítulo 8, "Uma regra de negócio ligada ao código".
- No CI: o caminho acima vale para qualquer repositório, de qualquer linguagem. O gate só precisa de Python no CI.
- Em tempo de execução, em Python, pelo pacote: desde a 4.9.0, o kit também sai no PyPI como
kg-ssot(pip install kg-ssot). Os módulos ficam emkg_ssot, e o pacote instala os comandoskg-validate,kg-conformance,kg-gate,kg-store,kg-query,kg-diff,kg-mcpekg-prov-export. O contrato e a suíte viajam dentro do pacote:
from kg_ssot import kg_store
store = kg_store.GitStore("/var/lib/meu-app/grafos", init=True) # fora do repositório do app
O pacote é o caminho para o app. Para o gate no CI, o vendor por tag continua o canal recomendado, porque o
contrato que julga o grafo fica no diff do repositório.
- Em tempo de execução, em Python, pelo vendor: ponha vendor/kg-ssot/tools no sys.path e rode sem gravar
bytecode, para o check do vendor seguir íntegro:
import sys
sys.dont_write_bytecode = True # ou PYTHONDONTWRITEBYTECODE=1 no ambiente
sys.path.insert(0, "vendor/kg-ssot/tools")
import kg_store # o armazém que só grava o que o contrato aprova
store = kg_store.GitStore("/var/lib/meu-app/grafos", init=True) # fora do repositório do app
Deixe a raiz do armazém fora do repositório do app. Dentro de um repositório git, o DiskStore e o
VersionedStore recusam a raiz. O GitStore com a raiz no próprio repositório commita no branch em que ele
estiver, fora do PR, e numa subpasta do repositório cria um repositório git aninhado.
- Em JavaScript (Node, React Native, Expo): desde a 4.9.0, o leitor JS sai no npm como kg-ssot
(npm install kg-ssot). A entrada dele não importa nada do Node, e o validador do contrato vem pré-compilado, sem
new Function nem eval, então roda no Hermes. verdict(texto, { format }) devolve o veredito MUST com os
códigos da suíte, e o comando kg-ssot-validate julga arquivos. Desde a 4.10.0, ele mede também os avisos SHOULD,
com os mesmos códigos do leitor de referência em todos os casos da suíte. O número da linha e as dicas ficam com o
leitor de referência em Python.
- Num cliente de MCP, como o Claude Desktop: desde a 4.9.0, o servidor MCP de referência também sai como MCP
Bundle (kg-ssot-<versão>.mcpb). Ele é anexado à release da tag no repositório público depois que o pacote
kg-ssot da mesma versão sai no PyPI, porque o bundle instala o pacote de lá. Na instalação, o cliente pergunta o
diretório do armazém e se o servidor fica só leitura, e instala o pacote pelo uv na sua máquina. O uv cria o
ambiente dentro da pasta da extensão, travado por um uv.lock com o hash de cada dependência. O bundle traz a
atestação de proveniência do GitHub
(gh attestation verify kg-ssot-<versão>.mcpb --repo marciocar/kg-ssot) e aparece no MCP Registry oficial como
io.github.marciocar/kg-ssot. O armazém é o versionado: cada gravação fica registrada, e o kg_history a mostra.
Uma pasta dentro de um repositório git usa o git, num repositório aninhado (o repositório de fora passa a vê-la
como pasta não rastreada). Quando a pasta não abre, o servidor sobe só com o julgamento e diz o motivo ao modelo,
que o conta no chat (desde a 4.9.1; na 4.9.0 o motivo ia só para o log). Roda no macOS, no Linux e no Windows. No
Windows, o armazém roda desde a 4.9.1 (na 4.9.0 ele não abria lá), com a proteção de caminho descrita no
capítulo 5. O servidor roda local, por stdio, e não fala com nenhum serviço externo.
- Em outra linguagem: não há leitor pronto além do Python e do JavaScript. Valide pelo CLI
(kg_validate.py --format json devolve o veredito JSON de spec/verdict.schema.json) ou por um modelo, com o
servidor MCP (kg_mcp.py). Um leitor próprio prova que concorda com o contrato passando na suíte
(capítulo 7).
Conferir uma release: procedência e SBOM¶
Desde a 4.45.0, cada artefato da release tem uma lista de materiais de software (SBOM) em CycloneDX 1.7 JSON, ao lado
da atestação de proveniência que ele já tinha. A release no GitHub leva um <artefato>.cdx.json para o wheel, o
sdist, o tarball npm, o MCP Bundle e a imagem OCI, e cada SBOM também é atestado com o predicado CycloneDX:
gh attestation verify kg-ssot-<versão>.mcpb --repo marciocar/kg-ssot # a procedência
gh attestation verify kg-ssot-<versão>.mcpb --repo marciocar/kg-ssot \
--predicate-type https://cyclonedx.org/bom # o SBOM
gh attestation verify oci://ghcr.io/marciocar/kg-ssot:<versão> --repo marciocar/kg-ssot \
--predicate-type https://cyclonedx.org/bom
- O que o SBOM lista: as dependências que o artefato declara, lidas de dentro dele (os metadados do wheel, o
package.jsondo npm, opyproject.tomle ouv.lockdo bundle). Nenhum pacote embute código de terceiros: o pip, o npm ou o uv instala as dependências do seu lado, por isso elas vão comisExternal: true, com a faixa declarada emversionRangequando há uma. A licença, a versão com que a release foi testada (kg-ssot:tested-version) e as dependências transitivas vêm do ambiente em que a suíte passou, e, no npm, do lockfile do build. O SBOM do MCP Bundle lista todo pacote douv.lockdele, com o hash de cada arquivo. O da imagem lista a imagem base pelo digest, os pacotes Debian dela, o interpretador Python e toda distribuição Python instalada. - Como sai: pelo
packaging/sbom/sbom.py, só com a biblioteca padrão do Python, no workflow da release. Ele recusa o artefato que embute código de terceiros (node_modules,site-packages, um wheel dentro de um pacote), e assim a afirmação do SBOM de que nada vem embutido é conferida a cada release. - Reportar uma vulnerabilidade: o SECURITY.md traz as versões suportadas, o canal privado (o GitHub Private Vulnerability Reporting), os prazos de resposta, o escopo e a política de divulgação.