Agent Commit Build Type

buildType: https://refs.arewm.com/agent-commit/v0.3

Description

This build type describes the production of a commit (patch) by an AI agent operating in a sandboxed execution environment. The agent receives a task description, reads a source repository, and produces a commit as output. The agent MAY invoke subagents, each with their own model, tools, and sandbox configuration.

The “builder” is the agent platform — the trusted infrastructure that provisions sandboxes, configures agent runtimes, manages credentials, records runtime observations, and assembles the output artifact. The agent itself is untrusted: it processes arbitrary input and may be compromised via prompt injection or other context manipulation. Provenance integrity depends on the builder platform, not the agent.

Runtime observations (network connections, filesystem activity, process execution) are recorded by an observer external to the agent sandbox (e.g., eBPF-based monitoring). These observations are included as byproducts in the provenance and are signed by the builder platform along with the rest of the attestation.

Why SLSA Provenance?

An agent commit is a build. An agent transforms inputs (a task description, a source repository) into an output artifact (a commit). The SLSA provenance v1 predicate already models this relationship. The buildType field is the extension point SLSA provides for describing build-specific parameters — agent identity, model configuration, sandbox policy, runtime observations — without requiring a new predicate type.

Build Definition

External Parameters

Parameters controlled by the external caller that triggered the agent run.

Parameter Type Required Description
task object Yes The task that triggered the agent
task.uri string Yes Human-readable reference to the task (e.g., issue URL)
task.content ResourceDescriptor Yes The verbatim task content the agent received, pinned by digest. For mutable sources (issues, tickets), this MUST be a snapshot of the content at the time of the run. Inlined content MUST be base64 encoded.
prompt string No The literal prompt provided by the caller, if distinct from the task content. MUST be base64 encoded.
target object Yes The repository and ref the commit applies to
target.repository string Yes Repository URI
target.baseRef string Yes Target branch ref (e.g., refs/heads/main)
target.baseCommit string Yes The commit SHA the agent started from

Internal Parameters

Parameters set by the builder platform, not the external caller.

Parameter Type Required Description
agent object Yes The agent configuration
agent.model object Yes Model identity: provider, modelId, digest
agent.runtime object Yes Agent runtime: name, version, digest
agent.tools array No MCP servers and tools available to the agent. Each entry includes name, uri, digest, and capabilities.
sandboxPolicy object No Declared security policy for the execution environment: network allowlist, credential policy, filesystem isolation
subagents array No Subagents invoked during the run. Each entry includes an invocationId, role, its own agent fields (model, runtime, tools), its own sandboxPolicy, input, output (with digest), and timing (start, end).

Resolved Dependencies

Artifacts consumed during the agent run. MUST include:

The source repository dependency implicitly covers all content the agent may read, including instruction files that the runtime loads automatically, skill files, configuration, and code. These are not enumerated separately.

Run Details

Builder

The builder identifies the agent platform — the transitive closure of trusted infrastructure responsible for provisioning the sandbox, running the agent, recording observations, and producing the provenance.

Field Type Required Description
id string (TypeURI) Yes URI identifying the builder platform
version map(string→string) No Component versions (e.g., platform, sandbox, observer)

Metadata

Field Type Required Description
invocationId string Yes Unique identifier for this agent run
startedOn Timestamp Yes When the agent run started
finishedOn Timestamp Yes When the agent run completed

Byproducts

Byproducts capture two kinds of additional information about the agent run: runtime observations and related attestation artifacts.

Runtime observations

Runtime behavior recorded by an observer external to the agent sandbox. The observer runs outside the agent’s trust domain (e.g., eBPF hooks in the host kernel, a sidecar proxy, or the sandbox runtime itself). The agent cannot influence or suppress these observations.

All observation entries MUST include timestamps so that events can be correlated across observation types (e.g., verifying that tests ran after all file writes completed). Ranges use start/end; point-in-time events use time. All timestamps are RFC 3339 UTC.

Observation byproducts SHOULD include:

