Skip to content

refactor(engine): extrai métricas puras de Race.tsx para src/lib/metrics.ts (#32) - #48

Merged
caioross merged 1 commit into
mainfrom
auto/issue-32-metrics-lib
Jul 22, 2026
Merged

refactor(engine): extrai métricas puras de Race.tsx para src/lib/metrics.ts (#32)#48
caioross merged 1 commit into
mainfrom
auto/issue-32-metrics-lib

Conversation

@caioross

Copy link
Copy Markdown
Owner

Contexto

A honestidade das métricas (WPM, precisão, erros, progresso) é a verdade sagrada do domínio (spec §0.3). Até aqui todo o cálculo morava inline em src/components/Race.tsx, impossível de testar por unidade — o único "teste" era uma cópia da lógica em .claude/skills/cr-typing-engine/scripts/validate-metrics.mjs, sujeita a divergir em silêncio do código de produção.

O que mudou e por quê

Extrai as quatro funções puras e determinísticas para src/lib/metrics.ts (fonte única de verdade) e cobre com src/lib/metrics.test.ts:

Race.tsx passa a importar e usar essas funções; correctChars segue no useMemo (área sagrada HANDBOOK §2 — nenhuma alocação/loop novo por keystroke no hot path).

Extração 1:1 — zero mudança de número na tela

O Parecer do Conselho antecipava uma contradição ("copie 1:1" × "nunca 100 com erro") porque avaliou uma revisão antiga com Math.round. Divergência: a correção de honestidade da #20 já está na main (Race.tsx já usava Math.floor + teto de 99%). Logo esta fatia é um refactor puro, byte-a-byte — nenhuma corrida mostra número diferente de antes. Os testes apenas pinam as invariantes já vigentes; não há impacto visível a declarar.

Como foi validado (gate real)

  • pnpm typecheck
  • pnpm build
  • pnpm test ✅ — 34/34 (13 novos em metrics.test.ts + 21 de room.test.ts)
  • node .claude/skills/cr-typing-engine/scripts/validate-metrics.mjs37/0
  • node scripts/validate-persistence.mjs33/0
  • pnpm lint = N/A (repo sem config ESLint; a CI não roda lint)

Riscos

Baixos. Refactor sem mudança de comportamento; a UI renderiza os mesmos valores. Diff mínimo (3 arquivos, +138/−15). Numa fatia futura, validate-metrics.mjs pode passar a importar de metrics.ts para eliminar de vez a duplicação de harness (fora do escopo mínimo aqui).

Segui o plano do Conselho, com uma divergência declarada: como a honestidade #20 já estava na main, não houve mudança de número nem necessidade de Closes #20.

Closes #32

…ics.ts (#32)

Move o cálculo de WPM, precisão, progresso e contagem de acertos de dentro
de Race.tsx para funções puras e determinísticas em src/lib/metrics.ts,
cobertas por src/lib/metrics.test.ts. Extração 1:1 — nenhuma mudança de
fórmula, arredondamento ou comportamento observável na tela.

- countCorrectChars / computeWpm / computeAccuracy / computeProgress
- correctChars segue no useMemo (área sagrada, HANDBOOK §2 — sem trabalho
  extra por keystroke no hot path da corrida)
- fonte única de verdade: elimina a divergência silenciosa entre a lógica
  de produção e a cópia em validate-metrics.mjs

A precisão honesta da issue #20 (floor + teto de 99% com erros) já estava
inline em Race.tsx e foi preservada byte-a-byte; os testes a pinam.

Refs #32
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

@caioross is attempting to deploy a commit to the caioross' projects team on Vercel, but is not a member of this team. To resolve this issue, you can:

  • Make your repository public. Collaboration is free for open source and public repositories.
  • Upgrade to pro and add @caioross as a member. A Pro subscription is required to access Vercel's collaborative features.
    • If you're the owner of the team, click here to upgrade and add @caioross as a member.
    • If you're the user who initiated this build request, click here to request access.
    • If you're already a member of the caioross' projects team, make sure that your Vercel account is connected to your GitHub account.

To read more about collaboration on Vercel, click here.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request extracts typing metrics calculation logic (countCorrectChars, computeWpm, computeAccuracy, and computeProgress) from the Race component into a dedicated metrics utility file, accompanied by a comprehensive unit test suite. Feedback was provided to optimize the countCorrectChars function by limiting the loop iteration to the minimum length of the typed and target strings, avoiding unnecessary comparisons with undefined.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/lib/metrics.ts
Comment on lines +14 to +18
export function countCorrectChars(typed: string, code: string): number {
let c = 0;
for (let i = 0; i < typed.length; i++) if (typed[i] === code[i]) c++;
return c;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Atualmente, o loop percorre todo o comprimento de typed. Se typed for maior que code, o código tentará acessar índices fora dos limites de code (retornando undefined), o que é ineficiente e conceitualmente incorreto. Embora a comparação com undefined resulte em false e não quebre a lógica, é mais seguro e eficiente limitar a iteração ao menor comprimento entre as duas strings usando Math.min(typed.length, code.length).

Suggested change
export function countCorrectChars(typed: string, code: string): number {
let c = 0;
for (let i = 0; i < typed.length; i++) if (typed[i] === code[i]) c++;
return c;
}
export function countCorrectChars(typed: string, code: string): number {
let c = 0;
const minLength = Math.min(typed.length, code.length);
for (let i = 0; i < minLength; i++) {
if (typed[i] === code[i]) c++;
}
return c;
}

@caioross

Copy link
Copy Markdown
Owner Author

🩺 Parecer do PR Doctor — APROVADA (§7.3 Normal)

Classificação: normal (refactor de engine com validate-metrics verde; não toca API de salas, persistência nem migração). Diff lido inteiro (3 arquivos, Race.tsx + metrics.ts + metrics.test.ts), head 769ebe3.

Conferido linha a linha (extração é 1:1, não há mudança de número na tela):

  • computeAccuracy (src/lib/metrics.ts:31) reproduz byte-a-byte o Math.floor + teto 99% que já estava na main — a honestidade da Engine/Honestidade: "Precisão 100%" coexiste com "Erros 1" — arredondamento mascara erro e rótulo confunde #20 não regride e não é reintroduzida em dobro.
  • computeWpm mantém o guard elapsedMin > 0.001; computeProgress mantém codeLen === 0 → 0 e o clamp em 1; countCorrectChars mantém o loop char-a-char sem estourar quando typed > code.
  • Área sagrada preservada (src/components/Race.tsx:58): correctChars continua no useMemo com as mesmas dependências — nenhuma alocação ou loop novo por keystroke no hot path.

Gate reproduzido localmente no worktree da branch (não confiei só no corpo do PR):

  • pnpm typecheck
  • pnpm test34/34 (13 de metrics.test.ts + 21 de room.test.ts)
  • node .claude/skills/cr-typing-engine/scripts/validate-metrics.mjs37 passaram, 0 falharam
  • CI Install, typecheck & build ✅. O check Vercel vermelho é infra de permissão da conta ("Git author caioross must have access to the project"), idêntico em todas as PRs abertas — não é falha de build.

Closes #32 está correto: a issue é a extração inteira, não uma fatia. Mergeando em squash.

@caioross
caioross merged commit af10e6c into main Jul 22, 2026
2 of 3 checks passed
@caioross
caioross deleted the auto/issue-32-metrics-lib branch July 22, 2026 13:19
caioross added a commit that referenced this pull request Jul 22, 2026
União com origin/main (conflito em Race.tsx) mantendo uma só fonte de verdade
em vez de reintroduzir cópias que a main acabou de eliminar.

- Race.tsx fica na forma composta desta branch (RaceTrack + TypingCore + chat);
  o cálculo inline que o #32 tirou de lá não volta.
- TypingCore.tsx passa a importar countCorrectChars/computeWpm/computeAccuracy/
  computeProgress de @/lib/metrics (#32, mergeada no PR #48). A extração era 1:1
  do Race.tsx pré-#32; sem isso o merge recriava a duplicação de fórmula que a
  #32 existiu para matar. `correctChars` segue no useMemo (área sagrada §2).
- /api/snippet usa resolveLang/resolveDifficulty de @/lib/room (#35, mergeada no
  PR #38) no lugar da checagem própria contra LANGUAGES/DIFFICULTIES — mesma
  política de fronteira das rotas de sala, uma implementação só.

Gate: typecheck OK · build OK (/practice 149 kB estática, /api/snippet dinâmica)
· vitest 34/34 · validate-metrics 37/0 · validate-persistence 62/0.

Refs #25

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

Engine/Testes: extrair métricas puras de Race.tsx para src/lib/metrics.ts + testes de honestidade (WPM/precisão)

1 participant