English · Português
House Party Protocol — by Rushar Labs

Manual do harness · v2.6.5 · Claude Code e Codex CLI

Da spec à prova.
Sem salto de confiança.

Um guia operacional do harness: como o hpp init instala, o que cada mapa projeta, como o loop avança, o que a política bloqueia e o que conta como prova. Toda afirmação abaixo carrega o comando que a reproduz; as saídas citadas foram medidas em 2026-09-23 contra a versão 2.5.8, a partir de um clone deste repositório na tag v2.5.8 e de uma instalação por pip da mesma versão, e cada bloco diz qual. O que está marcado como novo na 2.6.0 teve as saídas medidas num checkout de main antes dessa versão.

01 · modelo

Harness primeiro; módulos depois.

O produto tem camadas, e as camadas não são intercambiáveis. O harness é a superfície que você opera; o protocolo é o que ele impõe; os módulos são o que ele instala; a distribuição é como cada host os recebe.

Harness

python -m hpp — doctor, init, log de eventos, atestação, mapas, WorkGraph, política, roteamento, contexto, eval e benchmark — e, novos na 2.6.0, registros de decisão, pacotes de evidência, uma régua de recuperação e uma checagem de citações. Um pacote Python sem dependência de runtime; nada roda em segundo plano.

Protocolo

hpp.manifest.json — protocolo 2.1: quatro invariantes, três papéis (maker, checker, human-gate), cinco transições do loop com um gate nomeado cada, exit codes, cobertura por host de cada módulo, monitores declarados e bundles.

Módulos

Diretórios versionados, cada um instalável por conta própria. A cobertura por host é declarada como native, explicit-command ou unsupported; nenhum adaptador finge o contrário.

Claude Code

Canal nativo de plugin (/plugin marketplace add e /plugin install) e lifecycle hooks depois que você cola o wiring. O .claude/settings.local.json — hooks e statusLine — continua sendo gate humano.

Codex CLI

Cópia verificada por módulo pelo instalador de módulos: skills em .agents/skills, runtime em .agents/hpp/<módulo>, AGENTS.md lido pelo host. Não há lifecycle hooks; as mesmas capacidades são comandos explícitos.

As quatro invariantes

  • Um resultado verificado tem evidência registrada e um gate humano explícito.
  • Um checker é somente-leitura em relação ao workspace do maker.
  • Um loop só avança por um evento registrado.
  • Uma aprovação é inválida quando a spec, o commit ou o snapshot do repositório a que está atada mudam.

Fonte: hpp.manifest.json, chave invariants (o texto original está em inglês).

02 · começar

Diagnostique antes de instalar.

python -m hpp doctor
python -m hpp init --target ../seu-repo
python -m hpp init --target ../seu-repo --apply
python -m hpp benchmark -k 3

O hpp doctor valida o manifesto. Neste repositório ele imprime HPP doctor: ok · modules=10 · hosts=claude-code, codex · hooks=18 (permission gates=9 · llm egress=0) e, porque o marketplace.json está ao lado do manifesto, também cruza cada caminho de módulo, versão e manifesto de plugin; esse resultado só aparece com --json, onde distribution lê {"checked": true, "modules": 10, "status": "ok"}. A partir de uma instalação por pip a linha única é a mesma e o campo lê {"checked": false, "status": "source-contract"} — o comando diz o que não pôde verificar em vez de reportar uma checagem que não rodou.

hpp install --bundle reliable-coding --host codex --target ../seu-repo imprime um recibo com "mode": "plan-only" e não copia nada. A cópia é feita pelo instalador de módulos, que faz parte deste repositório em installers/kit-forge-1.4.2/kit_doctor.py, ao lado dos diretórios de módulo a partir dos quais ele instala (uma instalação por pip não carrega nem um nem outro): python installers/kit-forge-1.4.2/kit_doctor.py install --kit <caminho-do-módulo> --host <host> --target ../seu-repo imprime um plano, e o mesmo comando com --apply o executa. O hpp init imprime essas linhas para você no bloco de wiring.

Requisitos: Python 3.10 ou mais novo (pyproject.toml), git no PATH para a atestação e para a contagem de commits do detect; nenhum pacote de terceiro.

03 · init

Seis estágios fixos. Um plano antes de qualquer escrita.

O hpp init cumpre os seis estágios do contrato de instalação. Sem --apply ele imprime o plano e não escreve nada; com --apply escreve exatamente um arquivo, .hpp/profile.json, dentro do target. O wiring do host nunca é escrito — é um bloco que você cola.