Byproduct Description
observed-network All network connections: remote host, protocol, direction, byte counts, request counts, process identity, and time range (start/end). If the observer has L7 visibility (e.g., proxy-level logging), individual requests with HTTP method, path, and timestamp (time) SHOULD be included. L7 detail is not expected from socket-level observers.
observed-filesystem File reads (path, access count, time range) and writes (path, digest, timestamp)
observed-process Subprocesses spawned by the agent: binary path, arguments, exit code, parent PID, and time range (start/end)

Each observation byproduct uses the annotations field to carry the structured observation data. The mediaType SHOULD be application/vnd.runtime-observations+json.

If the agent run included subagents in separate sandboxes, each subagent’s observations are recorded separately and identified by invocationId matching the corresponding entry in internalParameters.subagents[].

Referenced attestations

Byproducts MAY also include ResourceDescriptor entries that reference attestation artifacts produced during the agent run. If the builder platform can identify attestation artifacts generated by tooling or instrumentation within the sandbox, it SHOULD include them as byproducts with a URI, digest, and mediaType. The builder does not need to understand the attestation content — it only needs to distinguish these artifacts from general filesystem activity. If the builder can extract the predicateType from a DSSE-wrapped in-toto attestation (a top-level field in the payload), it SHOULD include it in the annotations to allow consumers to filter byproducts without introspecting the referenced artifact.

Example

A coordinator agent receives a task from a GitHub issue, delegates implementation to a subagent running a different model, and produces a commit.

