Skip to content

Latest commit

 

History

28 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

        ██████╗ ███████╗██╗  ██╗
        ██╔══██╗██╔════╝██║  ██║
        ██████╔╝███████╗███████║
        ██╔═══╝ ╚════██║██╔══██║
        ██║     ███████║██║  ██║
        ╚═╝     ╚══════╝╚═╝  ╚═╝
   ProStaff Harness - Execução Verificável e Auditoria de Evidências

CI Bun TypeScript SQLite License: AGPL v3 Version


╔══════════════════════════════════════════════════════════════════════════════╗
║  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        ║
╚══════════════════════════════════════════════════════════════════════════════╝

O problema

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       │
└─────────────────────────────────────────────────────────────────────────────┘

Sumário

┌──────────────────────────────────────────────────────┐
│  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                                        │
└──────────────────────────────────────────────────────┘

01 · Plataformas

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.


02 · Instalação

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 único

Isolamento 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.


03 · Primeiros passos

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 completo

psh 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

04 · Como um portão decide

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

05 · Contrato de workflow

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.


06 · Códigos de saída

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/

07 · Integração com CI

- name: portões de qualidade
  run: psh adapter ci --json

O 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.


08 · O que o psh não faz

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, no psh doctor e 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 --hard e 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.


09 · Desenvolvimento

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 audit e em evidence, acima de 70% no resto, com todo arquivo de src/ entrando na medição.


10 · Roadmap

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

Licença

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.


Créditos

  • Fabio Akita (@akitaonrails) - autor do ai-jail, consumido aqui como dependência externa para isolamento de execução.

Parte do ecossistema ProStaff

About

Execução verificável e auditoria de evidências para agentes de código. Portões de qualidade que consomem evidência produzida pelo núcleo não por número informado

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages