| 1 |
# Oxygen (`oxy`): AI-Native Agent Workflows Specification |
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
| **Status** | Draft 1.1 | |
| 6 |
| **Date** | 2026-10-03 | |
| 7 |
| **Audience** | Implementers and AI tooling developers | |
| 8 |
| **Depends on** | spec.md §6 (changes), §9 (conflicts), §12 (operation log, workspaces), §14 (identity, policy), revsets (`revset_query_language.md`) | |
| 9 |
|
| 10 |
## 1. Overview and philosophy |
| 11 |
|
| 12 |
Coding agents, such as Aider or Claude Code, or local LLM loops, commit |
| 13 |
at machine speed. A version control system must let them work without |
| 14 |
giving them the keys to the kingdom, and without burying the human's |
| 15 |
history in micro-changes. |
| 16 |
|
| 17 |
The guiding principle is **delegated, bounded, reversible autonomy**: |
| 18 |
|
| 19 |
- **Delegated:** an agent acts under its human owner's identity, with its |
| 20 |
own key, so every change it makes is attributable to both. |
| 21 |
- **Bounded:** an agent can record changes but cannot move protected |
| 22 |
bookmarks, approve, or seal, whatever its owner may do. |
| 23 |
- **Reversible:** an agent works in its own workspace and bookmark, so |
| 24 |
discarding its session never touches the human's work. |
| 25 |
|
| 26 |
None of this weakens what Oxygen already guarantees. The change graph is |
| 27 |
immutable, every change is signed with Ed25519 and ML-DSA-65, policy is |
| 28 |
verified on fetch and push, and the operation log records everything. |
| 29 |
|
| 30 |
Each section says what **exists** today and what is **new**. |
| 31 |
|
| 32 |
## 2. Agent identity and delegation |
| 33 |
|
| 34 |
**Exists (§14.2):** an identity object has `delegates [8]: [KeyId]`. |
| 35 |
These are keys allowed to sign or seal changes whose `author` is that |
| 36 |
identity. Delegation is one level deep and not transitive, and |
| 37 |
`oxy identity delegate add|remove` manages it. Any change to an identity |
| 38 |
is a rotation: a successor object. |
| 39 |
|
| 40 |
- **The agent's own key pair.** An agent generates its own Ed25519 and |
| 41 |
ML-DSA-65 key pair locally. Its identity object's `name` says what it |
| 42 |
is, for example `"aider 0.x on andy-laptop"`. An agent never holds the |
| 43 |
human's secret keys. |
| 44 |
- **Authorship.** Changes the agent records set `author` to the human's |
| 45 |
`KeyId` and are signed with the agent's key. The signature's KeyId |
| 46 |
identifies the agent. **No marker is stored in the change:** `extra |
| 47 |
[9]` is reserved and must stay empty, and the signature already proves |
| 48 |
which agent made the change, which a marker could not. In practice |
| 49 |
(implemented 2026-10-04): the agent's keys live in its own `$OXY_HOME`, |
| 50 |
and its user config (or `$OXY_SIGNER`) sets `author.signer` to its |
| 51 |
KeyId; the checkout's `author.key` stays the human's, so `oxy commit` |
| 52 |
run by the agent carries the human as author and the agent's |
| 53 |
signature, and `oxy log -r 'delegated()'` lists what the agent did. |
| 54 |
|
| 55 |
**Scoped delegation (§14.2, implemented 2026-10-04).** Before it, a |
| 56 |
delegate moved references as far as its delegator may (§14.5). That is |
| 57 |
right for a hub signing browser edits, and wrong for an agent. `delegates` |
| 58 |
is a list of entries: |
| 59 |
|
| 60 |
``` |
| 61 |
delegates [8]: [{ key [0]: KeyId, scope [1]: ["changes" | "refs" | "seal"], expires [2]: u64? }] |
| 62 |
``` |
| 63 |
|
| 64 |
- An agent is added with `--scope changes`. Its signature authenticates |
| 65 |
changes, but a reference or tag attestation, or a seal, signed by it is |
| 66 |
refused. **An agent can never land on a protected bookmark**, even |
| 67 |
when its owner could. |
| 68 |
- A per-delegate `expires` limits an agent to a session or a day |
| 69 |
(`--expires 8h`), without rotating the human's own expiry. |
| 70 |
- Hosts that sign for users keep the current behaviour with |
| 71 |
`--scope changes,refs`. |
| 72 |
- A bare KeyId in an older identity means every scope. |
| 73 |
|
| 74 |
**Revocation by the delegator (implemented 2026-10-04).** Before it, |
| 75 |
only the revoked key itself or a policy root key could sign `revoke`. An agent's key may be lost on a |
| 76 |
compromised machine, and the policy root is often someone else, so the |
| 77 |
identity that delegated a key may also revoke it. Removing a delegate in |
| 78 |
a successor identity stops future use. Revoking it also marks the changes |
| 79 |
the agent already signed as *valid but revoked* in `oxy verify` and on |
| 80 |
the hub, so they get a second look. |
| 81 |
|
| 82 |
## 3. Isolation: one workspace and bookmark per session |
| 83 |
|
| 84 |
**Exists:** `oxy workspace add` creates an extra checkout that shares the |
| 85 |
repository, each with its own current bookmark. |
| 86 |
|
| 87 |
**New:** `oxy agent start NAME` does three things: |
| 88 |
1. creates a workspace under `.oxy/agents/NAME/`; |
| 89 |
2. creates the bookmark `agents/NAME/<session>` from the human's current |
| 90 |
heads; |
| 91 |
3. records a session operation carrying the agent's KeyId. |
| 92 |
|
| 93 |
The agent works only there. This replaces a session marker in the |
| 94 |
human's operation log: |
| 95 |
|
| 96 |
- **The human's working copy is never touched.** Changes commute, so the |
| 97 |
human keeps working on the same files at the same time, and merging |
| 98 |
the agent's bookmark later is an ordinary `oxy merge`. |
| 99 |
- **Discarding a session** is `oxy agent drop NAME`: delete the |
| 100 |
bookmark and the workspace. Nothing the human did is undone, unlike an |
| 101 |
undo in a shared workspace would. |
| 102 |
- **Undo within a session** is the agent's own operation log (§12), |
| 103 |
scoped to its workspace. The new `oxy undo --to OP` returns to any |
| 104 |
earlier operation of that workspace. |
| 105 |
|
| 106 |
## 4. Micro-changes: keep them, show them folded |
| 107 |
|
| 108 |
Agents produce many small changes. Oxygen keeps them, because each is |
| 109 |
signed, cheap, and commutes with everything that does not touch the same |
| 110 |
lines. The noise is a display problem first. |
| 111 |
|
| 112 |
- **New, display folding:** `oxy log` shows a run of consecutive changes |
| 113 |
signed by the same agent key as one line: "aider: 37 changes, 4 |
| 114 |
files". `--expand` shows them all. The hub does the same. |
| 115 |
- **New, adoption:** when the human wants one change in history, |
| 116 |
`oxy fold -r REVSET` folds the selected changes into one new change. |
| 117 |
It uses the revset language, for example |
| 118 |
`oxy fold -r 'signed_by(aider) & main..agents/aider/s1'`. The folded |
| 119 |
change is authored and signed by the human's own key. It records the |
| 120 |
folded changes' hashes in its description footer, so the trail to the |
| 121 |
agent's work stays verifiable while those changes remain stored. |
| 122 |
The existing `oxy squash` folds one change into a dependency and stays |
| 123 |
as it is. |
| 124 |
- **Folding is optional.** Unfolded agent changes stay valid history. |
| 125 |
Folding is for when a team's convention wants one reviewed change. |
| 126 |
|
| 127 |
## 5. Conflicts as data, for agents |
| 128 |
|
| 129 |
**Exists:** conflicts are recorded in states, not raised as errors (§9). |
| 130 |
A conflicted state can be committed and built on, and `oxy resolve` |
| 131 |
reports which conflicts the current files resolve. |
| 132 |
|
| 133 |
- **Reading conflicts:** `oxy status --json` lists each conflict: path, |
| 134 |
node, kind (order, name or tree), and the contributing ChangeIds. |
| 135 |
`oxy cat PATH --at CHANGE` gives each side's text for the agent's |
| 136 |
context. |
| 137 |
- **Resolving:** the agent writes the resolved files and runs |
| 138 |
`oxy commit`. **Oxy computes the resolution's dependencies**: a |
| 139 |
resolution must depend on every change that contributed an atom to |
| 140 |
the conflict. An agent never writes `deps` by hand. Correctness of the |
| 141 |
graph is Oxygen's job, not the model's. |
| 142 |
- **A valid resolution is not necessarily a right one.** The graph will |
| 143 |
be well-formed; the code may still be wrong. A policy rule for |
| 144 |
protected bookmarks can require that a resolution signed by an agent |
| 145 |
has a passing Flow receipt and a human approval before it lands. |
| 146 |
|
| 147 |
## 6. Review and policy on the hub |
| 148 |
|
| 149 |
- **Badges (implemented 2026-10-04):** a change signed by a delegate |
| 150 |
shows its author and, beside it, `via NAME` with an `agent` badge: in |
| 151 |
history rows on the hub, as a `via` row under the author on the change |
| 152 |
page (`↳ Antigravity <…> · delegate for changes, until … · key …`), |
| 153 |
under the author in Contributors ("via Antigravity 3"), as `via` in |
| 154 |
the API's change summaries, and the same in `oxy log` (`via NAME`) and |
| 155 |
`oxy show` (the `via` row). The author stays the human; the agent is |
| 156 |
the instrument, and a revoked or withdrawn delegation turns the badge |
| 157 |
red. The hub also links a rotated identity to its owner's account by |
| 158 |
itself when a push brings it, so delegating never breaks attribution. |
| 159 |
- **Approvals:** approvals count only signatures by keys that are not |
| 160 |
agent-scoped delegates, so an agent cannot approve its own work or |
| 161 |
anyone else's. |
| 162 |
- **Policy:** a policy rule may name agent rules, for example "agents may |
| 163 |
push `agents/**` bookmarks only" or "an agent-signed change needs a |
| 164 |
passing receipt to land". |
| 165 |
- **Audit:** the trust page and `oxy verify` list agent-signed changes |
| 166 |
separately, with the delegation that authorized each. |
| 167 |
|
| 168 |
## 6a. Optional: a human always lands AI work |
| 169 |
|
| 170 |
Whether a human must land every AI-authored change is a **per-project |
| 171 |
choice**. A regulated codebase may insist on it; a personal project or a |
| 172 |
team that trusts its agents and tests may not. Oxygen's job is to make |
| 173 |
the gate easy to turn on and impossible to bypass when it is on. It must |
| 174 |
never impose the gate on everyone. |
| 175 |
|
| 176 |
**With scoped delegation (this draft).** The agent is your delegate |
| 177 |
with `--scope changes`, so its changes carry your authorship and its |
| 178 |
signature. The policy decides whether it can land: |
| 179 |
|
| 180 |
``` |
| 181 |
rules: [ |
| 182 |
{ pattern: "main", keys: [andy, sam], threshold: 1, require: "signed" }, |
| 183 |
{ pattern: "release/*", keys: [andy, sam], threshold: 2, require: "signed" }, |
| 184 |
{ pattern: "agents/**", keys: [andy, sam], threshold: 1 }, |
| 185 |
] |
| 186 |
``` |
| 187 |
|
| 188 |
- **Gate on:** an agent-scoped delegate can never sign a reference |
| 189 |
attestation, so only Andy or Sam can move `main`. Approvals count only |
| 190 |
keys that are not agent-scoped, so a human approved and a human landed. |
| 191 |
The agent's changes keep their own signatures, so the landed history |
| 192 |
still shows which agent wrote what. |
| 193 |
- **Gate off:** delegate the agent with `--scope changes,refs` instead. |
| 194 |
It then moves references as far as its owner may, and can land on its |
| 195 |
own where the policy allows. The project chooses this per agent, and |
| 196 |
the trust page and `oxy verify` still show every agent-signed change and |
| 197 |
every agent-signed bookmark move. |
| 198 |
- **Per bookmark:** the usual setup gates `main` and `release/*` and |
| 199 |
leaves `agents/**` open, so agents push and move their own bookmarks |
| 200 |
freely while a human lands everything else. That needs refs scope |
| 201 |
that reaches only the open rules: a policy rule field naming agents |
| 202 |
explicitly, which is part of the §14.5 change. |
| 203 |
|
| 204 |
**With today's spec (no scoped delegation).** A delegate moves |
| 205 |
references as far as its owner, so the gate needs a different setup: |
| 206 |
1. Give the agent its **own identity**, not a delegate. |
| 207 |
2. List only human keys in the `main` rule, and the agent's key in an |
| 208 |
`agents/**` rule. |
| 209 |
3. Policy checks each change a bookmark move adds, so the agent's changes |
| 210 |
cannot reach `main` directly at all. A human adopts the work by folding |
| 211 |
or re-committing it under their own key. |
| 212 |
|
| 213 |
This gate is strict and works now. The cost is that the landed history |
| 214 |
records the human as signer, not the agent. |
| 215 |
|
| 216 |
**On the hub: agent-scoped tokens.** Policy governs keys, but landing on |
| 217 |
the hub is done with an account's token. An agent using a human's token |
| 218 |
lands *as that human*, and no policy can tell. The hub therefore needs |
| 219 |
**agent tokens**: |
| 220 |
- tied to an agent key and its human owner; |
| 221 |
- allowed to push `agents/**`, open and update proposals, and comment; |
| 222 |
- refused for landing, approving, changing settings or policy, and |
| 223 |
creating tokens, whatever the owner may do. |
| 224 |
|
| 225 |
Hub actions taken with an agent token are recorded as "AGENT on behalf of |
| 226 |
HUMAN" in the audit log and on the proposal. A project with the gate off |
| 227 |
may still let agent tokens land, as a repository setting that mirrors the |
| 228 |
policy, never by default. |
| 229 |
|
| 230 |
## 7. Summary of new work |
| 231 |
|
| 232 |
|
| 233 |
|
| 234 |
| Scoped, expiring delegates (done 2026-10-04) | §14.2 identity object, §14.4, §14.5 policy | |
| 235 |
| Revocation by the delegator (done 2026-10-04) | §14.2 | |
| 236 |
| `oxy agent start` / `drop`, session operation | §12, CLI | |
| 237 |
| `oxy undo --to OP` | §12, CLI | |
| 238 |
| Display folding, `oxy fold -r` | CLI, hub | |
| 239 |
| Agent-aware approvals and policy rules, optional human gate (§6a) | §14.5, hub spec | |
| 240 |
| Agent-scoped hub tokens | hub spec (API tokens) | |
| 241 |
| `oxy status --json` conflict listing (check what exists) | §9, CLI | |