{
  "_type": "https://in-toto.io/Statement/v1",
  "subject": [
    {
      "uri": "git+https://github.com/org/repo@refs/heads/agent/issue-42",
      "digest": { "gitCommit": "ff33aa..." }
    }
  ],
  "predicateType": "https://slsa.dev/provenance/v1",
  "predicate": {
    "buildDefinition": {
      "buildType": "https://refs.arewm.com/agent-commit/v0.3",
      "externalParameters": {
        "task": {
          "uri": "https://github.com/org/repo/issues/42",
          "content": {
            "uri": "oci://ghcr.io/org/agent-inputs/issue-42-snapshot",
            "digest": { "sha256": "a1b2c3..." }
          }
        },
        "prompt": "Zml4IHRoZSBhdXRoIGhhbmRsZXIgYXMgZGVzY3JpYmVkIGluIGlzc3VlICM0Mg==",
        "target": {
          "repository": "git+https://github.com/org/repo",
          "baseRef": "refs/heads/main",
          "baseCommit": "ee22bb..."
        }
      },
      "internalParameters": {
        "agent": {
          "model": {
            "provider": "anthropic",
            "modelId": "claude-opus-4-6",
            "digest": { "sha256": "..." }
          },
          "runtime": {
            "name": "claude-code",
            "version": "1.2.0",
            "digest": { "sha256": "..." }
          },
          "tools": [
            {
              "name": "mcp-github",
              "uri": "oci://ghcr.io/org/mcp-github:v2.1",
              "digest": { "sha256": "..." },
              "capabilities": ["issues.read", "pulls.create"]
            },
            {
              "name": "mcp-agent-spawner",
              "uri": "oci://ghcr.io/org/mcp-agent-spawner:v1.0",
              "digest": { "sha256": "..." },
              "capabilities": ["spawn_sandboxed_agent"]
            }
          ]
        },
        "sandboxPolicy": {
          "network": {
            "mode": "allowlist",
            "allowedEgress": [
              "api.github.com:443",
              "api.anthropic.com:443"
            ]
          },
          "credentials": "proxy-injected",
          "filesystem": "ephemeral-clone"
        },
        "subagents": [
          {
            "invocationId": "sub-impl-001",
            "role": "implementer",
            "agent": {
              "model": {
                "provider": "anthropic",
                "modelId": "claude-sonnet-4-6",
                "digest": { "sha256": "..." }
              },
              "runtime": {
                "name": "claude-code",
                "version": "1.2.0",
                "digest": { "sha256": "..." }
              },
              "tools": [
                {
                  "name": "mcp-filesystem",
                  "uri": "oci://ghcr.io/org/mcp-fs:v1.0",
                  "digest": { "sha256": "..." },
                  "capabilities": ["read", "write"]
                }
              ]
            },
            "sandboxPolicy": {
              "network": {
                "mode": "allowlist",
                "allowedEgress": ["api.anthropic.com:443"]
              },
              "credentials": "none",
              "filesystem": "copy-on-write"
            },
            "input": {
              "type": "delegated-task",
              "digest": { "sha256": "..." }
            },
            "output": {
              "type": "patch",
              "digest": { "sha256": "cafe01..." }
            },
            "start": "2026-04-16T14:01:15Z",
            "end": "2026-04-16T14:12:03Z"
          }
        ]
      },
      "resolvedDependencies": [
        {
          "uri": "git+https://github.com/org/repo",
          "digest": { "gitCommit": "ee22bb..." },
          "name": "source"
        },
        {
          "uri": "oci://ghcr.io/org/mcp-github:v2.1",
          "digest": { "sha256": "..." },
          "name": "mcp-github"
        },
        {
          "uri": "oci://ghcr.io/org/mcp-fs:v1.0",
          "digest": { "sha256": "..." },
          "name": "mcp-filesystem"
        },
        {
          "uri": "oci://ghcr.io/org/mcp-agent-spawner:v1.0",
          "digest": { "sha256": "..." },
          "name": "mcp-agent-spawner"
        }
      ]
    },
    "runDetails": {
      "builder": {
        "id": "https://github.com/cgwalters/devaipod/pod-provisioner/v1",
        "version": {
          "devaipod": "0.1.0",
          "podman": "5.4.0",
          "service-gator": "0.2.0"
        }
      },
      "metadata": {
        "invocationId": "run-coord-issue-42",
        "startedOn": "2026-04-16T14:00:00Z",
        "finishedOn": "2026-04-16T14:18:22Z"
      },
      "byproducts": [
        {
          "name": "observed-network",
          "mediaType": "application/vnd.runtime-observations+json",
          "annotations": {
            "observations": [
              {
                "invocationId": "run-coord-issue-42",
                "role": "coordinator",
                "connections": [
                  {
                    "remote": "api.anthropic.com:443",
                    "protocol": "https",
                    "direction": "egress",
                    "count": 9,
                    "bytesOut": 53000,
                    "bytesIn": 206000,
                    "start": "2026-04-16T14:00:01Z",
                    "end": "2026-04-16T14:18:20Z"
                  },
                  {
                    "remote": "api.github.com:443",
                    "protocol": "https",
                    "direction": "egress",
                    "count": 8,
                    "bytesOut": 4200,
                    "bytesIn": 87600,
                    "start": "2026-04-16T14:00:03Z",
                    "end": "2026-04-16T14:17:55Z",
                    "requests": [
                      { "method": "GET", "path": "/repos/org/repo/issues/42", "bytesIn": 4200, "time": "2026-04-16T14:00:03Z" },
                      { "method": "GET", "path": "/repos/org/repo/contents/src/main.rs", "bytesIn": 12800, "time": "2026-04-16T14:00:05Z" },
                      { "method": "POST", "path": "/repos/org/repo/pulls", "bytesOut": 3400, "bytesIn": 1200, "time": "2026-04-16T14:17:55Z" }
                    ]
                  }
                ]
              },
              {
                "invocationId": "sub-impl-001",
                "role": "implementer",
                "connections": [
                  {
                    "remote": "api.anthropic.com:443",
                    "protocol": "https",
                    "direction": "egress",
                    "count": 14,
                    "bytesOut": 89000,
                    "bytesIn": 312000,
                    "start": "2026-04-16T14:01:16Z",
                    "end": "2026-04-16T14:11:58Z"
                  }
                ]
              }
            ]
          }
        },
        {
          "name": "observed-filesystem",
          "mediaType": "application/vnd.runtime-observations+json",
          "annotations": {
            "observations": [
              {
                "invocationId": "sub-impl-001",
                "role": "implementer",
                "reads": [
                  { "path": "src/main.rs", "count": 3, "start": "2026-04-16T14:02:01Z", "end": "2026-04-16T14:08:33Z" },
                  { "path": "src/lib.rs", "count": 1, "start": "2026-04-16T14:02:15Z", "end": "2026-04-16T14:02:15Z" },
                  { "path": "Cargo.toml", "count": 2, "start": "2026-04-16T14:02:00Z", "end": "2026-04-16T14:06:10Z" }
                ],
                "writes": [
                  { "path": "src/main.rs", "digest": { "sha256": "..." }, "time": "2026-04-16T14:09:15Z" },
                  { "path": "src/handlers/auth.rs", "digest": { "sha256": "..." }, "time": "2026-04-16T14:09:44Z" }
                ]
              }
            ]
          }
        },
        {
          "name": "observed-process",
          "mediaType": "application/vnd.runtime-observations+json",
          "annotations": {
            "observations": [
              {
                "invocationId": "sub-impl-001",
                "role": "implementer",
                "executions": [
                  { "binary": "/usr/bin/cargo", "args": ["check"], "exitCode": 0, "start": "2026-04-16T14:05:12Z", "end": "2026-04-16T14:05:48Z" },
                  { "binary": "/usr/bin/cargo", "args": ["test"], "exitCode": 0, "start": "2026-04-16T14:10:02Z", "end": "2026-04-16T14:10:47Z" },
                  { "binary": "/usr/bin/git", "args": ["diff", "--cached"], "exitCode": 0, "start": "2026-04-16T14:11:30Z", "end": "2026-04-16T14:11:31Z" }
                ]
              }
            ]
          }
        },
        {
          "uri": "oci://ghcr.io/org/attestations/sub-impl-001-trace",
          "digest": { "sha256": "d4e5f6..." },
          "name": "runtime-trace",
          "mediaType": "application/x.dsse+json",
          "annotations": {
            "predicateType": "https://in-toto.io/attestation/runtime-trace/v0.1"
          }
        },
        {
          "uri": "oci://ghcr.io/org/attestations/sub-impl-001-config-verification",
          "digest": { "sha256": "e5f6a7..." },
          "name": "config-verification",
          "mediaType": "application/x.dsse+json",
          "annotations": {
            "predicateType": "https://example.com/attestation/config-verification/v1"
          }
        }
      ]
    }
  }
}

