Skip to content

chore(toolchain): lint vira oxlint type-aware e o TypeScript 7 entra sem side-by-side (ADR-0072) - #1035

Merged
GabrielAderaldo merged 12 commits into
devfrom
chore/toolchain-oxc-ts7
Oct 6, 2026
Merged

GabrielAderaldo merged 12 commits into
devfrom
chore/toolchain-oxc-ts7

Conversation

@GabrielAderaldo

Copy link
Copy Markdown
Contributor

Resumo

Troca o lint de ESLint + typescript-eslint para oxlint type-aware e leva o TypeScript 7 ao gate sem side-by-side — implementa o ADR-0072 (aceito em 05/10), que supersede a decisão de linter da Inquiry-0029 e o D2 do ADR-0067, e com isso tira do papel o D1 e o D3 do ADR-0067 (aceitos em 25/08, nunca implementados).

Por que agora, se a Inquiry-0029 decidiu ficar no ESLint: ela escolheu ESLint + --cache pela velocidade. Medido hoje:

  • o --cache dá falso verde — mudar só o tipo de retorno em a.ts deixa um no-floating-promises em b.ts passar com cache (exit 0) e falhar sem (exit 1); a FAQ do typescript-eslint confirma que o cache não acompanha dependência entre arquivos;
  • no CI o cache nunca é restaurado (Cache not found for input keys) — o passo Lint leva 39–62 s.

Commits

Commit O que é
fix(lint) os 16 achados que só o oxlint acusa — ⚠️ muda o OpenAPI: .meta({ example }) → examples: [...]
chore(toolchain) a troca: oxlint + tsgolint (exatos), TS 7.0.2 exato, gates de AST, hooks, CI, enforced_by, skills
docs(toolchain) CLAUDE.md, rules, README e comentários nomeiam o mecanismo real
docs(adr) ADR-0072, índices, Inquiry-0029 → superseded, agent typescript-language-expert
fix(toolchain) achado da revisão: o typescript estava em ^7.0.2; fixado e cobrado por asserção no gate

