mech.app

The mech.app newsletter

Agentic AI, minus the noise.

Get practical field notes on AI agents, automation, developer tools and security delivered to your inbox.

No spam. Unsubscribe anytime.

Dev Tools

GitHub's Canvas Pattern: Why Agentic Workflows Need Spatial State Beyond Chat Scrollback

How GitHub's canvas UI externalizes agent state from chat history into persistent artifacts for visibility, steering, and token efficiency.

Source: github.blog
GitHub's Canvas Pattern: Why Agentic Workflows Need Spatial State Beyond Chat Scrollback

Chat interfaces break down when agents do real work. The problem is not the model or the prompt. The problem is that chat history is a terrible data structure for multi-step workflows. GitHub’s canvas pattern is their answer: a spatial UI that externalizes agent state from scrollback into persistent, editable artifacts. This is not a cosmetic change. It changes how you observe, steer, and pay for agent work.

Why Chat History Fails for Agent Workflows

Chat is append-only. Every agent action, every intermediate result, every correction gets serialized into a linear log. You lose spatial context. You lose the ability to see what changed between steps. You lose the ability to edit intermediate state without re-running the entire chain.

The token cost compounds. Every turn sends the full history back to the model. A ten-step workflow might re-send the same context nine times. If the agent generates a 500-line file in step three, that file rides along in every subsequent request. You pay for it every time.

Observability is worse. You cannot see the current state of the artifact without scrolling. You cannot diff between agent edits. You cannot tell if the agent is stuck in a loop or making progress. Chat gives you a transcript, not a workspace.

What a Canvas Actually Is

A canvas is a persistent, editable surface that lives outside the chat thread. The agent writes to it. You edit it. The agent reads your edits and continues. The canvas holds the current state of the artifact (code, document, config file) and the chat holds the intent and feedback loop.

GitHub’s implementation separates three concerns:

  • Chat pane: Natural language intent, questions, corrections.
  • Canvas pane: The artifact under construction (code, markdown, JSON).
  • State boundary: The agent reads from the canvas, not from its own prior outputs in chat.

This separation changes the token economics. The agent does not re-send the entire artifact on every turn. It sends a reference or a diff. The canvas becomes the source of truth, not the chat log.

State Management and Version Control

The canvas model introduces a new problem: who owns the state? If the agent writes a function and you edit it, what happens when the agent tries to modify it again? GitHub’s approach treats the canvas as a shared workspace with explicit handoff points.

Human edits take precedence. If you change a line, the agent sees your version on the next turn. The agent does not overwrite your changes unless you explicitly ask it to regenerate.

Diffs are first-class. The canvas can show what the agent changed in the last step. You can accept, reject, or modify the diff before it lands. This is not a chat feature. This is version control embedded in the UI.

Partial rollback is possible. If the agent breaks something in step five of a ten-step workflow, you can revert that step without losing the other nine. Chat does not give you that. You either accept the whole chain or start over.

Observability Primitives

A canvas exposes state that chat logs cannot. You get:

  • Current artifact state: The actual code or document, not a description of it.
  • Change history: What the agent modified in each step, not what it said it would modify.
  • Dependency visibility: If the agent is working on multiple files, you see them side by side.
  • Execution traces: Some canvas implementations show which tool calls produced which changes.

This is the difference between a log and a debugger. Chat gives you a log. Canvas gives you a debugger.

Architecture: How Canvas State Flows

Here is the orchestration flow for a multi-step agent workflow with a canvas:

  1. User sends intent in chat: “Add error handling to this function.”
  2. Agent reads current canvas state (the function as it exists now).
  3. Agent plans changes and writes a diff to the canvas.
  4. Canvas UI shows the diff before applying it.
  5. User accepts or modifies the diff.
  6. Canvas state updates with the accepted changes.
  7. Agent reads updated canvas state for the next step.

The key difference: the agent does not read its own chat output. It reads the canvas. If you edited the canvas between steps, the agent sees your edits. The chat history is metadata. The canvas is the working memory.

Token Cost Comparison

ApproachContext Sent Per TurnTotal Tokens (10-step workflow)
Chat-onlyFull history + new prompt~50k (cumulative, grows quadratically)
CanvasCanvas reference + new prompt~15k (linear growth)
Canvas with diffsDiff + new prompt~8k (minimal overhead)

These numbers assume a 500-line artifact and 10 agent turns. Chat-only sends the artifact 10 times. Canvas sends it once and references it. Canvas with diffs sends only what changed.

The cost difference is not marginal. It is the difference between a $2 workflow and a $15 workflow. At scale, it is the difference between viable and unviable.

Failure Modes and Recovery

Canvases introduce new failure modes:

State desync: If the agent and the canvas disagree about the current state, the agent halts or produces garbage. This happens when the canvas update fails but the agent thinks it succeeded. Solution: atomic updates with rollback.

Edit conflicts: If you edit the canvas while the agent is writing to it, you get a merge conflict. Some implementations lock the canvas during agent writes. Others queue edits and resolve conflicts after the agent finishes.

Partial application: If the agent generates a multi-file change and one file fails to write, you get an inconsistent state. Solution: transactional updates or explicit checkpoints.

Lost context: If the agent relies on chat history for context (not just the canvas), you still pay the token cost. The canvas does not eliminate context windows. It reduces what you send.

When to Use a Canvas

Use a canvas when:

  • The agent produces artifacts larger than a few hundred lines.
  • You need to edit intermediate results without re-running the workflow.
  • You are running multi-step workflows where each step builds on the last.
  • Token cost is a constraint (it always is).
  • You need to observe what changed between steps, not just what the agent said it did.

Do not use a canvas when:

  • The workflow is a single turn (chat is simpler).
  • The artifact is small enough to fit in a chat message without scrolling.
  • You do not need to edit intermediate state.
  • The agent does not produce persistent artifacts (e.g., it only answers questions).

Implementation Sketch

Here is what a minimal canvas state manager looks like:

class Canvas:
    def __init__(self):
        self.state = {}  # artifact_id -> content
        self.history = []  # list of (artifact_id, diff, timestamp)
    
    def read(self, artifact_id):
        return self.state.get(artifact_id, "")
    
    def write(self, artifact_id, content, diff=None):
        old_content = self.state.get(artifact_id, "")
        self.state[artifact_id] = content
        self.history.append({
            "artifact_id": artifact_id,
            "diff": diff or self._compute_diff(old_content, content),
            "timestamp": time.time()
        })
    
    def rollback(self, artifact_id, steps=1):
        # Revert last N changes to this artifact
        relevant = [h for h in self.history if h["artifact_id"] == artifact_id]
        if len(relevant) < steps:
            raise ValueError("Not enough history to rollback")
        # Apply inverse diffs (simplified)
        for h in reversed(relevant[-steps:]):
            self.state[artifact_id] = self._apply_inverse(self.state[artifact_id], h["diff"])
    
    def _compute_diff(self, old, new):
        # Use difflib or similar
        pass
    
    def _apply_inverse(self, content, diff):
        # Reverse a diff
        pass

This is not production code. It is the shape of the problem. You need state storage, diff tracking, and rollback. The rest is UI.

Technical Verdict

Use a canvas when you are building multi-step agent workflows that produce editable artifacts. The token savings alone justify the complexity. The observability and steering benefits are larger.

Avoid a canvas if your agent only answers questions or produces single-turn outputs. Chat is simpler and good enough.

The canvas pattern is not a new idea. It is spatial version control for agent work. GitHub’s contribution is showing that it works at scale and publishing the design rationale. If you are building agent tooling, you need a canvas or something like it. Chat alone will not scale.