Configuration¶
The rituals read the [cabinet] section of the casting repository's
.vekna.toml. Every key has the default shown; a missing [cabinet] section
means all of them. A key that is not one of these is refused before anything
is checked out, so a misspelled key dies at the boundary. The values are not
checked against anything: a task name that does not exist fails when the
ritual runs it.
[cabinet]
forge = "github" # or "gitlab"
base = "main"
remote = "origin"
gate = "mise run pr-fix"
coverage = "mise run diff-cover"
fast_coverage = "mise run test:py:cov:diff"
sign_commits = true
review_skill = "~/.claude/skills/thermo-nuclear-code-quality-review/SKILL.md"
review_title = "Thermo-nuclear code quality review"
identify_skill = "~/.claude/skills/issues/SKILL.md"
smoke_skill = "~/.claude/skills/smoke-test/SKILL.md"
[cabinet.labels]
reviewed = "pr::thermo" # a review was posted; take it off to ask again
wait = "pr::wait" # hands off this pull request
qa = "v:guide:done" # the guide was posted; take it off to ask again
answered = "v:answer:done" # the answer task left no review thread open
[cabinet.ci.jobs.checks] # one table per CI job, keyed by a prefix of its check name
covering = false # red here is missing coverage
rerun = false # run it again once before fixing it
gate = "" # the command that reproduces it here; "" is none
fast = "" # re-run between repair rounds; "" is gate
[cabinet.ci.jobs.test]
[cabinet.ci.jobs."codecov/patch"]
covering = true
[cabinet.agent]
model = "opus"
effort = "high"
max_turns = 0 # 0 is unbounded
may_not_run = [] # slow tasks the prompt asks an agent to leave alone
The keys¶
| key | what it is |
|---|---|
forge |
github through gh, or gitlab through glab, hosted or self-hosted |
base |
the branch every pull request is merged from |
remote |
pushed to and fetched from by name; must be https, see the remote |
gate |
the command a merge has to pass before it is committed; the merge task repairs until it does |
coverage |
the full diff coverage measurement a covering job runs, named to agents as a task to leave alone |
fast_coverage |
the same measurement without the slow suites, re-run between repair rounds |
sign_commits |
false commits with commit.gpgsign=false, for casts nobody can unlock a key for |
review_skill |
the skill file the review task reviews with; see the review skill |
review_title |
the heading its review comments open with |
identify_skill |
the skill file identify has its agent read; see the issues skill |
smoke_skill |
the skill file the guide task writes its smoke test with; see the smoke-test skill |
[cabinet.labels]¶
| key | what it is |
|---|---|
reviewed |
on means a review was posted; take it off to ask for another |
wait |
hands off: a pull request wearing it is never taken, touched or reported on |
qa |
on means the guide was posted; take it off to ask for it again |
answered |
on means the answer task left no review thread open; it comes off when an answer task starts |
[cabinet.ci.jobs]¶
One table per CI job the rituals may fix, keyed by a prefix of the check's
name as the forge reports it; where two keys match a check, the longer wins.
A red check no key matches is never a ci todo. Listing jobs replaces the
defaults, so a project lists every job it wants read. A job listed bare is
still read; every key is optional:
| key | what it is |
|---|---|
covering |
red here is missing coverage, and the check's summary may name the patch coverage, "96.84% of diff hit" |
rerun |
the job may be a flake: run it again, once per head, before anything is fixed |
gate |
the command that reproduces the job here; the agent's fix is repaired until it passes |
fast |
the narrower command re-run between repair rounds; empty runs gate |
A red check on a listed job makes a ci todo in the survey, and so does a
green covering check whose summary names less than 100% of the diff. The
ci task then fixes it:
- With
rerun, the job runs again first, and the task ends for the next cast to read the result. - With a
gate, an agent reads the end of the job's log and fixes it, then the gate runs and is repaired until it passes,fastbetween rounds. - Without a
gate, the agent makes one attempt from the log and the ritual commits it. The next CI run checks it. - With
covering, coverage is measured here withgateandfast, orcoverageandfast_coveragewhere they are empty, and the agent writes tests for the lines the report names.
A check some other app posts, such as Codecov's, has no job to rerun or log
to read: give it covering, not rerun.
[cabinet.agent]¶
| key | what it is |
|---|---|
model |
the Claude model every agent call uses |
effort |
low, medium, high, xhigh or max |
max_turns |
the turn limit per agent call; 0 is unbounded |
may_not_run |
slow tasks the prompt asks an agent to leave alone, beside the gate and coverage commands; see Agents |
The review skill¶
The review task uses the thermo-nuclear code quality review, which is not
part of this tome: it is Cursor's, from the
cursor-team-kit
plugin. Put it where review_skill points before the first cast:
mkdir -p ~/.claude/skills/thermo-nuclear-code-quality-review
curl -fsSL -o ~/.claude/skills/thermo-nuclear-code-quality-review/SKILL.md \
https://raw.githubusercontent.com/cursor/plugins/refs/heads/main/cursor-team-kit/skills/thermo-nuclear-code-quality-review/SKILL.md
Or point review_skill at any other skill file and review_title at the
heading its comments should open with.
The issues skill¶
identify has its agent read the issues skill, which lives in this
repository's plugins. Put a copy where identify_skill points
before the first cast:
mkdir -p ~/.claude/skills/issues
curl -fsSL -o ~/.claude/skills/issues/SKILL.md \
https://raw.githubusercontent.com/fancysnake/cabinet/refs/heads/main/plugins/issues/skills/issues/SKILL.md
Or point identify_skill at the copy the plugin install left, or at a skill of
your own that names the same labels. A repository that already carries the
skill in its own tree can name that path instead, which is what this one does:
[cabinet]
identify_skill = "plugins/issues/skills/issues/SKILL.md"
The smoke-test skill¶
The guide task has its agent follow the smoke-test skill, which also lives
in this repository's plugins. Put a copy where smoke_skill
points before the first cast:
mkdir -p ~/.claude/skills/smoke-test
curl -fsSL -o ~/.claude/skills/smoke-test/SKILL.md \
https://raw.githubusercontent.com/fancysnake/cabinet/refs/heads/main/plugins/smoke-test/skills/smoke-test/SKILL.md
Or point smoke_skill at the copy the plugin install left, or at a skill of
your own.
The remote¶
Pushes go over https with the forge CLI as the credential helper. A cast has no terminal for ssh to ask a passphrase on, so an ssh remote is refused before the first fetch.
# GitHub
gh auth login && gh auth setup-git
git remote set-url origin https://github.com/<owner>/<repo>.git
# GitLab, hosted or self-hosted
glab auth login --hostname gitlab.example.com
git config --global credential.helper '!glab auth git-credential'
git remote set-url origin https://gitlab.example.com/<group>/<repo>.git
A repository that wants to keep its ssh origin adds a second remote for the
rituals and names it in remote: both point at the same repository, so this
is a route and not a destination.
Signed commits stay with gpg-agent. A cast that runs unattended cannot type
a passphrase, so either keep the key unlocked for the night or set
sign_commits = false.
Worktrees¶
heal.mine queues one heal.pr per pull request on the vekna daemon, and
the daemon runs each in a worktree from its pool. The pool is vekna's, set
under [circle] in .vekna.toml or in your vekna user config:
[circle]
worktrees = 3 # trees per repository, default 2
processes = 3 # casts running at once
prepare = "mise install && poetry install" # once, in each new tree
A tree holds only what git tracks, so .vekna.toml must be committed for a
heal.pr to find its [cabinet] section. Whatever prepare builds must be
gitignored, or every tree reads dirty and the pool blocks it. A heal.pr
that stands a branch down stashes its changes in its tree; the stash is shared
by every worktree of the repository, so git stash list in your own checkout
shows it.