Audit assertions and fix observable defects - #1
Merged
Merged
Conversation
Convenção da lição 11: reabertura consciente exige nomear a força real em plano aprovado antes da execução. As forças são duas — correção (defeitos confirmados por auditoria externa e reproduzidos: crash do servidor, nome×desenho discordando em 5 posições, glifo no aria-live, inglês no núcleo) e curricular (afirmações autorreferentes são defeitos latentes invisíveis ao auditor interno; a resposta estrutural é uma fitness function das afirmações no CI). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCJV7nKZ1kxi1CtSbvywDg
…elha
O grafo audita as fronteiras; nada auditava o que o projeto AFIRMA
sobre si. E afirmação autorreferente é defeito latente da mesma classe
da lição 4: invisível até que a força certa a exponha — aqui, uma
auditoria externa, que achou duas falsas ("português em tudo, até nos
identificadores"; "9 adaptadores intocados") sobrevivendo a três
passadas de conformidade. Resposta estrutural (lição 9): a classe
inteira vira código.
Quatro checagens em Node puro, zero dependências: idioma dos
identificadores em src/ (denylist do glossário; acesso .propriedade é
API, permitido), contagem de testes prometida × executada (de quebra
mata o falso-verde de um glob sem match: 0 ≠ N), hashes citados em
crases nos .md resolvendo no repositório, e "N portas, M adaptadores"
do console contra a composição real.
O fóssil — a primeira execução, registrada:
❌ Idioma — o glossário promete português nos identificadores, mas:
src/adaptadores/animacao-do-ciclo.js: "next" ×2
src/aplicacao/atualizar-lua.js: "calculator" ×3
src/dominio/servico-de-fases.js: "limit" ×8
src/dominio/servico-de-fases.js: "name" ×8
src/dominio/servico-de-fases.js: "phase" ×6
✅ testes: 49 = 49 · hashes: 19/19 · portas: 3 · adaptadores: 12
O vermelho é o argumento: a máquina reprova o discurso antes de o
código consertá-lo (próximo commit). CI ganha o terceiro job, com
fetch-depth 0 — hashes antigos não existem em checkout raso.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCJV7nKZ1kxi1CtSbvywDg
…tor verde O que o auditor reprovou no commit anterior, corrigido: limit/name/ phase viram limite/nome/fase no domínio (o arquivo exemplar do núcleo contradizia a meta-afirmação mais enfática do projeto), calculator vira calculadora na aplicação, next vira proxima na animação. E as contrapartes que o auditor v1 não cobre, corrigidas pela mesma régua: phase no dublê compartilhado, prev e os locais em inglês do oráculo (phases, expected, value, limit, table) — os DADOS históricos do oráculo ficam bit a bit intactos; só os nomes de variável mudam. Zero mudança de comportamento: o oráculo dos 101 valores passa sem se mover (terceiro pagamento — lição 3) e a suíte segue 49/49. Estes resíduos sobreviveram a três passadas de conformidade (d347986, efcbb44, ee569df) — regex não entende semântica (lição 7), e nenhuma máquina lia a afirmação. Agora lê: npm run afirmacoes, verde. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCJV7nKZ1kxi1CtSbvywDg
decodeURIComponent vivia FORA do try — e numa função async, a exceção que escapa do handler vira rejeição não tratada e encerra o Node. Reproduzido antes da correção: GET /%zz → URIError → processo morto → connection refused para sempre. Uma requisição malformada matava o servidor de desenvolvimento inteiro. O parse inteiro entra no try; URIError responde 400, o resto segue 404. Verificado após a correção: /%zz → 400 com o processo vivo, / → 200, /inexistente → 404. O único defeito de correção reproduzível da auditoria externa morava no único arquivo declarado "fora da arquitetura" — a qualidade vive exatamente onde a disciplina foi aplicada, e a falha onde ela foi dispensada. Não há teste automatizado: exigiria refatorar o servidor para injetar o handler, e elaboração fora da arquitetura é decoração (lição 10); a prova manual acima é o registro. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCJV7nKZ1kxi1CtSbvywDg
O README prometia "nome puro, sem glifo verbalizado" — e o
aria-valuetext cumpre. Mas a única região aria-live da página era o
#nome, montado com o emoji à frente: o leitor de tela falava o glifo
("lua cheia" pelo 🌕) e anunciava cada mudança DUAS vezes (valuetext +
live region). A promessa valia num canal e o outro a derrotava — o
mesmo problema de dois clientes de uma API, que a própria tabela do
README nomeia.
#nome vira aria-hidden: o aria-valuetext é agora o canal falado
único, exatamente o que o README declarava. Custo assumido e
registrado no glossário: "dia X de 29" sai do anúncio — enriquecer o
valuetext exigiria mudar a porta do anunciador, e o preço de um
contrato maior não se paga por um sufixo (lição 12).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCJV7nKZ1kxi1CtSbvywDg
…e disco cheio Os limiares de snap (0.02/0.98) eram escolhidos à parte das fronteiras de NOME — que vivem em fração nas posições históricas (0.05 → ≈0.0245; 0.48 → ≈0.9961). Na banda entre os dois critérios, os sliders 46, 47, 52, 53 e 54 desenhavam o disco totalmente cheio (nos dois substratos — a paridade por construção garantiu que os renderizadores concordassem entre si, inclusive no engano) sob o rótulo "Gibosa". Novos limiares: 0.0244 / 0.9961, logo aquém das fronteiras. O painel continua folha — a paridade nome↔sombra não vem de import, vem de um teste de varredura dos 101 valores: disco cheio ⇒ "Lua Cheia", disco escuro ⇒ "Lua Nova". Alinhamento perfeito é impossível com um único limiar (sliders 48 e 52 têm a MESMA fração e nomes diferentes — as fronteiras de nome são estritas de um lado só); o resíduo aceito é uma lasca sub-pixel no slider 48 sob "Lua Cheia", conferida de olho (lição 13): a gibosa quase-cheia agora parece exatamente isso. Suíte: 50 testes (o auditor de afirmações cobrou a atualização do README e do ci.yml — funcionando como projetado). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCJV7nKZ1kxi1CtSbvywDg
A composição chama renderizador.definirHemisferio() e o Alternável a propaga a TODAS as estratégias — todo renderizador precisa do método — mas a porta só declarava renderizar(). A capacidade vivia fora do contrato, escapando da regra "métodos lançam erro se não implementados": um terceiro renderizador (SVG, digamos) que a esquecesse só falharia como TypeError em runtime, na troca de hemisfério. Tensão honesta, registrada: foi essa omissão que permitiu à feature de hemisfério cumprir "diff zero em portas" (lição 2). O contrato correto custa admitir que aquela previsão foi cumprida com um débito — o PLANO da coexistência já dava o veredito para tocar estas camadas: "deve ser tratado como achado, não como ajuste". É o achado, tratado. RenderizadorFalso ganha a implementação mínima; 51 testes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCJV7nKZ1kxi1CtSbvywDg
iniciar() zerava ultimaAplicada = null, e avancar() só adota movimento externo quando ela não é null: um salto de teclado entre o play e o primeiro avanço de tempo era silenciosamente descartado — a animação continuava da posição capturada no play. O teste novo prova o defeito (falha no código antigo: anda para 0.1 em vez de adotar 0.5 e seguir a 0.6) e a correção: ultimaAplicada nasce com a posição atual, o que de quebra elimina a repintura redundante da largada. Junto, o dublê da entrada fica honesto (lição dos dublês): mover() passa a quantizar em centésimos como o <input type="range"> real — um dublê mais liso que o adaptador esconderia exatamente a interação que os dirigentes precisam sobreviver. 52 testes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCJV7nKZ1kxi1CtSbvywDg
A fumaça do canvas só aferia o semieixo b e o raio — os ângulos e sentidos do arco e o flag anticlockwise da meia-elipse (a decisão de para ONDE o bojo do terminador vira, que separa crescente de gibosa e sul de norte) não eram observados por teste algum. Um refactor que invertesse um flag desenharia a fase oposta com a suíte verde. E o ramo crescente inteiro (bojo para o lado iluminado, sombra cobrindo mais de meio disco) nunca era exercitado. Duas fumaças novas travam os quatro graus de liberdade: crescente no sul (limbo escuro à direita, bojo anticlockwise à esquerda) e o espelho no norte. A camada continua tabela de tradução — a decisão segue no núcleo — mas agora a tabela inteira tem guarda. 54 testes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCJV7nKZ1kxi1CtSbvywDg
A moeda deste projeto é a precisão sobre si mesmo; três erratas a recompõem: o PLANO admite que 8 (não 9) adaptadores ficaram intocados na coexistência; o comentário da calculadora troca "ângulo de fase" por elongação (a fórmula sempre esteve certa — o nome do ângulo, não: o ângulo de fase astronômico é o suplemento); e a assimetria 0.22↔0.8 das fronteiras ganha a linha de documentação que faltava — resíduo histórico preservado pelo oráculo, não física. E a lição 16 registra o que esta reabertura ensinou: afirmações autorreferentes são defeitos latentes invisíveis ao auditor interno, expostos apenas por auditoria externa — e a resposta estrutural foi transformar a classe inteira em código (2008a23 vermelho, 7fa5a52 verde). Os hashes citados nesta lição são validados pelo próprio auditor que ela descreve. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PCJV7nKZ1kxi1CtSbvywDg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds a fitness function for project assertions (what the codebase claims about itself) and fixes four observable defects discovered through external audit: a crash on malformed URIs, incorrect moon phase labels on specific slider positions, duplicate accessibility announcements, and English identifiers in the core domain.
Key Changes
New tooling:
ferramentas/auditar-afirmacoes.js— automated auditor that verifies four assertions:src/(denylist-based detection after stripping strings/comments)node --testoutputnpm run afirmacoesscript and CI job withfetch-depth: 0Domain fixes:
limit→limite,name→nome,phase→fase,calculator→calculadora,next→proximainServicoDeFases,CalculadoraDaLua,AtualizarLua, and test fileslimiarEscura: 0.0244,limiarCheia: 0.9961) to prevent full-disk rendering under wrong phase labels; verified by sweep test across all 101 slider positionsApplication fixes:
decodeURIComponentin try-catch to handleURIErroron malformed URIs (e.g.,/%zz) → 400 response instead of process crasharia-hidden="true"to#nomeelement; phase name now announced only viaaria-valuetext(single channel, no emoji duplication)AnimacaoDoCicloto detect external jumps on first frame by initializingultimaAplicadato current position instead of nullArchitecture:
definirHemisferio(hemisferio)toPortaRenderizadorDaLuacontract with test coverageanticlockwiseflag per hemisphere (south: true, north: false) to orient shadow bulge toward illuminated sideDocumentation:
Notable Details
.propertyaccess (API usage) is explicitly allowedhttps://claude.ai/code/session_01PCJV7nKZ1kxi1CtSbvywDg