Passo 5 · Módulo 4 · Provar · Módulo 4 · Provar · Provar: pty, cmap e o ambiente do processo
Do guia da Rocketseat ao Warp · Visual Course

Provar: pty, cmap e o ambiente do processo

Três bugs desta implementação eram invisíveis nos arquivos e óbvios na execução. Esta lição mostra os três, e o verificador de 26 checks que os teria pego de novo.

Leia primeiro (fonte primária)
eza-community/eza — detecção de tty e o modo --icons

A fonte que explica por que a mesma chamada muda de saída dentro e fora de um terminal — a raiz do primeiro dos três bugs desta lição.

Leia a versão simples, ou abra a camada técnica em qualquer seção.
1

A grande ideia


Existe uma diferença entre configurar e provar. Configurar é escrever a linha. Provar é executar o caminho real e observar a saída real. Nesta implementação, três bugs só apareceram na segunda etapa — nenhum deles seria visto lendo os arquivos.

Pense como… a diferença entre conferir a planta e ligar a torneira. A planta pode estar perfeita e a água não chegar.

O QUE A VERIFICAÇÃO ENCONTROU 26 checks automatizados 3 bugs reais achados 2 falsos negativos 0 falhas ao final
O saldo do Proof Gate. Note os dois falsos negativos: o verificador também precisou ser verificado.
PROFUNDIDADE DA PROVA ler o arquivo — prova que você escreveu 0 bugs achados rodar no pipe — prova que executa 1 bug achado rodar num pty — prova o que o olho veria 2 bugs achados auditar o dado — cmap, RGB, env do processo 3 bugs achados
As quatro profundidades de verificação, com quantos bugs cada uma teria encontrado nesta implementação.
suposição "o arquivo está com o valor certo" "o comando saiu com status 0" "na tela parece a cor certa" "o teste passou" (sem nunca falhar) Não distingue escrever de aplicar evidência WARP_HONOR_PS1=1 no processo filho os bytes que o comando imprimiu 38;2;153;255;228 = #99FFE4 o teste falhou antes de você corrigir Executa o caminho real, sempre a diferença é o que foi executado
As duas colunas soam iguais numa reunião. Só uma delas sobrevive a um bug.
A checagem executou o caminho real? sim É evidência vale como prova não É suposição não fecha o gate Ler o arquivo prova que você escreveu. Rodar prova que funciona.
A regra que separa evidência de suposição.
Ao fim desta lição você consegue
  • Distinguir "o arquivo diz" de "o sistema faz"
  • Usar um pty real quando a ferramenta muda de comportamento fora do terminal
  • Auditar a cobertura de glifos de uma fonte antes de confiar num prompt
  • Ler o ambiente de um processo filho para provar configuração de app
2

Bug 1 — o pty que muda tudo


Ferramentas de terminal se comportam diferente quando a saída não é um terminal. Verificar num pipe pode dar um falso negativo perfeito: o comando roda, sai com status 0, e não imprime nada.

O FALSO NEGATIVO DO PIPE verificando num pipe $ eza --icons -1 | cat backup course (sem icone nenhum) $ eza --icons | cat (saida VAZIA, status 0) verificando num pty real $ python3 ptyrun.py fish -lic 'ls'  backup  course  extract $ ... | hexdump -C | head -1 1b 5b 33 34 6d ee 97 bf ^^^^^^^^ = U+E5FF
O mesmo comando, dois contextos. Sem tty, o --icons em modo automático não emite glifo e o modo grid calcula zero colunas.

Cria um terminal de verdade, roda o comando dentro dele e captura os bytes — inclusive tamanho de janela.

ptyrun.py — 15 linhas que evitam um falso negativo
import os, pty, sys, select, fcntl, termios, struct
cmd = sys.argv[1:]
pid, fd = pty.fork()
if pid == 0:
    os.execvp(cmd[0], cmd)
fcntl.ioctl(fd, termios.TIOCSWINSZ, struct.pack("HHHH", 40, 120, 0, 0))
out = b""
while True:
    r, _, _ = select.select([fd], [], [], 20)
    if not r: break
    try: d = os.read(fd, 65536)
    except OSError: break
    if not d: break
    out += d
os.waitpid(pid, 0)
sys.stdout.buffer.write(out)

Como chegar: python3 ptyrun.py fish -lic 'ls'. O TIOCSWINSZ importa: sem largura, o eza calcula zero colunas.

A ferramenta muda cor, ícone ou coluna por tty? sim Verifique num pty pty.fork() + TIOCSWINSZ não Pipe basta saída é estável eza, bat, fzf, git, ls: todos mudam. Verifique num pty.
Quando a verificação precisa de um terminal de verdade.

