Skip to content

rmichaelthomas/liminate-invariant

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Invariant

Verify what AI agents claim, not just what they're allowed to do.

Part of the Liminate family — trust infrastructure for autonomous AI, built on a 61-word prose-as-syntax language.

Invariant is a deterministic claim-verification correction harness for AI agents. It hosts an agent behind a callable, runs a Liminate contract against the agent's output each cycle, reads structured failure details off the result stream, feeds them back to the agent, and re-runs until claims verify, stall, or the ground proves unstable. Per-claim status, partial-pass acceptance, and honest escalation — a stall is not the same as flaky ground, and neither is a plain fail — are the point.

run() never writes anything. It proposes a result; the caller decides what to do with it.

What it does

You write verification claims as Liminate statements (require, forbid, domain pack verbs). Invariant runs them in a K-sample stabilization loop, distinguishes genuine failures from noisy ground, and reports per-claim outcomes: verified, corrected, or escalated with a reason.

import liminate_invariant as invariant

result = invariant.run(contract_text, agent, probes={"build-output": run_build})

for claim in result.claims:
    print(f"{claim.name}: {claim.status}")  # verified | corrected | escalated

Built by Liminate

Invariant is built on Liminate, a prose-as-syntax language where plain English sentences execute directly.

Repo What it does
seshat-app Local agent harness. Uses Invariant for post-action environment verification.
liminate The language and interpreter. 61 words, deterministic execution, domain packs.
← this repo liminate-invariant Semantic verification harness. Deterministic claim-verification correction loop.
liminate-mcp Authoring MCP server. Helps an agent draft, validate, explain, and test Agreements.

liminate.dev

Install

Requires Python 3.11+.

pip install liminate-invariant

Or from source:

git clone https://github.com/rmichaelthomas/liminate-invariant.git
cd liminate-invariant
pip install -e ".[dev]"
pytest

No network calls, no telemetry, no server dependency. The harness runs entirely on your machine.

Used by Seshat

Seshat uses Invariant for post-action semantic verification. When a developer writes verification claims in ~/.seshat/invariant.limn, the Seshat harness runs invariant.run() after every permitted agent action and records the result on the hash-chained receipt. Claims that fail are recorded — they don't block the action. The Agreement governs permission; Invariant governs correctness.

Status vocabulary

Each claim resolves to one of:

  • verified — passed on the first cycle.
  • corrected — failed at least once, then passed after the agent revised its claim.
  • escalated — did not resolve. escalation_reason is one of:
    • "claim will not converge" — the same failure recurs across consecutive cycles; re-eliciting from the agent isn't fixing it.
    • "ground will not stabilize" — the probe-acquired ground itself won't settle into a stable pass or fail.
  • pending — reserved for a future prediction capability (condition-gated re-check). This build's loop never assigns it.

API

run()

The full structured interface:

import liminate_invariant as invariant
from liminate_invariant import AgentTask, AgentResponse

def my_agent(task: AgentTask) -> AgentResponse:
    # task.contract — the Liminate contract text
    # task.failures — failure details from the previous cycle
    # task.cycle — current cycle number
    return AgentResponse(claims={"source-name": "agent's claim value"})

result = invariant.run(
    contract_text,
    my_agent,
    probes={"build-output": run_build},  # optional ground-truth probes
    max_cycles=5,                         # default 5
    stabilization_k=3,                    # K-sample stabilization depth
)

agent receives the contract, cycle number, and previous failures, and returns the claims it produced this round. probes are optional callables that acquire ground truth directly (test output, a build status, a metric) — Invariant re-acquires and re-samples them each cycle to tell a genuinely unstable measurement (flaky) apart from a claim the agent keeps getting wrong (a stall).

simple()

Quick-start on-ramp for a plain string-in/string-out function:

import liminate_invariant as invariant

def call_my_agent(prompt: str) -> str:
    ...  # however you talk to your model of choice

agent = invariant.simple(call_my_agent, source="build-status")
result = invariant.run(contract_text, agent)

simple() serializes the contract and failure details into a single prompt string. It's the fast on-ramp — the structured AgentTask / AgentResponse interface is recommended for production, since serializing to a prompt string discards machine-readable failure identity.

InvariantResult

result.claims         # list[ClaimOutcome] — per-claim status, cycles, corrections, handler
result.total_cycles   # int — how many cycles ran
result.converged      # bool — True iff every claim is verified or corrected
result.new_handlers   # list[str] — Narratia-admitted handlers (if Narratia was used)
result.final          # ContractResult — the last interpreter result

Generating governance (Narratia)

Pass narratia= to have repeated failures propose durable inherited when handlers instead of just failing the same way forever:

import liminate_invariant as invariant

narratia = invariant.NarratiaReference(complete=lambda prompt: client(prompt))
result = invariant.run(contract_text, agent, narratia=narratia)

complete is any string-in/string-out completion callable — zero LLM SDK dependency inside Invariant. Every proposal passes a deterministic gate: parsed by the same engine that runs your contract, checked for contradictions against the contract's obligations. Every rejection is recorded, not silently dropped.

Admitted handlers apply in memory for the rest of the run and come back as result.new_handlers — canonical, attributed inherited when ... from narratia text. run() still never writes: committing those handlers to your actual contract is yours to do.

License

Apache 2.0. See LICENSE.


The harness proposes. The caller commits. Every claim earns its status or honestly fails to.

About

Verify what AI agents claim, not just what they're allowed to do. Semantic verification for the Liminate ecosystem.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages