# Agent recipes

Copy complete agent workflows for discovery, SVG generation, tactile backgrounds, Design Lab sequences, safe source edits, brand setup and checked exports.

Each recipe includes discovery and a completion check. Set `BASE_URL` to the Glyphfield origin, which is `http://localhost:3012` for a local development server.

## Discover before generating [#discover-before-generating]

```bash
BASE_URL=http://localhost:3012

curl -fsS "$BASE_URL/api/agent" | jq '{version, schemaVersion, resources, interfaces}'
curl -fsS "$BASE_URL/api/labs" | jq '.plugins[] | {id, name, capabilities}'
curl -fsS "$BASE_URL/api/materials" | jq '{count, defaults, engines, sharedBy}'
curl -fsS "$BASE_URL/api/identities" | jq '.identities[] | {id, name}'
curl -fsS "$BASE_URL/api/generate" | jq '{schemaVersion, kinds}'
```

Most discovery routes allow caching for five minutes (`Cache-Control: public, max-age=300`). Read `GET /api/generate` again before a request if the cached copy may be older.

## Generate and save a raw SVG [#generate-and-save-a-raw-svg]

```bash
curl -fsS -X POST "$BASE_URL/api/generate" \
  -H 'Content-Type: application/json' \
  -d '{
    "kind": "template",
    "template": "slides",
    "slideLayout": "statement",
    "texture": "dark",
    "title": "One system. Every surface.",
    "identity": { "preset": "gt" },
    "output": "raw"
  }' \
  -o one-system.svg

test -s one-system.svg
```

The task is complete when the file exists and is not empty. A `200` status alone does not prove that.

## Generate a tactile background [#generate-a-tactile-background]

```bash
curl -fsS -X POST "$BASE_URL/api/generate" \
  -H 'Content-Type: application/json' \
  -d '{
    "kind": "background",
    "identity": { "preset": "gt" },
    "settings": {
      "width": 1600,
      "height": 900,
      "style": "grain-gradient",
      "gradient": "mesh",
      "colorA": "#F5F5F2",
      "colorB": "#181818",
      "colorC": "#7058FF",
      "surfaceMaterial": "embossed-paper",
      "surfaceDepth": 36,
      "surfaceRoughness": 72,
      "surfaceScale": 44,
      "grain": 12
    },
    "output": "raw"
  }' \
  -o tactile-background.svg
```

## Create and render a Design Lab sequence [#create-and-render-a-design-lab-sequence]

Design Lab is the Studio's layered composition tool. First generate the composition document:

```js
const baseUrl = 'http://localhost:3012';
const response = await fetch(`${baseUrl}/api/generate`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    kind: 'design-sequence',
    identity: { preset: 'gt' },
    ratio: 'wide',
    backgroundColor: '#111216',
    includeBrandMark: true,
    texts: [
      { value: 'Open source', weight: 500 },
      { value: 'Every language. One source.', weight: 400 },
    ],
    shader: {
      materialId: 'paper-gem-smoke',
      shaderSize: 0.8,
      opacity: 1,
    },
    effect: {
      kind: 'bayer',
      opacity: 0.72,
      foreground: '#F5F5F2',
      background: '#111216',
    },
    sequence: { cutCount: 10, finalHoldMs: 5000, pace: 'accelerating' },
    export: { width: 1920, fps: 30, quality: 'best', gifLoop: 'seamless' },
  }),
});

if (!response.ok) throw new Error(await response.text());
const generated = await response.json();
```

Then open `/studio?tool=material` in the browser and run:

