SwarmOps
ReferenceMcp toolscommit
View as Markdown

commit#

Records a changeset from an explicit change list, on parent or else on main's head. With advance it also moves that ref to the changeset in the same transaction, only if the ref still names parent, which must then be the full 64-hex id you read; otherwise it records nothing (E_CAS_MISMATCH, details.committed false). Check result.advanced: a server that does not know advance moves no ref. An idempotency_key, a uuid v7 new for each commit, makes a retry safe: the same call again replays it (result.replayed) instead of recording it twice. A {"file": path} content is read by this server: give an absolute path.

Metadata
Since
0.1.0
Mutates
yes
Identity required
yes
Envelope op
commit
Cli command
swarmops commit
Request type
CommitReq
Annotations
{"destructiveHint":false,"idempotentHint":false,"openWorldHint":false,"readOnlyHint":false}
Max result chars
null
CLI command
swarmops commit

Input schema#

arguments is a CommitReq: swarmops schema CommitReq prints the type. A field the type does not know is ignored.

FieldTypeRequiredDefaultDescription
advancestring or nullnoAlso move this ref (main or release/<name>) to the new changeset, in the same transaction as the changeset itself, so that both are recorded or neither. The move happens only if the ref still names the changeset's parent (for a root commit, only if the ref does not exist yet): it is always a one-step fast-forward. Otherwise nothing is recorded and the commit fails with E_CAS_MISMATCH (details.committed: false, details.actual what the ref names). Give as parent the full id the ref held when you read it: without parent the commit builds on the ref's head when it runs, so the move catches a race from there on, not a change made since you read the ref (M1 has no merge). swarmops mcp refuses advance without a full 64-hex parent (E_INVALID_ARG). A store that does not know this field ignores it and moves no ref: its response has no advanced.
allow_emptybooleannofalseRecord the changeset even if it changes nothing.
changesarray of objectyesWhat to change, relative to the parent. The contents hold at most 1 GiB together, by the sizes they declare (inline text's length, the bytes base64 decodes to, a client file's length; a blob named by id counts nothing): more is E_TOO_LARGE with scope commit, refused before any content is read. Store large contents with blob_put first, and name them by id.
idempotency_keystring or nullnoMakes a retry of this commit safe: a uuid v7 (anything else is E_INVALID_ARG). The commit takes the key's time, to the second, as its signature time and records the key as commit_key metadata, so the same request on the same parent makes the same changeset, which the store holds already: the response says replayed, and nothing is written. With advance, a ref whose head carries the key is a replay too. Give parent by its full id: without it, a retry after the head moved commits again, on the new head.
messagestringyesThe commit message.
parentstring or nullnoThe changeset to commit on top of. An explicit parent always wins; when it is omitted, the current head of the ref advance names is used, else of main, if that ref exists, and otherwise the commit is a root commit. null is the same as omitting it. Once main exists, a root commit needs an advance naming a ref that does not exist yet (a known M1 limitation).

JSON Schema#