Parsing Rules

This build type follows the SLSA Provenance v1 parsing rules with the following additions:

Versioning

The buildType URI includes the major version. Minor version changes are backward compatible. A major version change indicates a breaking schema change and a new URI.

Changelog


Design Rationale

Why a custom buildType?

Existing SLSA build types (GitHub Actions workflows, Google Cloud Build) describe CI/CD pipelines where the build steps are deterministic and defined in advance. An agent commit is fundamentally different: the agent decides what to do at runtime based on the task, the repo content, and its model’s judgment. The inputs that shape the output — model identity, MCP tool availability, sandbox constraints — have no equivalent in existing build types.

Without a build type that names these fields, they end up as unstructured data in internalParameters with no schema contract. A verifier can’t write a policy like “reject commits from agents that had unrestricted network access” without a schema that guarantees sandboxPolicy is present and structured consistently.

Why observations belong in byproducts

Byproducts are defined in the SLSA spec as artifacts “useful for debugging/forensics but not the primary output.” Runtime observations fit this exactly — they’re not the commit, but they’re the evidence a verifier needs to assess whether the agent behaved as expected.

The alternative is a separate attestation with a different predicate type, signed independently by the observer. The tradeoff is operational complexity: multiple attestations to produce, store, and correlate. For most deployments, the builder platform controls both the agent sandbox and the observer, so they share a signing identity. If a deployment has a truly independent observer (e.g., a third-party audit service), a separate attestation is the right model. This build type does not preclude that — a verifier can consume both the provenance and a separate observation attestation for the same subject. Additionally, attestation artifacts produced by other tooling within the sandbox can be referenced as byproducts without embedding their content, keeping the provenance attestation focused on what the builder platform directly observed.

