Skip to content

Releases: AllanSantos-DV/token-guard

v2.2.0 — economia de saída, contrato com evidência real, replay sobre sessões reais

Choose a tag to compare

@AllanSantos-DV AllanSantos-DV released this 25 Aug 11:24

Economia de SAÍDA, contrato com evidência real, replay sobre sessões reais e o
backlog de hardening zerado (16 itens → 13+4 fechados, só monitoramento restante
— mapa em docs/BACKLOG.md).

Adicionado (rodada 2 — fechar tudo antes de lançar)

  • Replay de transcripts REAIS (bench/replay-transcripts.cjs): percorre os
    JSONL de sessões passadas do Claude Code e roda o decide() atual sobre cada
    chamada histórica. Na máquina do autor: 65 sessões, 8.197 chamadas → 28 denies
    legítimos, ~437k tokens líquidos estimados — e a auditoria dos suspeitos
    expôs duas classes de falso positivo que nenhuma suíte pegava (abaixo).
  • bigResult substitui de verdade também no Claude Code: v2.1.121 estendeu
    updatedToolOutput para todas as ferramentas; o post-hook emite o stub como
    substituição (versões antigas: orientação, nunca bloqueio).
  • Cursor ganha broadScan de volta: o Cursor passou a expor preToolUse
    genérico com matcher por tipo de ferramenta — adapter traduz o evento e o
    instalador registra; todas as 4 regras disparam nas versões recentes.

Adicionado (rodada 1)

  • Regra bigResult (pós-execução) — lib/postresult.cjs + adapter
    PostToolUse: resultado de ferramenta acima de ~25k caracteres vira stub com
    preview (cabeça+cauda), versão integral gravada em .token-guard/results/
    e a alternativa barata da família pronta para reexecutar. No modo plugin do
    Copilot a substituição é REAL (modifiedResult do SDK); no Claude Code o
    hook de comando orienta (additionalContext) — diferença documentada em
    docs/IDES.md. Fail-open absoluto (objeto circular, root impossível → passa
    intacto).
  • Injeção automática do contrato — adapter UserPromptSubmit fecha o
    circuito que estava aberto; e agora com evidência real: o PostToolUse
    acumula os arquivos tocados da sessão (dedupe, teto 50) e as seções
    quando:codigo/teste/docs entram conforme o tipo de trabalho. No modo
    plugin do Copilot, onUserPromptSubmitted injeta pelo mesmo estado.
  • mcp-cost acionável — seção RECOMENDAÇÕES: servidores >1,5k tok de schema
    recebem sugestão de corte/slim; ferramentas >500 tok, de encurtar descrição.
    Recomendação condicional ao uso real; servidor que falhou nunca recebe
    recomendação inventada. E --extra-files a.json,b.json traz configs fora dos
    locais conhecidos (Zed, JetBrains, frotas próprias) para o inventário.
  • Skill token-economy v2 — levers externos validados por pesquisa:
    compaction, model routing, cache-friendly habits, CLI>MCP, slimming de MCP,
    sub-agentes — com fontes.
  • docs/BACKLOG.md: melhorias óbvias fora de escopo ficam anotadas (origem +
    esforço) e são atacadas quando o escopo corrente fecha — política do dono,
    espelhada no Brain.
  • Teste EPIPE (test/epipe.test.cjs): stdout destruído com o filho vivo —
    exit 0, zero stack trace. CI ganhou Node 16 na matriz (engines >=16 passou a
    ser testada de fato); npm test roda as dez suítes.

Corrigido

  • Re-gate da rodada de features (reviewer + tester independentes), 7 achados:
    • knobs novos fora de DEFAULTS.limits burlavam o sanitize(): config lixo
      ("resultCharsWithoutTrim": "abc" → NaN) truncava TODA saída de ferramenta.
      Chaves registradas nos defaults + validação local em lib/postresult.cjs;
    • cfg sem limits desligava bigResult silenciosamente (TypeError engolido
      pelo fail-open) — defaults locais resolvem, e o teste que passava pelo
      motivo errado foi reescrito;
    • registro VIVO em layout antigo coexistia com o novo (dois guards por
      evento para sempre) → substituído com aviso (layout antigo substituído);
    • matcher drift congelado para sempre em quem já tinha instalado → matcher
      atualizado automaticamente quando difere do atual;
    • dedup de PostToolUse/UserPromptSubmit olhava só hooks[0] (duplicava
      canônico escondido após hook forasteiro) → reconcile() único com flatMap;
    • flag de "obsoleto reparado" vazava entre eventos (mensagens mentirosas)
      → estado por evento;
    • EPIPE derrubava os hooks novos via evento de stream; e prompt-hook
      persistia estado ANTES de emitir (entrega falha = contrato perdido na
      sessão) → handler de erro + emitir antes de persistir;
    • colisão de session-id sanitizado (sess/1 ≡ sess:1) suprimia injeção
      entre sessões distintas → sufixo sha1 do id original quando sanitizado.
  • Backlog P1: writeJson do instalador atômico (temp + rename — crash não
    trunca mais o settings.json do usuário); matcher drift também no alvo repo;
    CFG.load memoizado (TTL 2s, env na chave) para o modo plugin deixar de
    re-walk a árvore a cada evento.
  • renderAdvice: recomendações além da 8ª aparecem como "+N restantes";
    renderText tolera charsPerToken ausente/zero (via API programática).
  • Identidade de sessão sem id do harness deriva da raiz (hash), em vez de um
    'sem-sessao' global que misturava estado entre repositórios.
  • Removido debug.log esquecido na raiz.

Corrigido (rodada 2 — achados do replay real)

  • noisePath barrava leituras legítimas FORA do workspace (scratchpads e
    outputs de tarefa do próprio harness em %TEMP%\claude\…): o ancestral
    Temp casava com a lista de ruído quando o caminho escapava da raiz. Caminho
    fora do workspace não é ruído — é contexto escolhido. No replay real isso
    eram 69 denies injustos numa máquina só; agora são zero.
  • git ls-files docs/plans/ escopado era negado como dump inteiro; agora segue
    a mesma regra da busca por conteúdo: caminho explícito = barato, sem caminho
    = árvore inteira (barrado).