Files
DeskcommCRM/loop
PessoaandClaude Opus 5 169f04df1d fix(hooks): o freeze passa a consultar HEAD e a origem do outro lado do merge
Terceira rodada no mesmo arquivo. As duas versões anteriores foram refutadas por
lentes adversariais, e os dois furos que sobraram têm UMA raiz comum: o hook
decidia sem NUNCA ler `HEAD:<path>` nem perguntar de ONDE vem o outro lado do
merge. Medido pelo caminho de produção (`git commit` real, pelo dispatcher
`loop/hooks/pre-commit`), com MARCADOR único por lado — contagem não serve, a
versão da main tem o mesmo número de asserções.

FURO A · descartar a versão da PRÓPRIA branch dentro do merge

  As condições `:p == MERGE_HEAD:p` e `MERGE_HEAD:p != base:p` são TAMBÉM
  verdadeiras quando a branch tinha uma versão e a sessão a joga fora pegando a
  do outro lado. Num merge CONFLITADO com a main o 3-way do git resolve o
  invariante sozinho, com as DUAS asserções no índice; aí `git checkout
  origin/main -- <inv> && git add` e commit saíam exit 0, com o MARCADOR-COLEGA
  APAGADO do HEAD. `git checkout MERGE_HEAD -- <inv>` faz o mesmo. E a rota não
  pede válvula em passo nenhum: o fortalecimento do colega chega por merge LIMPO,
  que não chama hook.

FURO B · `MERGE_HEAD` é ref que a PRÓPRIA SESSÃO fabrica

  `git stash` cria commit sem passar pelo pre-commit; mesclá-lo com `--no-ff` dá
  um merge cujo "outro lado" é a própria sessão. Medido: main 1, 6ef3cf1fa 1,
  5899e4ac2 0 — REGRESSÃO contra os dois antecessores. A forma mundana é a mesma
  sem stash: merge conflitado da branch de um colega que apagou o invariante.

A especificação, agora com CINCO condições (todas têm de valer para excluir):

  1. há merge em curso, com EXATAMENTE UM lado;
  2. `git merge-base --is-ancestor MERGE_HEAD origin/main` — o outro lado é
     trabalho ACEITO, não branch de colega nem commit fabricado;
  3. `HEAD:p == base:p` — esta branch NUNCA tocou este invariante;
  4. `:p == MERGE_HEAD:p`, inclusive "ausente nos dois", para TODOS os paths da
     linha (o path velho de um `R` é uma deleção);
  5. `MERGE_HEAD:p != base:p` — o outro lado realmente mexeu ali.

Falha FECHADA em tudo o mais: sem MERGE_HEAD, octopus, sem ancestral comum,
`is-ancestor` não-zero ou sem a ref `origin/main`.

E um terceiro furo, achado ao re-medir a varredura de formas — este ABERTO, e
presente também na `main`: com `core.quotepath` no PADRÃO do git, um caminho com
byte não-ASCII sai citado e com escapes em octal, o campo começa com `"`, a
âncora `^tests/invariants/` não casa e a linha NUNCA entra na lista. `git rm`
desse invariante saía exit 0 e o arquivo DESAPARECIA do HEAD, fora de merge
nenhum. Aspas e barra invertida no nome eram citadas nas DUAS configurações.
Conserto: `-c core.quotepath=false` no `git diff` (o caminho normal volta a
resolver), `-F'\t'` (o path inteiro em `$2`/`$3`) e `"?` no regex (o que o git
cita mesmo assim fica na lista e falha FECHADA, em vez de sumir).

MUDANÇA DE COMPORTAMENTO DECLARADA: sem a ref `origin/main` (fork, clone raso) o
falso positivo do #1161 volta a ser acusado — a rodada anterior o resolvia de
graça ali, e a condição 2 cobra a ref de volta. É o lado seguro: quem não tem a
ref não pode provar que o outro lado é trabalho aceito, e tratar "não sei" como
"pode" é exatamente o que abriu o FURO B. A saída, nesse ambiente, é a válvula.

Medição — os 7 estados anteriores seguem IDÊNTICOS (0/1/1/1/1/0/1) e a varredura
de formas devolve o MESMO conjunto de acusações que 5899e4ac2 nas quatro
configurações (submódulo on/off × quotepath on/off). A suíte versionada foi de 41
para 99 casos e é instrumento vivo: contra 5899e4ac2 dá 23 vermelhos, e uma
sabotagem por condição deixa vermelho o caso previsto e só ele —
  2 → FURO-B, COLEGA-DEL, SEM-REF      4 → B+
  3 → FURO-A, FURO-A-MH                5 → MODO
  `-c quotepath` → B, E2E, ACENTO-LEGIT      `"?` → CITADO
A condição 5 precisou de caso novo (MODO): estas comparações são de BLOB, cegas
para MODO, e com a condição 3 no lugar o D2-MERGE — que a sustentava — passou a
ser pego pela 3. Num `chmod +x` os quatro OIDs são idênticos e só a 5 recusa.

Crédito às lentes adversariais: a primeira mostrou que identidade de CONTEÚDO
contra `origin/main` não distingue reverter de receber; a segunda achou o FURO A
e o FURO B e apontou que a condição 2 JÁ EXISTIA no irmão `validate-features.sh`,
com o motivo escrito — "é isso que impede o merge de virar lavanderia de edição".
Ela está aqui na forma dele, de propósito.

FICA PENDENTE, medido e não consertado: um `chmod` de invariante trazido PELA
MAIN é falso positivo (mesma cegueira de modo, do outro lado). Vale em
5899e4ac2 e aqui. Consertar exige comparar modo+OID nas quatro refs, o que troca
a base de comparação das cinco condições — mudança que merece a sua própria
rodada, não um apêndice desta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 16:57:31 -03:00
..
2026-07-31 12:19:57 -03:00