EstágioO que medeO que pode pará-lo
detectClassifica o target como greenfield, in-progress ou re-run a partir de .claude/settings*.json (hooks ou statusLine já configurados), AGENTS.md, .agents/, .hpp/events.jsonl, um .hpp/profile.json anterior e a contagem de commits do git. Lista o que já existe e é preservado.Log de eventos ou profile corrompido é reportado como aviso, com o arquivo a inspecionar.
prereqsPython em 3.10 ou acima; o contrato do manifesto; a distribuição quando há marketplace.json ao lado do manifesto; git no PATH.Python abaixo do piso ou divergência manifesto/marketplace interrompe a execução (exit 2). git ausente é aviso, com a dica de instalação para a sua plataforma.
profileQuatro respostas — host, bundle, modo de política e um conselheiro de decisão opcional (novo na 2.6.0; padrão off, nunca contado como default pendente) — vindas de flags, de um arquivo JSON via --profile, do prompt (só em TTY) ou do default, com a origem de cada uma registrada. O plano reporta would-write; --apply escreve; as mesmas respostas de novo são no-op.Um profile gravado com respostas diferentes é conflict: nada é sobrescrito, as chaves divergentes são nomeadas.
configureO plano de módulos para o host escolhido, com native ou explicit-command por módulo, e a verificação do CHECKSUMS.txt de cada diretório de módulo presente na árvore.Módulo sem suporte no host, ou checksum divergente, interrompe a execução (exit 2).
wire-suggestO bloco a colar para o host: linhas do canal de plugin para o Claude Code, linhas do instalador de módulos para o Codex CLI e para módulos explicit-command, e o comando de política como configurado. Antes do bloco ele imprime uma tabela HOOK CAPABILITIES: cada hook que os módulos escolhidos declaram, com seus eventos, sua política de saída (observe, warn ou block) e seus grupos de capacidade. Reporta 0 files written e o número de hooks que declaram capacidades.Nada; ele nunca escreve.
smokeQuatro controles: o classificador de política (rm -rf tem de dar BLOCK, pytest tem de dar ALLOW), um grafo de capacidades não vazio, o log de eventos projetando no loop, e o benchmark com k=1.Um controle reprovado dá exit 1 e o plano diz qual; --no-benchmark reporta esse controle como não verificado em vez de pulá-lo em silêncio.

Prontidão é contada, não estimada

Onze itens, cada um verified, not verified ou failed, cada um com o comando que o reproduz. O que não foi medido aparece como não verificado — nunca como zero e nunca como cem. Esta é a sequência de abertura e a linha de prontidão medidas num target vazio a partir de um clone deste repositório, que carrega os diretórios de módulo, seus CHECKSUMS.txt e o marketplace.json:

> detecting host...           ✓ greenfield · 0 existing item(s) preserved
> checking prerequisites...   ✓ python 3.14.3 · protocol 2.1
> mounting profile...         ✓ would-write · host=claude-code · bundle=reliable-coding · policy=audit · 3 default(s)
> loading modules...          ✓ 6 modules · reliable-coding · claude-code · 6/6 checksums verified
> wiring suggestions...       ✓ 7 commands to paste · 0 files written · 17 hooks declaring capabilities
> verifying evidence...       ✓ policy · graph · events · benchmark
> protocol online.

  READINESS  every line is a check that ran; the command below it reproduces it
  ████████████████░░░░  9/11 verified · 2 not verified · 0 failed

Os dois não verificados são os dois que só uma ação posterior prova: o profile (só plano; --apply o escreve) e o wiring do host (uma colagem que você mesmo faz). Comando: python -m hpp init --target <dir-vazio> --non-interactive --no-animation; o diretório-alvo tinha 0 arquivos depois.

A partir de uma instalação por pip da mesma versão, que carrega o harness, o manifesto e a suíte de benchmark, mas nenhum diretório de módulo e nenhum marketplace.json, o mesmo comando no mesmo target mostra:

> detecting host...           ✓ greenfield · 0 existing item(s) preserved
> checking prerequisites...   ✓ python 3.14.3 · protocol 2.1
> mounting profile...         ✓ would-write · host=claude-code · bundle=reliable-coding · policy=audit · 3 default(s)
> loading modules...          ✓ 6 modules · reliable-coding · claude-code
> wiring suggestions...       ✓ 7 commands to paste · 0 files written · 17 hooks declaring capabilities
> verifying evidence...       ✓ policy · graph · events · benchmark
> protocol online.

  READINESS  every line is a check that ran; the command below it reproduces it
  █████████████░░░░░░░  7/11 verified · 4 not verified · 0 failed

