Skip to content

vekna

Run coding agents as rituals: ordinary Python programs whose steps you control and whose agent calls happen inside those steps. Agents run permissively within a step; determinism lives at the step boundaries.

An agent left to plan its own work is impressive right up to the point where it decides the failing test was the problem. A ritual is the other arrangement: you write the loop, the branches and the stopping condition yourself, and hand the agent one bounded piece of work at a time.

The one below fits on a screen because it is an introduction; a ritual in daily use is a package with a test suite. What stays true at any size is that the control flow is yours to read.

Install

pip install vekna

Python 3.11 or newer. The coding medium reaches an agent through the Claude Code CLI, which installs separately — everything else works without it.

Your first ritual

Put a rituals.py in your project:

from typing import Annotated

from pydantic import BaseModel, Field

from vekna.folio.coding import coding
from vekna.folio.shell import shell
from vekna.lexicon import Transition, done, goto, ritual, step


class FixTests(BaseModel):
    # A retry budget counts down to zero, so the CLI rejects a negative one
    # rather than letting `--bound -1` run until the step backstop.
    bound: Annotated[int, Field(ge=0)] = 3


class Attempt(BaseModel):
    left: int


class Verdict(BaseModel):
    outcome: str


@step
async def fix(state: Attempt) -> Transition:
    result = await shell("pytest")
    if result.exit_code == 0:
        return done(Verdict(outcome="green"))
    if state.left <= 0:
        return done(Verdict(outcome="gave up"))
    await coding(f"The test suite fails:\n{result.stdout}\nFix it.")
    return goto(fix, Attempt(left=state.left - 1))


# `def`, not `async def`: naming the first step has nothing to await. A step or
# entrypoint is written whichever way its body needs.
@ritual("fix_tests")
def fix_tests(components: FixTests) -> Transition:
    return goto(fix, Attempt(left=components.bound))

Then cast it:

vekna cast fix_tests --bound 5

Output streams live as a tree of rites — one node per step, one nested under it per medium call, with the agent's own output indented beneath. The last line is the cast's result, as JSON:

result: {"outcome":"green"}

The retry budget is a number you wrote, the stopping condition is an if you can read, and the agent is called once per attempt with exactly what you chose to tell it.

Where to go next

  • Rituals — steps, transitions, components, and how a ritual is found.
  • Mediumscoding, shell and decide, and how to configure the agent behind them.
  • Testing rituals — run a ritual with every medium answering from a script.
  • Examples — the rituals this project runs on itself.
  • Safety — what vekna does not sandbox. Worth reading before your first cast.

If you use Claude Code, this repository ships a ritual-scribe skill — drop it in your own .claude/skills/ and the agent writes rituals against the real surface rather than inventing an API that reads plausibly.