Gated procedures for shipping code with AI coding agents
No “done” without evidence.
Kuznia Relay is an open procedure for delivering real features with Claude Code, Codex or any agent CLI. A fresh agent per step, every check separate from its fix, and every claim backed by evidence in files: git hashes, diff stats, test output. Let a coordinator run the loop overnight and read the summary over coffee, or approve each gate yourself.
Plain Markdown and git. No server, no account, nothing to install.
Long agent sessions fail quietly.
Long sessions forget what was decided at the start. Scope creeps in silently.
The author grades its own work. It finds what it expects to find.
“All tests pass” with no command, no count, no commit hash.
“I've implemented the version flag and updated the tests. Everything is working and all tests pass! The feature is complete. ✅”
FIGURE 2a.— What you usually get. Which commit? Which tests? Who checked?
commits: c66916d [P1-T1] Fix F3
git diff --stat:
.gitignore | 1 +
__pycache__/greet.cpython-313.pyc | Bin 1496 -> 0 bytes
__pycache__/test_greet.cpython-313.pyc | Bin 3369 -> 0 bytes
proof: git ls-files __pycache__ (empty)
python3 -m unittest -q
Ran 6 tests in 0.000s
OK
push: wip/p1-t1-version-flag -> wip/p1-t1-version-flagFIGURE 2b.— What a Relay agent hands back: real command output, in a file you can audit later.
The missing stage between ticket and pull request.
Requirements, acceptance criteria, design
- 01Analyze
- 02Spec check
- 03Build
- 04Review
- 05Done
Fresh agent per step · state in a Markdown knowledge base · gates approved by a delegated coordinator or by you
Feature branch, every task merged, tagged and evidenced
FIGURE 3.— Relay works on a feature branch, one task at a time. When every task is Done, you open the PR to your base branch. Agents never do.
Claude Code, Codex, or any CLI that can read a file and run git.
It shines on work that is too big for one context window.
A reviewer, a client, or you in three months, reading the paper trail.
Approve by hand, or delegate clean checks to a coordinator. Scope changes always stop for you.
Five steps. Two gates. One baton.
- Who
- Fresh agent, read-only
- Output
- Answers to the prompt's questions, with file:line references
- Exit condition
- Questions answered, or handed to the Owner
- Where it runs
- Throwaway detached worktree in its own scratch folder
May be skipped for a small task. The task card says why.
## Findings or changes
- Q1: where are CLI flags defined? greet.py:5-10 (build_parser)
- Q2: are flags tested end to end? no; test_greet.py:6-10 calls greet() directlyFIGURE 4.— Two gates: spec approval before Build, code approval (naming the reviewed commit) before Done. Delegated to a coordinator, or yours.
How a round runs
A coordinator (or you) talks to a planner (any LLM session) in plain commands. The planner writes the prompt and returns one line to paste into a fresh agent. The agent reports back in a file. Nothing important lives in a chat window.
- 1.Coordprompt P1-T1
- 2.Plannerwrites Workflow/Prompts/P1-T1 - 1 Analyze.md, replies with one paste line
- 3.Coordstarts a fresh agent with that line
- 4.Agentdoes the step, writes Workflow/Reports/P1-T1 - 1 Analyze.md
- 5.Coordreport P1-T1
- 6.Plannerreads the evidence, updates card, Board, Plan, Decisions. Says what's next
Flight modes: autopilot, or hands-on
The procedure is the same either way. Only who opens the gates changes.
A coordinator session drives the loop for you, overnight if you like.
- —Kickoff: an ordered task list, Done: yes or no
- —Approves clean checks when you delegate it. Each approval is logged in Decisions.md, marked to review
- —Leaves one short morning summary per task: what ran, what it decided without you, what you should do first
You send each command and open each gate yourself.
- —prompt, paste the line into a fresh agent, report
- —approve the spec, approve the reviewed commit
- —Best for your first features, or a codebase you don't trust yet
Clear roles. One writer per file.
| Role | Who | Does | Never does |
|---|---|---|---|
Owner Flight director | You, the human engineer. One per project. | Sets up branches, approves specs and code or delegates that, makes scope calls | Hand-edits a task's step history |
Planner Capcom | An LLM session in the knowledge base | Writes plans, specs and prompts. Reads reports. Updates cards, Board, Decisions | Changes the code repository or runs agents |
Agent Crew, fresh each leg | A new coding-agent session per step | Reads its prompt, does exactly one step, writes one report | Edits other files in the knowledge base, pushes elsewhere, opens PRs |
Coordinator Optional · autopilot | A long-running LLM session, one per feature | Drives the loop: sends planner commands, starts fresh agents, approves clean checks when delegated, writes morning summaries | Edits code or task docs, or passes a stop point |
Checks find. Fix rounds fix.
A fresh agent finds and logs. It never fixes, never offers to.
Planner copies findings to the card. Blocker / should fix → fix. Nit → later.
A scoped round: only the listed IDs, one commit, a failing-first test per finding.
Another fresh agent verifies each fix, then looks only at what changed.
FIGURE 5.— The loop exits when no blocker or should-fix finding is open. Then the gate opens: by a coordinator you delegated to, or by you.
A real review caught what the build missed.
.pyc files. Its tests were green. A fresh Review agent flagged it, a scoped fix round removed it, a re-check verified it.# Report: P1-T1 - 4 Review ## Summary1Reviewed origin/relay/p1...origin/wip/p1-t1-version-flag (3904ade).Behaviour matches the Spec and REQ-1; 6 tests pass in the throwawayworktree. The commit also adds two compiled .pyc files. ## Findings| ID | Severity | Finding2| F3 | should fix | Commit adds build output __pycache__/*.pyc| | | Fix: A git rm --cached · B A + .gitignore| F4 | nit | Tests duplicate stdout capture · later ## Git3git remote get-url --push --all origin -> matches RELAY.mdgit diff --stat (origin/relay/p1...3904ade): __pycache__/greet.cpython-313.pyc | Bin 0 -> 1496 bytes greet.py | 1 + test_greet.py | 29 +++++++++++++- ## TestsRan 6 tests in 0.000s OK 4Verdict: fix round needed: F3- 1A different agent
It reviews the pushed commit in a detached, throwaway worktree. It never saw the Build session.
- 2Findings, not fixes
ID, severity, location, and options when there's a real choice. The planner turns severity into a decision.
- 3Real command output
Remote checked, diff stat, test count. If a command fails, its output goes here instead.
- 4The verdict is the last line
Tools and the planner parse it. Nits alone mean clean.
Exhibit B · Mission log, P1-T1
The task card's step history, start to finish. Two findings fixed, two verified, two nits parked for later.
- 1 Analyzeskipped: one-line change, REQ-1 gives the behaviour in full
- 2 Spec checkfix round needed: F1
- 2 Spec fix (2)F1 fixed in Spec Tests
- 2 Spec check (2)clean; F1 verified
- Owner approvedSpec approved → 3 Build
- 3 Build3904ade pushed, 6 tests OK
- 4 Reviewfix round needed: F3
- 2 Spec fix (3)F3 option B: .gitignore added to the spec (small call, logged)
- 3 Build (2)F3 fixed in c66916d, proof command empty
- 4 Review (2)clean; F3 verified
- Owner approvedCode approved @ c66916d → 5 Done
- 5 Donemerged 585ee0e, tagged wip/p1-t1-done, pushed
Every rule has an incident behind it.
One writer per fileWhy · Two writers of the decisions log produced duplicate rows.
Kill by PID or port, never pkill -f <pattern>Why · A pattern kill on one task probably killed another worktree's test workers mid-suite.
Scratch only in your own /tmp/<repo>-<task-step>/Why · A browser-test setup in shared /tmp was removed by a later step.
No --no-verifyWhy · A build skipped the hook to get past lint, so the commit never met the checks CI runs.
Run the full test list at Done, re-run timeouts aloneWhy · Subsets missed regressions. Timeouts under load looked like failures.
Fetch and compare with the remote before startingWhy · Work moves between machines overnight.
Honest caveat: today these rules run on trust. Nothing yet stops an agent that ignores “never force-push”. A hooks pack that enforces them is on the roadmap.
Not ready to adopt it? Steal the ideas.
- Fresh context per step
A clean session sees the code, not the excuses.
- Evidence over claims
Status is rebuilt from output, not from what an agent says.
- Machine-readable verdicts
One fixed last line lets tools chain steps without guessing.
- Requirements have an order
When sources disagree, a written precedence list settles it.
# Minimal relay rules (paste into AGENTS.md or CLAUDE.md)
- One step per session. The session that reviews code never wrote it.
- Checks only find and log. Fixes happen in a separate round, then a re-check.
- Every claim needs output: commit hash, git diff --stat, test command and count.
- A check report's last line is "Verdict: clean" or "Verdict: fix round needed: F<n>, ...".
- State lives in files, not chat. Write the report even when stuck.
- Small technical calls: decide, log, continue. Scope and approvals: stop and ask.Start in 5 minutes.
- T–5:00
Make the knowledge base next to your repo
shellgit clone https://github.com/Kuznia-Tech/kuznia-relay.git cp -R kuznia-relay/kit my-project-kb cp kuznia-relay/SPEC.md kuznia-relay/AGENT-RULES.md my-project-kb/ - T–4:00
Fill in RELAY.md
The YAML at the top: repo path, allowed remote, test commands, your first feature. Put its requirements (ticket, acceptance criteria, design) in
sources/. - T–3:00
Create the feature branch and its worktree
shellcd my-project git fetch origin git branch --no-track feat/my-feature origin/main && git push -u origin feat/my-feature git worktree add ../my-project-worktrees/p1 feat/my-feature - T–2:00
Start the planner in the knowledge-base folder
The kit ships
AGENTS.md(Codex) andCLAUDE.md(Claude Code), so it loads its instructions on its own.plannerplan P1 prompt P1-T1 - T–1:00
Run the step in a fresh agent, in your repo
Give it write access to the knowledge base for its report, paste the planner's line, then tell the planner
report P1-T1. Repeat until Done.shellclaude --add-dir /home/you/my-project-kb - T–0:00
Deliver
When every task is Done, open the pull request from the feature branch yourself, then remove task worktrees, task branches and scratch folders.
Planner commands
| where are we? | Current step of every active task, who it waits on, what's next |
| plan <F> | Write the feature plan from its requirement doc, with a task list |
| new task <F>: <name> | Add one task: card, Board row, Plan line |
| prompt <task> | Write the prompt for the current step, reply with the paste line |
| report <task> | Read the latest report, update the docs, move the step |
| approve <task> | Spec approved → Build. Code approved @ <commit> → Done |
| fix <task> | Start the fix round for findings marked fix |
| findings <task> | Show open findings: ID, severity, decision, round |
Final poll
Spec. Build. Review. Merge. Go.
Read the spec in ten minutes, walk the worked example, then run your first task today.