Os dois itens a mais não verificados ali — integridade da distribuição e checksums dos módulos — não têm contra o que ser medidos num wheel, então são reportados como não verificados, nunca como aprovados.

Flags

  • --host, --bundle, --policy-mode audit|enforce — respondem as três perguntas obrigatórias; --modules a,b substitui o bundle por uma lista explícita.
  • --decision-advisor off|typesafe|openrouter|compatible — a quarta pergunta, opcional (padrão off; novo na 2.6.0): registra um conselheiro de decisão tipada que você declara e imprime como integrá-lo. O hpp nunca o chama e não guarda chave.
  • --profile answers.json — as mesmas respostas a partir de um arquivo (chaves host, bundle, policy_mode, modules e, nova na 2.6.0, decision_advisor; chave desconhecida é erro de uso).
  • --yes, --non-interactive, --json, ou CI definido no ambiente — nenhum prompt é alcançado; pergunta sem resposta assume o default e o relatório diz isso.
  • --no-animation — saída plana; NO_COLOR é respeitado; sem TTY a saída sai completa e sem cor.
  • --marketplace — o slug usado no bloco de wiring do Claude Code, para forks.

Exit codes do init: 0 ok · 1 aviso ou falha não bloqueante (git ausente, um controle do smoke reprovou) · 2 interrompido num estágio bloqueante · 3 a própria invocação estava errada (target não é diretório, arquivo de --profile inválido). Uma execução interrompida nunca imprime a linha de fechamento da marca.

04 · mapas

O estado tem mais de uma vista.

Mapas não têm estado próprio. Cada um é uma projeção de uma ou duas entradas, ordenada para que a mesma entrada produza a mesma saída byte a byte. O Monitor Map não sonda nada e o Lane Map não conhece as suas sessões: você fornece last_signal e heartbeats, e --now é explícito para que nenhuma projeção dependa do relógio ambiente.

MapaEntradaPergunta que respondeComando
Capabilitymanifestoqual módulo provê qual capacidade, em qual host, em qual bundlegraph --view capability
Operationalloop do manifestoqual evento move qual estado por qual gategraph --view operational
Agentpapéis e módulos; eventos opcionaisquem pode fazer, checar e aprovar; o que aconteceu, em ordemmap agent · graph --view agent
Evidencefixacomo critério, registro e veredito se relacionamgraph --view evidence
Codecomponentes do manifestoquais superfícies cada módulo declara (não é uma AST)graph --view code
LaneJSON de lanes, --now, limiaresquem é dono do quê, quem está vivo, onde reivindicações vivas se sobrepõemmap lane
ContextJSON de contexto, --budgeto que entrou no contexto compilado, o que foi omitido, com hashesmap context · context compile
MonitorJSON de monitores, --now, --skew-toleranceo que é observado, quão fresco, e qual gate consomemap monitor
WorkJSON da specquais unidades podem rodar juntas e em que ordemwork plan · work waves

Monitor Map: quatro estados, e um relógio que pode mentir

EstadoRegra
healthynow − last_signal ≤ freshness
staleo sinal é mais velho que a frescura declarada
skewlast_signal > now + skew_tolerance — timestamp no futuro não é evidência de frescura; tolerância default 5 s, ajustável com --skew-tolerance
unknownnenhum last_signal fornecido
python -m hpp map monitor examples/reliable-coding/monitors.json --now 1000

Medido: service-health (sinal 950, frescura 120) → healthy; data-freshness (sinal 700, frescura 120) → stale. Com um arquivo sintético, um sinal em 1010 contra --now 1000 projetou como skew, e como healthy quando se passou --skew-tolerance 20; um monitor sem last_signal projetou como unknown.

Lane Map: vitalidade a partir de heartbeats

Uma lane está alive quando o heartbeat tem no máximo --suspect-after segundos (default 300), suspect até --dead-after (default 900), e dead além disso ou quando declarada fechada. Lane sem heartbeat é unknown. Uma lane morta nunca produz colisão; duas lanes vivas e exclusivas com territórios sobrepostos, sim. Comando: python -m hpp map lane examples/reliable-coding/lanes.json --now 1000.

Competições: escolher uma de N é pass@N

Novo na 2.6.0