As tools/list gives it: each $ref replaced by its definition, so that it stands alone.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "`commit`: record a changeset from an explicit change list. It moves no ref, unless\n`advance` names one.",
  "properties": {
    "advance": {
      "description": "Also move this ref (`main` or `release/<name>`) to the new changeset, in the same\ntransaction as the changeset itself, so that both are recorded or neither. The move\nhappens only if the ref still names the changeset's parent (for a root commit, only if the\nref does not exist yet): it is always a one-step fast-forward. Otherwise nothing is\nrecorded and the commit fails with `E_CAS_MISMATCH` (`details.committed: false`,\n`details.actual` what the ref names). Give as `parent` the full id the ref held when you\nread it: without `parent` the commit builds on the ref's head when it runs, so the move\ncatches a race from there on, not a change made since you read the ref (M1 has no\nmerge). `swarmops mcp` refuses `advance` without a full 64-hex `parent` (`E_INVALID_ARG`).\nA store that does not know this field ignores it and moves no ref: its response has no\n`advanced`.",
      "type": [
        "string",
        "null"
      ]
    },
    "allow_empty": {
      "default": false,
      "description": "Record the changeset even if it changes nothing.",
      "type": "boolean"
    },
    "changes": {
      "description": "What to change, relative to the parent. The contents hold at most 1 GiB together, by the\nsizes they declare (inline text's length, the bytes base64 decodes to, a client file's\nlength; a `blob` named by id counts nothing): more is `E_TOO_LARGE` with `scope`\n`commit`, refused before any content is read. Store large contents with `blob_put` first,\nand name them by id.",
      "items": {
        "description": "One change of a commit.",
        "properties": {
          "content": {
            "anyOf": [
              {
                "description": "A `put`'s content, as an object with one key.",
                "oneOf": [
                  {
                    "additionalProperties": false,
                    "description": "UTF-8 text, verbatim.",
                    "properties": {
                      "inline": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "inline"
                    ],
                    "type": "object"
                  },
                  {
                    "additionalProperties": false,
                    "description": "Bytes in standard, padded base64.",
                    "properties": {
                      "base64": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "base64"
                    ],
                    "type": "object"
                  },
                  {
                    "additionalProperties": false,
                    "description": "A blob the store holds already, by its id (64 hex digits), as `blob_put` returns it: the\ncommit neither sends nor reads its bytes. An id the store holds no blob under (none, or\na tree's or a changeset's) is `E_INVALID_ARG`, and nothing is written. `blob_put` takes\nthe other forms only.",
                    "properties": {
                      "blob": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "blob"
                    ],
                    "type": "object"
                  },
                  {
                    "additionalProperties": false,
                    "description": "A file on the client's machine. The client reads it and sends its bytes, so the store is\nnever asked to read a path.",
                    "properties": {
                      "file": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "file"
                    ],
                    "type": "object"
                  }
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The new content, at most 512 MiB (more is `E_TOO_LARGE` with `scope` `put`); a `put` needs\nit, a `delete` takes none."
          },
          "mode": {
            "anyOf": [
              {
                "description": "The kind of entry a `put` writes.",
                "oneOf": [
                  {
                    "const": "file",
                    "description": "A regular file (git mode 100644).",
                    "type": "string"
                  },
                  {
                    "const": "exec",
                    "description": "An executable file (git mode 100755).",
                    "type": "string"
                  },
                  {
                    "const": "symlink",
                    "description": "A symlink; the content is the link's target.",
                    "type": "string"
                  }
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The kind of entry a `put` writes; `file` when omitted."
          },
          "op": {
            "description": "Whether to write the path or remove it. What a change does to its path, in a commit's change list and a changeset's changes alike.",
            "oneOf": [
              {
                "const": "put",
                "description": "Write the content at the path, creating or replacing the entry.",
                "type": "string"
              },
              {
                "const": "delete",
                "description": "Remove the path.",
                "type": "string"
              }
            ]
          },
          "path": {
            "description": "The repository path: `/`-separated, relative to the root.",
            "type": "string"
          }
        },
        "required": [
          "path",
          "op"
        ],
        "type": "object"
      },
      "type": "array"
    },
    "idempotency_key": {
      "description": "Makes a retry of this commit safe: a uuid v7 (anything else is `E_INVALID_ARG`). The\ncommit takes the key's time, to the second, as its signature time and records the key as\n`commit_key` metadata, so the same request on the same parent makes the same changeset,\nwhich the store holds already: the response says `replayed`, and nothing is written.\nWith `advance`, a ref whose head carries the key is a replay too. Give `parent` by its full\nid: without it, a retry after the head moved commits again, on the new head.",
      "type": [
        "string",
        "null"
      ]
    },
    "message": {
      "description": "The commit message.",
      "type": "string"
    },
    "parent": {
      "anyOf": [
        {
          "description": "A revision as the caller wrote it, for the store to resolve: a ref name (`main`,\n`release/1.2`), a full 64-hex changeset id or a unique prefix of at least 12 hex digits,\noptionally followed by `~N` for the N-th first-parent ancestor.",
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "description": "The changeset to commit on top of. An explicit `parent` always wins; when it is omitted,\nthe current head of the ref `advance` names is used, else of `main`, if that ref exists,\nand otherwise the commit is a root commit. `null` is the same as omitting it. Once `main`\nexists, a root commit needs an `advance` naming a ref that does not exist yet (a known M1\nlimitation)."
    }
  },
  "required": [
    "changes",
    "message"
  ],
  "title": "CommitReq",
  "type": "object"
}

Annotations#

It adds to the store and changes nothing there. Run again, a commit adds again, another changeset signed at its own time, unless the call carries an idempotency_key, which makes the same call again a replay; a blob_put of the same content finds it stored, and says so in its result.

AnnotationValueMeaning
destructiveHintfalseTrue: a tool that changes the store may overwrite or delete what is there, not only add to it.
idempotentHintfalseTrue: calling it again with the same arguments changes nothing more.
openWorldHintfalseTrue: it reaches beyond a closed set of things. Every tool here stays inside the store.
readOnlyHintfalseTrue: the tool does not change its environment, the store.

Its entry in tools/list carries no _meta.

CLI command#

swarmops commit runs the same operation and prints the same envelope.

Examples#

{
  "id": 5,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "changes": [
        {
          "content": {
            "blob": "[blob]"
          },
          "op": "put",
          "path": "README.md"
        },
        {
          "content": {
            "inline": "pub fn f() {}\n"
          },
          "op": "put",
          "path": "src/lib.rs"
        }
      ],
      "message": "first"
    },
    "name": "commit"
  }
}
{
  "id": 5,
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "text": "{\"ok\":true,\"op\":\"commit\",\"api\":1,\"result\":{\"changeset\":\"[c1]\",\"root\":\"[id1]\",\"files_changed\":2,\"blobs_written\":1,\"replayed\":false},\"meta\":{\"repo\":\"[tmp]/store\",\"elapsed_ms\":\"[elapsed_ms]\",\"resolved\":{},\"auth\":\"trust\"}}",
        "type": "text"
      }
    ],
    "isError": false,
    "structuredContent": {
      "api": 1,
      "meta": {
        "auth": "trust",
        "elapsed_ms": "[elapsed_ms]",
        "repo": "[tmp]/store",
        "resolved": {}
      },
      "ok": true,
      "op": "commit",
      "result": {
        "blobs_written": 1,
        "changeset": "[c1]",
        "files_changed": 2,
        "replayed": false,
        "root": "[id1]"
      }
    }
  }
}

The server ran as swarmops mcp --repo [tmp]/store --as agent/worker-17, after initialize. Recorded by the MCP tests. A name in square brackets, such as [tmp] or [c1], stands for what differs from run to run: the test's temporary directory, an id, a time, the version.

Details#

A commit records a changeset from an explicit change list, on parent or else on main's head.

  • Advance a ref. With advance, the same transaction moves that ref to the changeset, only if the ref still names parent; the server then needs parent as the full 64-hex id you read (E_INVALID_ARG otherwise). If the ref moved, nothing is recorded: E_CAS_MISMATCH with details.committed false. Check result.advanced: a server that does not know advance moves no ref.
  • Retry safely. An idempotency_key, a uuid v7 new for each commit, makes the same call again a replay (result.replayed) instead of a second changeset.
  • Content. A change carries its bytes inline or as base64, names a stored blob as {"blob": id} (see blob_put), or names a file as {"file": path}. The server reads that file itself, so the path must be absolute and name a regular file, not the server's own stdin or stdout; anything else is refused with E_INVALID_ARG.

To publish the changeset on main, land it.

Return information#

A tool result: the envelope, whose op is commit, as structuredContent and again as the text of its one content block. isError is true exactly when the envelope's ok is false. On success the envelope's result is what swarmops commit returns.

Errors#

A failed call is a tool result with isError: true; the envelope's error.code is one of the error codes.

  • E_INVALID_ARG: arguments that do not fit CommitReq, a field named twice among them included, or that break one of the server's own rules.
  • E_IDENTITY_REQUIRED: the server runs with no identity.
  • E_NOT_A_REPO: the server's store is not there; the server serves on.
  • E_INTERNAL: the operation panicked, a bug.

Then the errors of the operation, as swarmops commit lists them.

See also#

Edit on GitHub

On this page