Promessa de estabilidade da CLI
Aplica-se a partir da v0.42.3. Escrita para o
0.x— cada quebra listada abaixo tinha de esperar por um major. A v1.0.0 é esse major: fechou a única quebra pendente (os atalhos de topo), e esta lista passa a valer como a promessa de semver real de resto em diante — um breaking change deixa de caber num1.x.
Um motor sem contrato não se automatiza. Quem escreve um Makefile, um passo de
CI ou um script de deploy precisa de saber o que pode partir num upgrade — e a
resposta «é 0.x, tudo pode partir» é verdadeira e inútil: garante que ninguém
depende de nada, o que é o mesmo que ninguém adoptar.
Isto listava o que se compromete e o que não se compromete dentro do 0.x,
que é o que falta a maior parte dos projectos nessa fase. Desde a v1.0.0 é
mais do que isso: é o contrato de semver do projecto.
Estável — não quebra sem um major
Os verbos de ciclo de vida de container, com os nomes e a semântica que Docker e Podman lhes dão:
container run ps stop start restart kill rm exec logs
wait inspect port rename pause unpause
image pull ls remove build (delonix build)
Concretamente, garante-se:
- O nome do comando e a ordem dos argumentos posicionais.
- As flags curtas e longas listadas acima e os seus significados — em
run:-d,-p,-v,-e,--name,--rm,--net,--restart,--memory,--cpus,--entrypoint,-w,-u,--add-host,--wait,--health-*; emexec:-i,-t,-e,-w,-u. (Esta lista andou achatada, sem dizer a qual verbo cada flag pertence —-i/-tnunca existiram emrun, só emexec; um teste emmain.rsverifica agora as duas listas contra a árvoreclapreal, em vez de confiar na leitura.) - Os códigos de saída — ver a secção «Códigos de saída» abaixo.
- A saída JSON de
inspect— campos podem ser ACRESCENTADOS, nunca removidos nem com o tipo mudado.
Quebra de contrato na v1.0.0. Até à v0.69.0 os atalhos de topo (
ps,run,exec,logs,rm,images) estavam aqui, como reescrita de argv paracontainer <verbo>/image list. Saíram — corte limpo, sem alias: a grafia antiga falha comunrecognized subcommand, nunca em silêncio, a mesma regra que a reorganização da v0.30.0 já seguia. Os grupos continuam:delonix container ps,delonix container run,delonix container exec,delonix container logs,delonix container rm,delonix image list(ver a nota da v2.0.0 abaixo — essa grafia mudou outra vez). Os códigos de saída não mudam nesta versão — ver a secção abaixo.Quebra de contrato na v2.0.0. O B2 da reestruturação da CLI (v0.67.0) tinha renomeado
image ls/image rmparaimage list/image remove— a única excepção de nomenclatura numa CLI onde as outras 15 folhas do tipo "listar" usamls(network ls,volume ls,vm ls,pod ls,secret ls,stack ls, …), seguindo o padrão Docker/Podman que este mesmo documento já cita para os verbos de container. Revertido:image listvolta aimage ls(image removefica). Corte limpo, sem alias — a grafialistfalha comunrecognized subcommand, nunca em silêncio.
Códigos de saída
Alteração de contrato na v0.49.0. Até à v0.48.0 toda a falha do motor era
1— «não existe» e «rebentou» eram o mesmo número. Continua a valer que0é sucesso e não-zero é falha, portanto umif delonix …; thennão muda de comportamento; o que muda é para quem testa[ $? -eq 1 ]à espera de uma falha específica. Ver a nota de migração no fim desta secção.
| código | significado |
|---|---|
0 |
sucesso |
1 |
falha sem classe própria (o default de sempre) |
2 |
uso inválido (o do clap) — e, em stack plan --detailed-exitcode, «há alterações» |
3 |
o recurso existe mas não está a correr |
4 |
não existe esse recurso |
5 |
conflito — o nome já está tomado |
69 |
capacidade que este host não tem — uma ferramenta por instalar, um backend indisponível |
74 |
o sistema de ficheiros disse que não — disco cheio, caminho impossível |
77 |
permissão negada — o remédio é uma permissão, e depois repetir |
124 |
o prazo esgotou-se — stack wait --timeout, e o que vier a ter prazo |
3 e 4 não são números inventados: são os códigos de estado do LSB que o
systemctl ainda fala (3 = o programa não está a correr, 4 = não há tal
unidade). 5 não tem convenção por trás — é o número livre seguinte, abaixo da
gama que a shell usa (126/127, e 128+N para sinais).
74 e 77 saíram do balde do 1, e não de nenhuma classe publicada. Até
à v0.66.1 uma falha de I/O respondia 1, que a linha de cima descreve como
«falha sem classe própria» — dar-lhe classe é o que esta tabela existe para
fazer, e um script com *) exit 1 continua a cair no mesmo ramo. O 77 é
recortado do 74 pelo kind do erro, porque é assim que ele chega: medido
contra um state root sem bit de escrita, volumes create e secret create
vêm ambos como permissão negada. É a falha mais accionável que existe — corrige
a permissão e repete — e respondia o mesmo número que um disco cheio.
69 e 124 também não. 69 é o EX_UNAVAILABLE do sysexits.h; 124 é
o que o timeout(1) devolve quando o prazo passa, e está portanto já nos dedos
de quem embrulha um comando num. Entraram porque tinham produtores reais mal
classificados, não para completar uma tabela: um stack wait que esgotava o
tempo respondia 1 — o mesmo número de um apply rebentado, no comando cuja
função inteira é ser lido por CI — e um wg/virt-customize/ngrok em falta
respondia 1 também, indistinguível de um erro de escrita numa flag. As duas
chamadas seguintes de um reconciliador são opostas: esperar mais, ou parar e
instalar alguma coisa.
delonix stack wait -f delonix-manifest.yaml --timeout 120
case $? in
0) ;; # tudo de pé
124) exit 0 ;; # ainda a subir — o pipeline seguinte volta a tentar
69) echo "falta uma ferramenta neste nó" >&2; exit 1 ;;
*) exit 1 ;; # qualquer outra coisa: pára
esac
O número do dicionário: DX-CDNN
O código de saída e o DX_* dizem a classe. O número DX-CDNN diz qual falha
(ADR-0043): o milhar é a classe, a centena o domínio, os dois últimos a falha, e 00 a
própria classe. A CLI imprime-o na linha de erro — error[DX-4501] no such VM: dev — e
delonix explain DX-4501 diz o que significa e o que fazer (--json para scripts). O
dicionário completo está em delonix explain codes e na página
Dicionário de códigos, gerada da mesma tabela.
Um número nunca muda de significado nem é reutilizado: pode acrescentar-se, e uma falha
que deixe de existir fica com o número retirado. A mensagem ao lado pode ser reescrita e é
traduzida; o número é o contrato. Enquanto um sítio do motor ainda não tem entrada própria,
responde com a entrada genérica da sua classe (DX-4000, DX-1000, …).
A identidade textual: DX_*
O número serve quem lê $?. Quem lê texto — um cliente HTTP, um consumidor
de -o json, um pipeline de logs — tem o código DX_*, que é a mesma
classificação noutra grafia:
DX_* |
código | quando |
|---|---|---|
DX_NOT_FOUND |
4 |
não existe esse recurso |
DX_NOT_RUNNING |
3 |
existe, mas não está a correr |
DX_CONFLICT |
5 |
o nome já está tomado |
DX_UNAVAILABLE |
69 |
capacidade que este host não tem |
DX_TIMEOUT |
124 |
o prazo esgotou-se |
DX_INVALID_ARGUMENT · DX_REGISTRY · DX_SYSCALL_FAILED · DX_INVALID_STATE · DX_IO |
1 |
falhas sem número próprio |
Estes nomes são contrato: um código pode ser ACRESCENTADO; um existente nunca muda de grafia nem de significado.
A relação é assimétrica de propósito. Um DX_* mapeia sempre para UM
número — se mapeasse para dois, o $? e o texto contradiziam-se para a mesma
falha. Mas o 1 carrega VÁRIOS códigos, porque é o balde «sem classe própria» e
o texto pode dar-se ao luxo de ser mais fino: cada NÚMERO é uma promessa que tem
de valer o resto do 0.x, enquanto DX_REGISTRY ao lado de
DX_INVALID_ARGUMENT não custa nada. Há teste a exigir as duas metades desta
regra, incluindo que o balde continue a ser um balde.
Hoje o código sai na API de gestão (delonix serve api), como campo
acrescentado ao corpo de erro — {"error": "...", "code": "DX_NOT_FOUND"}. O
campo error não foi tocado: a regra do ADR-0005 (acrescentar sim, remover ou
mudar de tipo não) vale aqui como vale no -o json.
O que continua sem código próprio, e é honesto dizê-lo: permissão negada e
falha temporária foram considerados e ficaram de fora — não há hoje uma
variante de erro que os produza (as falhas de permissão chegam embrulhadas no
errno de uma syscall, e o retry que existe acontece dentro do motor e nunca
chega a quem chama). Publicar um número que nada constrói é um número que nunca
pode voltar.
O que isto resolve é concreto: um reconciliador — o Makefile, o passo de CI, o
ciclo em bash que conduz esta CLI — não conseguia separar «cria, porque falta»
de «pára, porque falhou» sem ler a MENSAGEM de erro. E a mensagem é traduzida
(--l18n=pt), portanto um script que faz grep 'no such' funciona na máquina
onde foi escrito e deixa de classificar num nó com outra locale.
delonix container inspect web >/dev/null 2>&1
case $? in
0) ;; # está lá
4) delonix container run -d --name web nginx ;; # falta — cria
3) delonix container start web ;; # existe, parado — arranca
*) exit 1 ;; # qualquer outra coisa: pára, não adivinhes
esac
O código de um run/exec continua a ser o do WORKLOAD, não o do motor.
run em primeiro plano devolve o código do processo do container, exec o do
comando, e healthcheck sai 1 quando não saudável — as três promessas de
sempre, inalteradas. A consequência a reter: um container que saia 4 sai 4,
logo o $? de um run/exec nunca se lê como uma das classes acima — esse
número foi escolhido pelo workload, não pelo motor.
O que fica de fora, e é honesto dizê-lo: uma pré-condição do host por
satisfazer (uma sessão sem delegação de cgroup, por exemplo) não tem código
próprio — o motor avisa e continua, portanto não há falha para classificar. E
os grupos ainda não estáveis (ver a lista mais abaixo) podem responder 1 onde
um comando estável já responde 4: o workload describe de um nome que não
existe é o caso conhecido.
Migrar
if delonix …; then/|| exit 1/set -e— nada muda.[ $? -eq 1 ]a testar «este comando falhou» — passa a ser[ $? -ne 0 ].[ $? -eq 1 ]a testar «não existe» — passa a ser[ $? -eq 4 ], que é o teste que antes não havia maneira de escrever.
Estável em conteúdo, não em formato
As tabelas de ls/ps. As colunas podem mudar de largura, de ordem ou
ganhar irmãs — são feitas para humanos e medem-se pelo conteúdo real. Um script
que faça awk '{print $3}' sobre elas parte, e isso não conta como quebra de
contrato.
Para automação há -o json, e é ele que é estável: um array JSON por
recurso, campos podem ser ACRESCENTADOS mas não removidos nem com o tipo mudado
(ADR-0005). Verificado a funcionar (2026-09-25, v4.4.0) em container ps,
image ls, volume ls, network ls, vm ls, secret ls, workload ls e no
verbo genérico get <plural> (get pods, get volumes, …), que é o caminho de
listagem dos Kinds sem ls próprio — o pod ls já não existe, e o grupo
storage fundiu-se no volume. Também inspect e -q/--quiet. Excepção
medida: backup ls ainda não aceita -o json.
Uma versão anterior deste documento dizia que
-o jsonestava «por fazer e é a lacuna reconhecida aqui». Estava errado — existe desde a ADR-0005. É exactamente o erro que oparidade-docker-podman.mdabre por corrigir: inferir ausência a partir de um sintoma, sem ir ver.
O schema dos manifestos — estável, e é o que mais importa
Esta secção substitui a linha que dizia que o schema «é aditivo na prática, mas não é uma promessa». Era o compromisso ao contrário: a CLI estava mais protegida que o formato declarativo, quando é o formato que as pessoas põem em git e revêem em PR. Um
delonix-manifest.yamlsó se versiona se se souber que não parte sozinho.
Para os Kinds com spec tipado — Container, Pod, Volume, Network —
garante-se, dentro do 0.x:
- Um campo nunca é removido, nem muda de tipo, nem muda de significado.
- Um campo novo é sempre opcional e tem um default que preserva o comportamento anterior. Um manifesto escrito hoje continua a fazer o mesmo amanhã.
- Um nome antigo que seja renomeado mantém-se aceite como alias (é o que já
acontece com
restart→restartPolicy,options→mountOptions,wg_ip→wgIp). apiVersion: delonix.io/v1só muda com umv2, e umv2não sai sem ov1continuar a ser aceite. Desde a v0.64.0 cada Kind tem também o grupo do seu domínio (compute.delonix.io/v1alpha1,networking.…, …) — odelonix api-resourcesdiz qual é o de cada um. As duas grafias são aceites; a de grupo é a canónica, e o Kind só aceita o grupo dele ou o legado.
A verdade não é este texto, é o schema: delonix manifest schema emite-o a
partir do próprio código (ADR-0007), e o mesmo ficheiro está publicado em
schema/v1/delonix.json. Um teste do repositório
falha se o publicado deixar de ser o gerado, precisamente para esta página não
voltar a poder mentir.
O que o schema RECUSA, e vale saber porque é o que aparece sublinhado no editor:
um campo desconhecido (um typo), um kind que o motor não conhece — um typo
no nome, ou um Kind removido — e um apiVersion que não é o grupo daquele
Kind. Até à v0.65.x o kind era uma string livre e qualquer nome passava, por
isso um kind: Contaner validava limpo e um Egress já removido também; e o
apiVersion era fixo no legado, por isso a grafia de grupo — a canónica — era
sublinhada como se fosse erro. Um validador que discorda do motor é pior do que
nenhum, porque o visto verde é o que as pessoas seguem.
Aponta o editor e escreve manifestos com completação e validação:
# yaml-language-server: $schema=https://angolardevops.github.io/delonix-runtime/schema/v1/delonix.json
apiVersion: compute.delonix.io/v1alpha1
kind: Pod
metadata: { name: web }
spec:
containers:
- name: web
image: nginx:1.27
Há também uma extensão de VS Code que traz isto já ligado, mais um template por
Kind: angolardevops/delonix-vscode.
Aponta para o MESMO ficheiro publicado acima — buscado ao vivo, não embutido —
por isso o editor e o stack apply não podem discordar.
Para saber o que mudou entre duas versões, não há uma página escrita à mão — haveria a segunda fonte de verdade que a ADR-0007 aboliu. Há um comando:
scripts/schema-diff.sh v0.46.0 # dessa tag até à árvore actual
scripts/schema-diff.sh v0.46.0 v0.47.0 # entre duas tags
Compara campo a campo (nome e tipo), não o JSON cru, e sai 1 quando há diferenças — serve directamente como gate de CI. Um campo removido ou com o tipo mudado é assinalado como quebra de contrato, que é o que esta secção promete não acontecer.
Todos os Kinds têm hoje schema gerado. Esta secção dizia que Vm,
Cluster, ShareVolume, Image, Secret, Ingress, FirewallPolicy,
HTTPRoute, Tunnel, Workload e Stack não o tinham — deixou de ser
verdade: delonix explain <Kind> responde para cada Kind que
delonix api-resources lista (verificado a 2026-09-25, v4.4.0), com os nomes
canónicos (VirtualMachine, KubernetesCluster, NetworkPolicy, Gateway; os
nomes antigos Vm/Cluster/FirewallPolicy/Tunnel continuam a resolver como
alias). ShareVolume já não existe: é um kind: Volume com bloco share:. Se
um Kind futuro chegar sem schema, o delonix explain recusa-o a dizer que Kinds
o têm, em vez de o omitir.
Três Kinds deixaram de existir nesta série, fundidos no que já faziam:
Egress→FirewallPolicycomdirection: egress;Dependency→FirewallPolicy(açúcar, reduzido no load);Storage→kind: Volumecom um bloconfs:/cifs:/webdav:. Os nomes antigos continuam a carregar, com aviso de depreciação — a regra do «corte limpo» aplica-se a comandos, e um manifesto em git merece um degrau em vez de um erro.
O ficheiro de providers do nó — config.delonix.io/v1
O providers.yaml (ADR-0054) diz que providers o nó tem, como o motor chega a
cada um e qual serve um pedido que não nomeia nenhum. Desde o catálogo 1.1.0
(ADR-0059 F1) aceita também um appliance OPNsense (type: opnsense) e o bloco
networkDefaults, que diz que provider responde a cada papel de rede
(segment, gateway; nat, ipam e dns são recusados até existir quem os
sirva). O defaultProvider continua a ser só o de computação. Procura-se por
esta ordem, e o primeiro ficheiro que existe ganha, sem fusão:
DELONIX_PROVIDERS_CONFIG, $XDG_CONFIG_HOME/delonix/providers.yaml (ou
~/.config/…), /etc/delonix/providers.yaml.
É um formato publicado, com a mesma promessa que o schema dos manifestos dentro
do 0.x: uma chave nunca é removida nem muda de significado, uma chave nova é
sempre opcional, e apiVersion: config.delonix.io/v1 só muda com um v2 que
não sai sem o v1 continuar a ser lido.
A verdade é o schema, gerado dos tipos que o motor lê:
delonix provider config schema, publicado em
schema/v1/providers.json, com o mesmo teste a
falhar se o publicado deixar de ser o gerado. O schema recusa uma chave
desconhecida, outra versão, um tipo de provider que não existe e um segredo
escrito no próprio ficheiro (tokenSecret, password, secret — só por
referência: tokenSecretFile, passwordFile, secretFile, secretRef).
O schema diz se o ficheiro está bem escrito; o
delonix provider config validate diz se ele serve: um
defaultProvider ou um networkDefaults sem entrada no ficheiro, um ficheiro
de token ou de segredo legível por outros utilizadores, uma CA que não existe. Nenhum dos dois contacta um nó.
# yaml-language-server: $schema=https://angolardevops.github.io/delonix-runtime/schema/v1/providers.json
apiVersion: config.delonix.io/v1
defaultProvider: libvirt
providers:
- type: libvirt
- type: opnsense
url: https://fw.example
auth: { keyFile: /etc/delonix/opnsense.key, secretFile: /etc/delonix/opnsense.secret }
networkDefaults:
gateway: opnsense
NÃO estável — pode mudar em qualquer versão
serve cri,serve api,serve node-api,serve docker-api— superfícies de protocolo em construção. Odocker-apipublica a sua cobertura emdelonix serve docker-api --matrix, e é essa tabela que diz o que existe hoje, não esta promessa. A API de gestão (serve api) é local (socket unix, só o próprio uid) e não tem contrato publicado: não construas automação sobre ela — para isso existe a CLI, com-o json. As rotas dela estão congeladas (só correcções) enquanto o contrato local de nó que a substitui é decidido (ADR-0040 e ADR-0041, ambos Proposed); uma promessa para esse contrato só entra nesta página quando a primeira vaga dele fechar. As rotas que mudavam a rede sem o registo do container —/v1/net/firewall/:ip,/v1/net/egress[/:bridge],/v1/net/attach-extra…eDELETE /v1/net/attach/:id/:ip— passaram a responder501comDX-6302: o reapply seguinte desfazia o que escreviam, e só cobriam o IP primário de um container com várias redes. O caminho é a CLI (net ingress,net egress,network connect/disconnect), que passa pelo registo.- O nome do executável
delonixé deste motor. O control plane deixa de produzir um binário com esse nome (ADR 0038 do control plane); umdelonixinstalado por este projecto é sempre a CLI descrita nesta página. compatibility— a mesma tabela doserve docker-api --matrix, mas como verbo de topo e com-o json. Ainda só cobredocker;compose/cri/ocisão superfícies futuras, cada uma com o seu próprio "três estados" por construir. Sem promessa de campos atécompose/cri/ocientrarem — o formato pode mudar de forma para os acomodar.provider—ls/describe/matrix: o que cada provider (libvirt, cloud-hypervisor, proxmox, linux, opnsense) declara contra o catálogo de capacidades (ADR-0050, 1.1.0 desde o ADR-0059), medido no host. Os tipos sãocompute,network,storage,imageegateway; umgatewayresponde às linhas de rede. Os NOMES das capacidades (vm.snapshot.memory,net.namespace-isolation, …) e os seis estados são o que um script lê, e seguem a versão do catálogo (catalog_versionno JSON): uma entrada nova sobe o minor, renomear ou remover sobe o major. As colunas da tabela e a forma do JSON além desses campos podem ainda mudar.cluster,vm,pod,workload,net,systemcontainer— a superfície ainda está a assentar. (O schema dekind: Podé estável, ver acima; o que não é estável é o grupo de comandosdelonix pod.)storage/sharevolumedeixaram de ser grupos (#216, «fold storage/sharevolume into volume») — esta secção continuou a nomeá-los durante um tempo depois de já não existirem, o que é exactamente o erro que a lista mais abaixo existe para impedir (um nome classificado que já não é um grupo real). A superfície deles vive hoje emdelonix volume.volume,network,secret— só a saída-o jsone (paraVolume/Network) o schema do manifesto têm promessa (ver as duas secções acima); o resto da superfície imperativa (create/rm/inspecte as suas flags) nunca foi revisto para uma promessa própria.volumeem particular acabou de absorverstorage/sharevolume(#216) — está literalmente a meio de assentar, o pior momento possível para prometer nada sobre ele.apply,plan,wait,stack,manifest,get,describe,delete,diff,drift— o ciclo de reconciliação nativo (IaC, v0.47.0+). O SCHEMA dos manifestos que estes comandos consomem já é estável (secção acima); os VERBOS e flags destes comandos em si (--detailed-exitcode,--prune,--replace, …) nunca tiveram revisão própria, e a Kind mais recente (Service) ainda está a ganhar wiring nalguns destes caminhos.migrate—migrate assesslê um ficheiro compose que já tens e diz o que este motor serve, recusa e não implementa. É um RELATÓRIO (não cria, não puxa, não corre), e a tabela por trás dele é a docompatibility compose— uma medição com data, que se move quando o motor ganha chaves.compose— suporte nativo adocker-compose.yml, ainda a ganhar chaves do Compose Spec (profiles,configs/secrets, multi-ficheiro — ver oAGENTS.mdpara a lista completa do que falta).system,dashboard,completion,init,man,config,explain,api-resources,version— utilitários/introspecção, sem promessa declarada em nenhuma versão.hosts—hosts syncpublica no/etc/hostsos nomes de serviço dos containers expostos (ADR-0048); superfície nova, sem promessa ainda.mcp— o servidor Model Context Protocol (ADR-0025), superfície nova. O transportestdioé o suportado (um processo filho do cliente de IA por sessão, nunca um daemon); um transporte HTTP local, se vier a existir, seria loopback-only com um token local, como oserve apijá é local-only e sem contrato publicado — não construas automação sobre ele ainda.policy—delonix policy unset(M04,docs/roadmap/ 13-improvements-traceability.md), o único verbo imperativo dekind: RuntimePolicy. Superfície nova, sem promessa declarada — opolicy.jsonque ele apaga já existia e já era lido porcontainer run/vm create; o que é novo é a forma declarativa (stack apply) e este comando de remoção. A leitura passa pordelonix get runtimepolicies/delonix describe runtimepolicy <nome>, os verbos genéricos, que herdam a promessa (ou falta dela) desses verbos e não uma própria deste grupo.backup— o grupo não estava declarado de nenhum dos lados, e essa omissão é ela própria um defeito: quem quisesse saber se podia depender dedelonix backup vm xnão tinha resposta. Fica NÃO estável enquanto os arquivos ganham verbos. O formato do arquivo é outra coisa e tem a sua própria guarda: obackup.jsontraz um número de versão e um leitor que não o conheça RECUSA em vez de adivinhar uma disposição que nunca viu.
Mudou na v0.67.0: delonix backup <kind> <nome> passou a
delonix backup create <kind> <nome>, e o delonix restore de raiz passou a
delonix backup restore <arquivo> — sem o <kind> posicional, que o arquivo
já regista (continua a poder afirmar-se com --kind para que uma discordância
seja recusada). O agendamento saiu de flags do backup para
delonix backup schedule, com a mesma semântica: instala o temporizador e
tira o primeiro arquivo. Corte limpo, sem aliases — a forma antiga falha com
«unrecognized subcommand», nunca em silêncio.
delonix system backup não foi tocado nessa data. É outro âmbito — o
state root inteiro de um nó — e não uma segunda porta para este grupo.
Mudou depois (Sprint 4 da reestruturação da CLI, 2026-09-12): o próprio
system backup/system restore foi renomeado para system snapshot
create/system snapshot restore — mesmo vocabulário de vm snapshot/
volume snapshot (captura num instante), só que à escala do nó. O ÂMBITO
não mudou nem se fundiu com este grupo — só a PALAVRA partilhada («backup»)
desapareceu, que era a fonte real da confusão que o parágrafo anterior já
andava a explicar por escrito. Corte limpo: a forma antiga falha com
«unrecognized subcommand».
* Tudo o que começa por net netns — plumbing interno exposto por
conveniência de depuração.
* O formato dos ficheiros de estado em $DELONIX_ROOT. Lê-se pelo inspect,
nunca do disco.
* stack history, stack rollback e o conteúdo de $DELONIX_ROOT/stacks/ (ADR-0019). É um
registo do que foi aplicado, e o desenho promete explicitamente que nada o
lê para decidir o que existe: apagar essa pasta não muda o que o plan, o
apply, o prune ou o destroy fazem — só perde o histórico, e há gate na
bateria E2E a exigi-lo. Automação que dependa da presença de uma revisão está
a apostar contra essa promessa.
Como uma quebra é feita, quando tem de acontecer
Precedente já cumprido pelo projecto: a reorganização da v0.30.0 (netns →
net netns, cri → serve cri, …) foi um corte limpo, sem aliases — a
forma antiga falha com «unrecognized subcommand», nunca em silêncio.
Isso mantém-se como regra: falhar alto. Um alias de compatibilidade que muda de comportamento é pior que um erro.
E uma lição que custou esta sessão: essa quebra deixou um chamador INTERNO por
actualizar (o delonix-cri continuou a invocar delonix netns attach), o que
partiu a criação de pod rootless durante meses — ver
cri-conformance.md. Um corte limpo obriga a fazer o grep
por chamadores em TODO o workspace, não só na documentação.