O quadro do lane-kit (lane_board.py) roda um best-of-N: N lanes constroem, cada uma, o seu próprio item para uma mesma tarefa, o compete os declara candidatos e o select registra um vencedor. Quem seleciona tem de estar em outra lane e ser de outra família de modelo que todos os builders dos candidatos, e cada candidato tem de estar CHECKPOINT-READY com evidência, ou VERIFIED. Os perdedores viram NOT-SELECTED, um estado terminal; nenhum candidato chega a MERGED antes de a tarefa ter um vencedor; select --checker-unavailable registra DEFERRED, nunca um vencedor. Escolher uma de N é pass@N, não confiabilidade: o vencedor ainda precisa do seu próprio VERIFIED antes de MERGED, e de pass^k antes que alguém o chame de confiável.

python multi-session/lane-kit-1.4.1/scripts/lane_board.py compete --task TASK-1 --items ITEM-A,ITEM-B --lane lane-a --model claude-opus-4-8
python multi-session/lane-kit-1.4.1/scripts/lane_board.py select --task TASK-1 --winner ITEM-B --lane lane-r --model gpt-5.6 --reason "same tests, half the diff"

Medido num checkout de main, num quadro de rascunho onde ITEM-A (lane lane-a, claude-opus-4-8) e ITEM-B (lane lane-b, claude-sonnet-4-6) foram cada um reivindicado, construído e posto em CHECKPOINT-READY com evidência: compete → exit 0; select por claude-haiku-4-5 → maker≠checker violated: … SAME model family (claude), exit 1; select por gpt-5.6 → vencedor ITEM-B, not_selected ITEM-A, exit 0. Depois disso o ITEM-A não pôde se mover (NOT-SELECTED -> UNDER-REVIEW recusado) e o ITEM-B não pôde ir de CHECKPOINT-READY para MERGED, exit 1 cada. O quadro sai com 0 ok · 1 recusado · 2 uso inválido.

Context compile: blocos inteiros dentro de um orçamento

hpp context compile INPUTS --budget N lê um array JSON de blocos — source (único), priority (um inteiro) e content — e preenche um orçamento de caracteres em ordem de prioridade. Um bloco entra inteiro ou não entra: o que não cabe é listado como omitido, nunca fatiado, e o bloco seguinte ainda é tentado. O separador entre dois blocos (duas quebras de linha) é cobrado do orçamento. Todo bloco, incluído ou omitido, carrega seus chars e seu sha256; o resultado carrega used, remaining e o text compilado. Conteúdo com cara de segredo — atribuição de chave, token, segredo ou senha, cabeçalho PEM, token sk- — é recusado antes de qualquer compilação, e a recusa nomeia a fonte.

python -m hpp context compile examples/reliable-coding/context.json --budget 100

Medido: spec e acceptance incluídos, background omitido, used 86, remaining 14. Um bloco cujo conteúdo era uma atribuição api_key= → hpp: secret-like material refused from source: env, exit 2.

05 · loop e waves

O loop só avança por um evento registrado.

planned --work_started--> active --evidence_recorded--> evidenced --check_passed--> checked
        [scope]                   [fresh-evidence]                 [read-only-checker]

checked --human_approved--> approved --verified--> verified
        [human]                      [closure]

O log de eventos é o .hpp/events.jsonl no workspace, somente-acréscimo. Um evento que não cabe no estado atual é recusado antes de qualquer escrita, e verified exige ao menos um item de evidência registrado. O hpp status projeta o log na máquina acima e nomeia o próximo passo; o hpp resume devolve a mesma resposta em JSON. Nenhum dos dois pede a um modelo que lembre de alguma coisa.

python -m hpp event append --type work_started
python -m hpp status
python -m hpp resume

Medido num diretório vazio: event append --type verified como primeiro evento → hpp: invalid transition at event 1: planned --verified--> ?, exit 2, nenhum arquivo criado. Depois event append --type work_started → estado active, e hpp status imprime HPP status: active · events=1 · next=record fresh evidence.

Paralelismo segue o WorkGraph

Uma spec lista unidades de trabalho, cada uma com dependências, critérios de aceitação não vazios e um tier (economy, balanced, frontier). O compilador rejeita ciclo de dependência como erro — nunca como wave vazia — e emite waves topológicas: cada unidade fica na primeira wave depois de todas as suas dependências. Ele não dispara nada; quem respeita a barreira entre waves é o operador.

python -m hpp work plan examples/reliable-coding/workgraph.json
python -m hpp work waves examples/reliable-coding/workgraph.json

Medido no exemplo: wave 1 spec · wave 2 build, docs · wave 3 verify; contagem por tier economy 2 · balanced 1 · frontier 1.

Roteamento: primeiro um tier, depois um provedor que você declarou

