← Glyph · AGENTS.md · Gallery · Essay

Two agents on a chart

Agent A renders. Agent B audits and patches. Agent A applies the patch and re-renders. Same JSON. Same SVG bytes. The collaboration demo no other chart library can run — because no other library is byte-deterministic and patchable as JSON.

I.The setup

The user has a CSV of bicycle rides and asks for a chart. Two agents are in the room: Agent A, the renderer, and Agent B, the auditor. Both speak MCP, both have Glyph installed (claude mcp add glyph -- npx -y @glyph/mcp), and they share the same scratch space.

Their contract is simple: Agent A writes Glyph JSON specs, Agent B finds problems, Agent B emits RFC 6902 patches, Agent A applies them. Neither agent regenerates the chart from scratch on each iteration — that would lose the byte-determinism trail.

II.Agent A's first draft

Agent A sees the CSV and drafts a bar chart of rides per hour. It calls glyph_render with this spec:

{
  "version": "glyph/0.1",
  "title": "Bike rides by hour",
  "data": { "source": "rides.csv" },
  "layers": [{
    "mark": "bar",
    "encoding": {
      "x": { "field": "hour", "type": "ordinal" },
      "y": { "field": "rides", "type": "quantitative" }
    }
  }]
}

Glyph emits an SVG and an Explanation envelope back. The envelope records the resolved scale domains, the SHA-256 seal, and any audit warnings. Agent A passes the spec, the SVG, and the envelope to Agent B.

III.Agent B audits

Agent B calls glyph_audit_spec on the same spec. Glyph's auditor returns a structured list of findings:

{
  "findings": [
    {
      "severity": "warning",
      "path": "/layers/0/encoding/y/scale",
      "message": "y axis has no explicit domain; auto-fit may exaggerate variation",
      "suggested_patch": [{
        "op": "add",
        "path": "/layers/0/encoding/y/scale",
        "value": { "domain": [0, null] }
      }]
    },
    {
      "severity": "info",
      "path": "/title",
      "message": "title lacks units",
      "suggested_patch": [{
        "op": "replace",
        "path": "/title",
        "value": "Bike rides by hour (count per hour)"
      }]
    }
  ]
}

Agent B's job isn't to argue — it's to surface the problems. Agent A decides what to apply.

IV.Agent A applies the patch

Agent A accepts both suggestions and emits an RFC 6902 patch:

[
  { "op": "add",
    "path": "/layers/0/encoding/y/scale",
    "value": { "domain": [0, null] } },
  { "op": "replace",
    "path": "/title",
    "value": "Bike rides by hour (count per hour)" }
]

Agent A applies the patch to its own spec, calls glyph_render again, and ships SVG v2 to the user. Because Glyph is byte-deterministic, every other agent (and every CI run) that applies the same patch to the same spec gets exactly the same bytes back. The whole conversation is reproducible.

V.The result, side by side

First-draft chart from Agent A: bar chart of rides by hour, with auto-fit y-axis and bare title 'Bike rides by hour'. Two visible peaks at hour 8 and hour 17 — the morning and evening commutes.
v1 — Agent A's first draft. Auto-fit y-axis. Title without units.
Second draft after Agent B's audit: y-axis explicitly pinned to [0, 1500] and title clarified to 'Bike rides by hour (count per hour)'. Same data, but the y-axis floor is now anchored at zero so the morning + evening peaks aren't exaggerated.
v2 — after Agent B's patch. Y-axis pinned to [0, 1500]. Title carries units.

These are the actual byte-locked fixtures from packages/core/__fixtures__/two-agents/. Both renders are deterministic and tested in CI — see two-agents.test.ts for the full assertion suite (parse → render → patch → re-render → byte-equal).


Why this demo only works in Glyph

Determinism. Vega, Plotly, D3 all produce SVG whose bytes drift between platforms (subpixel rounding, ID generation, timestamp embedding). Without byte-equality, "the same spec produced this byte stream" is not a verifiable claim.

Audit-as-a-verb. Glyph's MCP surface includes glyph_audit_spec — the auditor is part of the library, not a separate tool. The second agent doesn't need a custom reviewer prompt to identify chart problems.

JSON patches against a stable schema. Glyph specs are flat JSON validated by Zod. RFC 6902 patches against them are auditable, idempotent, and replayable. A Vega-Lite spec compiled to D3 code is none of those.

SHA-256 seal. Every rendered SVG carries the hash of (spec, rows, schema). Anyone, including Agent C, can verify the bytes correspond to the claimed spec without re-rendering.

Try it yourself

Open two Claude conversations side by side (or one Claude conversation with two roles). In both, run claude mcp add glyph -- npx -y @glyph/mcp. Then:

Conversation 1 (you'll be Agent A): "Render a bar chart from rides.csv showing rides per hour. Save the spec to spec-v1.json and the SVG to chart-v1.svg." — prompt for Agent A
Conversation 2 (you'll be Agent B): "Load spec-v1.json. Call glyph_audit_spec. For each finding with severity warning or higher, emit an RFC 6902 patch. Write the patch list to patch.json." — prompt for Agent B
Back in conversation 1: "Apply patch.json to spec-v1.json, save as spec-v2.json, and re-render to chart-v2.svg." — prompt for Agent A again

Both chart-v1.svg and chart-v2.svg will be byte-identical to anyone else running the same prompts. That's the trick.