script -q /dev/null <cmd> é a alternativa de uma linha e funciona às vezes: quando o stdin do processo pai já não é um tty, ele falha com tcgetattr/ioctl: Operation not supported on socket. Em ambiente de automação isso acontece o tempo todo. O pty.fork() não depende do stdin do pai e ainda deixa você definir a largura da janela — que é justamente o que faltava para o modo grid do eza imprimir alguma coisa.

3

Bug 2 — três setas que não existem


Trocar a fonte tem uma consequência que ninguém lembra: os símbolos do seu prompt precisam existir na fonte nova. Três não existiam.

COBERTURA REAL DA FONTE U+21E1 ahead (antes) AUSENTE U+21E3 behind (antes) AUSENTE U+21D5 diverged (antes) AUSENTE U+2191 ahead (depois) na fonte U+2193 behind (depois) na fonte U+2195 diverged (depois) na fonte U+276F character na fonte U+258C pointer do fzf na fonte NF U+E5FF pasta (eza) na fonte NF U+F002 busca (fzf) na fonte NF U+F115 pasta aberta na fonte NF U+F48A arquivo (eza) na fonte
Auditoria de cmap: cada glifo realmente em uso, checado contra as 16 faces da JetBrainsMono Nerd Font. As três primeiras vinham do [git_status] do starship.toml. As caixas NF são ícones da Nerd Font — existem no terminal, mas não nesta página: o navegador não tem a fonte.

Junta os símbolos do config, do prompt renderizado e da saída do eza, e checa cada um contra o cmap da fonte.

auditoria de glifos
from fontTools.ttLib import TTFont
f = TTFont(FONT, fontNumber=0, lazy=True)
cmap = set()
for t in f['cmap'].tables:
    cmap.update(t.cmap.keys())

# junta TUDO que esta em uso de verdade:
syms  = set(open('~/.config/starship.toml').read())
syms |= set(render_prompt_in_pty())      # o prompt renderizado
syms |= set(eza_output('--icons=always'))  # os icones do eza
missing = [c for c in syms if ord(c) > 0x7f and ord(c) not in cmap]

Como chegar: Precisa de fontTools (pip install fonttools). Rode contra cada face — regular, bold, italic.

Por que a auditoria inclui o prompt renderizado, e não só o starship.toml?
Porque o config declara os símbolos configuráveis, mas o Starship também emite glifos que vêm dos seus defaults — e o eza emite ícones que não estão em config nenhum. Auditar só o arquivo deixaria passar tudo que não foi escrito à mão.
terminal — provando as setas novas $ cd repo-com-upstream-divergente $ git status -sb | head -1 ## master...origin/main [ahead 4, behind 1] $ python3 ptyrun.py fish -lic 'fish_prompt' ahead on master:main [↕↑4↓1] ❯
Para provar a correção foi preciso forçar o estado: um repositório com upstream divergente, para que diverged aparecesse no prompt de verdade.

O macOS substitui um glifo ausente por outro de qualquer fonte instalada — então "funciona". Mas a fonte substituta tem métricas próprias: a seta sai com largura diferente da célula, e tudo à direita dela desalinha. Num prompt isso é cosmético; numa saída em colunas (eza -l, git status) vira ruído difícil de atribuir. Trocar por um glifo que existe custa três caracteres no config.

4

Bug 3 — quando o verificador é o errado


A primeira execução do verify.sh deu duas falhas. Nenhuma delas era da configuração: as duas eram do verificador.

Falso negativo 1 · pipefail + grep -q

fc-list : family | tr ',' '\n' | grep -qx "JetBrainsMono Nerd Font" falhava — com a fonte instalada.

O grep -q sai assim que acha, o fc-list leva SIGPIPE, e com set -o pipefail o pipeline inteiro retorna falha. Achar a fonte era o que fazia o teste falhar.

Correção: grep -cx … >/dev/null — conta em vez de sair cedo.

Falso negativo 2 · o fish e o --

string match -q "*#FFC799*" "$FZF_DEFAULT_OPTS" devolvia unknown option.

O valor começa com --height=60%, e o fish leu o operando como opção.

Correção: string match -q -- "*#FFC799*" "$FZF_DEFAULT_OPTS". O -- não é preciosismo: é obrigatório sempre que o valor pode começar com hífen.

A lição dos dois

Um verificador que falha por conta própria é pior que nenhum: ele treina você a ignorar vermelho.

Por isso as duas falhas viraram comentário dentro do verify.sh — a próxima pessoa que ler o arquivo já encontra a explicação ao lado da linha.

