|
| 1 | +# Kai Live Sync — 4-Agent Demo |
| 2 | + |
| 3 | +**Length:** ~2:30 |
| 4 | +**Audience:** anyone who's watched two AI agents edit the same codebase and wondered how the hell that could possibly work. |
| 5 | +**The one thing they should leave with:** *Four agents can work in the same repo, at the same time, without anyone writing a merge resolver.* |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## The argument in one paragraph |
| 10 | + |
| 11 | +An AI agent is a great way to finish one task quickly. Four AI agents are, in theory, a great way to finish four tasks quickly — except the moment you run them against the same codebase, you get race conditions, clobbered files, and contradictory edits that someone still has to reconcile. The industry's current answer is "run them one at a time," which is another way of saying "don't use the parallelism that's right in front of you." Kai's answer is different: let them work simultaneously, semantically merge their edits in real time, and track who did what so you can actually review the result. |
| 12 | + |
| 13 | +This demo shows four Claude Code agents building one small feature together — live, in the same repo, via kai's MCP live-sync channel. |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +## Setup |
| 18 | + |
| 19 | +### Prerequisites |
| 20 | + |
| 21 | +- `kai` ≥ 0.12.3 on PATH |
| 22 | +- Four independent Claude Code sessions (four terminal windows, four tabs, or a tmux 2×2) |
| 23 | +- The kai MCP server already configured in your Claude Code install |
| 24 | +- A kaicontext account you're logged into (`kai auth status` should show your email) |
| 25 | + |
| 26 | +### Layout — don't just show 4 claude prompts |
| 27 | + |
| 28 | +The problem with a naive 2×2 grid of Claude sessions is that **sync is invisible**. When agent A saves a file and agent B's kai quietly writes that file to B's disk, nothing on B's screen changes until B's Claude decides to re-read the tree. The viewer never sees the magic happen. So this demo uses a slightly more involved layout. |
| 29 | + |
| 30 | +Each of the four agent cells is split into **two stacked panes**: |
| 31 | + |
| 32 | +``` |
| 33 | +┌─────────────────────────────┬─────────────────────────────┐ |
| 34 | +│ ┃A─ Claude Code (top, 55%) │ ┃B─ Claude Code (top, 55%) │ |
| 35 | +│ ┃ │ ┃ │ |
| 36 | +│ ├─ live filesystem (45%) ───┤ ├─ live filesystem (45%) ───┤ |
| 37 | +│ ┃ src/greet.js mtime │ ┃ src/greet.js mtime │ |
| 38 | +│ ┃ contents… │ ┃ contents… │ |
| 39 | +├─────────────────────────────┼─────────────────────────────┤ |
| 40 | +│ ┃C─ Claude Code │ ┃D─ Claude Code │ |
| 41 | +│ ┃ │ ┃ │ |
| 42 | +│ ├─ live filesystem ─────────┤ ├─ live filesystem ─────────┤ |
| 43 | +│ ┃ … │ ┃ … │ |
| 44 | +└─────────────────────────────┴─────────────────────────────┘ |
| 45 | + │ |
| 46 | + ▼ fifth pane below or to the side: |
| 47 | + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ |
| 48 | + ┃ SYNC FEED (live SSE stream, all 4 agents) ┃ |
| 49 | + ┃ 13:21:04 [A → sync] write src/greet.js (412 B) ┃ |
| 50 | + ┃ 13:21:05 [B ← sync] recv src/greet.js ┃ |
| 51 | + ┃ 13:21:05 [C ← sync] recv src/greet.js ┃ |
| 52 | + ┃ 13:21:05 [D ← sync] recv src/greet.js ┃ |
| 53 | + ┃ 13:21:12 [B → sync] write tests/greet.test.js ┃ |
| 54 | + ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ |
| 55 | +``` |
| 56 | + |
| 57 | +Three things this buys you on camera: |
| 58 | + |
| 59 | +1. **The filesystem pane updates when sync lands**, not when the agent decides to read. Viewers see the file *arrive*. |
| 60 | +2. **The sync feed** is a single narrative column that lists every send/receive across all four agents, timestamped. It's the running subtitle of the whole demo. |
| 61 | +3. **Color-code the four agents** (A=red, B=blue, C=green, D=yellow) and carry those colors everywhere — pane borders, sync-feed rows, voiceover callouts. Without colors, a viewer staring at a 2×2 grid can't tell which agent wrote what. |
| 62 | + |
| 63 | +### How to build it |
| 64 | + |
| 65 | +Easiest path: tmux, one pre-made session. Save this as `demo-layout.sh` alongside the setup block: |
| 66 | + |
| 67 | +```bash |
| 68 | +#!/bin/sh |
| 69 | +tmux new-session -d -s livesync -x 240 -y 60 |
| 70 | +# Top-left: Agent A split vertically (Claude 55%, watcher 45%) |
| 71 | +tmux send-keys -t livesync 'cd /tmp/demo-a && clear && echo "AGENT A — backend"' C-m |
| 72 | +tmux split-window -v -p 45 -t livesync \ |
| 73 | + "cd /tmp/demo-a && watch -n 0.3 -t -c 'printf \"\\033[31m┃ A \\033[0m\\n\"; ls -la --color=always src tests docs 2>/dev/null; echo ---; cat src/greet.js 2>/dev/null | head -20'" |
| 74 | + |
| 75 | +# Top-right: Agent B |
| 76 | +tmux split-window -h -t livesync:0.0 |
| 77 | +tmux send-keys -t livesync:0.2 'cd /tmp/demo-b && clear && echo "AGENT B — tests"' C-m |
| 78 | +tmux split-window -v -p 45 -t livesync:0.2 \ |
| 79 | + "cd /tmp/demo-b && watch -n 0.3 -t -c 'printf \"\\033[34m┃ B \\033[0m\\n\"; ls -la --color=always src tests docs 2>/dev/null; echo ---; cat tests/greet.test.js 2>/dev/null | head -20'" |
| 80 | + |
| 81 | +# Bottom-left: Agent C |
| 82 | +tmux split-window -v -p 50 -t livesync:0.0 |
| 83 | +tmux send-keys -t livesync:0.4 'cd /tmp/demo-c && clear && echo "AGENT C — frontend"' C-m |
| 84 | +tmux split-window -v -p 45 -t livesync:0.4 \ |
| 85 | + "cd /tmp/demo-c && watch -n 0.3 -t -c 'printf \"\\033[32m┃ C \\033[0m\\n\"; ls -la --color=always src tests docs 2>/dev/null; echo ---; cat src/App.jsx 2>/dev/null | head -20'" |
| 86 | + |
| 87 | +# Bottom-right: Agent D |
| 88 | +tmux split-window -v -p 50 -t livesync:0.2 |
| 89 | +tmux send-keys -t livesync:0.6 'cd /tmp/demo-d && clear && echo "AGENT D — docs"' C-m |
| 90 | +tmux split-window -v -p 45 -t livesync:0.6 \ |
| 91 | + "cd /tmp/demo-d && watch -n 0.3 -t -c 'printf \"\\033[33m┃ D \\033[0m\\n\"; ls -la --color=always src tests docs 2>/dev/null; echo ---; cat docs/greet.md 2>/dev/null | head -20'" |
| 92 | + |
| 93 | +tmux attach -t livesync |
| 94 | +``` |
| 95 | + |
| 96 | +The `watch -n 0.3` redraws each filesystem pane every 300 ms. When a file arrives from sync, its `mtime` in the `ls -la` line jumps to the current second — *that is the sync event, visible*. |
| 97 | + |
| 98 | +### The sync feed (the fifth pane) |
| 99 | + |
| 100 | +Run this in a fifth terminal or a sixth tmux pane, pointed at any one of the four dirs (it reads the shared channel from the server): |
| 101 | + |
| 102 | +```bash |
| 103 | +cd /tmp/demo-a |
| 104 | +kai activity --follow 2>&1 | while IFS= read -r line; do |
| 105 | + case "$line" in |
| 106 | + *"write"*|*"→ sync"*) color="\033[0m" ;; |
| 107 | + *"recv"*|*"← sync"*) color="\033[2m" ;; # dim for incoming |
| 108 | + *"merge"*) color="\033[33m" ;; # yellow = auto-merge |
| 109 | + *"conflict"*) color="\033[31m" ;; # red = conflict |
| 110 | + *) color="\033[0m" ;; |
| 111 | + esac |
| 112 | + printf "%b%s %s\033[0m\n" "$color" "$(date +%T)" "$line" |
| 113 | +done |
| 114 | +``` |
| 115 | + |
| 116 | +If `kai activity --follow` isn't available in your build, fall back to tailing the sync log directly: |
| 117 | + |
| 118 | +```bash |
| 119 | +tail -F /tmp/demo-a/.kai/sync.log 2>/dev/null |
| 120 | +``` |
| 121 | + |
| 122 | +### One more clever trick: the "diff heartbeat" |
| 123 | + |
| 124 | +If you want the filesystem panes to do even more work, swap `cat` for a `diff` against the previous state: |
| 125 | + |
| 126 | +```bash |
| 127 | +# Replace the `cat src/greet.js` call in each watcher with: |
| 128 | +diff -u .prev-greet src/greet.js 2>/dev/null | tail -15 || cat src/greet.js 2>/dev/null |
| 129 | +cp src/greet.js .prev-greet 2>/dev/null |
| 130 | +``` |
| 131 | + |
| 132 | +Now each filesystem pane shows **only the lines that just changed**, in git-diff style (red `-`, green `+`). A sync arrival isn't just "file got newer" — it's "these five lines appeared, this second, on my disk, without me doing anything." |
| 133 | + |
| 134 | +It's a little more mise-en-place but it's the difference between the viewer *seeing* sync happen and the viewer *feeling* it. |
| 135 | + |
| 136 | +### Post-production flash (optional, but makes it pop) |
| 137 | + |
| 138 | +If you're editing the recording after the fact: detect the frame where each filesystem pane's `mtime` changes and add a 200 ms colored border pulse matching that agent's color. Even without a full VFX pass, a simple drop-shadow flash makes the "it appeared" moment land hard. |
| 139 | + |
| 140 | +### Color assignments (use these everywhere) |
| 141 | + |
| 142 | +| Agent | Role | ANSI code | On-screen cue | |
| 143 | +|-------|----------|-------------|--------------------------| |
| 144 | +| A | backend | `\033[31m` | red border, red in feed | |
| 145 | +| B | tests | `\033[34m` | blue | |
| 146 | +| C | frontend | `\033[32m` | green | |
| 147 | +| D | docs | `\033[33m` | yellow | |
| 148 | + |
| 149 | +Stick to this palette. In the sync feed, in the pane borders, in the voiceover callouts. Consistency is what lets a viewer track four things at once for two minutes. |
| 150 | + |
| 151 | +### One-shot setup (paste into a fifth, temporary shell) |
| 152 | + |
| 153 | +```bash |
| 154 | +set -e |
| 155 | +kai version | grep -qE '0\.(1[2-9]|[2-9][0-9])' || { echo "need kai >= 0.12.3"; exit 1; } |
| 156 | + |
| 157 | +# Bare repo that all four agents will sync through kaicontext.com |
| 158 | +rm -rf /tmp/demo-seed /tmp/demo-a /tmp/demo-b /tmp/demo-c /tmp/demo-d |
| 159 | +mkdir /tmp/demo-seed && cd /tmp/demo-seed |
| 160 | +git init -q -b main |
| 161 | +git config user.email demo@demo && git config user.name Demo |
| 162 | +git config commit.gpgsign false |
| 163 | + |
| 164 | +# Scaffolding small enough that every agent fits on one screen |
| 165 | +mkdir -p src tests docs |
| 166 | +cat > src/greet.js <<'EOF' |
| 167 | +// TODO: implement greet(name) |
| 168 | +EOF |
| 169 | +cat > tests/greet.test.js <<'EOF' |
| 170 | +// TODO: tests for greet(name) |
| 171 | +EOF |
| 172 | +cat > docs/greet.md <<'EOF' |
| 173 | +# greet(name) |
| 174 | +
|
| 175 | +TODO: describe |
| 176 | +EOF |
| 177 | +cat > README.md <<'EOF' |
| 178 | +# live-sync demo |
| 179 | +Four agents build greet(name) together. |
| 180 | +EOF |
| 181 | + |
| 182 | +git add -A && git commit -q -m "scaffold" |
| 183 | +kai init # first run: interactive org pick |
| 184 | +kai capture -m "scaffold" |
| 185 | +kai push |
| 186 | + |
| 187 | +# Clone into four working dirs. Each will be one agent's workspace. |
| 188 | +for d in a b c d; do |
| 189 | + cp -r /tmp/demo-seed /tmp/demo-$d |
| 190 | + # Independent kai databases per agent — they meet on the kaicontext server, |
| 191 | + # not on the filesystem. Reset local .kai so each agent initializes fresh. |
| 192 | + rm -rf /tmp/demo-$d/.kai /tmp/demo-$d/.git |
| 193 | + cd /tmp/demo-$d |
| 194 | + git init -q -b main && git config user.email demo@demo && git config user.name Demo |
| 195 | + git config commit.gpgsign false |
| 196 | + git add -A && git commit -q -m scaffold |
| 197 | + # Point origin at the kaicontext repo we just pushed |
| 198 | + KAIREPO=$(cd /tmp/demo-seed && grep -oE 'remote/origin/.*' .kai/db.sqlite 2>/dev/null | head -1 || echo "") |
| 199 | + kai remote set origin $(cd /tmp/demo-seed && kai remote get origin 2>/dev/null || echo "") |
| 200 | + kai fetch origin 2>&1 | tail -1 |
| 201 | +done |
| 202 | + |
| 203 | +echo "=== ready: open 4 Claude Code sessions, one in each /tmp/demo-{a,b,c,d} ===" |
| 204 | +``` |
| 205 | + |
| 206 | +(If the `kai remote set`/`fetch` dance is flaky at your install, just `kai clone` the same kaicontext repo four times into the four paths — the point is each agent ends up pointing at the same remote.) |
| 207 | + |
| 208 | +### Enable live-sync in each Claude session |
| 209 | + |
| 210 | +Open a Claude Code window in each directory, then give each one its *first* prompt (verbatim) so they all join the same sync channel: |
| 211 | + |
| 212 | +``` |
| 213 | +Use kai_live_sync with channel="greet-demo" to join the sync channel, |
| 214 | +then wait for your role prompt. |
| 215 | +``` |
| 216 | + |
| 217 | +All four agents are now on the same channel. Any file they write gets pushed via `/v1/sync/push`, and the other three receive it in <1s. |
| 218 | + |
| 219 | +--- |
| 220 | + |
| 221 | +## The four role prompts |
| 222 | + |
| 223 | +Have these ready in your paste buffer — each one goes into its specific window after Claude acknowledges it's on the `greet-demo` channel. |
| 224 | + |
| 225 | +### Agent A — backend |
| 226 | + |
| 227 | +> You are the backend agent. Your job is to implement `src/greet.js` so the function `greet(name)` returns `` `hi, ${name}` `` when called. No frills. When it's working, call `kai_checkpoint_now` with `label="greet: implementation"` and `assert=tests-pass` (only include the assert if tests actually pass — you can check by reading `tests/greet.test.js` after agent B pushes it). |
| 228 | +
|
| 229 | +### Agent B — tests |
| 230 | + |
| 231 | +> You are the tests agent. Your job is to fill in `tests/greet.test.js` with at least two test cases for `greet(name)`: one for a normal name, one for an empty string. Watch `src/greet.js` — agent A will update it shortly. Once the tests match the implementation, call `kai_checkpoint_now` with `label="greet: tests"`. |
| 232 | +
|
| 233 | +### Agent C — frontend |
| 234 | + |
| 235 | +> You are the frontend agent. Read `src/greet.js` once agent A has updated it, then create `src/App.jsx` that imports `greet` and renders `<h1>{greet("world")}</h1>`. When done, call `kai_checkpoint_now` with `label="greet: frontend wired"`. |
| 236 | +
|
| 237 | +### Agent D — docs |
| 238 | + |
| 239 | +> You are the docs agent. Watch `src/greet.js`, `tests/greet.test.js`, and `src/App.jsx`. As each lands, update `docs/greet.md` with a short description of the function's signature, its behavior, and one call-site example. When all three source files are in place and your docs reference all of them, call `kai_checkpoint_now` with `label="greet: docs"`. |
| 240 | +
|
| 241 | +None of these prompts mentions the other agents by name. They only know the files on disk. Sync makes the choreography work. |
| 242 | + |
| 243 | +--- |
| 244 | + |
| 245 | +## Storyboard |
| 246 | + |
| 247 | +``` |
| 248 | +[0:00 – 0:15] Title card + one-paragraph framing (read voiceover). |
| 249 | + Cut to the 2×2 grid with all four empty Claude prompts. |
| 250 | +
|
| 251 | +[0:15 – 0:30] All four agents get their "join channel" prompt. Each |
| 252 | + one calls kai_live_sync. Subtle hook highlight on the |
| 253 | + MCP tool call in each window. |
| 254 | +
|
| 255 | +[0:30 – 0:45] Paste the four role prompts into their respective |
| 256 | + windows simultaneously. Each Claude starts "thinking." |
| 257 | +
|
| 258 | +[0:45 – 1:15] HERO SHOT. |
| 259 | + Agent A writes src/greet.js. |
| 260 | + In the SYNC FEED: a red "[A → sync]" line appears. |
| 261 | + ~300 ms later: three dim lines appear in the feed |
| 262 | + "[B ← sync] recv src/greet.js" |
| 263 | + "[C ← sync] recv src/greet.js" |
| 264 | + "[D ← sync] recv src/greet.js" |
| 265 | + IN THE SAME FRAME: the filesystem panes of B, C, D |
| 266 | + each flash their border (post-production), and |
| 267 | + src/greet.js appears in their ls -la with a fresh |
| 268 | + mtime. |
| 269 | +
|
| 270 | + THIS IS THE MOMENT. The voiceover shuts up for ~2s. |
| 271 | + Let the viewer watch the file physically arrive on |
| 272 | + three other disks. |
| 273 | +
|
| 274 | + Then agents B, C, D's Claude windows each start |
| 275 | + typing — not because we prompted them, but because |
| 276 | + they noticed the new file on their own tree. |
| 277 | +
|
| 278 | +[1:15 – 1:45] Agents B, C, D finish their pieces. Each fires |
| 279 | + kai_checkpoint_now with a labeled milestone. The |
| 280 | + labels appear in each other's sync activity. |
| 281 | +
|
| 282 | +[1:45 – 2:15] Back to setup shell, run: |
| 283 | + kai activity |
| 284 | + kai ref list | head |
| 285 | + Show the four milestone checkpoints + the four git.HEAD |
| 286 | + snapshots, all within the last two minutes, by four |
| 287 | + different agents. |
| 288 | +
|
| 289 | +[2:15 – 2:30] Payoff card: |
| 290 | + "Four agents. One repo. Zero merge conflicts." |
| 291 | + CTA: get.kaicontext.com |
| 292 | +``` |
| 293 | + |
| 294 | +--- |
| 295 | + |
| 296 | +## Voiceover — 80 words, ~30 seconds at a natural pace |
| 297 | + |
| 298 | +> "One AI agent is fast. Four agents, in theory, should be four times faster. In practice, they fight — race conditions, clobbered files, contradictory edits. So most teams run them one at a time. |
| 299 | +> |
| 300 | +> Kai is a different answer. Four Claude Code agents, same repo, same sync channel. Each one picks up the others' work the second it lands. Semantic merge handles the overlaps. Milestone checkpoints mark who did what, so review is sane. |
| 301 | +> |
| 302 | +> Four agents. One repo. Zero merge conflicts." |
| 303 | +
|
| 304 | +Hit the `[beat]` pauses between sentences. The hero shot in Scene 4 — file appearing on three other screens — is the one the voiceover should **not** compete with. Let the visual carry it. |
| 305 | + |
| 306 | +--- |
| 307 | + |
| 308 | +## What's actually happening under the hood |
| 309 | + |
| 310 | +1. **`kai_live_sync` (MCP tool)** — opens an SSE connection from the agent's kai session to `kaicontext.com/<org>/<repo>/v1/sync/live`. Every file the agent writes is pushed via `/v1/sync/push`; every file the channel receives is applied to the local working tree. |
| 311 | +2. **Merge strategy** — when two agents change the same file, kai runs a semantic 3-way merge against the last common snapshot. For JS/TS/Python/Ruby/Rust/Go that's function-level. For JSON/YAML/Markdown it's a naive line-merge that applies cleanly when the edited regions don't overlap. A true conflict logs to `kai activity` and leaves the local edits preserved. |
| 312 | +3. **`kai_checkpoint_now` (MCP tool)** — emits a labeled milestone on the sync channel. No file write, just a marker. Other agents' activity feeds show it and can use it as a coordination signal. |
| 313 | +4. **No coordination protocol between agents.** There's no mutex, no lock, no chat. Each agent reads the filesystem, writes the filesystem, and kai ferries the bytes and merges when they collide. That's the whole thing. |
| 314 | + |
| 315 | +--- |
| 316 | + |
| 317 | +## Recording notes |
| 318 | + |
| 319 | +- **Pre-record the Claude responses if you need tight timing.** Live LLMs have variable latency; a 30-second response ruins the 2:30 pace. Recording once clean and stitching is fine — the sync itself is the demo, not Claude's speed. |
| 320 | +- **Highlight the sync moments.** When agent A saves `src/greet.js` and it appears on B/C/D, add a brief pulse or border flash on the receiving windows. Viewers need to see that the file arrived *without an agent being told to pull*. |
| 321 | +- **Watch for the `[kai-sync]` lines on stderr.** Kai prints `[kai-sync] merged foo.js (auto-resolved)` when a semantic merge fires. If your demo produces one of these organically, consider cutting to a close-up — it's the unsung money shot. |
| 322 | +- **Don't try to demo a true conflict.** Live merging two competing signature changes is interesting but chaotic on camera. Save it for the 5-minute deep dive. |
| 323 | +- **Dark terminal, ≥ 18pt, high contrast.** The 2×2 grid cuts per-window legibility in half. If the 30-second tweet demo needed 18pt, this one needs 22pt. |
| 324 | + |
| 325 | +## What to say if someone pushes back |
| 326 | + |
| 327 | +- *"Couldn't I just use git branches?"* → Four branches means four merges. Live sync means zero merges, because the merge happens continuously and never accumulates. Different shape of problem. |
| 328 | +- *"This is basically live-share / Figma for code."* → Yes, and the interesting part is that the participants aren't humans reading each other's cursors — they're agents reading each other's *code*. The substrate is the same; the load on it is very different. |
| 329 | +- *"Does it work with more than four agents?"* → Yes. The sync channel is fanout-ish; the limit is whatever the server-side `/v1/sync/live` SSE can handle. We haven't found the ceiling. |
| 330 | +- *"What if an agent writes garbage?"* → Same failure mode as any tool handing code to an agent. Kai tracks authorship so you know which agent wrote what line — filter them out, revert their checkpoint, ban their MCP session. Authorship is the answer here, not prevention. |
| 331 | + |
| 332 | +--- |
| 333 | + |
| 334 | +## Troubleshooting |
| 335 | + |
| 336 | +- **Agent says `kai_live_sync` isn't available.** Your Claude Code `.mcp.json` is missing the kai MCP server entry. Run `kai mcp install` (if the command exists in your version) or manually add: |
| 337 | + ```json |
| 338 | + { |
| 339 | + "mcpServers": { |
| 340 | + "kai": { "command": "kai", "args": ["mcp", "serve"] } |
| 341 | + } |
| 342 | + } |
| 343 | + ``` |
| 344 | +- **Files don't appear on other agents' disks.** Each agent must be on the same channel name. Run `kai ref list | grep sync` to confirm. If channels differ, re-prompt each agent with the same `channel="greet-demo"`. |
| 345 | +- **Merge conflict messages on console.** Expected — kai logs conflicts for visibility. Local edits are preserved; re-prompt the affected agent to reconcile against the on-disk state. |
| 346 | +- **Usage: 140 / 150 agent sync events.** Each agent write is one sync event. Four agents editing actively will burn through free-tier budget in a long demo. Upgrade or set `KAI_TELEMETRY=0` for recording. |
0 commit comments