hpp route --request F --providers F [--policy economy|balanced|frontier], padrão balanced. A requisição declara stage, risk e complexity (low, medium ou high) e context, um inteiro não negativo; cada provedor declara id, tiers, stages e max_context. Risco alto, complexidade alta ou contexto acima de 16000 exige frontier sob qualquer política; médio, ou contexto acima de 8000, exige ao menos balanced; fora isso vale o tier da própria política. Um provedor é elegível quando declara o stage e o seu max_context cobre o contexto. A rota devolve o tier e um dos ids de provedor que você declarou, com empate desfeito pelo id. Quando nenhum provedor elegível oferece o tier pedido, ela sobe — só para cima, para o tier mais próximo — e diz isso em fallback; quando nada no tier ou acima dele é elegível, ela recusa (exit 2). Ela nunca nomeia modelo, lê preço ou ranqueia fornecedores.

python -m hpp route --request examples/reliable-coding/route-request.json --providers examples/reliable-coding/providers.json

Medido: a saída carrega schema (hpp.route/v1), policy, requested_tier, selection (provider, tier), fallback, eligible_providers e rationale. No exemplo (risco baixo, complexidade baixa, contexto 4000), balanced seleciona local-frontier em balanced, economy seleciona local-economy e frontier seleciona local-frontier em frontier. Contra uma lista sintética que só oferecia balanced, uma requisição economy o selecionou com motivo de fallback upgraded-above-requested-tier; uma requisição de risco alto contra uma lista só economy → hpp: no provider satisfies the frontier risk floor, exit 2.

06 · política

Advisory não se fantasia de bloqueio.

O classificador devolve um de três vereditos e nunca executa o comando. O modo decide o que o veredito custa: em audit o exit code é sempre 0 e o veredito só é registrado; em enforce o veredito vira o exit code.

ALLOW · exit 0

Nenhuma regra casou

O comando segue. O conjunto de regras é pequeno e explícito; ele não afirma pegar toda forma destrutiva.

MANUAL · exit 1 em enforce

Gate humano

Qualquer git push e qualquer curl ou wget para uma URL — e, novo na 2.6.0, rodar o adaptador de exemplo de decisão tipada (examples/typed-decisions/decide.py; o pacote 2.5.8 devolve ALLOW para ele): publicação ou transferência externa precisa de uma pessoa.

BLOCK · exit 2 em enforce

Recusado

Deleção recursiva em qualquer ordem de flags (-rf, -fr, -r -f, --recursive --force, rmdir /s), force push, push em main ou master, curl | sh, DROP/TRUNCATE.

python -m hpp policy check --mode audit --command "git push origin main"
python -m hpp policy check --mode enforce --command "git push origin main"
ComandoVeredito · regraauditenforce
git push origin mainBLOCK · main-pushexit 0exit 2
git push origin featureMANUAL · external-pushexit 0exit 1
rm -rf srcBLOCK · recursive-deleteexit 0exit 2
python -m pytest -qALLOWexit 0exit 0

As oito células foram medidas nesta árvore com python -m hpp policy check. rm arquivo.txt, grep -rf padroes.txt e cp -rf a b não são bloqueio: a checagem lê o conjunto de opções de cada invocação de rm, não uma grafia.

07 · prova

Capacidade e confiabilidade não são a mesma métrica.

pass@k mede se um caso passou ao menos uma vez em k execuções. pass^k mede se passou todas as vezes. O gate de capacidade exige pass@k ≥ 0.90; o gate de regressão exige pass^k = 1.0; --gate both exige os dois. Uma suíte que passa no primeiro e reprova no segundo é instável, e o relatório diz em quanto.

python -m hpp eval run examples/reliable-coding/benchmark-suite.json -k 3 --gate both
python -m hpp benchmark -k 3 --json

Medido: HPP benchmark: pass@k=1.00 · pass^k=1.00 · gate=PASS sobre dez controles (contrato do manifesto, enforcement de política, waves do WorkGraph, colisão de lanes, frescura de monitor, proveniência de contexto, piso de roteamento, gate evento/evidência, determinismo do grafo, atestação de evidência), três execuções cada. O relatório JSON carrega o sha256 da suíte e a plataforma em que rodou. Exit 0 quando o gate passa, 1 quando não.

Uma decisão tomada fora é medida, nunca confiada por padrão

Novo na 2.6.0

O harness não chama modelo. Uma decisão pequena tomada fora dele — por uma regra, uma pessoa, um modelo local ou um modelo de decisão tipada hospedado — pode ser registrada como hpp.decision/v1: sempre advisory; com raise-only ela pode elevar um valor declarado e nunca baixá-lo; abstention e instrument-failure são desfechos que não mudam nada. hpp decide eval roda o decisor que você nomeia como comando contra casos rotulados e reporta cobertura, acerto seletivo, erros confiantes, abstenções e falhas separadamente, com uma curva de cobertura por limiar de confiança. A versão 1 mede só perguntas de escolha.

