Example: Code into a Repo

Emit code files directly into a working repo (clone) using targetSlot.kind: 'repos'.

Not runnable today. This example depends on the workspace harvest, which is not running: files the agent writes into the repo working copy do not become artifacts, so open-pr has no artifact ids to build a PR from. The spec and its targetSlot are still valid. See the note on the section index.


Spec

json
1{
2  "ref": "engineering-standard",
3  "kind": "custom",
4  "outputContract": {
5    "title": "Engineering Implementation",
6    "mode": "workspace-files",
7    "targetSlot": {
8      "kind": "repos",
9      "repoSlug": "my-service"
10    },
11    "notes": "Agent writes source files, configs, and migrations under the repo working copy. Repo slug is supplied by the workflow context."
12  }
13}

The custom kind doesn't constrain individual file paths — the agent is free to add, modify, or delete files in the repo working copy. Validation only checks that some files were written.

For tighter contracts (e.g. "must touch these specific files"), use custom with an explicit files[] block listing required paths.

Workflow

yaml
1apiVersion: xema.dev/workflow/v1alpha1
2kind: Workflow
3metadata:
4  name: code-emission-example
5  version: 1.0.0
6on:
7  workflow_dispatch:
8    inputs:
9      changeRequest:
10        type: string
11        required: true
12permissions:
13  artifacts: write
14  repos: write
15jobs:
16  engineer:
17    title: Engineering implementation
18    uses: xema/agent
19    with:
20      agentRef: engineering
21      stageKey: engineering
22      deliverableSpecRef: engineering-standard
23      agentSession: false
24      agentContext:
25        prompt: ${{ inputs.changeRequest }}
26    permissions:
27      repos: write
28    outputs:
29      handoff: ${{ job.outputs.structuredOutput }}

An open-pr job consuming outputs.deliverables used to follow here. It is removed rather than rewritten: that output no longer exists, so the job would name a field the action does not declare and open a pull request over no files.

Reading the result

This is the envelope the harvest was designed to expose. It is not produced today — shown so the spec's intent is legible, not as something to consume:

ts
1agentResult: {
2  deliverable: {
3    specRef: 'engineering-standard',
4    kind: 'custom',
5    targetSlot: { kind: 'repos', repoSlug: 'my-service' },
6    content: {
7      kind: 'custom',
8      files: [
9        { path: '/workspace/repos/my-service/src/handler.ts',
10          artifactId: 'art-1', versionId: 'ver-1', version: 1,
11          contentKind: 'text', sizeBytes: 1840 },
12        { path: '/workspace/repos/my-service/test/handler.test.ts',
13          artifactId: 'art-2', versionId: 'ver-2', version: 1,
14          contentKind: 'text', sizeBytes: 920 },
15        { path: '/workspace/repos/my-service/migrations/2026-05-01-init.sql',
16          artifactId: 'art-3', versionId: 'ver-3', version: 1,
17          contentKind: 'text', sizeBytes: 412 },
18      ],
19    },
20  },
21}

A downstream scm-open-pr action would consume those artifact ids to build a PR with all written files.

Validating the result

xema/validate-deliverables is live and checks artifact counts, so it is what would make "the agent wrote nothing usable" visible. With the harvest not running there are no file artifacts to count, so a custom spec validated this way returns fail with INSUFFICIENT_ARTIFACTS on every run — not because the agent misbehaved, but because nothing collects what it wrote. See 04 Validation.

Tighter contracts

Add a files[] block to require specific paths exist:

json
1"files": [
2  {
3    "canonicalPath": "src/handler.ts",
4    "required": true,
5    "purpose": "Main request handler",
6    "contentKind": "text"
7  }
8]

files[] is the list of paths the harvest was to consider, in order, when looking for the deliverable. It is part of the spec contract and is still stored and served; nothing walks it today.


Previous: ← JSON schema Next: Response-only →