Observed since 20 Oct, 18:40 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Autorize a ação. Prove o resultado. Assurance de execução para agentes de IA.
(era ESCOPO até 19/09/2026 — o pacote agora é
actrova;escopocontinua funcionando como atalho durante a transição.)
Autorize a ação. Prove o resultado.
Camada de assurance para agentes de IA que executam ações no mundo real.
A regra que atravessa cada peça desta biblioteca: nunca transformar ausência, silêncio ou tentativa em certeza.
Ferramenta de observabilidade te diz que reembolsar() devolveu 200.
Isso é o agente dizendo que fez. Não é o dinheiro tendo voltado.
observabilidade o que aconteceu no código?
governança quem podia fazer?
ACTROVA ele tinha autoridade, ficou dentro do limite,
e o resultado foi REALMENTE produzido?
INTENÇÃO o que o agente QUER fazer (antes)
VEREDITO o que a política PERMITE (antes)
EXECUÇÃO o que a função RETORNOU (durante)
VERIFICAÇÃO o que a FONTE DE VERDADE confirma (depois)
RECIBO a evidência encadeada de tudo (permanente)
Juntar EXECUÇÃO com VERIFICAÇÃO é o erro que este projeto existe para impedir.
PENDING ainda não deu tempo de conferir
VERIFIED consultei a fonte e está certo
FAILED consultei a fonte e está ERRADO
UNVERIFIABLE NÃO CONSEGUI consultar
⚠️ FAILED e UNVERIFIABLE são coisas radicalmente diferentes. Sistema que
só tem "deu certo / deu errado" acaba inventando evidência que não tem — e é
assim que agente destrói dado achando que está trabalhando.
Isso não é hipótese. É o incidente que originou o projeto: em
ceo_agent.py:192 do Jarvis,
vendas = _vendas_por_fonte(dias) # {} quando o GraphQL erra
vk = vendas.get(fonte, {"vendas": 0, "comissao": 0.0}) # ← ausência vira zero
if vk["vendas"] > 0: vd = "VENDE"
elif n >= min_posts: vd = "MORTA" # ← 36 fontes de uma vez
Uma consulta falhou e 36 fontes de conteúdo foram desabilitadas porque "sem dado" foi lido como "vendeu zero".
from actrova import Escopo
escopo = Escopo(politicas="politicas", dados="dados")
escopo.registrar_verificador(MeuVerificador())
@escopo.guarda(agente="jarvis.ceo", acao="source.disable", alvos="fontes")
def podar_fontes(fontes):
...
O contrato, em YAML:
agente: jarvis.ceo
acao: source.disable
modo: observe # não bloqueia nada — só registra
regras:
- id: limite_absoluto
se: {campo: quantidade, op: ">", valor: 50}
entao: DENY
motivo: acima de 50 não é decisão operacional, é incidente
- id: lote_destrutivo
se: {campo: quantidade, op: ">", valor: 5}
entao: HOLD
motivo: operação destrutiva em lote precisa de gente olhando
padrao: ALLOW
verificacao:
verificador: jarvis.fontes
tentativas: 3
espera_segundos: [0, 5, 30]
espera:
desabilitadas: $quantidade
E o recibo que sai:
RECIBO #1 a3c4825a1fb6
AGENTE jarvis.ceo
INTENÇÃO source.disable (36 alvo(s))
CONTRATO jarvis.ceo.source.disable@v1
REGRA lote_destrutivo
DECISÃO HOLD
MODO observe
INVOCOU SIM
EFEITO UNKNOWN
VERIFICAÇÃO UNVERIFIABLE
a função foi invocada → sim, com 36 fontes o mundo mudou? → não se sabe o resultado foi provado → não e agora essas três coisas são campos diferentes
VERIFIED que não deixa ninguém concluir demaisDizer "verificado" e parar ali é o jeito mais fácil de exagerar. O recibo de um reembolso sai assim:
ESTADO VERIFIED
CONTAGEM pedidos 1 · confirmados 1 · falhos 0 · incertos 0
COBERTURA PARTIAL
⚠️ SEM COBERTURA `cardholder.credit_received` — ninguém sabe avaliar isso
QUANTO PROVA PROVIDER_STATE · SAME_SOURCE_REREAD · PROVISIONAL
⚠️ EXIGE RECONCILIAÇÃO — esta conclusão tem prazo e ninguém voltou a olhar
NOTA a Stripe confirma o REGISTRO do reembolso; a chegada do dinheiro
ao portador do cartão NÃO foi verificada
Quatro perguntas, quatro respostas, nenhuma inflando a outra:
o que deveria acontecer? intenção + efeito autorizado
o que conseguimos observar? estado + contagem
quanto a evidência prova? assurance
o que ficou FORA da cobertura? coverage
assurance tem três eixos, e não vira um número. O que foi provado (o
registro no provedor ou a consequência no destinatário), de onde veio a
evidência (o próprio executor ou um terceiro), e se ainda pode mudar. Um
reembolso da Stripe vai de succeeded para failed — eles fornecem cenário
de teste para simular. nivel = 3 destruiria as três dimensões, pelo mesmo
motivo que executed: true destruía invocação, efeito e verificação.
coverage é capacidade declarada, não resultado de execução. O contrato
diz quais afirmações a intenção exige; o verificador diz quais sabe avaliar.
Se a fonte cai, a cobertura não muda — o verificador continua sabendo o
que sabia, só não estabeleceu nada naquela rodada. E as lacunas são nominais,
nunca percentuais: 3 de 4 = 75% parece ótimo até alguém notar que a quarta
era "o dinheiro chegou no cliente".
⚠️ Três coisas que nunca colapsam, e é aqui que este eixo se paga:
SEM COBERTURA não existe quem avalie → buraco de PRODUTO
UNVERIFIABLE existe, e não deu nesta rodada → problema de INFRA
FAILED existe, consultou, e contradiz → INCIDENTE
📌 E o melhor efeito é o que ninguém precisa pedir: quando alguém fortalece o
contrato — acrescenta cardholder.credit_received às exigidas — o verificador
passa a acusar a lacuna sozinho, sem uma linha alterada. A biblioteca diz,
sem ninguém perguntar: o modelo de intenção ficou mais forte que a capacidade
de prova.
⚠️ O que ainda NÃO existe: PROVISIONAL descreve que a conclusão tem
prazo, e nada volta a olhar. A reconciliação é um mecanismo que não foi
construído — o campo torna a dívida visível, não resolvida.
observe é o padrão. Ninguém coloca um fornecedor desconhecido no caminho
crítico de uma operação que mexe com dinheiro. Em observe, a Actrova registra o
veredito e não interrompe nada — rodando semanas assim se descobre quais
políticas importam antes de deixar alguma parar a operação.
Nenhuma dependência de nuvem para executar. Contrato é arquivo local, avaliação é em processo, recibo é arquivo local. Se a internet cair — ou se a Actrova sumir amanhã — a decisão continua acontecendo na máquina do cliente. Mesmo desenho de OPA e Cedar: data plane local, control plane remoto.
Recibo é encadeado por hash. Log que o fornecedor consegue editar não é prova; é a palavra do fornecedor, formatada. Cada recibo carrega o hash do anterior, e alterar um do meio quebra todos os seguintes — detectável por qualquer um, sem confiar na gente.
⚠️ Tamper-evident, não tamper-proof. Quem tem acesso de escrita pode editar um recibo do meio e recalcular a cadeia inteira a partir dali; aí ela volta a fechar. A cadeia prova consistência interna e pega alteração, remoção, reordenação e corrupção — não pega reescrita completa.
Fechar isso exige assinar a cabeça da cadeia —
actrova.selo, desde 22/09. Como cada recibo carrega o hash do anterior, assinar o hash do #500 é assinar os 500: reescrever o passado passa a exigir também forjar a assinatura.🔥 E o que protege ali é a separação, não a criptografia. Com a chave no mesmo disco do livro, quem reescreve os recibos reassina a cabeça e o selo confere — isso é um TESTE, de propósito (
teste_selo.py, caso 6). O selo vale o que valer a distância entre quem escreve e quem guarda a chave: KMS, HSM, outra máquina, ou o arquivo de selos publicado onde quem grava não alcança.⚠️ Por isso a frase continua sendo "toda alteração deixa marca", nunca "ninguém consegue alterar". O selo aumenta o custo da alteração; não a torna impossível.
O livro é a verdade; a fila de verificação é uma projeção. O recibo da
ação carrega o plano inteiro — quem confere, contra qual asserção, quantas
tentativas — então reconciliar() reconstrói a fila a partir do livro. Apagar
fila.json, trocar de máquina ou restaurar um backup não faz a Actrova dizer
"não há nada pendente" quando a verdade é "perdi a lista".
Um escritor por vez no livro. flock segurado do "ler o último recibo"
até o "escrever o próximo". Sem isso, dois processos gravam com o mesmo prev
e a cadeia passa a acusar adulteração onde houve concorrência — e cadeia que
grita lobo perde o valor de evidência.
Verificação não altera o recibo da ação. Ela é deferida (reembolso demora a liquidar, ERP sincroniza em lote), então anexa-se um recibo novo apontando para o da ação pelo hash. Livro-razão, não linha editável.
Invocação, efeito e verificação são três campos. Um executed: true
sozinho junta "a Actrova chamou o código" com "o mundo mudou", e as duas
respostas divergem o tempo todo: a função que se absteve, a que quebrou no
meio, a que devolveu 200 sem nada ter liquidado. invoked a Actrova sabe;
effect ela só sabe quando a fonte de verdade confirma — até lá é UNKNOWN,
e UNKNOWN escrito é melhor que true insinuado.
A aplicação pode declarar abstenção, nunca sucesso. Levantar SemEfeito é
a função dizendo "fui chamada e de propósito não fiz nada" — abstenção ela
conhece de dentro. Não existe contrapartida para declarar APPLIED: isso é a
fonte de verdade quem diz.
O núcleo conhece estados; as integrações conhecem o mundo. A Actrova não implementa Stripe — implementa a gramática para você explicar como a Stripe prova alguma coisa, e depois confere se a explicação obedece às invariantes:
from actrova import conformar
print(conformar(MeuVerificadorStripe(), cenarios={...}).texto())
⚠️ A suíte exige o cenário fonte_fora e se recusa a certificar sem ele.
De fora, um _consultar que devolve {} numa falha é indistinguível de um
que devolve {} porque a fonte respondeu vazio — e essa confusão é o bug que
originou o projeto. Certificado que passa sem testar isso dá confiança onde
não há evidência.
O avaliador de política é andaime. OPA/Rego e AWS Cedar já resolvem isso, de
graça e melhor. O produto da Actrova é provar o resultado, não decidir se
pode. Avaliador é interface justamente para essa troca.
Uma linha, no seu código, sem instalar a Actrova em lugar nenhum:
python3 -m actrova conformar meu_modulo.py:MeuVerificador
Ele já diz o que dá para afirmar sem fixture nenhuma. Para responder a pergunta que decide tudo, monte os três cenários que só você consegue montar — porque só você sabe o que faz a sua fonte responder cada coisa:
python3 -m actrova exemplo > cenarios.py # esqueleto comentado, edite
python3 -m actrova conformar meu_modulo.py:X --cenarios cenarios.py:CENARIOS
O resultado que interessa é este, e é o bug que originou o projeto:
❌ cenário `fonte_fora`
a fonte está indisponível (rede caída, 500, timeout)
→ esperado um de UNVERIFIABLE, veio FAILED
fonte_fora REPROVADO — respondeu, e respondeu errado
resultado NÃO CONFORME — 1 invariante(s) violada(s)
Um except: return {} acabou de transformar "não consegui consultar" em
"consultei e está errado". De fora, os dois são idênticos — e é por isso que
a suíte exige esse cenário e se recusa a certificar sem ele.
Saídas: 0 conforme · 1 violou uma invariante · 2 sem certificado, porque
não testado não é aprovado · 3 erro de uso. As três primeiras são
diferentes de propósito: "ninguém perguntou" e "perguntamos e ele mentiu"
não podem colapsar na sua CI, pelo mesmo motivo que não podem colapsar no
recibo.
⚠️ E o relatório diz sozinho o que não afirma: quem escreveu a fixture do
fonte_fora foi a mesma pessoa que escreveu o _consultar. Se ela supõe que
a fonte levanta num 500 e a fonte devolve {} com HTTP 200, a conformidade é
sobre uma fonte que não existe. Isso é um manifesto, não um selo.
O livro local é a primeira cópia. A Actrova Cloud guarda a segunda — uma
que quem executou a ação não controla. Quem manda é o actrova enviar, num
timer, nunca de dentro do agente: com a Cloud fora do ar o agente segue, e
a próxima rodada manda o que ficou para trás.
# a credencial vem da Actrova (só escreve recibos, num livro só), por scp:
chmod 600 /etc/actrova/runtime.token
actrova enviar --livro escopo_dados/livro.jsonl --livro-id NOME-DO-LIVRO \
--cloud https://api.actrova.com --token-arquivo /etc/actrova/runtime.token
# se o livro tem chave matriculada na Cloud: --chave caminho/da/chave.pem
Num timer do systemd (a cada minuto):
# /etc/systemd/system/actrova-enviar.service
[Service]
Type=oneshot
ExecStart=/caminho/do/venv/bin/actrova enviar --livro /caminho/livro.jsonl \
--livro-id NOME-DO-LIVRO --cloud https://api.actrova.com \
--token-arquivo /etc/actrova/runtime.token
# /etc/systemd/system/actrova-enviar.timer
[Timer]
OnBootSec=1min
OnUnitActiveSec=1min
[Install]
WantedBy=timers.target
Saída: 0 em dia · 2 🔥 achado (bifurcação, recibo recusado, livro local
com número faltando ou repetido, redirecionamento — alguém precisa olhar) ·
3 credencial recusada · 4 Cloud fora ou sobrecarregada (o timer tenta de
novo) · 1 configuração. O token só vem de arquivo 600, a URL só https,
e nenhuma saída mostra token ou corpo de recibo. Até onde a Cloud confirmou
fica em <livro>.cloud.json; apagá-lo só faz reenviar (a Cloud responde que
já tinha).
🔒 Redirecionamento nenhum é seguido. Um 3xx para o envio ali mesmo: o
token só vai ao endereço do --cloud, nunca a outro — nem de https para
http. Se a Cloud mudou de endereço, mude o --cloud.
🔥 Livro local com buraco não é "em dia". Antes de mandar, o enviar
confere que o livro é #1, #2, #3… — inteiros a partir do 1, sem número
faltando nem repetido. Se
não for, não manda nada e sai 2: o livro foi copiado, restaurado ou
editado, e quem decide o que vale é uma pessoa.
python3 teste_escopo.py # 340 asserções — o núcleo
python3 teste_assegura.py # 29 — os três eixos de quanto a prova prova
python3 teste_cobertura.py # 34 — prova de quê, e o que ficou de fora
python3 teste_stripe.py # 24 — o segundo domínio (pip install stripe)
python3 demonstracao.py # o incidente das 36 fontes, de ponta a ponta
Só depende de pyyaml. E é python3 — em Ubuntu limpo python não existe.
⚠️ teste_stripe.py pula sem o pacote stripe — e pulado não é
aprovado. A CI roda os 19 teste_*.py em Python 3.10–3.13 com o SDK
instalado e reprova qualquer saída com PULADO.
⚠️ 0.0.x é experimental: a API e a semântica ainda mudam entre versões.
0.0.4 · 01/10/2026 — correções de segurança do actrova enviar, da
revisão do Astra (30/09). Quem usa o enviar da 0.0.3 deve atualizar.
Location — outra origem, e
até de https para http. Agora o 3xx para o envio (saída 2), sem mandar
nada para lugar nenhum além do --cloud.[#1, #3] mandava só o #1 e saía 0. Agora número faltando,
repetido ou fora do domínio (0, negativo, não inteiro) no livro local para
tudo antes de mandar (saída 2).0.0.3 · 29/09/2026 — o runtime passa a mandar para a Actrova Cloud.
actrova enviar (actrova.replica): manda, em ordem, os recibos que a
Cloud ainda não confirmou, exatamente como estão no livro; assina cada um
quando o livro tem chave matriculada (--chave, extra [selo]). Para e
avisa em bifurcação ou recibo recusado (saída 2), em credencial recusada
(3); com a Cloud fora do ar, só adia (4). Rode num timer, nunca de dentro
do agente — ver "Mandar para a Actrova Cloud".600; URL só https (http só em 127.0.0.1);
nenhuma saída mostra token ou corpo de recibo.0.0.2 · 26/09/2026 — o trabalho de 21 a 26/09, que a 0.0.1 não levava.
actrova.conector e
actrova.conectores: stripe (reembolso; succeeded só confirma com o
valor autorizado informado), http (genérico: o código HTTP nunca vira
veredito, e o que um 404 prova é declarado por quem integra) e postgres.pedir_urllib nasce endurecido pela inspeção de 26/09: o alvo — que
vem dos ids que a ação do agente produziu — vai codificado como um
segmento só, e ./.. dão UNVERIFIABLE sem consultar a fonte. Sem
isso, fantasma/../ped_real lia outro recurso e um pedido inexistente saía
VERIFIED. Caminho fixo vai na base, nunca no alvo; base só http/https.actrova.selo, extra [selo]): assinatura da cabeça da cadeia,
com o limite escrito — tamper-evident, não tamper-proof.PROVISORIO → RECONCILIADO), prometidas no próprio contrato YAML.FALHOU > PARCIAL > VERIFICADO > PENDENTE > INVERIFICAVEL).trava
confere que o flock exclui de verdade no sistema de arquivos; origem do
contrato gravava UNVERIFIABLE no lugar do caminho do arquivo.setuptools>=83 (CVE-2026-59890).0.0.1 · 21/09/2026 — primeira publicação.
v0. Prova uma coisa só, e prova: distinguir "o agente foi invocado" de "o mundo mudou" de "o resultado foi comprovado", num caso real.
Neste pacote, deliberadamente não existe: dashboard, login, multi-tenant,
billing — isso é a Actrova Cloud, à parte, e este pacote nunca depende dela
(enviar só fala HTTP). Ainda não existe: SDK TypeScript, landing page. Os
conectores existem e são experimentais (Connector Contract v0).
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.0.5 | 9 Oct 2026 | 4 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 18:40 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:6346cbf37734129b2eb48132d4d35a03d40cdcda2d4eb5c29089c07178a08e89