Why instruction files are not enumerated

Agent runtimes load certain files automatically into the agent’s context by convention — CLAUDE.md, .github/copilot-instructions.md, skill files, etc. This makes them distinct from other repository content that the agent must actively choose to read. However, the discovery rules vary by runtime, version, and configuration. Enumerating instruction files in provenance requires the provenance producer to replicate the runtime’s discovery logic exactly. If they disagree, the provenance is misleading.

Any file the agent reads can influence its behavior — code comments, README files, configuration — not just instruction files. Enumerating a subset creates a false sense of completeness.

The source repository is already captured as a resolved dependency with a commit digest. This pins all content at the exact state the agent saw. The agent runtime (name, version, digest in internalParameters) determines which files it loads automatically.

Why mutable task content needs a snapshot

GitHub issues, PR descriptions, and ticket bodies are mutable — they can be edited after the agent run completes. If the provenance only records a URI (e.g., https://github.com/org/repo/issues/42), a verifier cannot determine what the agent actually saw.

The externalParameters.task.content field captures a snapshot of the verbatim content the agent received, pinned by digest. For OCI-based workflows, this is an OCI artifact. Content MAY also be inlined in the attestation (covered by the DSSE signature); inlined content MUST be base64 encoded. The issue URI remains as a human-readable reference; the snapshot is the source of truth for verification.

Subagent model

Subagents are captured as entries in internalParameters.subagents[] rather than as separate attestations. The builder platform typically has all the data at the end of the run — subagent configuration, inputs, outputs, and timing — so a single attestation is sufficient.

Each subagent entry captures its own model, runtime, tools, sandbox policy, inputs, outputs, and timing. This is sufficient for a verifier to assess each subagent independently (e.g., “the implementer used a different model than the reviewer,” “the implementer had no credentials”).

If subagents run in separate sandboxes, their runtime observations are recorded separately and correlated by invocationId in the byproducts. An MCP server for subagent orchestration is the natural coordination point — it creates the sandbox, triggers the observer, and collects results.

Why observations are embedded rather than using runtime-trace

The in-toto runtime-trace predicate (v0.1) captures similar categories of runtime behavior — process execution, network activity, file access. It is designed to be used alongside provenance attestations and could serve as a companion to this build type.

Observations are embedded as byproducts rather than requiring a separate runtime-trace attestation because the runtime-trace predicate leaves process and network log schemas as monitor-dependent (“The exact format of this field is currently dependent on the monitor”). Policy engines need a consistent schema to write rules against. This build type defines a concrete observation schema with named fields and types. Runtime-trace also has no concept of subagents, invocationId, or agent roles — correlating observations with specific subagent invocations requires structure that the runtime-trace predicate does not provide. A single attestation is also simpler to produce, store, and verify than a provenance attestation plus a separate runtime-trace attestation that must be correlated by subject.

This build type does not preclude using runtime-trace. If a deployment already produces runtime-trace attestations (e.g., via Tetragon), those can be referenced as byproducts alongside the embedded observations, or attached as separate attestations for the same subject.

Threat model and residual risk

This provenance captures how an artifact was produced and what was observed during production. It does not capture whether the output is correct.

An agent compromised via prompt injection can produce a subtly backdoored commit with clean observations — normal network activity, expected file access. The provenance will faithfully record a clean run that produced malicious output. This is the same threat as a malicious human developer producing a plausible-looking PR. Code review remains the defense for output quality.

What provenance does catch:

The provenance is most valuable when combined with deterministic controls (network allowlisting, credential proxying, filesystem isolation) that prevent the attacks provenance would detect. Provenance then serves as the verification layer — confirming that the controls were in place and that observed behavior stayed within declared policy.


This specification is a draft. Feedback and issues: refs.arewm.com