Skip to content

Commit 3ba26bd

Browse files
committed
docs: 4-agent live-sync demo script with visible-change staging
New demo doc for the 'four Claude Code agents in one repo' scenario. Core cleverness is in how the recording is staged: each agent cell is split into a Claude pane (top) + a filesystem watcher pane (bottom) that redraws every 300ms. A fifth 'sync feed' pane shows every [A → sync] / [B ← sync] line with the agent's color. Optional diff- based watcher shows the exact lines that just arrived. Without this staging, sync is invisible — the receiving agent's screen stays blank until its Claude re-reads the tree. With it, the viewer sees the file physically arrive on three other disks ~300ms after one agent writes it. That's the hero shot.
1 parent 8559a9d commit 3ba26bd

1 file changed

Lines changed: 346 additions & 0 deletions

File tree

docs/demo-livesync.md

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
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

Comments
 (0)