Home / Articles

IntentFlow: Governing LLM Agents With Compiled Policy and Auditable Traces

A look at IntentFlow, an experimental language that enforces agent policy outside the model and writes hash-chained traces an auditor can verify independently.

Publisher: REACTAPP.TOP8 min read1780 words

This article is published in English.

Sources: IntentFlow: Governed LLM Agents With Auditable, Hash-Chained Traces

Imagine an LLM agent wired to read a GitHub issue, search the repository, draft a reply and, since someone was in a hurry, post that reply and close the issue. The guardrails went into the prompt: never close an issue, always cite evidence, ask a human when unsure. Then the model closed an issue anyway.

It wasn't malice. Guardrails written in prose are suggestions, and a model pushed by a cleverly worded issue body will sometimes treat suggestions as optional. When a security reviewer asks for proof that the agent never took a forbidden action, what exists is application logs written by the same code path, in a format someone invented. That isn't proof. It's a diary.

IntentFlow is an experimental language built to close that gap.

Where governance usually lives

Most agent governance sits in one of three places, and each leaks:

  • In the prompt. It's prose the model may ignore, and the only audit is reading the final output and hoping.
  • In application code. That is real enforcement, but the policy is now tangled with the Python that calls the model, and the question "what was the agent allowed to do?" is answered by reading the codebase.
  • In a framework's pile of callbacks. Functions get wired together while the policy lives in someone's head and in ad-hoc logging.

What's missing is a single reviewable artifact describing the envelope of acceptable behaviour: the evidence the agent must have, the actions it may take, the ones it must never take, what forces a human into the loop and how its output is checked. That artifact should be enforced outside the model and should leave behind a witness anyone can verify independently afterwards.

The idea in a paragraph

IntentFlow is a small declarative language. A .iflow file states a goal's objective, evidence requirements, action policy, verification rules, uncertainty handling and a typed output contract. A compiler turns the file into a JSON execution plan, which a runtime made of 13 phases then carries out, and sends every tool call through an ActionGate, including reading evidence from disk. The gate consults the compiled policy and never reads model output, so the model can't talk its way past it. Every run writes a hash-chained trace, optionally signed. Then intentflow audit rebuilds the rules from the source file and shows that the run stayed within them. All it needs is that file and the trace, with no need to trust the runtime, the backend or the model.

The project's tagline sums it up: Python programs deterministic computation, and IntentFlow programs governed cognition.

What a program looks like

The flagship example is an issue-triage goal, shown here in its governance core. It begins with a name and an objective:

goal TriageGitHubIssue {
  objective:
    triage a GitHub issue safely and propose a maintainer-ready response

The remaining sections cover evidence, allowed actions, verification, uncertainty handling and the output shape:

  evidence:
    require issue_body
    require comments
    require repo_context
    optional related_issues
    distrust unsupported_claims  actions:
    allow read_issue
    allow search_repo
    allow draft_comment
    require_approval post_comment
    deny close_issue  verify:
    require cites_evidence
    require maintainer_safe_tone
    require no_unverified_claims
    check confidence >= 0.65  uncertainty:
    if confidence < 0.65 ask_human
    if missing_evidence ask_human
    if security_risk block_action  output:
    summary: string
    likely_cause: string?
    confidence: number
    suggested_response: markdown
    proposed_labels: list[string]
}

Read it top to bottom and you know what the agent may do. deny close_issue isn't advice. It is compiled into the gate. With require_approval post_comment, the outward step is refused by default until an approval is granted by a person or another process. And check confidence >= 0.65 is a hard numeric test. The output: block is a typed contract that the result is validated against.

How it works

The flow is brief, and every stage restricts what the next one can do:

.iflow source -> parser -> analyzer (IFLOW diagnostics) -> compiler ->
execution plan (JSON) -> runtime (13-phase machine) -> traced result ->
replay / audit

Several design choices make the audit meaningful instead of decorative:

  1. The gate sits outside the model. Denied or unlisted actions raise errors, and approval-gated actions fail closed without a grant. Since the gate never reads model output, prompt injection has nothing to negotiate with.
  2. Calibration comes first. A backend reports a raw confidence score, the runtime adjusts it according to the plan's calibration policy, and the uncertainty rules then look at the adjusted figure. The trace stores both numbers.
  3. Verification is typed, not assumed. Machine-checkable rules such as cites_evidence, requires_phrase and threshold checks run against real state. Rules that need an LLM judge sit in a separate trust tier and, when no judge is available, are recorded as skipped and never silently passed.
  4. The trace is append-only and complete. Each phase, gate decision, escalation and check enters a hash-chained log, where every event stores a hash of the previous hash joined with the canonical event. Signing the trace seals the root with an HMAC so a key holder can detect tampering.

