AUR-364 — Fixar ferramentas de documentação
Documento histórico do board. Pode descrever componentes removidos; para uso atual, consulte o guia do produto.
Este documento descreve o esquema, os limites, os códigos de erro, o exemplo sintético, o diagnóstico e o comando OCI offline do card AUR-364.
Artefatos do card:
| Caminho | Papel |
|---|---|
.board/bootstrap/locks/docs.yml |
o lock derivado (schema: bootstrap-lock-v5) |
tests/acceptance/AUR-364.sh |
o oráculo e o gerador (--generate, --attest) |
tests/bootstrap/locks/AUR-364/resolved.lock |
o artefato de resolução (versão exata + digest por par) |
tests/bootstrap/locks/AUR-364/read-paths.attested |
o selo dos read_paths do card |
tests/bootstrap/locks/AUR-364/cases.tsv |
a tabela de casos adversariais |
tests/bootstrap/locks/AUR-364/mutations.sh |
as mutações, uma por caso |
tests/bootstrap/locks/AUR-364/verify-fixtures.sh |
o executor das fixtures e os seis oráculos |
docs/specs/AUR-364.md |
este documento |
read_paths do card, na ordem do card:
Gemfile.aurumcode/Gemfile.docker/docs.Dockerfile.github/workflows/documentation.yml
1. Por que o desenho anterior foi vetado (rodadas 1 a 3)
As rodadas se sucederam fechando instâncias enquanto a classe sobrevivia. A classe tem nome: o aceite classifica por ramo — um ramo por tipo de entrada, uma heurística por sintaxe — e sempre existe um ramo em que a checagem simplesmente não acontece.
- r2 verificava completude por cardinalidade mecânica, com uma função
count_real_citations()que era ela mesma uma lista de verbos de instalação.RUN pipx install ...ecurl ... | bashnão moviam nenhum dos dois lados da igualdade eAC-001retornavapass. - r3 apagou a lista de verbos (e ela continua apagada), mas trocou o problema de lugar. As duas revisões cegas derrubaram r3 com quatro achados que são a mesma classe em dois disfarces:
| # | Achado | Consequência reproduzida com oci-run real |
|---|---|---|
| B1 | a heurística de pin era “a constraint contém um dígito” | gem "x", "0" e gem "x", ">= 0" — que casam com qualquer versão publicada — saíam pinned: true / pinned-lockable |
| B2 | o pin era calculado por nome, sobre a concatenação das constraints de todos os manifestos | um manifesto que pina lavava a declaração genuinamente sem versão do outro (já visível nos dados comitados: jekyll-seo-tag, jekyll-sitemap, webrick) |
| B3 | declaração sem versão recebia rótulo unpinned-must-not-install e passava, porque o oráculo só conferia que seus próprios contadores concordavam entre si |
consistência interna não é propriedade de segurança |
| B4 | a atestação comparava o número de linhas com o número de read_paths e não marcava caminho já visto |
duplicar uma linha no lugar de outra mantinha a contagem e tirava um arquivo inteiro do selo, com curl | sh vivo no Dockerfile |
Os quatro passavam pelo fluxo honesto (--generate + --attest reais), com
exit_code: 0 e result: pass saindo do container selado.
2. O reprojeto da rodada 4: parar de julgar constraint, ler a resolução
A análise semântica de linguagem de constraint foi apagada, não estendida.
Não existe mais campo pinned, nem parsing de operador, nem teste de dígito,
nem qualquer noção de “isto é um pin de verdade?” em lugar nenhum do arquivo.
Aquela superfície era ilimitada (quatro ecossistemas, todo operador, todo
limite degenerado) e era a fonte direta de B1, B2 e B3.
No lugar dela entra a propriedade que um lockfile resolvido já tem por construção — exatamente uma versão resolvida por ferramenta:
PROPRIEDADE R. Para todo manifesto
Me todo nome de dependênciaNque a gramática deMproduz, o artefato de resolução do card contém exatamente um registro(M, N)com uma versão exata de release e um artifact digest. E para todo registro(M, N)na resolução, a gramática deMproduzN. Os dois lados são comparados como CONJUNTOS, chaveados no PAR(manifesto, nome)— nunca só no nome (B2), nunca por cardinalidade (B4).
Consequências, enunciadas como totais e não como lista de casos cobertos:
gem "x",gem "x", "0",gem "x", ">= 0",gem "x", "~> 0",gem "x", "!= 9.9.9"e toda forma de constraint que um Bundler futuro venha a inventar são tratadas de forma idêntica: a constraint é registrada literalmente no lock (tool[N].version_constraint), marcadaconstraint_interpreted: false, e nunca é lida por nenhuma decisão. O que vincula é a versão exata da resolução.- Declaração sem registro de resolução é falha tipada
(
docs_tool_lock_incomplete), nunca omissão. - Registro de resolução sem declaração é falha tipada
(
invalid_lock_or_manifest): a resolução não carrega peso morto que nada no repositório pediu. - Se a fonte de resolução exigida por um formato de manifesto não existir,
isso é falha tipada (
docs_tool_lock_incomplete), nunca um pulo silencioso.
RE_EXACT_VERSION (^[0-9]+\.[0-9]+\.[0-9]+$) é a definição inteira de
“resolvido” neste card. Três componentes numéricos, sem operador, sem faixa,
sem curinga.
2.1 Onde está o lockfile resolvido, e por que não é o Gemfile.lock
O lockfile nativo do ecossistema Bundler é o Gemfile.lock. Este
repositório não versiona nenhum: não existem Gemfile.lock nem
.aurumcode/Gemfile.lock, e nenhum dos dois é read_path deste card, logo
nenhum dos dois é materializado dentro do container do aceite.
Esse fato é divulgado em limits[3] do lock gerado e aqui — não é
contornado pulando a checagem. O card carrega a sua própria fonte de
resolução, tests/bootstrap/locks/AUR-364/resolved.lock, dentro de paths:
como o Public contract exige, e a ausência dela é falha tipada. Quando um
Gemfile.lock real for versionado, ele precisa ser acrescentado aos
read_paths do card antes de poder ser honrado; até lá,
required_resolution_of_format aponta para o artefato card-scoped, e isso
está declarado em nonderivable[5].
resolved.lock é mantido à mão e nunca é escrito por --generate ou
--attest. Isso é deliberado: é a verdade de referência contra a qual a
Propriedade R é comparada, e uma verdade de referência gerada a partir da
coisa que ela verifica não provaria nada (Regra dos Seis, item 6).
2.2 Derivação 2 (inalterada, e não quebrada por nenhuma das duas revisões)
Todo read_path que não é manifesto legível por máquina não é
interpretado: entra no lock como o sha256 do arquivo inteiro, selado uma
segunda vez em read-paths.attested, que --generate nunca escreve.
brew install, curl ... | sh, um blob base64 ou uma sintaxe que ninguém
inventou ainda mudam os bytes do arquivo e todos reprovam — não porque este
card os reconheça, mas porque nunca precisou reconhecer.
3. Conjuntos, nunca contagens
Toda checagem de cobertura marca cada item canônico como visto num array associativo, rejeita duplicata, e depois afirma que cada item foi visto pelo nome. As únicas comparações aritméticas que sobraram no aceite carregam uma das três etiquetas:
| Etiqueta | Significado |
|---|---|
# NONEMPTY |
Lei 12 item 4: conjunto derivado de tamanho zero é FALHA |
# BOUND |
teste de tamanho ou limite contra uma constante declarada |
# ACCOUNTING |
Regra dos Seis item 3: a soma de classificação por linha dentro dos parsers, que prova que nenhum continue largou uma linha física |
O oráculo set-not-count-audit de verify-fixtures.sh re-grepa o aceite
inteiro e reprova se qualquer comparação aritmética estiver sem etiqueta, ou
se qualquer comparação tiver uma cardinalidade de conjunto dos dois lados
— exatamente a forma que deixou B4 passar. O detector é validado contra um
controle positivo sintético (uma linha sem etiqueta, uma com etiqueta e
uma com duas cardinalidades): se o detector não enxergar as três, o silêncio
dele sobre o arquivo real não vale nada (Lei 12).
3.1 A checagem que não acontecia por SIGPIPE
Enquanto esta rodada era construída, um stress de 200 execuções do caso
nominal, com bytes idênticos, reprovou 7 vezes com
invalid_lock_or_manifest. A causa é a mesma classe sob julgamento, num
disfarce novo: set -o pipefail combinado com um consumidor que sai assim que
tem resposta (grep -q, head).
# ANTES — o status do pipeline depende de uma CORRIDA
if grep -vE '<forma de chave>' "$lock_file" | grep -q . ; then fail ; fi
Se o grep -q da direita sai antes de drenar, o grep -v da esquerda morre
com SIGPIPE, o pipeline reporta 141, e o if lê isso como “não achou nada”:
a checagem simplesmente não acontece. No sentido oposto — nas checagens de
terminador — a mesma corrida reprovava um nominal legítimo ao acaso.
Toda decisão do aceite passa a ser tomada a partir do status de um único
comando ou de uma here-string (grep -Fq ... <<< "$texto"), nunca do
status de um pipeline cuja cabeça pode ser morta. O oráculo no-pipefail-race
proíbe a forma mecanicamente e é validado por controle positivo. Depois da
correção, 200/200 execuções do nominal saem exit 0.
4. Esquema do lock (bootstrap-lock-v5)
Cabeçalho: schema, card, generated_without_network, três derivation[],
trusted_registry, o bloco resolution.{path,present,sha256,schema,digest_domain}
e o bloco spec.{path,sha256}, que sela este documento: docs/specs/AUR-364.md
é path declarado do card, portanto é path lido (Lei 20), e um byte alterado
aqui reprova o aceite até que --generate seja rodado de propósito.
| Bloco | Campos |
|---|---|
read_path[N] |
path, role (manifest|document), format, bytes, lines, sha256 |
manifest[N] |
path, format, runtime, source, sha256, required_resolution, lines_total, lines_accounted, declarations |
document[N] |
path, sha256, bytes, lines, interpreted: false, install_instructions: uninterpreted-sealed-by-whole-file-digest |
runtime[N] |
family, resolved_version, artifact_digest, manifests |
tool[N] |
name, declared_in, runtime, manifest_format, version_constraint, constraint_interpreted: false, resolved_version, artifact_digest, runtime_version, declaration_sha256, manifest_sha256, allowlisted_command, output_schema |
count.* |
apenas dados. Nenhuma decisão do oráculo compara dois desses números |
violation[N] |
path, line, kind — o número da linha e a classe, nunca o texto ofensor |
limits[N], nonderivable[N] |
limites divulgados e constantes de engenharia não deriváveis |
tool[] tem uma entrada por par (manifesto, nome), ordenada
canonicamente por nome e depois por manifesto em LC_ALL=C. É por isso
que jekyll aparece duas vezes: .aurumcode/Gemfile e Gemfile são duas
declarações distintas, e tratá-las como uma só foi o blocker B2.
4a. O diretório de fixtures declarado, selado arquivo a arquivo (Lei 20)
O quarto path declarado do card é um diretório:
tests/bootstrap/locks/AUR-364. Até a rodada 4 apenas dois dos arquivos dentro
dele eram abertos pelo aceite (resolved.lock e read-paths.attested);
cases.tsv, mutations.sh e verify-fixtures.sh — o aparato de TDD inteiro
deste card — podiam ser apagados ou reescritos com o aceite continuando
verde. Isso é exatamente a Lei 20 na granularidade de arquivo, e a Lei 12 na
forma mais fácil de não ver: o portão estava verde porque a checagem nunca
acontecia.
O aceite agora lista o diretório declarado e sela cada arquivo:
| Campo | Conteúdo |
|---|---|
card_artifact.dir |
o diretório declarado |
card_artifact.count |
quantos arquivos a listagem encontrou |
card_artifact[N].{path,bytes,sha256} |
um registro por arquivo, em ordem de byte (LC_ALL=C) |
A lista não é digitada: ela é a listagem do diretório. Um arquivo acrescentado amanhã é selado sem que ninguém edite nada; um arquivo removido some da derivação e sobra no lock comitado. A seção 7k do aceite compara os dois lados como conjunto, nas duas inclusões, marcando cada path por nome:
- em disco → no lock, com o digest dos bytes de agora: adulteração reprova;
- no lock → em disco: remoção reprova. Este é literalmente o teste que a Lei 20 prescreve.
Symlink, byte NUL, arquivo vazio e tamanho acima do limite são falhas tipadas
antes de qualquer digest. Oito casos de cases.tsv, classe
fixture-read-path, executam remoção de cada um dos três arquivos antes
invisíveis, adulteração, symlink, arquivo vazio e a direção que ninguém testa:
um arquivo acrescentado ao diretório sem reselo.
Consequência operacional: como read-paths.attested está dentro do diretório
selado, --attest tem de rodar antes de --generate. Ver §7.
4.1 Esquema da resolução (bootstrap-resolution-v1)
resolution-schema: bootstrap-resolution-v1
digest-domain: synthetic-offline-identifier-v1
runtime<TAB>ruby<TAB>3.2.2<TAB>sha256:<64 hex>
gem<TAB><manifesto><TAB><nome><TAB><versão exata><TAB>sha256:<64 hex>
...
end: bootstrap-resolution-v1
Gramática allowlist, com separação por TAB exata: um campo vazio ou um
campo a mais é violação, nunca um valor que o shell dobrou. IFS=$'\t' read
não é usado para entrada hostil, porque TAB é IFS whitespace e o bash
colapsa sequências dele — a<TAB><TAB>b viraria dois campos em vez de três.
O aceite usa split_tabs, que preserva campo vazio e não apara nada. Pelo
mesmo motivo, os valores extraídos dos manifestos passam por um tab guard:
gem 'webrick<TAB>evil' seria emitido como um registro de webrick — que
é resolvido — lavando uma dependência de outro nome para dentro de um
registro existente.
Regras adicionais da resolução, todas como propriedades de conjunto:
- uma identidade de artefato por
(nome, versão)(artifact-digest-inconsistent); - nenhum digest compartilhado por dois artefatos diferentes (
artifact-digest-collision); - digest degenerado (
0…0/f…f) é sentinela declarada (artifact-digest-null); - uma versão resolvida por nome em todo o card (
resolved-version-conflict); - toda família de runtime usada por um manifesto tem runtime resolvido, e todo runtime resolvido é usado por algum manifesto (as duas inclusões).
5. Códigos de erro
Esta tabela não é documentação: é um dos dois lados de um portão. A seção
3b do aceite deriva, dos próprios bytes de tests/acceptance/AUR-364.sh, o
conjunto de códigos que o script consegue emitir, deriva desta tabela o
conjunto documentado, e exige igualdade de conjunto nas duas inclusões.
Renomear um código no código e esquecer a tabela reprova; documentar um código
que nenhum ramo alcança reprova; conjunto vazio de qualquer lado reprova
(Lei 12). Nenhum dos dois lados é digitado à mão em lugar nenhum.
| Código | Quando |
|---|---|
docs_tool_lock_incomplete |
lock/atestação/resolução/spec ausente ou vazia; declaração sem registro de resolução; manifesto que não declara nada; conjunto derivado vazio |
source_integrity_error |
symlink, byte NUL, read_path ausente/não regular |
invalid_lock_or_manifest |
envelope inválido; linha fora da gramática (manifesto ou resolução); linha/entrada duplicada; registro de resolução sem declaração; papel do lock divergente da derivação; atestação fora da gramática |
input_limit_exceeded |
qualquer bound declarado excedido |
docs-tool-unlocked |
bytes de read_path ou do spec divergentes do selo ou do lock; lock diferente da derivação; tabela de códigos do spec divergente do conjunto emitido |
docs-tool-mutable |
versão resolvida não exata, digest malformado/nulo/colidente; fato resolvido no lock divergente da resolução; campo obrigatório de tool[] ausente |
runtime-resolution-inconsistent |
runtime não resolvido ou com versão não exata; conflito de versão ou de família por nome entre manifestos |
docs-tool-source-untrusted |
manifesto sem source aceito ou com origem fora do allowlist de um |
docs-tool-identity-mismatch |
entrada de tool[] que não corresponde a uma declaração real, duplicada, ou declaração ausente do lock |
unknown-selector |
seletor fora dos quatro aceitos (AC-001, ContractAUR364, IntegrationAUR364, E2EAUR364); sai 64, não 1 |
5.1 O código que mudou de nome na rodada 5, e por quê
Até a rodada 4 o código acima se chamava runtime-incompatible, e o
Outcome do card prometia “runtime compatível”. Uma revisão cega mediu que
nenhum ramo deste card compara a versão de uma ferramenta com a versão do
runtime: não há comparação semver, não há sort -V, não há tabela de
mínimos. Os três ramos que emitiam aquele código testam consistência interna
da resolução — runtime não resolvido, versão de runtime não exata, e duas
resoluções divergentes para o mesmo nome entre manifestos.
O rótulo prometia uma capacidade que o código nunca teve, e o teste que
deveria prová-la (mut002_jekyll_runtime_incompatible) exercitava o ramo do
conflito cruzado, não o par ferramenta/runtime que o nome descrevia. O nome do
código, o nome do caso, a MUT-002 do card e esta tabela foram todos reescritos
para dizer exatamente o que acontece. A lacuna está declarada no limite 6 da
seção 6, e o caso disclosed_runtime_not_compatibility_checked a executa: ele
resolve o runtime ruby para 2.0.0 mantendo jekyll 4.3.3 e passa, de
propósito, com o resultado registrado na fixture.
6. Limites declarados
read_pathsdocumentais são selados por digest de arquivo inteiro e nunca interpretados: o card prova que nenhum byte mudou sem um reselo deliberado; não classifica o que um comando de documentação faz.- Identidade de ferramenta só é derivada de
read_pathcujo basename é formato registrado de manifesto. Formato conhecido sem parser registrado para o aceite para o aceite, em vez de ser chutado ou rebaixado a documento opaco. - O repositório não versiona lockfile nativo do ecossistema; a fonte de resolução exigida é o artefato card-scoped, e sua ausência é falha tipada.
- Os digests vêm da resolução sob o
digest-domaindeclarado e não são verificados contrarubygems.org, porque o aceite não tem rede. O card prova que cada ferramenta tem exatamente um digest bem formado, que uma identidade de artefato mapeia para um digest, que dois artefatos não compartilham digest e que o valor é estável byte a byte entre execuções; não prova que o digest é igual ao checksum do.gemupstream. - Constraints são registradas literalmente e nunca interpretadas: uma constraint que admitiria muitas versões não é nem abençoada nem rejeitada pela sintaxe. O que vincula é a versão exata resolvida.
- A versão do runtime é verificada por consistência de resolução, nunca por
compatibilidade. O card prova que toda família de runtime usada por um
manifesto tem exatamente uma versão exata resolvida, que todo runtime
resolvido é usado por algum manifesto, e que toda entrada
tool[]cita aquela mesma família e aquela mesma versão. Ele não prova que a versão resolvida do runtime satisfaz orequired_ruby_versionde nenhuma ferramenta — e, offline, não poderia: esse metadado vive no gemspec upstream e não aparece emread_pathnenhum deste card. Concretamente: resolverrubypara2.0.0mantendojekyll 4.3.3passa, e o casodisclosed_runtime_not_compatibility_checkedexecuta exatamente isso.
Delegar ao resolvedor também não resolveria, e isso foi medido, não
argumentado. Com bundler 2.4.19 sob ruby 3.2.11 (2026-08-06), um
Gemfile declarando ruby "2.4.0" junto de gem "jekyll", "4.3.3" —
cujo gemspec exige ruby >= 2.5.0, verificado com
gem specification jekyll -v 4.3.3 --remote required_ruby_version —
produziu lockfile com exit 0 e gravou RUBY VERSION ruby 3.2.11p268,
isto é, o interpretador que rodou a resolução, não o pin declarado. O
bundler aplica required_ruby_version contra o interpretador que o
executa (medido no sentido oposto: bundler 1.17.3 sob ruby 2.4.10 recusou
a mesma gem com exit 6 e a mensagem jekyll (= 4.3.3) was resolved to
4.3.3, which depends on ruby (>= 2.5.0)), nunca contra uma versão de
runtime declarada. Logo, “a resolução existir” não é prova de
compatibilidade do pin declarado, e o resolved.lock deste card não é
saída de resolvedor nenhum: é artefato mantido à mão, como a seção 7 diz.
O único subcomando do bundler que recusa aquele Gemfile é
bundle check / bundle install: ambos saíram 18 com Your Ruby
version is 3.2.11, but your Gemfile specified 2.4.0. Isso é um teste de
igualdade entre o pin declarado e o interpretador que já está
executando — dois runtimes —, não um julgamento ferramenta-vs-runtime; e
ele só acontece antes da resolução, contra o interpretador real. Além
disso a imagem selada do aceite
(bash@sha256:ae4668c2560999e65e89532cd2ad1b6688bb23298189f0bd229ef80fa4bd0831)
não tem ruby, nem bundle, nem gem — command -v para os três
saiu 127 —, e o perfil roda com --network none. Nenhum resolvedor pode
ser executado dentro do aceite, e um lockfile produzido fora dele só seria
evidência para quem também confiasse na rede que ele precisou. Delegar,
portanto, não é uma opção disponível: é outra promessa não verificada.
Uma matriz de mínimos escrita à mão seria enumeração — a mesma classe que custou três rodadas a este card — e teria o mesmo estatuto epistemológico do limite 4: uma afirmação sobre upstream que o aceite offline não pode verificar. Por isso a lacuna é declarada aqui em vez de simulada por uma tabela.
7. Fluxo de benção: três ações deliberadas e separadas
Acrescentar uma dependência nova exige três edições revisáveis, e nenhuma delas é implícita:
- editar o manifesto (
Gemfileou.aurumcode/Gemfile); - escrever à mão um registro em
resolved.lockcom versão exata e digest distinto; --attestpara o selo e depois--generatepara o lock.
A ordem do passo 3 é obrigatória, e a rodada 5 a mediu. Desde que o lock
passou a selar todo o diretório de fixtures declarado (limite/seção 4a), ele
sela também read-paths.attested. Rodar --generate antes de --attest
produz um lock que carrega o digest do selo antigo, e o aceite reprova com
docs-tool-unlocked — não por capricho, mas porque o lock estaria afirmando
bytes que já não existem. O caso disclosed_new_gem_fully_resolved executa a
ordem correta; ele reprovou na ordem invertida durante esta rodada, e foi assim
que a dependência de ordem apareceu.
Rodar apenas 1 e 3 — o “fluxo de regeneração recomendado” que as revisões
cegas usaram para quebrar r3 — continua reprovando, agora com
docs_tool_lock_incomplete. É exatamente o que o caso
blockerR3_full_honest_flow_unpinned executa, e o que
disclosed_new_gem_fully_resolved mostra passando quando as três ações
acontecem.
8. Como rodar
# aceite oficial, container OCI pinado, sem rede
./.board/bin/oci-run --profile bootstrap-readonly-v1 --card AUR-364
# local, sem container
bash tests/acceptance/AUR-364.sh AC-001 ; echo "EXIT=$?"
# reselar os read_paths (ação separada e deliberada) — SEMPRE ANTES do lock
bash tests/acceptance/AUR-364.sh --attest > tests/bootstrap/locks/AUR-364/read-paths.attested
# regenerar o lock (não toca no selo nem na resolução; sela os bytes do selo)
bash tests/acceptance/AUR-364.sh --generate > .board/bootstrap/locks/docs.yml
# suíte de fixtures + os seis oráculos
bash tests/bootstrap/locks/AUR-364/verify-fixtures.sh
9. Diagnóstico
| Sintoma | Causa provável |
|---|---|
docs_tool_lock_incomplete logo após editar um Gemfile |
falta o registro (manifesto, nome) em resolved.lock — o passo 2 do §7 |
docs-tool-unlocked sem ter mexido em Gemfile |
um read_path documental mudou de bytes; rode --attest depois de revisar o diff |
docs-tool-unlocked no CI mas não local |
drift de outro trabalho em .github/workflows/documentation.yml; rebase e regenere lock + selo |
invalid_lock_or_manifest num Gemfile que o Bundler aceita |
a linha é Ruby válido mas não é produção da allowlist (comentário no fim da linha, git:, source:, bloco source ... do) — deliberadamente estrito |
runtime-resolution-inconsistent |
duas resoluções do mesmo nome divergem entre manifestos, ou o runtime da família não está resolvido/não tem versão exata. Não significa incompatibilidade: ver limite 6 |
docs-tool-unlocked logo após editar docs/specs/AUR-364.md |
o spec é selado no lock; rode --generate depois de revisar o diff |
docs-tool-unlocked logo após editar cases.tsv, mutations.sh ou verify-fixtures.sh |
todo o diretório de fixtures declarado é selado (§4a); rode --generate |
docs-tool-unlocked logo após um --attest |
--generate foi rodado antes do --attest; a ordem correta é selo primeiro, lock depois (§7) |
docs-tool-unlocked logo após acrescentar ou renomear um fail <código> |
a tabela da seção 5 e o conjunto emitido divergiram; atualize a tabela |
set-not-count-audit reprovando |
alguém acrescentou uma comparação aritmética sem etiqueta no aceite |
no-pipefail-race reprovando |
alguém escreveu ... \| grep -q ... ou ... \| head ... no aceite; use here-string ou um comando só |
| nominal reprovando de forma intermitente | procure por decisão tomada a partir do status de um pipeline (§3.1) |