Skip to main content

Overview

GLYPH patches enable efficient incremental updates to structured state. Instead of sending the entire state document after each change, send only the operations that transform one state to another.

Why Patches?

Token Efficiency

Send O(1) operations instead of O(n) full state

Base Hash Verification

Cryptographic proof you’re updating the correct version

Optimistic Concurrency

Detect and reject conflicting updates

Audit Trail

Log operations for debugging and replay

Patch Operations

GLYPH supports four core patch operations:
OperatorNamePurposeExample
=SetReplace a value= score 95
+AppendAdd to list/map+ items {id=3 name=widget}
~IncrementAdd to number~ count 1
-DeleteRemove key/index- items[0]

Set Operation (=)

Replace a field value completely.
# Before
{score=90 status=pending}

# Patch
= score 95
= status=complete

# After
{score=95 status=complete}
import glyph

# Create patch
patch = glyph.patch([
    ("=", "score", 95),
    ("=", "status", "complete"),
])

# Apply to state
state = {"score": 90, "status": "pending"}
new_state = glyph.apply_patch(state, patch)
# {"score": 95, "status": "complete"}

Append Operation (+)

Add an element to a list or a key-value pair to a map.
# Before
{items=[{id=1 name=foo} {id=2 name=bar}]}

# Patch
+ items {id=3 name=baz}

# After
{items=[{id=1 name=foo} {id=2 name=bar} {id=3 name=baz}]}
import glyph

# Append to list
patch = glyph.patch([
    ("+", "items", {"id": 3, "name": "baz"}),
])

state = {"items": [{"id": 1, "name": "foo"}, {"id": 2, "name": "bar"}]}
new_state = glyph.apply_patch(state, patch)
# {"items": [{"id": 1, "name": "foo"}, {"id": 2, "name": "bar"}, {"id": 3, "name": "baz"}]}

Increment Operation (~)

Add a numeric value to an existing number (supports negative increments).
# Before
{count=5 score=100}

# Patch
~ count 1
~ score -10

# After
{count=6 score=90}
import glyph

# Increment counters
patch = glyph.patch([
    ("~", "count", 1),
    ("~", "score", -10),
])

state = {"count": 5, "score": 100}
new_state = glyph.apply_patch(state, patch)
# {"count": 6, "score": 90}

Delete Operation (-)

Remove a key from a map or an index from a list.
# Before
{temp_data=xyz count=5 items=[a b c]}

# Patch
- temp_data
- items[1]

# After
{count=5 items=[a c]}
import glyph

# Delete fields
patch = glyph.patch([
    ("-", "temp_data", None),
    ("-", "items[1]", None),
])

state = {"temp_data": "xyz", "count": 5, "items": ["a", "b", "c"]}
new_state = glyph.apply_patch(state, patch)
# {"count": 5, "items": ["a", "c"]}

Base Hash Verification

Patches include a base fingerprint - the first 16 characters of the SHA-256 hash of the canonical form of the base state. This enables:
  • Optimistic concurrency: Reject patches applied to stale state
  • Streaming validation: Verify state consistency without full doc transfer
  • Debugging: Trace state divergence in distributed systems

Patch Format with Base

@patch @base=1a2b3c4d5e6f7890
= score 95
+ events "Goal!"
@end
The @base= attribute contains the first 16 hex characters of sha256(canonicalize(baseState)).

Creating Patches with Base

import glyph

# Base state
base_state = {"score": 90, "events": []}

# Create patch with base fingerprint
patch = glyph.PatchBuilder(target) \
    .with_base_value(base_state) \
    .set("score", 95) \
    .append("events", "Goal!") \
    .build()

# Patch includes: @base=1a2b3c4d5e6f7890

Applying Patches with Base Verification

import glyph
from glyph import stream

# Receiver verifies base hash before applying
@handler.on_patch
def handle_patch(sid, seq, payload, state):
    # Base hash already verified by handler
    patch = glyph.parse_patch(payload)
    new_state = glyph.apply_patch(state.value, patch)
    handler.cursor.set_state(sid, new_state)
    return new_state

@handler.on_base_mismatch
def handle_mismatch(sid, frame):
    # State diverged - request full resync
    logger.warning(f"State mismatch on {sid}")
    request_full_state(sid)

Concurrent Safety

Base hash verification prevents the “lost update” problem in distributed systems.

The Problem

1. Agent A reads state: {count=5}  (hash: abc123...)
2. Agent B reads state: {count=5}  (hash: abc123...)
3. Agent A writes: {count=6}       (hash: def456...)
4. Agent B writes: {count=6}       ❌ Overwrites A's update!

The Solution

1. Agent A reads state: {count=5}  (hash: abc123...)
2. Agent B reads state: {count=5}  (hash: abc123...)
3. Agent A sends patch with @base=abc123... → Accepted
4. Agent B sends patch with @base=abc123... → Rejected (base mismatch)
5. Agent B re-reads state: {count=6} (hash: def456...)
6. Agent B sends patch with @base=def456... → Accepted

Example: Optimistic Concurrency

import glyph
from glyph import stream

def update_with_retry(writer, sid, seq, update_fn, max_retries=3):
    """Apply update with optimistic concurrency control."""
    for attempt in range(max_retries):
        # Read current state
        state = read_state(sid)
        base_hash = glyph.fingerprint_loose(state)
        
        # Compute patch
        patch = update_fn(state)
        
        # Send with base hash
        try:
            writer.write_frame(
                sid=sid,
                seq=seq,
                kind="patch",
                payload=patch,
                base=base_hash[:16],
            )
            return  # Success
        except BaseMismatchError:
            # State changed, retry
            logger.info(f"Retry {attempt + 1}/{max_retries}")
            continue
    
    raise Exception("Max retries exceeded")

# Usage
update_with_retry(
    writer, 
    sid=1, 
    seq=5,
    update_fn=lambda state: glyph.patch([("~", "count", 1)])
)

Performance Benefits

Token Savings

OperationFull StatePatchSavings
Increment counter~120 tokens~8 tokens93%
Append to list~200 tokens~15 tokens92%
Update 1 of 10 fields~180 tokens~12 tokens93%
Update 3 of 10 fields~180 tokens~35 tokens80%

When to Use Patches

For state that changes frequently (counters, progress, live metrics).Example: Agent updating turn count, token usage, or task completion percentage.
When the full state is large but updates are small.Example: 10KB agent memory, updating a single field.
When multiple agents may update the same state concurrently.Example: Multi-agent planning with shared task queue.
For streaming logs, events, or observation history.Example: ReAct loop appending tool results to observations.

Next Steps

Fingerprinting

Deep dive into SHA-256 state hashing

GS1 Streaming

Learn about frame-based patch delivery

Agent Patterns

See patches in multi-agent coordination

API Reference

Explore patch APIs in all languages

Build docs developers (and LLMs) love