██████╗ ███████╗██╗ ██╗
██╔══██╗██╔════╝██║ ██║
██████╔╝███████╗███████║
██╔═══╝ ╚════██║██╔══██║
██║ ███████║██║ ██║
╚═╝ ╚══════╝╚═╝ ╚═╝
ProStaff Harness - Execução Verificável e Auditoria de Evidências
╔══════════════════════════════════════════════════════════════════════════════╗
║ PSH - Harness de execução para agentes de código ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ Portões de qualidade que consomem evidência produzida pelo núcleo, nunca ║
║ número informado pelo agente. ║
║ ║
║ Cobertura, teste, lint e segurança viram registro assinado por hash da ║
║ árvore que foi verificada. ║
║ ║
║ v0.2.0 · fronteira aplicada pelo kernel · 325 testes · binário único ║
╚══════════════════════════════════════════════════════════════════════════════╝
Um agente de código que reporta a própria nota não está sendo avaliado.
Está se autodeclarando aprovado.
O padrão aparece sempre da mesma forma:
agente: "rodei os testes, cobertura 87%, pode avançar de fase"
harness: portão aprovado
Ninguém rodou nada.
O número veio do modelo.
E mesmo quando o teste roda de verdade, nada impede continuar editando o código depois e seguir usando aquele resultado.
O psh fecha esses dois buracos.
O portão lê exclusivamente registros de evidência que o próprio núcleo produziu.
Cada registro carrega o hash da árvore de arquivos que estava no disco no momento da verificação.
Editou depois, a evidência vence.
$ psh advance --coverage 99
psh: portão não aceita métrica vinda do chamador: [coverage].
Valor de portão só vem de registro de evidência produzido por 'psh verify'.
$ echo $?
5
A recusa é explícita de propósito.
Tratada como "flag desconhecida", o caminho nunca apareceria em teste e ninguém saberia se a garantia existe.
▶ Funcionalidades (clique para expandir)
┌─────────────────────────────────────────────────────────────────────────────┐
│ [■] Evidence Engine - o núcleo executa o verificador, o agente nunca │
│ [■] Frescor por hash - editou arquivo observado, a evidência vence │
│ [■] Trilha encadeada - JSONL com prev_hash, detecta remoção e edição │
│ [■] Âncora externa - pega reescrita coordenada da cadeia inteira │
│ [■] Workflow Engine - fases, portões e protocolo de falha por classe │
│ [■] Contrato validado - JSON Schema + integridade referencial no load │
│ [■] Override rastreado - passed-with-override permanente, nunca passed │
│ [■] Extratores nativos - lcov, Cobertura, SimpleCov, go cover, JSON Ptr │
│ [■] Exit code manda - texto extrai detalhe, nunca decide veredito │
│ [■] Adapter CI - headless, JSON, código de saída estável │
│ [■] psh doctor - diagnóstico fora do runtime, colável em issue │
│ [■] Binário único - bun build --compile, sem runtime instalado │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ 01 · Plataformas │
│ 02 · Instalação │
│ 03 · Primeiros passos │
│ 04 · Como um portão decide │
│ 05 · Contrato de workflow │
│ 06 · Códigos de saída │
│ 07 · Integração com CI │
│ 08 · O que o psh não faz │
│ 09 · Desenvolvimento │
│ 10 · Roadmap │
└──────────────────────────────────────────────────────┘
O núcleo (workflow, evidência, auditoria) roda onde o Bun roda.
O que depende de plataforma é como a fronteira de escrita é aplicada.
| Plataforma | Fronteira | Estado |
|---|---|---|
| Linux | mount pelo kernel, via bubblewrap e Landlock | testado no CI e contra o binário real |
| macOS | mount pelo kernel, via seatbelt | testado no CI, sem o sandbox instalado |
| Windows via WSL2 | igual ao Linux | testado no CI, em WSL2 de verdade |
| Windows nativo | nenhuma | não suportado |
Windows exige WSL2.
Não é preguiça de portar: a fronteira precisa de namespace de usuário e de Landlock, que são construções do kernel Linux.
O CI roda a suíte dentro de um WSL2 real, e mede o que aquele kernel oferece antes de rodar qualquer teste:
kernel: Linux 6.18.33.2-microsoft-standard-WSL2
landlock: 101 símbolos em kallsyms
bwrap: bubblewrap 0.9.0
bwrap real: funciona
Ou seja, o WSL2 tem as primitivas necessárias.
A linha da tabela acima é medição, não suposição.
Fora do WSL2 o psh cai no modo degradado, que detecta e reverte escrita
fora da fronteira em vez de impedir, e isso é uma garantia mais fraca.
O modo aparece no psh status, no psh doctor e dentro de cada registro de
evidência, nunca em silêncio.
Sem sandbox instalado, em qualquer plataforma, o comportamento é o mesmo modo degradado declarado.
Dá para trabalhar assim, mas quem impede a escrita passa a ser um snapshot, não o kernel.
Requer Bun 1.3 ou superior para compilar.
git clone https://github.com/Bulletdev/ProStaff-Harness.git
cd ProStaff-Harness/psh
bun install
bun run build # gera dist/psh, binário únicoIsolamento de execução é opcional e usa o ai-jail 1.19 ou superior.
Sem ele o psh roda em modo degradado.
O modo é declarado no psh status, no psh doctor e dentro de cada
registro de evidência, nunca silencioso.
psh init --profile lean # detecta a stack, mostra o plano, pede confirmação
psh verify # o NÚCLEO roda os verificadores do portão atual
psh status # fase, tentativa, portão, sandbox, trilha
psh advance # avalia o portão e decide a transição
psh audit verify # integridade da trilha encadeada
psh doctor # diagnóstico completopsh init é não destrutivo.
Mostra o plano, faz backup do que sobrescrever e só escreve dentro de
.harness/.
Use --dry-run para ver o plano sem aplicar nada.
$ psh status
perfil lean
fase phase.5.build - Build + Quality
tentativa 2 (retries 1/2)
status in-progress
sandbox ai-jail 0.10.0 operante
fronteira ausente (C3 entra na v0.2)
trilha íntegra (47 entradas, 0 problemas)
portão all-of: REPROVADO
ok verifier-status:tests observado 0 esperado exit 0
NÃO verifier:coverage observado 78.4 esperado min 85
78.4 abaixo do mínimo 85
psh verify -> núcleo executa o verificador dentro do sandbox
-> extrai o valor do relatório, ou usa o código de saída
-> calcula o hash da árvore dos paths observados
-> grava .harness/evidence/<fase>/<tentativa>/<verificador>.json
psh advance -> lê APENAS registros de evidência
-> recalcula o hash da árvore agora
-> divergiu = evidência obsoleta, nomeando o arquivo que mudou
-> compara com o threshold do contrato
-> grava evento, encadeia na trilha, atualiza state.json
O que isso impede, na prática:
| Tentativa | Resultado |
|---|---|
| Passar a métrica por argumento | recusa nomeada, saída 5 |
| Avançar sem ter verificado | reprova por "não verificado" |
| Verificar e continuar editando | reprova por evidência obsoleta, com o arquivo citado |
| Verificador que falhou ao rodar | reprova, e a métrica do relatório não é aproveitada |
| Suíte morta por timeout ou sinal | falha, nunca zero |
| Apagar uma linha da trilha | psh audit verify acusa, saída 4 |
| Reescrever a trilha inteira relinkada | a âncora fora do arquivo acusa |
Fases, portões e verificadores ficam em .harness/workflow.json.
O arquivo é validado contra JSON Schema no carregamento.
Contrato inválido é falha fatal, nunca aviso.
{
"_type": "psh-workflow",
"version": 1,
"profile": "lean",
"verifiers": [
{
"id": "coverage",
"run": ["npm", "run", "test:coverage"],
"extract": { "kind": "lcov", "file": "coverage/lcov.info", "metric": "lines.pct" },
"watch": ["src/**", "tests/**", "package.json"],
"timeout_s": 900
}
],
"phases": [
{
"id": "phase.5.build",
"name": "Build + Quality",
"terminal": false,
"next": ["phase.6.ux-gate"],
"gate": {
"type": "all-of",
"checks": [{ "kind": "verifier", "verifier": "coverage", "min": 85 }],
"on_fail": {
"action": "rework",
"loopback_to": "phase.5.build",
"message": "cobertura abaixo do mínimo"
}
},
"on_failure": { "class": "quality", "max_auto_retries": 2 }
}
]
}O threshold mora em um lugar só: no check do portão, nunca no verificador.
O contrato não tem onde declarar o mesmo número duas vezes com valores diferentes.
Fase terminal é declarada com "terminal": true, nunca inferida de um next
vazio.
Um next que aponta para fase inexistente derruba o carregamento, em vez de
virar erro em runtime quando já é tarde.
| Código | Significado |
|---|---|
0 |
sucesso |
1 |
falha genérica: uso incorreto, verificador com erro |
2 |
portão reprovado |
3 |
contrato inválido |
4 |
cadeia de auditoria comprometida |
5 |
métrica forjada recusada |
6 |
projeto sem .harness/ |
- name: portões de qualidade
run: psh adapter ci --jsonO adapter verifica, avalia o portão e decide a transição em uma chamada, sem TTY e sem interação.
Ele não oferece --force.
Override é ato humano com confirmação, e CI não tem humano para confirmar.
{
"_type": "psh-ci-report",
"phase": "phase.5.build",
"sandbox_mode": "ai-jail",
"boundary_engine": "absent",
"verify": { "ran": [{ "verifier": "coverage", "status": "ok", "value": 87.4 }] },
"gate": { "passed": true },
"advance": { "decision": "advanced", "to": "phase.6.ux-gate" },
"audit_ok": true
}--gate-only avalia sem mexer no estado.
--skip-verify reaproveita evidência existente em vez de reverificar.
Esta seção existe porque um harness que promete garantia que não tem é pior que não ter harness nenhum.
-
Sem sandbox, não impede: detecta e reverte.
A fronteira só é aplicada pelo kernel quando há um sandbox operante.
Fora disso o modo é
degraded: snapshot antes, comparação depois, reversão do que saiu da fronteira.A diferença aparece no
psh status, nopsh doctore em cada registro de evidência. -
O mount não expressa arquivo novo em diretório gravável fora do escopo.
A raiz do projeto permanece gravável, então uma entrada criada ali durante a corrida escapa do kernel.
É o snapshot que fecha esse resíduo, e por isso ele continua ligado também no modo enjaulado.
-
Detecção de comando destrutivo não é proteção.
rm -rf,git reset --harde afins geram alerta na trilha, nunca bloqueio.Casamento por texto erra nos dois sentidos, e tratar isso como controle criaria confiança que o mecanismo não sustenta.
-
Não gerencia modelo, custo ou memória entre sessões.
Marcos posteriores.
cd psh
bun run check # typecheck + verificação estática + testes com cobertura
bun test # 236 testes
bun run build # binário único┌──────────────────────────────────────────────────────────────────────────┐
│ psh/src/workflow fases, portões, protocolo de falha, estado │
│ psh/src/evidence execução de verificador, extratores, frescor │
│ psh/src/gate avaliação de portão sobre evidência │
│ psh/src/audit trilha encadeada por hash │
│ psh/src/adapters ci (headless) │
│ psh/schemas contratos de dados versionados │
└──────────────────────────────────────────────────────────────────────────┘
Regras da suíte de testes:
-
Todo arquivo em
tests/roda por glob, nunca por lista enumerada.Teste que não roda é pior que teste ausente, porque cria confiança.
-
Nenhum teste escreve no diretório de trabalho nem toca no Git da árvore real.
-
Cobertura de linha acima de 85% em
audite emevidence, acima de 70% no resto, com todo arquivo desrc/entrando na medição.
| Versão | Escopo | Estado |
|---|---|---|
| 0.1 | Núcleo verificável: workflow, evidência, auditoria, CLI, adapter CI | entregue |
| 0.2 | Motor de fronteira, integração com ai-jail, modo degradado, suíte adversarial | entregue |
| 0.3 | Adapter Claude Code e memória entre sessões | planejado |
| 0.4 | Roteamento de modelo, contabilidade de token e custo | planejado |
| 0.5 | Adapter OpenCode, perfis por stack | planejado |
| 1.0 | Endurecimento, binários assinados, matriz de CI completa | planejado |
AGPL-3.0, a mesma do prostaff-api.
O ai-jail é GPL-3.0 e entra como dependência externa invocada como
processo, nunca linkada: o psh monta um argv e executa o binário.
Não há obra derivada, e as duas licenças convivem.
- Fabio Akita (@akitaonrails) - autor do ai-jail, consumido aqui como dependência externa para isolamento de execução.
Parte do ecossistema ProStaff