Files
DeskcommCRM/docs/audits/2026-08-14-alinhamento-stable-v1.3.0.md
Rafael MelgaçoandClaude Opus 5 b123827687 docs: a auditoria vira arquivo versionado, e os retratos param de fingir que são o presente
A varredura estava em /private/tmp — 242 KB de trabalho medido que sumiria no
próximo restart. Agora é `docs/audits/2026-08-14-afirmacoes-de-estado.md`, com o
COMANDO de cada achado preservado. É o comando que faz o documento valer: quem for
tratar um achado mede de novo em vez de confiar na linha. O relatório envelhece
exatamente como aquilo que ele critica, e diz isso no topo.

Junto, a evidência do alinhamento do canal `stable`
(`2026-08-14-alinhamento-stable-v1.3.0.md`): os três pacotes tinham `stable` e
`1.3.0` em digests diferentes, e agora batem. Medido ANONIMAMENTE depois, com token
de pull público — escrita que só se confirma pela sessão que a fez não está
confirmada. `latest` e `main` conferidos como controle: intactos.

## A triagem é por TIPO de documento, não por gravidade

Foi a chave que faltava, e ela inverte o que eu faria sozinha: gravidade alta num
documento-retrato **não** se conserta.

**RETRATO** (`current-state.md`, `harness-audit.md`) — carimbados, não corrigidos.
Os dois já traziam `audited_against: 789dfa6` no front-matter, e ninguém lê
front-matter: o corpo falava no presente. O carimbo agora é a primeira coisa
visível, com a distância medida — **1.014 commits e 71 migrations** entre o commit
auditado e hoje — e declara que nada ali é mantido. Documento honestamente datado
nunca mente; documento atualizado uma vez volta a mentir na semana seguinte, e pior,
porque a atualização recente faz confiar mais.

**AUTORIDADE** — corrigidos, e onde deu, a afirmação virou COMANDO:

- os três READMEs diziam "**Quatro checks são obrigatórios**". São cinco. Em vez de
  trocar o número, entrou o `gh api … --jq '.required_status_checks.contexts'` com a
  saída do dia — e a admissão de que a linha já disse "quatro" e "cinco";
