Commit 8683350
fix(catalogo): um byte ruim deixa de condenar o arquivo inteiro a windows-1252
REGRESSÃO achada por varredura adversarial dos merges do próprio dia — e ela
piorou o caminho que não era o alvo do #500.
O desempate era `if (utf8.includes("�"))`: decisão por ARQUIVO tomada
sobre um sinal por BYTE, sem proporção. Um único byte inválido no meio de um
arquivo perfeitamente UTF-8 — 0x92, a aspa curva do Word, sobra comum de
copiar-colar — jogava TODAS as linhas para o windows-1252.
Medido com as funções deste arquivo:
arquivo limpo → "Ação" corretos=500 mojibake= 0
+ 1 byte 0x92 no meio → "Ação" corretos= 0 mojibake=500
antes do #500 → "Ação" corretos=500, com U+FFFD=1
Ou seja: nesse caminho, o `file.text()` que o #500 substituiu era MELHOR — ele
corrompia um caractere, não o arquivo. E o `upsert` por
`(organization_id, codigo)` sobrescreve os nomes bons que já estavam no
catálogo, com `erros: []` e 200 OK. Falha aberto, e apaga dado certo.
O CONSERTO é a densidade, e o número veio de medição, não de palpite — as duas
causas ficam a três ordens de grandeza de distância no mesmo texto:
latin-1 de verdade (o que o #500 conserta) → 1 U+FFFD a cada 5 bytes
UTF-8 com 1 byte inválido → 1 U+FFFD a cada 11.392
misto: 499 linhas UTF-8 + 1 linha latin-1 → 1 U+FFFD a cada 5.692
`MAX_BYTES_POR_SUBSTITUICAO = 100` fica no meio desse vale, com mais de uma
ordem de grandeza de folga para cada lado. E é generoso de propósito, o que
está escrito no código: errar para o lado do UTF-8 corrompe um caractere;
errar para o outro corrompe o arquivo inteiro. Os dois erros não custam o mesmo.
PROVA nos três casos, com a função real: 500/500 corretos em todos — inclusive
o latin-1 puro, que é o conserto original do #500 continuando de pé.
O TESTE traz o par que impede o degenerado: "latin-1 de verdade CONTINUA sendo
detectado", senão "sempre devolva UTF-8" satisfaria o caso novo e desfaria o
#500 inteiro. E um terceiro caso documenta a folga do número, para avisar se um
dia as duas causas se aproximarem.
Sabotagem (voltar ao desempate binário): previ 2 vermelhos, medi 1. A previsão
estava errada — o caso da densidade mede o `TextDecoder` diretamente, não a
função, então não alcançava o mecanismo sabotado.
Gates: planilha-em-latin-1 13/13 · typecheck exit=0 (cache apagado, e
--listFilesOnly confirma o csv.ts carregado) · changelog-cabe-na-tela 5/5.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>1 parent 79c7bef commit 8683350
2 files changed
Lines changed: 110 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
30 | 56 | | |
31 | 57 | | |
32 | 58 | | |
| |||
39 | 65 | | |
40 | 66 | | |
41 | 67 | | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
42 | 78 | | |
43 | 79 | | |
44 | 80 | | |
45 | 81 | | |
46 | | - | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
47 | 90 | | |
48 | 91 | | |
49 | 92 | | |
| |||
Lines changed: 65 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
152 | 152 | | |
153 | 153 | | |
154 | 154 | | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
0 commit comments