Every run finishes with one of six possible statuses, and the process exit code mirrors it. The important guarantee is that failed verification ends the run as failed_verification. The runtime can't report it as completed, and one of the auditor's checks exists specifically to catch any attempt to hide that.

Try it in five minutes

The core has no runtime dependencies, and every command below runs offline and deterministically without an API key. It targets Python 3.10 or newer.

git clone https://github.com/dgenio/intentflow
cd intentflow
pip install -e .

Validate the flagship example, ask for an explanation and run it:

intentflow validate examples/opensource_triage.iflow
intentflow explain  examples/opensource_triage.iflow
intentflow run      examples/opensource_triage.iflow \
    --backend simulate --trace-dir traces

The run prints a summary like this one, representative of a real run on version 0.6.0:

--- run summary ---
  goal:               TriageGitHubIssue
  status:             completed
  backend:            simulate
  confidence:         0.676
  verification:       passed
  uncertainty:        clear
  trace id:           b9f44900...
trace written to traces/TriageGitHubIssue-...json

Now check that it behaved. The audit command recompiles the source and proves the trace stayed within the program's envelope:

intentflow audit examples/opensource_triage.iflow traces/TriageGitHubIssue-*.json

A clean result looks like this:

{
  "goal": "TriageGitHubIssue",
  "conformant": true,
  "violations": []
}
AUDIT: CONFORMANT — the trace stayed inside the program's envelope

To see the restrictions actually trigger, try the example that is risky on purpose. It permits a deploy action without any approval step. The analyzer raises IFLOW010, the compiled risk profile comes out as high, and when it runs the rule if security_risk block_action kicks in:

intentflow run examples/high_risk_deploy.iflow --backend simulate
# -> status: blocked

The denied write_database action can never execute no matter what the model says.

The same features are available from Python. Start with the import:

import intentflow

Then load a program, run it, and register a Python function as a governed action:

program = intentflow.load("examples/opensource_triage.iflow")
result = program.run(backend="simulate")
assert result["status"] == "completed"# register a Python function as a governed action (runs through the gate):
program.register_tool("lookup_user", serves=("user_record",),
                      handler=lambda src: "enterprise plan")report = intentflow.audit_document(program.compile(), result)
assert report["conformant"]

Registering a tool doesn't bypass governance. Your function still runs through the action gate.

Why confidence falls from 0.72 to 0.676

This isn't a defect, and it captures the entire design idea. The production-diagnosis example uses a threshold of 0.7. With the simulator the raw confidence is 0.72 and would clear it, but calibration reduces it to 0.676, under the bar, so the run intentionally finishes as needs_human. A diagnosis the agent is unsure about shouldn't complete unattended. The simulator is there so this whole control structure of gating, calibration, verification, escalation and tracing can be tested end to end without a network or flaky behaviour.

Honest limitations

IntentFlow is at version 0.6.0 and classed as pre-alpha, with no CI pipeline yet. It works, and the repository carries roughly 233 tests that need neither network nor API keys, but the gaps are real:

  • The grammar is line-oriented and has no compound conditions, so there is no if a and b.
  • Calibration is a fixed shrinkage map and not a learned model, although a learned map is planned.
  • Object-shaped outputs have a type only at the outer level, with nothing enforced on their contents.
  • Uncertainty primitives beyond ask_human and block_action are recorded but not executed.
  • Tools with side effects are covered by policy but never really run. The gate rules on them, and carrying out genuine outward writes is still to come.
  • Pipelines are linear, with DAGs on the roadmap.

The simulator fakes the reasoning step and says so. Backends for OpenAI and Anthropic exist and pass through the same governance path, and a cassette-replay mode allows deterministic CI runs without keys. Still, the reference for conformance is the deterministic simulator.

For a production agent governor today, this isn't it yet. For thinking clearly about what governed, auditable agent execution should look like, and for experimenting with it, the project is unusually concrete for its age.

What to take away

  • Keep policy outside the model. Prose is only a request, whereas a compiled deny backed by a gate that ignores model output is a hard rule.
  • Self-written logs don't prove much, but a trace an independent party can check does. The auditor derives the rules again from source and needs just the source and the trace.
  • Adjusted uncertainty ought to be able to halt a run. When 0.72 is pulled down to 0.676 and misses a 0.7 bar, that is intended behaviour: an agent that isn't sure hands off to a person instead of bluffing.
  • The whole control loop can be tried offline: no dependencies, a deterministic simulate backend and no API key.
  • Set expectations for pre-alpha software: it's working and well tested, but there's no CI yet, pipelines are linear, calibration is fixed and side-effecting tools aren't executed.

Testing an idea like this properly means giving it a real goal. Write a .iflow file for an agent you actually operate, see if the explain command describes what you intended, then run it in simulate mode and audit the trace.