- os três READMEs mandavam ter o Google Authenticator à mão porque "o primeiro login
  de admin exige MFA". **Não exige** — o `bootstrap-owner.ts` grava `mfa_required:
  false` explícito. Um comprador espera uma tela que não vem;
- os três diziam que o comando local era "a lista **completa** dos gates
  obrigatórios". Não é: `e2e` e `imagens-ok` só rodam no CI. Verde na sua máquina
  não é verde no merge;
- `AGENTS.md` afirmava que **6 vars de `lib/env.ts` faltam no `.env.example`,
  incluindo 3 secrets**. Medido: das 45 chaves, a única ausente é `NODE_ENV`, e os
  três secrets nomeados estão todos lá. A regra do DoD continua; a dívida caiu;
- `AGENTS.md` também dizia que login/signup/convite estavam sem rate limit. O
  `lib/auth/rate-limit.ts` cobre os quatro, por IP e por identificador hasheado.
  Crons e MCP seguem sem, e isso ficou escrito;
- `AGENTS.md` prometia um "DoD de 15 itens" — agora são 16, e a linha virou o
  `grep` que conta, em vez de um número que envelhece de novo no próximo item.

**PLANEJAMENTO** (`docs/stories/`, epics) — intocado. É onde vivem 385 dos ponteiros
mortos do repo, e registro histórico pode apontar para o que nunca existiu. O gate
já exclui essa pasta de propósito.

## O processo — duas linhas, não um sprint

1. `CONTRIBUTING.md`: quem toca um doc de autoridade corrige as afirmações **daquele**
   doc. Não sai caçando nos outros — a dívida decai sozinha se ninguém a alimentar.
2. `CLAUDE.md`, DoD item 16: se o PR muda comportamento, procurar a afirmação de
   estado sobre esse comportamento. Só sobre o que mudou. E onde puder virar comando,
   trocar em vez de corrigir.

## Um erro de instrumento, no meio disto

O script com que filtrei os achados "que passam de afirmação envelhecida" imprimia
sempre a MESMA citação — variável de loop vazando para o loop seguinte. As saídas
medidas estavam certas, as citações não. Reescrito e refeito: 25 achados, não os 43
que a primeira passada listou. Um instrumento quebrado não devolve erro; devolve uma
lista plausível.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RKm2XcTcfgi1vdMcDYSRWa
2026-08-14 17:54:26 -03:00

3.2 KiB

Alinhamento do canal stable da v1.3.0 — evidência

Data: 2026-08-14 · Operação: re-aponte da tag stable para o manifest da 1.3.0, nos três pacotes. Sem rebuild — o mesmo índice OCI, sob outra tag.

Por que foi preciso

O workflow reagia a push: tags e a release: published. gh release create faz as duas coisas, então o mesmo commit foi construído duas vezes (19:53 e 19:58 de 2026-08-13). O segundo run moveu 1.3.0 e 1.3 para o build novo e não moveu stable, que ficou no build anterior. revision e version eram idênticos nos seis manifests; só o created diferia. Gatilho removido no PR #251, com guarda em tests/unit/packaging-artefato-do-cliente.test.ts.

Como foi feito

O daemon do Docker estava parado, então em vez de docker buildx imagetools create a operação foi feita pela API do registry — que é o que o imagetools faz por baixo: GET do índice de 1.3.0 e PUT do mesmo corpo sob a tag stable, com o mesmo Content-Type: application/vnd.oci.image.index.v1+json. Blob nenhum foi copiado: o índice referencia os manifests filhos por digest, e eles já estavam no mesmo repositório. Os três PUT responderam HTTP 201.

Antes e depois

A medição do "depois" foi feita com token anônimo de pull, não com a credencial que escreveu — uma escrita que só se confirma pela própria sessão que a fez não está confirmada.

pacote tag antes depois
deskcommcrm 1.3.0 fc10b029e326 fc10b029e326 alinhado ✓
deskcommcrm 1.3 fc10b029e326 fc10b029e326 alinhado ✓
deskcommcrm stable c4bc70b606c8 fc10b029e326 alinhado ✓ (alterado)
deskcommcrm latest 2f90346bf86d 2f90346bf86d controle — não podia mudar ✓
deskcommcrm main 2f90346bf86d 2f90346bf86d controle — não podia mudar ✓
deskcomm-worker 1.3.0 81e5af567cc8 81e5af567cc8 alinhado ✓
deskcomm-worker 1.3 81e5af567cc8 81e5af567cc8 alinhado ✓
deskcomm-worker stable 3fe292cad2bd 81e5af567cc8 alinhado ✓ (alterado)
deskcomm-worker latest 6f7c03a5b96c 6f7c03a5b96c controle — não podia mudar ✓
deskcomm-worker main 6f7c03a5b96c 6f7c03a5b96c controle — não podia mudar ✓
deskcomm-scheduler 1.3.0 4396263ba807 4396263ba807 alinhado ✓
deskcomm-scheduler 1.3 4396263ba807 4396263ba807 alinhado ✓
deskcomm-scheduler stable a0d5c3ad2296 4396263ba807 alinhado ✓ (alterado)
deskcomm-scheduler latest c6be866e2b26 c6be866e2b26 controle — não podia mudar ✓
deskcomm-scheduler main c6be866e2b26 c6be866e2b26 controle — não podia mudar ✓

Confira você mesmo

for p in deskcommcrm deskcomm-worker deskcomm-scheduler; do
  for t in 1.3.0 1.3 stable; do
    echo -n "$p:$t "
    docker buildx imagetools inspect ghcr.io/melgarafael/$p:$t | awk '/^Digest:/{print $2}'
  done; done

Os três de cada pacote têm que bater. Se não baterem, alguma coisa republicou depois do push da tag — e isso é o invariante 3 da doutrina quebrado dentro de casa.