1 / 3
ONDE OS BUGS APARECERAM Extrair browser pass 1 Warp honor_ps1 pass 3 bat Vesper pass 4 fish pty pass 5 Glifos cmap pass 6 verify 26/0 pass 7
Os sete passos da implementação. Os três bugs apareceram nos passos 5, 6 e 7 — todos na verificação, nenhum na escrita.
ONDE OS BUGS ESTAVAM ESCONDIDOS config arquivos OK execução bug 1 · pty saída vazia fonte bug 2 · cmap 3 setas ausentes verificador bug 3 · pipefail fish sem --
Os três bugs, por camada. A primeira raia — a única que dá para inspecionar lendo — está limpa.
O QUE SOBRA COMO EVIDÊNCIA rocketseat-warp/ [intacto] └─ SCOPE.md D1..D10 escritos antes [novo] └─ verify.sh 26 checks repetíveis [novo] └─ LOOP-LOG.md 7 passos, com prova [novo] └─ ROLLBACK.md como desfazer [novo] └─ backup/<timestamp>/ 5 arquivos originais [novo]
Os artefatos de prova. O SCOPE.md vem primeiro de propósito: a régua escrita antes não se move depois.
PROFUNDIDADE × BUGS ENCONTRADOS lendo os arquivos 0 rodando no pipe 1 rodando num pty 2 auditando a fonte 3
Bugs encontrados por profundidade de verificação. Ler o arquivo encontra zero.
5

O verificador, inteiro


Verificação que não é repetível vira anedota. O verify.sh refaz os 26 checks do zero, sempre — depois de um update do brew, de um reinstall, ou de você mexer em alguma coisa.

COMO ESCREVER UM CHECK QUE NÃO MENTE done-when antes de codar check falha com o bug ativo implementar uma unidade check passa no real SCOPE.md valida o check bounded evidência
A ordem que faz um check valer alguma coisa. Pular o segundo passo produz testes decorativos.

Um comando. Ele imprime PASS/FAIL por check e sai com status diferente de zero se algo quebrou.

terminal
bash ~/Documents/Projects/rocketseat-warp/verify.sh

Como chegar: Cada bloco corresponde a um critério D1..D9 do SCOPE.md — o contrato escrito antes de implementar.

terminal — verify.sh $ bash verify.sh D2 — fonte JetBrainsMono Nerd Font no Warp PASS settings.toml font_name = JetBrainsMono Nerd Font PASS familia instalada no sistema D4 — prompt do starship dentro do Warp PASS honor_ps1 = true PASS fish_prompt renderiza: ... ❯ D8 — cobertura de glifos na fonte PASS todo glifo em uso existe em todas as faces resultado: 26 PASS, 0 FAIL
A saída real do Proof Gate desta implementação.

Nove critérios, escritos antes de mexer em qualquer arquivo:

  • D1 extração completa · D2 fonte · D3 tema
  • D4 prompt no Warp · D5 fish · D6 plugins
  • D7 Warp nativo · D8 glifos · D9 reversível

Cada done-when nomeia sua própria prova antes da implementação começar — é isso que impede a régua de se mover depois. D4, por exemplo, não diz "o Starship funciona": diz honor_ps1 = true e fish_prompt renderizando saída não vazia numa sessão fish real. Duas afirmações falsificáveis, verificadas em camadas diferentes (config e execução).

PROVA
Quando ler o arquivo de config é prova suficiente?
clique para virar
Nunca, sozinho. Prova que você escreveu — não que o app aplicou. Confirme no comportamento (ambiente do processo, saída real).
PTY
Quando exigir um pty na verificação?
clique para virar
Sempre que a ferramenta muda de comportamento fora do terminal: cor, ícone, largura de coluna, paginação.
FONTE
O que fazer antes de confiar num prompt com fonte nova?
clique para virar
Auditar o cmap: todo glifo em uso — do config, do prompt renderizado e das ferramentas — em todas as faces.
6

Experimente


Escrever um check que não mente
1
Escreva o done-when antes: o que exatamente precisa ser verdade, e como se mede.
2
Execute o caminho real — num pty se a saída for para um terminal.
3
Compare com um valor exato (o RGB, o codepoint, o valor da variável), não com "parece certo".
4
Rode o check antes de aplicar a mudança e confirme que ele falha.
Agora você: o passo 4 é o que quase todo mundo pula. Um check que nunca falhou não é um check — é decoração. Reverta uma linha e veja o vermelho aparecer.
Revisão da lição 5
Um comando de terminal roda no seu script e não imprime nada, com status 0. O que investigar?
a está certa: eza, bat, fzf e vários outros detectam tty e mudam cor, ícone e layout. b daria command not found, com status diferente de zero. c daria erro de permissão — e o status também não seria zero.
Você trocou a fonte do terminal. Qual verificação é específica dessa mudança?
b está certa: é a única que pega glifo ausente antes de virar tofu ou fallback silencioso. c chega perto, mas o fallback do macOS pode fazer parecer certo na sua máquina e quebrar em outra. a verifica outra coisa: tema é cor, fonte é forma.
Falta a última lição: o que fazer no dia a dia, e como desfazer tudo em cinco comandos se você quiser voltar.