python -m hpp decide validate decision.json
python -m hpp decide eval examples/typed-decisions/gotcha-family-suite.json --decider-command '["python", "examples/typed-decisions/baseline_decider.py"]'
  • decide eval SUITE — --decider-command recebe o argv do decisor como array JSON de strings, executado sem shell; omita-o para reexecutar os registros guardados nos casos da suíte, onde um caso sem registro conta como falha de instrumento. --timeout (padrão 10.0) é o número de segundos por caso antes de esse caso contar como falha de instrumento. O gate: --min-selective-accuracy (padrão 0.9), --max-confident-errors (padrão 0; erro confiante é uma resposta errada com confiança igual ou acima do confidence_floor da suíte, 0.6 quando a suíte não define um) e --max-failures (padrão 0). Exit 0 o gate passou · 1 o gate reprovou, ou nada foi decidido e não há acerto a medir · 2 registro, suíte ou argv inválido.
  • decide validate RECORD — imprime valid e o valor efetivo sobre o qual um consumidor pode agir, com a sua ação (raised, kept, advised ou none). Exit 0 válido · 2 recusado, nomeando a primeira regra quebrada.

Medido num checkout de main: o decisor baseline na suíte de exemplo → 15 casos, 12 decididos, 3 abstenções, 0 falhas de instrumento, cobertura 0.8, acerto seletivo 1.0, every threshold held, exit 0. A mesma suíte sem --decider-command não guarda registros → 15 falhas de instrumento, no decision was made, exit 1. Um registro com "authority": "binding" → hpp: authority must be 'advisory': …, exit 2. O pacote 2.5.8 não tem o comando decide: ali, hpp decide é uma escolha inválida, exit 2.

Veja examples/typed-decisions para o adaptador, seus riscos e como medi-lo antes de confiar nele.

Pacotes de evidência: o exit code de um critério, medido fora do modelo

Novo na 2.6.0

O hpp não dirige navegador e não chama modelo. O hpp evidence run roda o comando-critério que você declara — uma spec ponta a ponta, uma suíte pytest, qualquer script — como argv sem shell, mede o exit code, faz o hash de cada arquivo de artefato que você declarou e escreve .hpp/evidence/<id>-<UTC>.json (hpp.evidence/v1): o comando, o commit-base, o exit code, o veredito, stdout e stderr como contagem de bytes e sha256 (nunca o texto), os artefatos e um auto-hash. O veredito só é passed quando o comando saiu com 0 e cada padrão declarado casou com um arquivo que esta execução escreveu (um arquivo intocado desde antes da execução sai como unchanged e não conta); fora isso é failed, missing-artifacts, timeout ou could-not-start. O hpp evidence verify re-deriva um registro a partir dos arquivos no disco. O auto-hash torna visível um registro editado; ele não é assinatura — quem pode escrever o arquivo pode reescrevê-lo —, então um checker que não pode confiar no maker roda o comando de novo a partir da sua própria lane em vez de se apoiar só no verify.

python -m hpp evidence run --id smoke-page --artifact out/report.html --artifact out/smoke.log -- python examples/evidence/smoke_page.py
python -m hpp evidence verify <record_path>
  • evidence run --id ID [--artifact GLOB]... -- COMMAND — --artifact é um glob relativo ao workspace, repetível; --timeout (padrão 600.0) é o número de segundos antes de a execução contar como timeout; --out é o diretório do registro, relativo ao workspace (padrão .hpp/evidence); --record-event acrescenta evidence_recorded ao log de eventos quando, e só quando, o pacote passou, e exige ao menos um --artifact. Um argv com cara de segredo, um caminho de artefato ou de --out fora do workspace, um id inválido ou um timeout não positivo são recusados antes de qualquer execução. Exit 0 passou · 1 não passou · 2 recusado, ou o evento não pôde ser acrescentado.
  • evidence verify RECORD — o record_path que o run imprimiu. Exit 0 valid, registro íntegro de uma execução que passou · 1 not-evidence, registro íntegro de uma execução que não passou · 2 blocked, o registro foi editado ou se contradiz, ou um artefato mudou ou sumiu.