```js
function readyStudio() {
  return new Promise((resolve) => {
    try {
      if (window.glyphfield?.studio) return resolve(window.glyphfield.studio);
    } catch {
      // The tool is still loading. Wait for the ready event.
    }
    window.addEventListener('glyphfield:studio-api-ready', () => resolve(window.glyphfield.studio), { once: true });
  });
}

const studio = await readyStudio();
if (studio.activeTool() !== 'material') throw new Error('Open /studio?tool=material first.');

await studio.applySource(generated.document);
const artifact = await studio.invoke('design.export', {
  format: 'mp4',
  mode: 'shader-sequence',
  download: true,
});

if (!(artifact.blob instanceof Blob) || artifact.blob.size === 0) {
  throw new Error('Design Lab returned an empty export.');
}
```

The Browser API covers only the active tool's workspace, so it cannot click the Studio sidebar. To change tools, load `/studio?tool=<tool id>&project=<project id>`, wait for the API again and read `window.glyphfield.studio` again.

## Edit an existing composition safely [#edit-an-existing-composition-safely]

```js
const studio = window.glyphfield.studio;
const source = JSON.parse(studio.readSource());

const firstText = Object.values(source.elements)
  .find((element) => element.kind === 'text');

if (!firstText) throw new Error('No text layer exists.');
firstText.content = 'Updated by an agent';
firstText.style.opacity = 0.88;

await studio.applySource(source);
const applied = JSON.parse(studio.readSource());
if (applied.elements[firstText.id].content !== 'Updated by an agent') {
  throw new Error('The source did not round-trip.');
}
```

Keep the unknown fields of the document you read. Edit that document in place instead of rebuilding the canvas envelope.

## Set up a brand and export its tokens [#set-up-a-brand-and-export-its-tokens]

Validate without a browser, then apply in the Studio. [Brand setup with an agent](/docs/agents/brand-setup) describes the full workflow.

```bash
curl -fsS "$BASE_URL/api/brand/validate" | jq '{schemaVersion, mergeRules, logoSlots: [.logoSlots[].id]}'

curl -fsS -X POST "$BASE_URL/api/brand/validate" \
  -H 'Content-Type: application/json' \
  -d '{ "mode": "patch", "identity": { "tagline": "Weather you can build on.", "colors": [{ "id": "emphasis", "hex": "#1F6FEB" }] } }' \
  | jq '{ok, issues}'
```

```js
// On /studio?tool=identity&project=<id>
const studio = window.glyphfield.studio;
const patch = { tagline: 'Weather you can build on.', colors: [{ id: 'emphasis', hex: '#1F6FEB' }] };

await studio.invoke('identity.apply', { identity: patch });
const report = await studio.invoke('identity.audit');
await studio.invoke('identity.save');
const tokens = await studio.invoke('identity.export.kit', { format: 'tailwind' });
```

## Use a local file [#use-a-local-file]

In Design Lab, the file input with the accessible label `Choose logos for the canvas` adds each file as a logo layer:

```js
// fileHandle comes from a file picker the user approved.
const picked = await fileHandle.getFile();
const file = new File([picked], 'mark.svg', { type: 'image/svg+xml' });
window.glyphfield.studio.set('Choose logos for the canvas', file);
```

Create a `File` only from a source the user authorized. Wait until the new layer appears in the source or on the canvas before exporting.

## Export every supported format [#export-every-supported-format]

```js
for (const format of ['png', 'jpg', 'gif', 'mp4']) {
  const artifact = await window.glyphfield.studio.invoke('design.export', {
    format,
    download: true,
  });
  console.log(format, artifact.fileName, artifact.blob.size);
}
```

Do not run these exports in parallel. They share live canvases and browser encoders. Export one format, check it, then start the next.

## Failure handling [#failure-handling]

1. For an HTTP failure, read `error.code`, `error.field` and `error.message`.
2. Do not retry unchanged input after a validation error.
3. For a Browser API failure, call `describe()` and `controls()` again, because the active tool may have changed.
4. If the browser cannot encode MP4, report the failure and keep the source document. Do not substitute GIF.
5. If WebGL is unavailable, use an HTTP `background` SVG only when it still meets the request.

See [API errors and limits](/docs/agents/errors) for every error code.
