test(crm-vivo): a regra da evidência citada vira CHECK, não promessa

Achado do @MaestroConexoes, e a crítica dele é mais afiada que o achado: o
critério que eu tinha acabado de enunciar — "imagem citada entra, imagem não
citada fica fora" — VAZOU MINUTOS DEPOIS, na mesma leva em que foi aplicado.
Seis referências do handoff continuavam apontando para arquivos fora do git.

Nas palavras dele: "enquanto a regra viver só no seu critério, ela decai
exatamente como decaiu agora, e ninguém percebe até alguém clicar num caminho
morto daqui a semanas". Regra sustentada por disciplina é promessa no mesmo
formato da doc que promete rate limit e não entrega.

O teste lê os documentos da entrega, extrai as referências evidence/*.png e falha
nomeando as que não estão em `git ls-files` — o que o git ENTREGA, não o que o
disco tem. É um check que DECIDE, não que relata: a mesma distinção que ele
apontou na sonda do veto uma hora antes.

PROVADO NOS DOIS SENTIDOS: reprovou listando as seis; com as seis versionadas
(~880 KB), passa.

Critério de escopo ratificado por ele, com um argumento que eu não tinha usado:
`git` não esquece. Os 6,7 MB do evidence/ não entrariam "uma vez" — entrariam
para sempre, e num projeto ABERTO todo mundo que clonar paga esse peso em toda
clonagem, por imagem que ninguém cita.

De carona, atendendo ao aviso do hook de segurança: `execFileSync` com lista de
argumentos no lugar de `execSync` com string de shell, aqui e no `carimbar()`.
Naquele havia superfície real — os caminhos vêm de quem chama, e helper de teste
é justamente o código que alguém copia para outro lugar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gpdoAwEGCcM9DwzC6qtkL
This commit is contained in:
Rafael Melgaço
2026-07-24 23:18:17 -03:00
co-authored by Claude Opus 5
parent 6dce40307b
commit 49a3cb07c6
8 changed files with 64 additions and 3 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 KiB

+7 -3
View File
@@ -8,7 +8,7 @@
import type { Locator, Page } from "@playwright/test";
import * as crypto from "node:crypto";
import { execSync } from "node:child_process";
import { execFileSync } from "node:child_process";
import * as fs from "node:fs";
import * as path from "node:path";
@@ -222,8 +222,12 @@ export async function shotPage(page: Page, file: string, fullPage = true): Promi
* depender de alguém lembrar de ler o log.
*/
export function carimbar(dependencias: string[]): string {
const head = execSync("git rev-parse --short HEAD", { encoding: "utf8" }).trim();
const sujos = execSync(`git status --porcelain -- ${dependencias.join(" ")}`, {
const head = execFileSync("git", ["rev-parse", "--short", "HEAD"], { encoding: "utf8" }).trim();
// `execFileSync` com lista de argumentos, NÃO string de shell: os caminhos
// vêm de quem chama, e interpolar isso numa linha de comando seria injeção
// esperando acontecer. Não há entrada hostil aqui hoje — mas helper de teste
// é justamente o código que alguém copia para outro lugar.
const sujos = execFileSync("git", ["status", "--porcelain", "--", ...dependencias], {
encoding: "utf8",
})
.split("\n")
+57
View File
@@ -0,0 +1,57 @@
/**
* O handoff não aponta para o vazio.
*
* Regra enunciada nesta wave: **imagem citada num documento é lastro de
* afirmação; imagem não citada é artefato de build.** A primeira entra no
* repositório, a segunda fica fora (o `evidence/` inteiro são 6,7 MB, e num
* projeto aberto `git` não esquece — todo mundo que clonar paga esse peso para
* sempre, por imagem que ninguém cita).
*
* O achado do `@MaestroConexoes` é que a regra **vazou minutos depois de ser
* enunciada**, na mesma leva em que foi aplicada: seis referências continuaram
* apontando para arquivos fora do versionamento.
*
* Daí este teste, e não uma promessa. Nas palavras dele: *"enquanto a regra
* viver só no critério de alguém, ela decai exatamente como decaiu agora, e
* ninguém percebe até alguém clicar num caminho morto daqui a semanas."*
*
* É um check que **DECIDE** — não que relata. Foi essa a diferença que ele
* mesmo apontou na sonda do veto uma hora antes: valor calculado que só é
* impresso é enfeite que passa por cobertura.
*/
import { execFileSync } from "node:child_process";
import * as fs from "node:fs";
import * as path from "node:path";
import { describe, expect, it } from "vitest";
const RAIZ = process.cwd();
const DOCS = ["HANDOFF-crm-vivo.md", "BRIEFING-crm-vivo.md", "HANDOFF-lgpd.md"];
/** Tudo que o git ENTREGA — não o que o disco tem. */
function versionados(): Set<string> {
const saida = execFileSync("git", ["ls-files", "evidence/"], { cwd: RAIZ, encoding: "utf8" });
return new Set(saida.split("\n").filter(Boolean));
}
/** Referências a imagens de evidência dentro dos documentos da entrega. */
function citadas(doc: string): string[] {
const texto = fs.readFileSync(path.join(RAIZ, doc), "utf8");
const achados = texto.match(/evidence\/[\w./-]+\.png/g) ?? [];
return [...new Set(achados)];
}
describe("evidência citada", () => {
for (const doc of DOCS) {
it(`${doc} não cita imagem fora do versionamento`, () => {
if (!fs.existsSync(path.join(RAIZ, doc))) return;
const noGit = versionados();
const mortas = citadas(doc).filter((ref) => !noGit.has(ref));
expect(
mortas,
`${doc} aponta para imagem que não está em git ls-files — quem clonar acha o vazio:\n` +
mortas.map((m) => ` ${m}`).join("\n"),
).toEqual([]);
});
}
});