Medido num checkout de main, numa cópia da árvore para que out/ e .hpp/ fossem escritos ali: o demo → passed, exit 0, e o verify do seu registro → valid, exit 0. Com --break depois do script, os dois artefatos foram escritos e o veredito foi failed, exit 1; o verify desse registro → not-evidence, exit 1, e o de uma cópia editada para dizer passed → blocked, the record was edited after it was written, exit 2. O primeiro registro, verificado de novo depois que a segunda execução reescreveu out/ → blocked, 2 artifact(s) changed since the run, exit 2: runners ponta a ponta que limpam o diretório de saída no início de cada execução fazem o mesmo, então dê aos artefatos que precisam continuar verificáveis um diretório por execução. --record-event sem work_started no log → exit 2, evento não acrescentado; depois de event append --type work_started ele levou o loop a evidenced. O pacote 2.5.8 não tem o comando evidence: ali, hpp evidence é uma escolha inválida, exit 2.

Veja examples/evidence para o mesmo padrão em volta de um runner ponta a ponta de verdade.

Régua de recuperação: o recuperador, medido separado da resposta

Novo na 2.6.0

O harness não roda índice e não chama modelo. Um recuperador é um comando que você declara: ele lê {"query": …, "k": …} como JSON no stdin e imprime ids ranqueados, o melhor primeiro. O hpp retrieval eval pontua o top k de cada resposta contra os ids que uma suíte hpp.retrieval-suite/v1 marca como relevantes — hit@k, recall@k, precision@k, MRR e nDCG@k — e conta à parte as falhas de instrumento (não iniciou, timeout, exit diferente de zero, saída que não é JSON, id duplicado): elas nunca são pontuadas, e as médias cobrem só os casos medidos. Sem nenhum caso medido as métricas são nulas e o gate diz por quê, nunca 0%. Se uma boa resposta pode ser gerada a partir do que foi recuperado é outra pergunta, que não se faz aqui.

python -m hpp retrieval eval examples/retrieval/suite.json
python -m hpp retrieval eval examples/retrieval/suite.json --retriever-command '["python", "examples/retrieval/keyword_retriever.py"]'
  • retrieval eval SUITE — --retriever-command recebe o argv do recuperador como array JSON de strings, executado sem shell; omita-o para reexecutar os resultados guardados nos casos da suíte. -k substitui o corte da suíte. --timeout (padrão 10.0) é o número de segundos por caso antes de esse caso contar como falha de instrumento. O gate: ao menos um caso medido, --min-recall (padrão 0.8) para o recall@k médio e --max-failures (padrão 0). Exit 0 o gate passou · 1 o gate reprovou · 2 suíte ou argumento recusado.

Medido num checkout de main: os dois comandos → 7 casos medidos, 0 falhas de instrumento, recall@3 médio 0.786, hit@3 0.857, MRR 0.786, nDCG@3 0.749, mean recall@k 0.786 < 0.8, exit 1 — o baseline por palavra-chave erra por completo a pergunta parafraseada, um 0 medido, e o gate padrão reprova; com --min-recall 0.75 → every threshold held, exit 0. Um recuperador que sai com 3 em todo caso → 7 falhas de instrumento, métricas nulas, no case was measured (7 instrument failures); there is no recall to measure, exit 1. --retriever-command '"python"' → hpp: --retriever-command must be a JSON array of strings (argv, no shell), exit 2. A suíte é sintética e o baseline foi escrito junto com ela: estes números provam que a régua funciona, não que algum recuperador é bom. O pacote 2.5.8 não tem o comando retrieval: ali ele é uma escolha inválida, exit 2.

Veja examples/retrieval para o contrato do recuperador e o formato da suíte.

Checagem de citações: todo marcador resolve, ou o exit code diz qual não resolve

Novo na 2.6.0

Uma resposta escrita a partir de fontes marca cada afirmação com o id da fonte em que se apoia — [ID:<id>] por padrão. O hpp cite check lê a resposta e a lista de itens de contexto a partir da qual ela foi escrita e reporta, sem modelo: um marcador cujo id não está no contexto (UNKNOWN_ID), um marcador que nomeia um intervalo ou uma lista (RANGE) e um marcador vazio (EMPTY_MARKER) bloqueiam; mais de --max-per-sentence marcadores numa frase (TOO_MANY) e uma frase com número, percentual, valor monetário ou data e nenhum marcador (UNCITED_CLAIM) avisam. Ela prova que cada marcador resolve, não que a fonte citada sustenta a frase: esse julgamento precisa de um leitor. O separador de frases e o detector de números são heurísticas, listadas na docstring de hpp/citations.py.

