fix(OPS62-followup): elimina flake do municipalitiesWithoutUpdateTool — fixture com relógio ancorado #89

Merged
fsolla merged 1 commit from OPS62-followup-ci-flake-do-municipalitieswithoutupdatetool-u into main 2026-08-19 16:16:51 +00:00
Owner

Closes #81

OPS62-FOLLOWUP — flake do municipalitiesWithoutUpdateTool.unit.spec.ts eliminado pela raiz (2 ocorrências 2026-08-18: gate local 19:22 + CI run 779 23:53).

Causa raiz (reproduzida — refuta a hipótese da Issue de locale/ICU): o bug era do fixture do teste, não do ambiente nem do sort de produção. daysAgo() chamava Date.now() por chamada (spec); as duas construções daysAgo(40) de Beta/Alpha podiam cair em milissegundos diferentes sob carga (GC/preempção do fork do vitest em máquina compartilhada), deixando o timestamp de Alpha estritamente mais novo → em sortStale (src/utilities/ai/tools/getMunicipalitiesWithoutUpdate.ts) o sort por timestamp vencia o tie-break por nome → o teste recebia [Beta, Alpha] no lugar de [Alpha, Beta] (só o par trocado, resto correto — o sort atuou, sobre timestamps acidentalmente desiguais).

Repro mecânica (sort de produção verbatim + fixture, node /tmp/opencode/ops62-followup-repro.mjs — não commita): fixture atual 0.08% de falhas em 200k runs sem pausa e 100% com pausa de 1 ms injetada (amostra ["Mike","Zulu","Beta","Alpha","Gama"] — o diff observado); fixture ancorado: 0 falhas em 2000 runs sob a mesma pausa.

Fix (test-only; nada em produção — o sort real é determinístico sobre timestamps fixos do DB):

  1. Âncora fixtureNowMs = Date.now() no load do módulo (padrão já usado por prioritiesTool.unit.spec.ts:12), com comentário explicando a classe de flake;
  2. Hoist do timestamp empatado (const stale40 = daysAgo(40) compartilhado por Beta/Alpha) — a igualdade vira estrutural, imune até a uma regressão da âncora;
  3. Scan da mesma classe: prioritiesTool (âncora no load) e suggestionCatalog (now fixo) já seguros; nenhum outro spec combina Date.now() por chamada com assunção de igualdade.

Verificação: spec 30/30 runs; unit full 249 arquivos/2405 testes; int 89/791; tsc/lint/format/check:cycles verdes (knip = limitação P3 pré-existente). Changelog: docs/changelog/2026-08-19-ops62-followup.md. Impl plan: docs/plans/ops62-followup-ci-flake-fixture-timestamps-impl.md. /simplify: 3 reviewers aprovaram (1 ajuste aplicado: hoist; 1 processo aplicado: fase de changelog no plano); débito de review restante descartado (dependência do stable sort do V8 é garantida por spec, não é risco).

Closes #81 **OPS62-FOLLOWUP — flake do `municipalitiesWithoutUpdateTool.unit.spec.ts` eliminado pela raiz** (2 ocorrências 2026-08-18: gate local 19:22 + CI run 779 23:53). **Causa raiz (reproduzida — refuta a hipótese da Issue de locale/ICU):** o bug era do fixture do teste, não do ambiente nem do sort de produção. `daysAgo()` chamava `Date.now()` **por chamada** (`spec`); as duas construções `daysAgo(40)` de `Beta`/`Alpha` podiam cair em milissegundos diferentes sob carga (GC/preempção do fork do vitest em máquina compartilhada), deixando o timestamp de `Alpha` estritamente mais novo → em `sortStale` (`src/utilities/ai/tools/getMunicipalitiesWithoutUpdate.ts`) o sort por timestamp vencia o tie-break por nome → o teste recebia `[Beta, Alpha]` no lugar de `[Alpha, Beta]` (só o par trocado, resto correto — o sort **atuou**, sobre timestamps acidentalmente desiguais). Repro mecânica (sort de produção verbatim + fixture, `node /tmp/opencode/ops62-followup-repro.mjs` — não commita): fixture atual **0.08%** de falhas em 200k runs sem pausa e **100%** com pausa de 1 ms injetada (amostra `["Mike","Zulu","Beta","Alpha","Gama"]` — o diff observado); fixture ancorado: **0 falhas** em 2000 runs sob a mesma pausa. **Fix (test-only; nada em produção — o sort real é determinístico sobre timestamps fixos do DB):** 1. Âncora `fixtureNowMs = Date.now()` no load do módulo (padrão já usado por `prioritiesTool.unit.spec.ts:12`), com comentário explicando a classe de flake; 2. Hoist do timestamp empatado (`const stale40 = daysAgo(40)` compartilhado por `Beta`/`Alpha`) — a igualdade vira **estrutural**, imune até a uma regressão da âncora; 3. Scan da mesma classe: `prioritiesTool` (âncora no load) e `suggestionCatalog` (`now` fixo) já seguros; nenhum outro spec combina `Date.now()` por chamada com assunção de igualdade. **Verificação:** spec 30/30 runs; unit full 249 arquivos/2405 testes; int 89/791; tsc/lint/format/`check:cycles` verdes (knip = limitação P3 pré-existente). Changelog: `docs/changelog/2026-08-19-ops62-followup.md`. Impl plan: `docs/plans/ops62-followup-ci-flake-fixture-timestamps-impl.md`. /simplify: 3 reviewers aprovaram (1 ajuste aplicado: hoist; 1 processo aplicado: fase de changelog no plano); débito de review restante descartado (dependência do stable sort do V8 é garantida por spec, não é risco).
fsolla added 1 commit 2026-08-19 16:02:31 +00:00
fix(OPS62-followup): âncora de relógio no fixture do municipalitiesWithoutUpdateTool — elimina o flake de ordenação por raiz
Some checks failed
CI (PR) / checks (pull_request) Successful in 2m58s
PR Ready + auto-merge / ready-automerge (pull_request) Failing after 4m37s
7c73b0c536
Causa raiz (reproduzida, refuta a hipótese da Issue): daysAgo() chamava
Date.now() por chamada; as duas daysAgo(40) de Beta/Alpha podiam cair em ms
diferentes sob carga, deixando Alpha estritamente mais novo — sortStale
ordenava por timestamp e o tie-break por nome nunca disparava (0.08% das
construções sem pausa; 100% com pausa de 1 ms; fixture ancorado: 0 em 2000).
Fix test-only: âncora fixtureNowMs no load do módulo (padrão prioritiesTool)
+ hoist do timestamp empatado (igualdade estrutural) + comentário.
Nada muda em produção — o sort real é determinístico sobre timestamps fixos.
Verificação: spec 30/30, unit 2405, int 791, tsc/lint/format/cycles verdes.
fsolla force-pushed OPS62-followup-ci-flake-do-municipalitieswithoutupdatetool-u from 7c73b0c536 to 39fb3b47f0 2026-08-19 16:13:15 +00:00 Compare
fsolla merged commit 39fb3b47f0 into main 2026-08-19 16:16:51 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: fsolla/teqo#89
No description provided.