Files
DeskcommCRM/.gitattributes
T
JM Schmitz 4b39d92ad6 fix(dx): os gates passam num clone Windows
Num clone limpo desta main, com o padrão do instalador git pra Windows,
`pnpm test:unit` dá 9 arquivos / 20 casos vermelhos e `pnpm lint:channels`
acusa 155 arquivos. Nenhum é defeito de produto: são os próprios gates lendo
o disco de um jeito que só funciona em Linux. Quem contribui do Windows abre
o repo já vermelho e não tem como saber o que é seu.

Três causas, medidas separadamente.

1. CRLF no checkout (5 arquivos, 12 casos)

   Os blobs estão em LF, mas `core.autocrlf=true` — padrão do instalador git
   pra Windows — entrega CRLF no disco, e os gates leem texto o tempo todo:
   workflow YAML, mapa de jornadas, selo de tradução, lista de specs do e2e.
   Um `\r` no fim da linha derruba regex ancorada.

   Medido, mesma main, mesma máquina:

       core.autocrlf=true   ->  9 arquivos / 20 casos
       core.autocrlf=false  ->  5 arquivos /  8 casos

   O `.gitattributes` já resolvia isto para `*.sql`, e o comentário de lá já
   escreve o raciocínio inteiro — inclusive que "CI passa porque
   ubuntu-latest não converte". Este commit estende a mesma regra ao resto.
   `git add --renormalize .` não altera NENHUM arquivo: os blobs já são LF,
   então muda só o que o checkout entrega.

2. Separador de caminho (3 arquivos)

   `path.relative` devolve barra invertida no Windows, e as listas declaradas
   contra as quais o resultado é comparado usam barra normal. Falha nas duas
   pontas ao mesmo tempo: o arquivo justificado vira infrator, e a
   justificativa vira órfã. Novo helper `tests/unit/helpers/caminho.ts`, num
   lugar só — quatro cópias da mesma linha é a garantia de que uma diverge.

   Mesma causa em `scripts/lint-channels.ts`, onde `ALLOWED` (regex ancorada
   em `^lib/channels/`) e `DEBT` (Set de strings) nunca casavam: 155 acusados
   numa árvore limpa, agora "ok (60 de dívida conhecida, nenhum novo)".

3. `npx` não é executável no Windows (1 arquivo)

   `import-puro-sem-env` chamava `execFileSync("npx", ...)`, e no Windows o
   `npx` é `npx.cmd`: `spawnSync npx ENOENT`. Agora chama `process.execPath`
   com `node_modules/tsx/dist/cli.mjs`.

   E o defeito grave que isso escondia: o CONTROLE POSITIVO passava com o
   aparato quebrado. Ele espera `ok:false` para `@/lib/env`, e um filho que
   nunca nasceu também dá `ok:false`. O controle ficava verde ao lado do caso
   real vermelho, afirmando que o instrumento estava bom. `respondeu` separa
   os dois: só conta como resposta uma saída com "OK" ou "ERRO:".

Sem fragmento em `.changes/`: nada aqui muda comportamento visível a quem
opera uma VPS.
2026-08-29 19:17:06 -03:00

69 lines
3.9 KiB
Plaintext

# Merge entre sessões paralelas.
#
# O conflito que mais custa neste repo não é o do CHANGELOG (o git auto-mescla
# aquele: medido entre os PRs #354 e #349, `git merge-file` sai com 0). É o dos
# dois arquivos append-only que TODA migration toca — e neles os dois lados
# estão sempre certos: cada sessão acrescentou a sua linha no fim.
# `union` = fica com os dois lados, sem marcador. Para uma tabela de registro,
# é a resolução que um humano faria à mão em 100% dos casos.
supabase/migrations/MANIFEST.md merge=union
# ─── E por que `supabase/baseline.sql` NÃO está aqui ────────────────────────
#
# Porque `union` nele é pior que o conflito. Medido num merge real seguido de
# aplicação num Postgres 17: o merge sai LIMPO (exit 0, "Merge made by the
# 'ort' strategy", zero marcadores) e o SQL resultante intercala duas funções —
# uma declarada e nunca definida, a outra com o corpo da primeira. Aí:
#
# - no `install.sh` (banco novo, ON_ERROR_STOP=1) morre com syntax error;
# - no `update.sh` do CLIENTE (sem ON_ERROR_STOP) sai com codigo 0, deixando
# zero funções criadas e engolindo o resto do arquivo em silêncio.
#
# O marcador de conflito, aqui, é FEATURE: ele é a única coisa que obriga
# alguém a olhar antes de o arquivo chegar à VPS de um cliente. `merge=binary`
# tem o mesmo defeito pelo outro lado (fica com "ours" e um `git add` descarta
# o outro lado sem ninguém ver).
#
# O conserto certo para o baseline é de processo, não de atributo: apêndice
# idempotente por migration, como a doutrina de migrations do CLAUDE.md manda.
# ─── Fim de linha fixo em LF pra todo .sql ───────────────────────────────────
#
# supabase/baseline.sql tem blocos que se autoconferem por md5(body) contra uma
# constante `v_md5` gravada no próprio arquivo (ex.: migration 0191, playbook
# `agendamento`). Sem esta regra, um clone com `core.autocrlf=true` (padrão do
# instalador git pra Windows) faz checkout do arquivo com CRLF — o hash
# declarado foi calculado sobre o blob LF do git, então o corpo inserido no
# banco (lido do disco, já CRLF) diverge do declarado e o self-check do
# `do $pub$ ... end $pub$;` derruba `pnpm test:db` antes de qualquer teste
# rodar. CI passa porque `ubuntu-latest` não converte por padrão — só quem
# clona no Windows vê a falha. `eol=lf` fixa LF no checkout independente do
# `core.autocrlf` de quem clonou.
*.sql text eol=lf
# ─── Fim de linha fixo em LF para TODO arquivo de texto ──────────────────────
#
# A regra de `*.sql` acima resolveu um caso de um problema que é geral, e o
# resto dele continuava de pé. Num clone feito no Windows com o padrão do
# instalador git (`core.autocrlf=true`), TODO arquivo de texto chega ao disco
# com CRLF — e os gates deste repo leem arquivo de texto o tempo todo: o
# workflow YAML, o mapa de jornadas, o selo da tradução, a lista de specs do
# e2e. Um `\r` no fim da linha derruba regex ancorada e comparação de string.
#
# Medido num clone limpo desta main, rodando `pnpm test:unit` em Windows:
#
# core.autocrlf=true (padrão) -> 9 arquivos / 20 casos vermelhos
# core.autocrlf=false -> 5 arquivos / 8 casos vermelhos
#
# Os 5 arquivos e 12 casos da diferença NÃO têm defeito nenhum: são
# `tag-so-nasce-da-main`, `e2e-cobertura-completa`, `numero-de-jornada-e-unico`,
# `traducao-nao-defasa` e `marca-logo-spec-ancora-a-rota` lendo `\r` que o CI
# nunca vê, porque `ubuntu-latest` não converte. É o mesmo raciocínio que o
# bloco de `*.sql` já escreveu — só que ele vale para o repo inteiro.
#
# `text=auto` deixa o git detectar binário sozinho; só o que ele já trata como
# texto é normalizado. Os blobs JÁ estão em LF, então isto não reescreve
# arquivo nenhum: muda apenas o que o checkout entrega.
* text=auto eol=lf