python -m hpp cite check --text examples/citations/answer.md --context examples/citations/context.json
  • cite check --text FILE --context FILE — --context é uma lista JSON de itens com id (e o seu text), ou de ids soltos; --max-per-sentence (padrão 4); --marker recebe uma regex com exatamente um grupo de captura, o id. Texto vazio, texto que é só código, texto ou contexto com cara de segredo e uma regex inutilizável são recusados. Exit 0 limpo · 1 aviso (TOO_MANY, UNCITED_CLAIM) · 2 bloqueio (UNKNOWN_ID, RANGE, EMPTY_MARKER) ou entrada recusada.

Medido num checkout de main: a resposta de exemplo → veredito ok, 7 frases, 5 marcadores, 4 dos 5 ids de contexto citados, exit 0. Em cópias dela: [ID:glossary] trocado por [ID:glossary-v2] → UNKNOWN_ID, exit 2; por [ID:runbook-7,sla-2026] → RANGE, exit 2; o marcador removido da frase com 4 engineers → UNCITED_CLAIM, exit 1. E o limite, medido: 42 minutes trocado por 90 minutes, ainda citado à fonte que diz 42 → veredito ok, exit 0. O pacote 2.5.8 não tem o comando cite: ali ele é uma escolha inválida, exit 2.

Veja examples/citations para outra sintaxe de marcador e o formato do relatório.

A atestação amarra uma aprovação a bytes

hpp attest create registra um veredito junto com o hash da spec, o commit-base, um digest do snapshot completo do repositório (rastreado e não rastreado), o maker, o checker e a sessão. Maker e checker têm de ser diferentes, ou o registro é recusado. hpp attest verify re-deriva cada amarração e bloqueia quando qualquer uma se moveu.

python -m hpp attest create --repo . --spec spec.md --output att.json --maker a --checker b --session s1 --verdict approved
python -m hpp attest verify att.json --repo .

Medido num repositório de rascunho: --maker a --checker a → hpp: maker and checker must be different non-empty actors, exit 2. Com atores diferentes o registro sai approved e o verify devolve valid, exit 0; depois de uma linha da spec mudar, o verify devolve blocked com mismatches: spec_sha256, snapshot_digest, exit 2. A atestação exige git.

Veja a matriz de prova e o contrato do benchmark.

08 · hosts

Mesmo contrato, diferenças visíveis.

Claude CodeCodex CLI
Distribuiçãomarketplace de plugins (/plugin marketplace add rusharlabs/house-party-protocol, depois /plugin install <módulo>@house-party-protocol)cópia verificada por módulo via kit_doctor.py install --host codex
Onde caio store de plugins do host; .claude/settings.local.json para hooks e statusLine.agents/skills para skills, .agents/hpp/<módulo> para runtime; AGENTS.md é o que o host lê
Lifecycle hooksnativos, depois que o wiring é colado (gate humano; o hpp init lista os módulos que declaram hooks)nenhum; as mesmas capacidades são comandos explícitos
Cobertura no manifestoseis módulos native, quatro explicit-commandnove explicit-command; claude-dev-kit é unsupported, e o hpp init interrompe em vez de planejá-lo ali

Fonte: hosts em cada entrada de módulo do hpp.manifest.json; os caminhos por host são os que o hpp init imprime no bloco de wiring. hpp init --host codex planeja o bundle reliable-coding com os seis módulos como explicit-command.

09 · exit codes

Um contrato: 0 ok · 1 warn · 2 block · 3 error.

Comando0123
policy check --mode enforceALLOWMANUALBLOCK—
policy check --mode auditsempre———
eval run · benchmarkgate passougate reprovousuíte inválida—
decide eval (novo na 2.6.0)gate passougate reprovou · nada decididoregistro, suíte ou argv inválido—
decide validate (novo na 2.6.0)válido—recusado—
evidence run (novo na 2.6.0)passounão passourecusado · evento não acrescentadoerro interno
evidence verify (novo na 2.6.0)validnot-evidenceblockederro interno
retrieval eval (novo na 2.6.0)gate passougate reprovousuíte ou argumento recusadoerro interno
cite check (novo na 2.6.0)limpoavisobloqueio · entrada recusadaerro interno
attest create · attest verifyapproved · valid—revise/blocked · invalid · recusado—
initok · no-opaviso, falha não bloqueanteinterrompidoerro de uso
event append · status · mapas · route · context compileok—transição inválida, log corrompido, entrada inválida, nenhum provedor no piso de risco, contexto com cara de segredoerro interno

Fonte: exit_codes no manifesto, hpp/cli.py e as medições citadas acima. Qualquer erro conhecido (manifesto, instalação, estado, eval, atestação, JSON inválido) sai com 2 e uma mensagem de uma linha; uma exceção inesperada sai com 3.