An open-source agent approval workflow that ends in a diff
An approval workflow for AI agents only pays off where a person can read what changed. Kortix is the open-source CompanyOS: your agents, their skills, your company memory, and every connector in one git repo you own, with the agents working on real cloud computers.
The loop, in the docs' own words
A person approves changes to the projectWhen an agent changes the project itself, for example a skill or its memory, it opens a change request. A person reviews it on the Review page and merges it.People stay in control. A change to the project waits on the Review page until a person merges it.A connector rule set to Ask holds an action until a person approves it.The Guide's "How it works" step for project changes, beside its summary of where people stay in control.
The gate that pays is the one with a diff
Every description of human-in-the-loop opens with the same picture. The agent reaches for a tool, a dialog appears, a person clicks approve, and the session carries on. The dialog does something, and it is not review. Whoever clicks is deciding on a tool name and a block of arguments, at the pace the agent produces them, with no record that outlives the session, and by the fortieth prompt the click has become a reflex.
Move the same question to the end of the work and it changes shape. The agent has edited the company's own files: a skill, a memory file, a prompt. What waits for approval now is a diff. A diff is something a person can read, argue with and merge. When an agent changes the project itself, for example a skill or its memory, it opens a change request. A person reviews it on the Review page and merges it.
That step carries the whole position on control. The unit of approval is a change to the company, not a step inside a task, and the reviewer sees the artifact they would expect from a colleague: what is there now, what would replace it, and who signed off.
Three seams where a rule can sit
An approval workflow needs somewhere to attach. A Kortix project offers three, and they answer different questions.
A connector call is the narrowest. A rule attached to a connector matches a pattern of calls and decides whether the call runs or waits. The pattern can be a glob, a regular expression, or a test on the arguments of the call itself, so the rule can be as tight as linkedin.create_* or as broad as everything one app can do.
The session's branch is the middle. Every session works on its own branch, and session work reaches main through a change request. Merge is default-deny for agents. What a reviewer opens is a proposal against a branch, not a company being edited underneath them while they read.
The change request is the widest, and the only seam that catches what a pattern cannot. A rule can know that a call publishes to LinkedIn. It cannot know that a rewritten skill quietly dropped the step that checks a price first. A person reading the diff can see the step is gone.
A rule that names a call, not an agent
Connector rules live in the manifest, in the same kortix.yaml that declares the agents, so the permission sits beside the capability it limits. This is the block from the project shown on kortix.com:
connectors: - slug: linkedin provider: composio app: linkedin policies: - match: "linkedin.create_*" action: require_approvalThe credentials are not in that block, and not anywhere in the file. The connector is named by slug and provider, while the login stays on the Kortix server and never enters the agent's computer. A reviewer sees which app is reachable and which of its calls must ask, and a non-technical colleague can read the same lines: the marketing agent may draft, and every create call stops for a person. Nothing in the rule mentions a model or a session.
What a person should approve
A workable rule for a first pass: approve whatever changes the company's definition of itself, and whatever leaves the building.
- a write to a skill, a prompt, or a memory file, because next month's agents start from it;
- a call that publishes, sends, pays, or deletes, because the mistake is outside the repo;
- any change that widens what an agent may reach, because that is how a small exception becomes a standing permission.
Leave the rest unattended. Reads, searches, drafts held for review, and reports a person will read before anyone acts on them need no gate. A company that gates all of it learns to click through gates, which is the failure the whole design is trying to avoid.
Scheduled work raises the stakes. An agent that runs at 06:00 with nobody at the keyboard cannot ask, so its output had better land somewhere a person reviews, which is why scheduled agents and approval rules are the same conversation.
A gate you set, not one you inherit
Approval gates you set. Off until you set them.
Your own rules start unset. The built-in My Kortix agent is the one that asks by itself: before an invitation or a role change, before a delete, and before a change that gives an agent more access. Everything else follows the project's policy block, whose default_mode falls back to allow_all, and a project with no policy declared runs every action it is asked to run.
Off by default is defensible for a product a team adopts one rule at a time, and it puts a real job on the person who sets it up. Open a new project's manifest and check whether anyone added a rule before you hand it work that writes. Start narrow, on the one call that would embarrass you if it fired unattended, and widen after a week of watching what the rule caught.
What approval without a diff looks like
A daily check-in with nothing to read is the other failure mode. One published account of a seven-agent company records it: half the agents' own files were out of date within a week, because no proposal, no diff and no list of changes reached the reviewer. Each check compared a chat answer against the owner's memory of the org chart, and nobody was reading the files.
Approval at the merge fixes that by giving the daily check-in a subject. The question stops being "what have you been up to" and becomes "do we keep this line change". A reviewer with a diff can be lazy and still catch the important thing, because the important thing is the part of the diff they did not expect.
The record the loop leaves behind
Each approved change request is a dated statement of what the company decided, written in the file the decision affects. Next quarter, when someone asks why the support agent stopped issuing refunds, the answer is a diff with a name on it rather than a session nobody can find. That history is also what makes a tenth agent cheaper to run than the first. Each new agent arrives with a manifest that lists what it may reach, and each widening of that reach shows up in the same place: a branch, a diff, and a merge a person clicks. The company as code argument and the approval argument are the same argument from two sides.
Four questions about agent approval
The short answers, before you write the first rule.
- What should require human approval when using AI agents?
Edits to the company's own files, and actions that leave the building. A skill change, a memory rewrite, or a call that publishes, sends, pays or deletes deserves a look first, while reads and searches do not.
- Does every agent action need a person to click approve?
No. A click on every call trains people to click without reading, and your own rules start unset. Gate the calls that change something outside the repo, and let the rest run.
- Who can merge an agent's change?
A person, on the Review page. Session work reaches
mainthrough a change request, and merge is default-deny for agents, so an agent needs an admin to grant it the merge capability before it can land its own work.- What stops an agent from editing its own permissions?
The manifest and the merge gate. Agent access is denied until a block grants it, and a change that widens that access arrives as a change request that a person merges.
Set the first rule, then read what it catches
Start with one connector call and one project file, and keep the change requests coming.