# Oxygen (`oxy`): AI-Native Agent Workflows Specification | | | |---|---| | **Status** | Draft 1.1 | | **Date** | 2026-10-03 | | **Audience** | Implementers and AI tooling developers | | **Depends on** | spec.md §6 (changes), §9 (conflicts), §12 (operation log, workspaces), §14 (identity, policy), revsets (`revset_query_language.md`) | ## 1. Overview and philosophy Coding agents, such as Aider or Claude Code, or local LLM loops, commit at machine speed. A version control system must let them work without giving them the keys to the kingdom, and without burying the human's history in micro-changes. The guiding principle is **delegated, bounded, reversible autonomy**: - **Delegated:** an agent acts under its human owner's identity, with its own key, so every change it makes is attributable to both. - **Bounded:** an agent can record changes but cannot move protected bookmarks, approve, or seal, whatever its owner may do. - **Reversible:** an agent works in its own workspace and bookmark, so discarding its session never touches the human's work. None of this weakens what Oxygen already guarantees. The change graph is immutable, every change is signed with Ed25519 and ML-DSA-65, policy is verified on fetch and push, and the operation log records everything. Each section says what **exists** today and what is **new**. ## 2. Agent identity and delegation **Exists (§14.2):** an identity object has `delegates [8]: [KeyId]`. These are keys allowed to sign or seal changes whose `author` is that identity. Delegation is one level deep and not transitive, and `oxy identity delegate add|remove` manages it. Any change to an identity is a rotation: a successor object. - **The agent's own key pair.** An agent generates its own Ed25519 and ML-DSA-65 key pair locally. Its identity object's `name` says what it is, for example `"aider 0.x on andy-laptop"`. An agent never holds the human's secret keys. - **Authorship.** Changes the agent records set `author` to the human's `KeyId` and are signed with the agent's key. The signature's KeyId identifies the agent. **No marker is stored in the change:** `extra [9]` is reserved and must stay empty, and the signature already proves which agent made the change, which a marker could not. In practice (implemented 2026-10-04): the agent's keys live in its own `$OXY_HOME`, and its user config (or `$OXY_SIGNER`) sets `author.signer` to its KeyId; the checkout's `author.key` stays the human's, so `oxy commit` run by the agent carries the human as author and the agent's signature, and `oxy log -r 'delegated()'` lists what the agent did. **Scoped delegation (§14.2, implemented 2026-10-04).** Before it, a delegate moved references as far as its delegator may (§14.5). That is right for a hub signing browser edits, and wrong for an agent. `delegates` is a list of entries: ``` delegates [8]: [{ key [0]: KeyId, scope [1]: ["changes" | "refs" | "seal"], expires [2]: u64? }] ``` - An agent is added with `--scope changes`. Its signature authenticates changes, but a reference or tag attestation, or a seal, signed by it is refused. **An agent can never land on a protected bookmark**, even when its owner could. - A per-delegate `expires` limits an agent to a session or a day (`--expires 8h`), without rotating the human's own expiry. - Hosts that sign for users keep the current behaviour with `--scope changes,refs`. - A bare KeyId in an older identity means every scope. **Revocation by the delegator (implemented 2026-10-04).** Before it, only the revoked key itself or a policy root key could sign `revoke`. An agent's key may be lost on a compromised machine, and the policy root is often someone else, so the identity that delegated a key may also revoke it. Removing a delegate in a successor identity stops future use. Revoking it also marks the changes the agent already signed as *valid but revoked* in `oxy verify` and on the hub, so they get a second look. ## 3. Isolation: one workspace and bookmark per session **Exists:** `oxy workspace add` creates an extra checkout that shares the repository, each with its own current bookmark. **New:** `oxy agent start NAME` does three things: 1. creates a workspace under `.oxy/agents/NAME/`; 2. creates the bookmark `agents/NAME/` from the human's current heads; 3. records a session operation carrying the agent's KeyId. The agent works only there. This replaces a session marker in the human's operation log: - **The human's working copy is never touched.** Changes commute, so the human keeps working on the same files at the same time, and merging the agent's bookmark later is an ordinary `oxy merge`. - **Discarding a session** is `oxy agent drop NAME`: delete the bookmark and the workspace. Nothing the human did is undone, unlike an undo in a shared workspace would. - **Undo within a session** is the agent's own operation log (§12), scoped to its workspace. The new `oxy undo --to OP` returns to any earlier operation of that workspace. ## 4. Micro-changes: keep them, show them folded Agents produce many small changes. Oxygen keeps them, because each is signed, cheap, and commutes with everything that does not touch the same lines. The noise is a display problem first. - **New, display folding:** `oxy log` shows a run of consecutive changes signed by the same agent key as one line: "aider: 37 changes, 4 files". `--expand` shows them all. The hub does the same. - **New, adoption:** when the human wants one change in history, `oxy fold -r REVSET` folds the selected changes into one new change. It uses the revset language, for example `oxy fold -r 'signed_by(aider) & main..agents/aider/s1'`. The folded change is authored and signed by the human's own key. It records the folded changes' hashes in its description footer, so the trail to the agent's work stays verifiable while those changes remain stored. The existing `oxy squash` folds one change into a dependency and stays as it is. - **Folding is optional.** Unfolded agent changes stay valid history. Folding is for when a team's convention wants one reviewed change. ## 5. Conflicts as data, for agents **Exists:** conflicts are recorded in states, not raised as errors (§9). A conflicted state can be committed and built on, and `oxy resolve` reports which conflicts the current files resolve. - **Reading conflicts:** `oxy status --json` lists each conflict: path, node, kind (order, name or tree), and the contributing ChangeIds. `oxy cat PATH --at CHANGE` gives each side's text for the agent's context. - **Resolving:** the agent writes the resolved files and runs `oxy commit`. **Oxy computes the resolution's dependencies**: a resolution must depend on every change that contributed an atom to the conflict. An agent never writes `deps` by hand. Correctness of the graph is Oxygen's job, not the model's. - **A valid resolution is not necessarily a right one.** The graph will be well-formed; the code may still be wrong. A policy rule for protected bookmarks can require that a resolution signed by an agent has a passing Flow receipt and a human approval before it lands. ## 6. Review and policy on the hub - **Badges:** changes signed by a delegate with `changes` scope show "agent: NAME on behalf of HUMAN" on the hub and in `oxy log`. - **Approvals:** approvals count only signatures by keys that are not agent-scoped delegates, so an agent cannot approve its own work or anyone else's. - **Policy:** a policy rule may name agent rules, for example "agents may push `agents/**` bookmarks only" or "an agent-signed change needs a passing receipt to land". - **Audit:** the trust page and `oxy verify` list agent-signed changes separately, with the delegation that authorized each. ## 6a. Optional: a human always lands AI work Whether a human must land every AI-authored change is a **per-project choice**. A regulated codebase may insist on it; a personal project or a team that trusts its agents and tests may not. Oxygen's job is to make the gate easy to turn on and impossible to bypass when it is on. It must never impose the gate on everyone. **With scoped delegation (this draft).** The agent is your delegate with `--scope changes`, so its changes carry your authorship and its signature. The policy decides whether it can land: ``` rules: [ { pattern: "main", keys: [andy, sam], threshold: 1, require: "signed" }, { pattern: "release/*", keys: [andy, sam], threshold: 2, require: "signed" }, { pattern: "agents/**", keys: [andy, sam], threshold: 1 }, ] ``` - **Gate on:** an agent-scoped delegate can never sign a reference attestation, so only Andy or Sam can move `main`. Approvals count only keys that are not agent-scoped, so a human approved and a human landed. The agent's changes keep their own signatures, so the landed history still shows which agent wrote what. - **Gate off:** delegate the agent with `--scope changes,refs` instead. It then moves references as far as its owner may, and can land on its own where the policy allows. The project chooses this per agent, and the trust page and `oxy verify` still show every agent-signed change and every agent-signed bookmark move. - **Per bookmark:** the usual setup gates `main` and `release/*` and leaves `agents/**` open, so agents push and move their own bookmarks freely while a human lands everything else. That needs refs scope that reaches only the open rules: a policy rule field naming agents explicitly, which is part of the §14.5 change. **With today's spec (no scoped delegation).** A delegate moves references as far as its owner, so the gate needs a different setup: 1. Give the agent its **own identity**, not a delegate. 2. List only human keys in the `main` rule, and the agent's key in an `agents/**` rule. 3. Policy checks each change a bookmark move adds, so the agent's changes cannot reach `main` directly at all. A human adopts the work by folding or re-committing it under their own key. This gate is strict and works now. The cost is that the landed history records the human as signer, not the agent. **On the hub: agent-scoped tokens.** Policy governs keys, but landing on the hub is done with an account's token. An agent using a human's token lands *as that human*, and no policy can tell. The hub therefore needs **agent tokens**: - tied to an agent key and its human owner; - allowed to push `agents/**`, open and update proposals, and comment; - refused for landing, approving, changing settings or policy, and creating tokens, whatever the owner may do. Hub actions taken with an agent token are recorded as "AGENT on behalf of HUMAN" in the audit log and on the proposal. A project with the gate off may still let agent tokens land, as a repository setting that mirrors the policy, never by default. ## 7. Summary of new work | Item | Spec section to change | |---|---| | Scoped, expiring delegates (done 2026-10-04) | §14.2 identity object, §14.4, §14.5 policy | | Revocation by the delegator (done 2026-10-04) | §14.2 | | `oxy agent start` / `drop`, session operation | §12, CLI | | `oxy undo --to OP` | §12, CLI | | Display folding, `oxy fold -r` | CLI, hub | | Agent-aware approvals and policy rules, optional human gate (§6a) | §14.5, hub spec | | Agent-scoped hub tokens | hub spec (API tokens) | | `oxy status --json` conflict listing (check what exists) | §9, CLI |