Como a paridade foi provada (não presumida)

  • .oxlintrc.json gerado do eslint --print-config dos cinco contextos: 154 das 158 regras com a mesma severidade, opções e exceções por pasta; todas as categorias do oxlint desligadas.
  • Diferencial: com as ~221 diretivas eslint-disable desligadas nos dois, 222 de 224 violações reais achadas no mesmo arquivo e linha; as 2 restantes são naming-convention.
  • Sondas de opção: 36 de 36 (ex.: 4 parâmetros têm de passar com max: 4 — o default do oxlint é 3).
  • As 4 regras sem equivalente: no-octal o tsc recusa (TS1121); class, naming-convention e member-ordering viram gate de AST em tests/cleanup/lint-gaps.test.ts (API typescript/unstable/*, concentrada em tests/support/ts-ast.ts). Conferido contra o ESLint: 46 de 46 nomes idênticos em 2036 arquivos; fixtures 15 de 15; prova invertida reprova as três.
  • Gate completo: typecheck · format:check · lint (2,3 s) · test — 11.904 pass, 0 fail, 20 skip.

Riscos declarados

  • Os gates de AST dependem da API unstable/ do TS 7 — por isso o typescript é exato, e agora o gate cobra isso.
  • Três regras portadas são nursery no oxlint (no-unnecessary-condition, prefer-optional-chain, no-useless-assignment).
  • O tsc do projeto passa a ser o TS 7 (typecheck ~1,9 s contra ~7,2 s no TS 6, ambos verdes).
  • ⚠️ Não rodados localmente: os 2 testes de integração alterados no fix(lint) (writer.integration, renumber-by-vigencia.drizzle-mysql) — só a asserção muda; o CI de integração os cobre.

Fora do escopo, registrado

O Prettier fica; a troca para oxfmt vem em PR próprio, com ADR.

…a passar

Medidos ao rodar o oxlint em paridade 1:1 com o ESLint (ADR-0072): o código que
o ESLint aprova tinha 16 violações que só o oxlint enxerga. Nenhuma muda
comportamento, exceto a primeira.

- 4× no-deprecated: `.meta({ example })` do Zod, depreciado pelo zod-openapi,
  vira `examples: [...]`. ⚠️ Muda o OpenAPI gerado (`example` → `examples`,
  o formato do OpenAPI 3.1). Nenhum teste dependia do campo.
- 7× prefer-optional-chain: `x !== null && x.campo === v` → `x?.campo === v`;
  o compilador segue estreitando `x` para não nulo depois da comparação.
- 3× no-unnecessary-type-parameters: genérico usado uma vez vira `unknown` ou
  `never` — `TableRead<T>` é todo readonly, então a troca é covariante e segura.
- 2× prefer-readonly-parameter-types e 1× consistent-generic-constructors.

Os dois testes de integração alterados (writer.integration e
renumber-by-vigencia.drizzle-mysql) mudam só a asserção; ficam para o CI, que
roda contra MySQL.

Assisted-by: Claude-Code:claude-opus-5-5
…tra sem side-by-side

Implementa o ADR-0072 (que supersede a decisão de linter da Inquiry-0029 e o D2
do ADR-0067) e, com ele, o D1 e o D3 do ADR-0067, aceitos em 25/08 e nunca
levados ao código.

Lint:
- `oxlint 1.86.0` + `oxlint-tsgolint 7.0.2003`, versões exatas; `pnpm run lint`
  é `oxlint`, sem cache — 2036 arquivos com type-aware em ~2,3 s (o ESLint
  levava 30–32 s sem cache e 39–62 s no CI, onde o cache nunca era restaurado).
- `.oxlintrc.json` GERADO do `eslint --print-config` dos cinco contextos: 154 das
  158 regras com a mesma severidade, opções e exceções por pasta; todas as
  categorias do oxlint desligadas. Paridade provada por diferencial (222 de 224
  violações reais no mesmo arquivo e linha) e por 36 de 36 sondas de opção.
- As 4 sem equivalente: `no-octal` o compilador já recusa (TS1121); `class`,
  `naming-convention` e `member-ordering` viram gate de AST em
  `tests/cleanup/lint-gaps.test.ts`, sobre a API `typescript/unstable/*`
  concentrada em `tests/support/ts-ast.ts`. Conferido contra o ESLint: 46 de 46
  nomes idênticos em 2036 arquivos; fixtures do autoteste 15 de 15; prova
  invertida reprova as três. As exceções `__brand` e `__nonZeroMoney` saem das
  diretivas `eslint-disable` para a allowlist do gate, com motivo.

TypeScript:
- `typescript` 7.0.2, versão exata (os gates usam a API `unstable/`). Saem
  `eslint`, `typescript-eslint`, `eslint-config-prettier`, `@eslint/js` e
  `@typescript/native-preview`; sai o `eslint.config.js`. O Prettier fica.

Harness e registros que dependiam do arquivo removido:
- Hooks Stop e pre-commit apontam para o `.oxlintrc.json`; o CI perde o passo
  de cache do ESLint; ignores atualizados.
- `enforced_by` dos registros de decisão apontam o mecanismo REAL: ADR-0009 e
  ADR-0011 → `.oxlintrc.json`; ADR-0045 e ADR-0051 → `module-boundary.test.ts`;
  ADR-0037 e ADR-0047 → `[]`, porque o `eslint.config.js` nunca cobriu a
  fronteira que eles afirmavam.
- Skills que linkavam `eslint.config.js` passam a linkar o mecanismo atual.

Assisted-by: Claude-Code:claude-opus-5-5
…nismo

CLAUDE.md, seis rules, o README, o SCHEMA dos registros de decisão, docblocks de
gates e comentários de código afirmavam que o ESLint barrava `class`, casing e
libs proibidas. Depois do ADR-0072 quem barra é o oxlint ou o gate de AST
`tests/cleanup/lint-gaps.test.ts`, e cada texto passa a nomear o mecanismo real.

Uma afirmação já era falsa antes da troca e foi corrigida, não só renomeada: a
`domain.md` dizia que `class`/`this` eram barrados pelo `no-restricted-syntax`,
mas a config só tinha seletores de `class` — `this` nunca foi barrado.

Nos comentários de código ("o ESLint recusa X"), a regra citada é uma das 154
portadas para o oxlint; a afirmação continua verdadeira e só a ferramenta muda.

Assisted-by: Claude-Code:claude-opus-5-5
Aceito pelo Tech Lead em 2026-10-05. Supersede a decisão de linter da
Inquiry-0029 (§6, ESLint + `--cache`) e o D2 do ADR-0067 (alias do TS 6); o D1,
D3–D6 do 0067 seguem vigentes e agora implementados.

O que derrubou a 0029 foi medido, não opinado: o `--cache` do ESLint dá falso
verde em regra type-aware (mudar só o tipo de retorno em `a.ts` deixa um
`no-floating-promises` em `b.ts` passar com cache e falhar sem; a doc do
typescript-eslint confirma que o cache não acompanha dependência entre
arquivos) e nunca era restaurado no CI (`Cache not found`, lint de 39–62 s).

- O ADR registra decisões, provas (diferencial, sondas, nomes 46/46, fixtures,
  prova invertida), custos — gates sobre API `unstable/`, três regras `nursery`,
  a mudança no OpenAPI — e quando reavaliar.
- Índice de ADRs: linha do 0072; o 0067 vira "Accepted (D2 superseded por
  0072)", sem editar o ADR aceito.
- Inquiry-0029 → `superseded`, com aviso no topo; o corpo, que registra as
  medições de 2026-08-06, fica intacto. Índices regerados por `docs:index`.
- O agent typescript-language-expert deixa de se apresentar como TS 6 com
  roadmap via `native-preview`.

Assisted-by: Claude-Code:claude-opus-5-5
Achado da revisão do PR: o ADR-0072 (D4) e o cabeçalho de
`tests/support/ts-ast.ts` afirmam que o `typescript` está em versão exata —
porque os gates de `lint-gaps` leem a AST pela API `typescript/unstable/*` —,
mas o manifesto dizia `^7.0.2`. O `pnpm add --save-exact` manteve o estilo de
faixa da entrada que já existia, e nada conferia a afirmação.

O valor é corrigido e a afirmação vira asserção em
`tests/cleanup/lint-gaps.test.ts`: uma faixa no `typescript` reprova o gate
(prova invertida feita — `^7.0.2` acusa "fixar a versão exata").

Assisted-by: Claude-Code:claude-opus-5-5
Copilot AI balanced review requested due to automatic review settings October 5, 2026 22:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…e a 24.21.0

O repositório declarava a versão do Node em cinco lugares e rodava três versões
diferentes: `.nvmrc` e `devEngines` em 24.16.0, `FROM`/`LABEL` em 24.15, e quatro
workflows em `node-version: '24'` — presos a nenhum dos dois e flutuando a cada
release upstream. O gate existente passava verde porque cobrava apenas o major, e a
divergência era de minor; o próprio ADR-0058:41 registrara a divergência em 05/08 como
"observação, não norma".

O `.nvmrc` passa a ser a fonte única por ser o único arquivo que o `fnm` (no `cd`) e o
`actions/setup-node` (`node-version-file`) leem sem tradução humana. As seis derivadas
são conferidas por gate, cada uma na granularidade que o alvo permite.

- ADR-0073, que supersede PARCIALMENTE o ADR-0058 (§2 e §4). A §1 — acompanhar o LTS
  recomendado — e a §3 — troca de tecnologia por inquiry — seguem vigentes; o corpo do
  ADR aceito não foi editado, só o status no índice.
- `tests/cleanup/node-version-single-source.test.ts` refeito: de 3 pontos por major para
  6 por derivação, com guarda contra verde por vacuidade. Foi essa guarda, na versão
  anterior, que acusou a troca de `node-version` por `node-version-file`.
- Versão alinhada em 24.21.0 (ponta da linha 24). O digest da imagem base foi
  recalculado. O `pnpm-lock.yaml` acompanha porque o pnpm registra o runtime do
  `devEngines` — sem isso o `--frozen-lockfile` do CI recusaria.
- Os workflows deixam de declarar `node-version` e passam a ler o `.nvmrc`.

Medido em 06/10/2026 contra `nodejs/Release/schedule.json`: a linha 24 entra em
maintenance em 20/10/2026 e a 26 vira Active LTS em 28/10/2026. O salto ao 26 é PR
próprio, deliberadamente fora daqui — rodar Current em produção, e empilhar um major de
runtime sobre a migração de toolchain desta branch, são duas coisas que este commit não
faz.

Refs #1035

Assisted-by: Claude-Code:claude-opus-5
…antes

45 dos 64 scripts do `package.json` carregavam `--experimental-strip-types
--enable-source-maps --no-warnings`, e as três foram medidas no runtime do `devEngines`
(24.21.0) em 06/10/2026 — nenhuma fazia diferença:

- `--experimental-strip-types`: o stripping é DEFAULT desde o Node 23.6 (backport na
  22.18). A prova verificável é `--no-strip-types` existir como negação no `--help`. O
  repositório tem zero `enum` e zero `namespace` (garantido por `erasableSyntaxOnly`),
  então nada aqui precisaria de `--experimental-transform-types` no lugar.
- `--enable-source-maps`: o stripping troca tipo por espaço em branco, então a posição no
  `.ts` é preservada e o stack trace sai idêntico com e sem a flag (medido: mesmo
  `arquivo.ts:27:9`). E nenhuma das 10 dependências de produção publica `.js.map` — a
  flag não tinha alvo algum neste repositório.
- `--no-warnings`: existia para calar o `ExperimentalWarning` do stripping, que não é mais
  emitido. Sem ela, script real e `node --test` emitem zero linha em stderr.

A terceira é a que justifica o commit. As duas primeiras eram inertes; `--no-warnings`
estava ENGOLINDO `DeprecationWarning` em 45 comandos — o canal pelo qual o Node anuncia o
que quebra na próxima major, e a informação que torna acionável a disciplina de subida de
runtime do ADR-0073. Em produção o mesmo valia por `NODE_NO_WARNINGS=1` no Dockerfile,
que sai junto: o aviso passa a chegar ao stderr do contêiner, logo ao log do ECS.

`tests/cleanup/node-flags-not-redundant.test.ts` impede o retorno, que aconteceria por
imitação do script vizinho — foi assim que chegou a 45.

Um gate quase quebrou em silêncio: `migrate-boot-inversion.test.ts` ancorava em
`'--no-warnings src/server.ts'` para distinguir a invocação real do `pkill -f` do
cleanup. Sem a flag, os dois `indexOf` iriam a -1. A âncora passa a ser `node` no início
da linha, que o `pkill` não casa. Uma flag sem função sustentava a asserção.

Fora do escopo, de propósito: ~70 citações em `handbook/specs/` e `handbook/interviews/`
ficam intactas — é acervo datado, e reescrever registro histórico é falsificá-lo.

Refs #1035

Assisted-by: Claude-Code:claude-opus-5
…ndante

A flag removida do código voltaria pela porta da frente: `.claude/` tinha 43 ocorrências
das três flags em 16 arquivos, e é ela que o agente lê ANTES de olhar o repositório. O
mapa de propagação estava visível em `git grep -c` ordenado — `package.json:45` →
`nodejs-runtime-expert.md:8` → `Procfile:6`: o manifesto espalhou para a instrução, que
ensinava a espalhar de novo.

O achado mais caro não eram os comandos, e sim duas afirmações FALSAS do
`nodejs-runtime-expert`, que numa sessão futura teriam me feito recusar esta limpeza:

- "`--experimental-strip-types` está em Active development em Node 24 (não Stable)" — é
  default desde a 23.6, e produção já dependia disso (o ENTRYPOINT da imagem não passa
  flag alguma).
- `@types/node` declarado como `^22.10.0`, quando o manifesto diz `^24.0.0`.

As duas foram corrigidas e o agente ganhou um bloco explícito proibindo as três flags,
com a razão medida de cada uma.

Também atualizados: `rules/testing.md` (carrega por path em todo teste), as skills
`nodejs-fs-scripter`, `nodejs-process-runner`, `ts-domain-modeler`, `tdd-strategist`,
`tdd-theorist`, `tdd-tutor`, `test-pyramid-engineer`, `ts-quality-checker` e os agentes
`cnab240-bradesco` e `docker-compose-expert`.

Preservado de propósito: o gatilho de ativação do `nodejs-runtime-expert` (alguém ainda
pode PERGUNTAR sobre a flag), `--enable-source-maps` na lista de flags de diagnóstico do
Node, e os ponteiros para `handbook/reference/nodejs/`, que descrevem a doc oficial — ela
documenta essas flags, e o ponteiro não é instrução de uso.

Refs #1035

Assisted-by: Claude-Code:claude-opus-5
… um minor fixo)

O `Dockerfile` declarava `# syntax=docker/dockerfile:1.10` e a ponta estável é `1.27.1`
(medida em 06/10/2026 no Docker Hub) — 17 minors atrás, sem receber correção desde que a
`1.11` saiu, e sem nada no repositório capaz de apontar isso.

A doc oficial é explícita, e é a fonte aqui:

  "docker/dockerfile:1 - kept updated with the latest 1.x.x minor and patch release.
   (…) If a specific version is used, such as 1.2 or 1.2.1, the Dockerfile needs to be
   updated manually to continue receiving bugfixes and new features."
  — https://docs.docker.com/build/buildkit/frontend/

Um minor fixo é a pior das três posições possíveis: não é imutável (recebe patches
`1.10.x`) nem atualizado (congelou na `1.11`). A doc só oferece duas coerentes — `:1` ou
uma versão exata com digest.

Por que isto NÃO contraria o digest pin do ADR-0011: o frontend do `# syntax` é baixado
em tempo de BUILD e não entra na imagem final; ele não afeta a reprodutibilidade do que
roda em produção, que é o que aquele ADR protege. A imagem base segue pinada por
`@sha256`. A distinção está registrada em comentário no próprio Dockerfile, porque é o
tipo de coisa que alguém "corrige" de volta por coerência aparente.

O repositório tinha TRÊS versões divergentes do mesmo directive: `Dockerfile` em `1.10`,
`.claude/agents/docker-compose-expert.md` em `1.7` — instrução viva, mais velha que o
código — e `handbook/reference/docker/` em `:1`, que já era a cópia correta da doc
oficial. As duas primeiras foram alinhadas; a reference não foi tocada. O agente ganhou a
regra com o link, para o número não voltar.

Refs #1035

Assisted-by: Claude-Code:claude-opus-5
…u não seguia o symlink do pnpm

Achado da revisão do PR. O commit 95abe48 removeu as três flags afirmando que
"nenhuma das 10 dependências de produção publica .js.map". **Era falso, e por erro de
medição:** o `find` rodou sem `-L`, e `node_modules/<dep>` é SYMLINK sob pnpm — a
varredura nunca desceu no alvo e devolveu zero para tudo.

Remedido com `find -L`: `drizzle-orm` publica **444** arquivos `.js.map` e
`fast-xml-parser` mais 4. O efeito é reproduzível num erro lançado dentro da lib:

  sem a flag:  at getTableColumns (…/drizzle-orm/utils.js:109:15)
  com a flag:  at getTableColumns (…/drizzle-orm/src/utils.ts:208:9)

O raciocínio original — "o stripping preserva a posição no `.ts`" — está correto para o
código DESTE repositório, e é por isso que enganou: a flag nunca serviu a ele. Serve às
dependências que publicam mapa, e `drizzle-orm` é a mais usada do projeto (183 imports).
Perder isso degradaria o stack trace de `serve`, dos workers e dos jobs em produção.

A flag volta a todos os pontos de invocação: `package.json` (45 scripts), `Procfile`,
`NODE_OPTIONS` do Dockerfile, `scripts/e2e/*.sh`, o spawn de `test-integration.ts` e os
4 testes que spawnam processo. As outras duas seguem removidas — a medição delas não
dependia de `node_modules` e foi reproduzida.

Os outros três achados da revisão, todos de alcance de gate:

- `node-flags-not-redundant` cobria só o `package.json`, enquanto a `rules/testing.md`
  prometia por escrito "todos os comandos do repositório". Passa a varrer os 12 pontos
  de invocação, com caso que falha se um caminho da lista deixar de existir. ⚠️ Na
  primeira execução ampliada ele acusou `Dockerfile` e `test-integration.ts`, que apenas
  DOCUMENTAM as flags em comentário — a armadilha uso × menção. Agora ignora linha de
  comentário (`#`, `//`, `*`). Prova invertida: `--no-warnings` injetado numa linha de
  código do `Procfile` é acusado.
- `node-version-single-source` só enxergava workflow que JÁ usava `setup-node`.
  `audit.yml` executava `corepack enable` + `pnpm audit` sem declarar runtime, caindo no
  Node pré-instalado do runner — o "CI escolhe sozinho" que o ADR-0073 §2 encerra. O
  gate passa a cobrar por QUEM RODA Node, e o `audit.yml` ganhou o passo.
- `migrate-boot-inversion` ancorava em `^\s*node src/…`, que a volta da flag quebraria.
  A âncora aceita as flags que houver entre `node` e o caminho.

Textos que repetiam a premissa errada foram corrigidos, não só renomeados:
`rules/testing.md`, `agents/nodejs-runtime-expert.md` e o comentário do Dockerfile. O
agente ganhou a regra operacional que faltava: **ao medir dentro de `node_modules` sob
pnpm, usar `find -L`**.

Refs #1035

Assisted-by: Claude-Code:claude-opus-5
A branch do toolchain estava parada à espera da dev. Merge, não rebase: ela está
publicada e tem o PR #1035 aberto.

Cinco conflitos, dos treze arquivos que os dois lados tocaram:

- `scripts/e2e/bruno-all.sh` (delete/modify) — remoção aceita. O Bruno saiu do
  projeto inteiro pelo ADR-0074, e o runner não se ressuscita.
- `eslint.config.js` (delete/modify, invertido) — remoção aceita. A branch retira
  o ESLint pelo ADR-0072; a dev havia acrescentado `.tmp/**` aos ignores. A entrada
  foi portada para `ignorePatterns` do `.oxlintrc.json`, ao lado dos irmãos de mesma
  categoria (`tools/bugs-scripts/**`, `tests/reports/**`): descartável, gitignored,
  fora de qualquer tsconfig. Sem ela, script ad-hoc em `.tmp/` volta a travar o lint.
- `package.json` — os dois lados removeram coisas diferentes e a união remove ambas:
  sai `test:integration:all` (dev, apontava para o runner do Bruno) e sai
  `@usebruno/cli` (dev), sai `@typescript/native-preview` (branch, ADR-0072).
  Preservadas as flags que o TS 7 tornou redundantes, já retiradas pela branch.
- `pnpm-lock.yaml` — lado da dev aceito e regenerado com `pnpm install`, como manda
  o lockfile. Coexistem as deps do toolchain (oxlint, oxlint-tsgolint, typescript
  7.0.2) e o que a dev trouxe: fastify 5.12.5, nodemailer 10.0.15, zero `usebruno`,
  zero `typescript-eslint`, zero `native-preview`.
- `handbook/architecture/adr/README.md` — conflito aditivo: 0072 e 0073 da branch,
  0074 da dev. Os três preservados em ordem; 0034 e 0038 seguem `Superseded by 0074`.

Um vermelho veio do merge e foi corrigido aqui: `node-flags-not-redundant.test.ts`
listava `scripts/e2e/bruno-all.sh` em `INVOCATION_POINTS`, e o arquivo deixou de
existir. As três falhas tinham essa causa única — o `readSource` estourando ENOENT
derrubava junto os dois testes que apenas iteram a mesma lista, que é exatamente o
cenário descrito no docstring do gate. Nenhum dos dois lados produz esse vermelho
sozinho: a lista é artefato do ADR-0072, o arquivo foi removido pelo ADR-0074.

Gate verde: typecheck, format:check, lint (1017 arquivos, 154 regras, 0 erros) e
11963 testes com fail 0, 20 skipped.

Assisted-by: Claude-Code:claude-opus-5
… vacuidade

Nenhum defeito de correção no fonte de produção. O que a revisão achou foram duas
guardas que passariam vazias e duas inconsistências de registro.

`node-version-single-source`: o padrão `RUNS_NODE` varria uma única forma de
invocação — `run:` em linha própria. Medido em 06/10, ele não casava nem
`- run: pnpm install`, que é a forma mais comum em YAML, nem `run: |` com o comando
na linha seguinte; `audit.yml` só foi pego porque, além dos três blocos escalares,
tinha um `run: corepack enable` numa linha. Passa a cobrir as quatro formas. Provado
invertido contra caso sintético: o padrão antigo deixa passar três das quatro, o novo
acusa as quatro e segue ignorando comentário YAML, `uses:` e menção em prosa. Os 9
workflows de hoje recebem o mesmo veredito nas duas versões — o buraco era latente, e
o próximo workflow escrito com bloco escalar o tornaria vivo com o gate verde.

`lint-gaps`: a guarda contra vacuidade era `scanned > 1500`, um limiar. Com 2041
arquivos, dezenas poderiam escapar sem derrubar o número — e escapam de verdade os
`.ts` versionados que caem fora dos globs de `include` do tsconfig, porque
`trackedTs()` lista `src tests scripts db` enquanto o programa só entrega
`db/drizzle/**`. Contagem não é a propriedade: passa a afirmar que nenhum arquivo
versionado fica de fora, listando quais quando houver. A guarda contra lista vazia do
git virou caso próprio, senão a comparação de conjuntos vazios passaria sozinha.

`integration.yml`: a remoção das flags redundantes levou junto `--enable-source-maps`,
que não é redundante no Node 24 — as redundantes eram `--experimental-strip-types` e
`--no-warnings`. Todos os outros pontos de invocação a mantêm. Sem ela, um stack trace
do próprio runner de integração aponta posição no `.js` empacotado.

`compose.yaml`: o comentário afirmava "Sem NODE_OPTIONS" enquanto o `Dockerfile:128`
define `NODE_OPTIONS="--enable-source-maps"`, herdado por todos aqueles serviços.
Registro que descreve o contrário do código.

`node-flags-not-redundant`: `INVOCATION_POINTS` não listava três hooks que executam o
runtime (`pre-commit-typecheck.sh`, `stop-quality-gate.sh`, `prettier-write.sh`), onde
as flags poderiam voltar sem vermelho. O critério de entrada é executar, não citar:
`block-npm.sh` e `block-inline-interpreter.sh` mencionam `node` e `pnpm` dezenas de
vezes no texto de ajuda e não invocam nada. Fica registrado no docstring que a lista é
manual e que nada guarda contra entrada ausente — só contra entrada morta.

Gate: typecheck, format:check e lint verdes; 11964 testes, fail 0, 20 skipped.

Assisted-by: Claude-Code:claude-opus-5
@GabrielAderaldo

Copy link
Copy Markdown
Contributor Author

Sincronizado com a dev + achados da revisão

A branch estava 9 commits atrás da dev. Trazida por merge (não rebase — ela está publicada e este PR está aberto). Agora 0 atrás, e a dev é ancestral de fato (git merge-base --is-ancestor).

Conflitos: 5

Arquivo Tipo Resolução
scripts/e2e/bruno-all.sh delete/modify remoção aceita — o Bruno saiu pelo ADR-0074
eslint.config.js delete/modify invertido remoção aceita (ADR-0072) + .tmp/** portado para .oxlintrc.json
package.json conteúdo união das duas remoções
pnpm-lock.yaml conteúdo lado da dev + pnpm install
handbook/architecture/adr/README.md conteúdo aditivo — 0072, 0073 e 0074 preservados

O eslint.config.js merece nota: a branch deletou o arquivo e a dev o modificou, acrescentando .tmp/** aos ignores. Aceitar a remoção sem mais nada perderia essa correção em silêncio — o ignorePatterns do oxlint não tinha a entrada. Ela foi portada para junto dos irmãos de mesma categoria (tools/bugs-scripts/**, tests/reports/**): descartável, gitignored, fora de qualquer tsconfig.

No manifesto, cada lado removera coisa diferente: saíram test:integration:all e @usebruno/cli (dev) e @typescript/native-preview (branch). O lock passa a conter o toolchain e o que a dev trouxe — fastify@5.12.5, nodemailer@10.0.15, zero usebruno, zero typescript-eslint, zero native-preview.

O audit (produção — blocking) deve virar verde

Ele falhava porque a branch, aberta antes do #1037, ainda carregava fastify@5.12.1 e nodemailer@9.1.1. Medido após o merge: pnpm audit --prod --audit-level=high → No known vulnerabilities found.

Achados do code-review tratados

Nenhum defeito de correção no fonte de produção. Dois gates aprovavam por vacuidade:

  • node-version-single-source — o padrão RUNS_NODE varria só run: em linha própria. Não casava nem - run: pnpm install, a forma mais comum, nem run: | com o comando na linha seguinte. Passa a cobrir as quatro formas, provado invertido contra caso sintético; os 9 workflows recebem o mesmo veredito nas duas versões, então o buraco era latente.
  • lint-gaps — a guarda era scanned > 1500, um limiar: com 2041 arquivos, dezenas escapariam sem derrubar o número. Passa a afirmar a propriedade — nenhum .ts versionado fora do programa — e lista quais, quando houver.

Mais três correções de consistência: integration.yml recuperou --enable-source-maps (que não é redundante no Node 24, ao contrário de --experimental-strip-types e --no-warnings); o comentário do compose.yaml afirmava "Sem NODE_OPTIONS" enquanto o Dockerfile:128 a define; e INVOCATION_POINTS ganhou três hooks que executam o runtime.

Para a revisão da borda

.meta({ example }) → .meta({ examples: [...] }) muda o OpenAPI publicado em 4 pontos (auth ×3, budget-plans ×1). É o keyword correto para 3.1.1 e está declarado no ADR-0072 §D6, mas consumidor que leia schema.example perde o valor — vale conferir do lado do BFF.

Gate

typecheck ✅ · format:check ✅ · lint ✅ (1017 arquivos, 154 regras, 0 erros) · 11964 testes, fail 0, 20 skipped.

Assisted-by: Claude-Code:claude-opus-5

@GabrielAderaldo
GabrielAderaldo merged commit 9085882 into dev Oct 6, 2026
21 checks passed
@GabrielAderaldo
GabrielAderaldo deleted the chore/toolchain-oxc-ts7 branch October 6, 2026 23:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants