Human Guides / Agent Lifecycle
Operator Lifecycle
Plan-Phase Questions
Third-Party (Supabase)
Error + Verifier Agent
The Setup
What you see in cmux
One workspace. Operator on top. Agents as splits below. Each agent is a real separate Claude Code process.
cmux — workspace:9 — ay-framework
workspace:9
Outreach
walid's session
aylm-ui
walid's session
◈ ay-framework
conductor active
◈ OPERATOR surface:23 · main · /ay-framework
CONDUCTING
# Operator boots — reads BOARD.md, sees 4 ready tasks
→ Task 03 assigned to canvas-agent (surface:24)
→ Task 07 assigned to toolbar-agent (surface:40)
→ Task 09 assigned to collab-agent (surface:41)
→ Task 12 assigned to export-agent (surface:42)
# fleet-scan running every 4 min...
✓ all 4 agents WORKING · no blocks
canvas-agent surface:24 · agent/canvas
WORKING
# Task 03: Canvas setup + pan/zoom
# Self-inventory: Read/Write/Bash/Agent ✓
# Locked task-03.lock
✶ Thinking… (1m 12s)
Installing fabric@6.3.0...
Creating src/components/Canvas.tsx
✻ Working… (2m 34s)
toolbar-agent surface:40 · agent/toolbar
WORKING
# Task 07: Select/move/resize tools
# Self-inventory: Read/Write/Bash/Agent ✓
# Locked task-07.lock
Reading canvas-agent HANDOFF...
→ Using object.id (not index) as noted
✻ Working… (48s)
collab-agent surface:41 · agent/collab
BLOCKED
# Task 09: WebSocket + Yjs sync
# Self-inventory done · lock claimed
⚠ Need decision before continuing:
WebSocket provider choice affects architecture.
Option A: y-websocket (self-hosted, free)
Option B: Liveblocks (managed, $25/mo)
Writing to BLOCKERS.md...
Do you want to proceed? [Y/n]
export-agent surface:42 · agent/export
DONE
# Task 12: PNG export
✓ Canvas-to-blob implemented
✓ Download button wired
✓ Tests pass (3/3)
HANDOFF written → all
Lock released · PR ready
❯ _
👁
collab-agent is BLOCKED — fleet-scan detected it (exit code 2). The operator sees this within 4 minutes and needs to answer the WebSocket question before the agent can continue. This is one of the mandatory architectural decisions that can't be automated.
The Full Lifecycle
Every agent goes through the same states
The operator drives the transitions, not the agent.
Operator
dispatches
sends task + context via cmux
Agent
self-inventory
checks tools, context, constraints
Agent
claims lock
writes task-N.lock
Agent
builds
spawns own subagents if needed
Agent
self-verify
reads output, runs tests
Agent
handoff
writes HANDOFFS.md, releases lock
Operator
reviews
drift check → PR create → merge
Agents spawning subagents: An agent is not limited to doing work itself. canvas-agent might spawn 3 subagents in parallel to build rect, ellipse, and text tools simultaneously — then collect results and integrate them. The operator doesn't see or control those sub-workers directly; it only sees the agent's final output.
What the Operator Actually Sends
Real conversation with each agent
The operator sends a single detailed prompt per agent. Then it watches and only interrupts when needed.
OPERATOR → canvas-agent (surface:24) You are canvas-agent. Your task is Task 03: Canvas setup + pan/zoom.

Read your task spec: cat $AY/tasks/03-canvas-setup.md
Read HANDOFFS.md for context from other agents.

Build: HTML Canvas with Fabric.js, pan (spacebar+drag), zoom (ctrl+wheel), grid background. Output to src/components/Canvas.tsx.

Claim lock before writing any code. Release when done. Write HANDOFF with: canvas element API, object ID pattern, any gotchas for toolbar-agent.
4 minutes later — fleet-scan tick
OPERATOR → collab-agent (surface:41) — answering block Use y-websocket (self-hosted). Add it to DECISIONS.md: "chose y-websocket over Liveblocks to avoid vendor lock-in and $25/mo cost." Server should run on port 1234. Export yjsProvider from lib/collab.ts for canvas-agent to import.
collab-agent → HANDOFFS.md (operator reads it) Task 09 done. y-websocket server at ws://localhost:1234. Y.Doc key is "canvas". yjsProvider exported from lib/collab.ts — canvas-agent should import this, not create its own Y.Doc. Presence cursors: each client needs a clientId (use nanoid on mount).
OPERATOR → canvas-agent (surface:24) — forwarding unblock collab-agent finished. Import yjsProvider from lib/collab.ts for real-time sync. clientId = nanoid() on canvas mount. See HANDOFFS.md for full details.
The operator never touches code. It reads HANDOFFS.md, decides which agents need to know what, and forwards the relevant info. This is the coordination layer — not email, not Slack, just files in .ay/.
Why This Matters
If you don't ask upfront, every agent will block
Imagine 5 agents all pausing and asking "what framework do you want?" separately. The operator collects every decision into a single conversation before spawning anyone.
The bad pattern: Agent boots → builds for 20 minutes → discovers it needs to know the auth provider → stops → writes to BLOCKERS.md → operator unblocks → agent starts over. Time wasted: 20+ minutes per agent.
The good pattern: Operator asks ALL questions before spawning agents. Every agent gets a complete task spec with all decisions already made. Zero blocks from ambiguity.
The First Contact Conversation
Operator collects everything in one shot
You Build me a Figma-like design tool.
Operator (First Contact) Got it — analyzing what's needed. Before I spawn agents and create tasks, I need your answers on a few things that will affect every agent's work. Answer these now and nothing will block later.

Tech stack decisions (agents can't guess these):
1. Frontend framework? Next.js / React (Vite) / SvelteKit / Vue
2. Styling? Tailwind / CSS Modules / styled-components
3. TypeScript? Yes / No

Infrastructure (determines entire architecture):
4. Database? Supabase / Planetscale / SQLite / None yet
5. Auth provider? Supabase Auth / Clerk / NextAuth / Custom
6. Real-time collaboration? Yes (which provider?) / No / Later

Deployment (affects how agents set up config):
7. Where will this run? Vercel / Railway / self-hosted / local only
8. Any existing accounts/API keys I should know about?
You Next.js 14, Tailwind, TypeScript. Supabase for DB + auth. Real-time yes, use Liveblocks. Deploy to Vercel. I have a Supabase project already — URL and anon key in .env.
Operator Perfect. Writing decisions to .ay/tracking/DECISIONS.md...

Now I'll ask the task-level questions. These affect task ordering:

Scope decisions:
9. Comments on designs? (adds 2 tasks, collab complexity)
10. Version history / undo past sessions? (adds 3 tasks)
11. Export formats beyond PNG? SVG, PDF, or just PNG?
12. Team/organization accounts or solo only for now?
You No comments yet. No version history. PNG only. Solo accounts first.
Operator All decisions recorded. Creating 18 tasks (not 26 — scope reduced). Spawning 5 agents now. Every agent's task spec already includes: Next.js 14, Tailwind, TS, Supabase URL from .env, Liveblocks for real-time, Vercel deployment target.

No agent will ever need to ask you a question about framework or infrastructure. Zero blocks from ambiguity guaranteed.
The Decision Record
What gets written to DECISIONS.md before any agent runs
.ay/tracking/DECISIONS.md
# Architecture Decisions — my-design-tool

## ADR-001: Frontend Stack
Decision: Next.js 14 (App Router), TypeScript, Tailwind CSS
Reason: user preference. All agents must use this stack.
Impact: canvas-agent, toolbar-agent, shell-agent — all components use .tsx

## ADR-002: Database + Auth
Decision: Supabase (existing project)
NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY already in .env
Reason: user has existing Supabase project. Do not create new one.
Impact: shell-agent handles auth using @supabase/ssr package.

## ADR-003: Real-time Collaboration
Decision: Liveblocks (managed WebSocket + presence)
Reason: user confirmed, willing to pay managed cost
Impact: collab-agent uses @liveblocks/react. API key needed — operator to provide.

## ADR-004: Scope (v1)
PNG export only. No comments. No version history. Solo accounts.
Reason: user explicitly scoped down. Do not implement excluded features.
Why this is powerful: Every agent reads DECISIONS.md as part of its task spec. When toolbar-agent needs to import something from collab-agent, it already knows it's Liveblocks, not Yjs. When shell-agent sets up auth, it already knows to use the existing Supabase keys from .env. Zero back-and-forth during the build phase.
What Happens Without This
The blocking cascade — when you skip plan questions
shell-agent boots
starts building auth...
↓ 20 min later
BLOCKED
"Which auth provider? Supabase, Clerk, NextAuth?"
↓ operator answers
BLOCKED again
collab-agent: "y-websocket or Liveblocks?"
↓ operator answers
BLOCKED again
export-agent: "Vercel or Railway for deployment config?"
↓ 3 separate blocks = 3× the time
finally building
1 hour wasted on questions
The Pattern
Agent can't create API keys — but it can tell you exactly what it needs
shell-agent → BLOCKERS.md BLOCKED: Task 02 (auth setup) cannot proceed.

What I need:
• NEXT_PUBLIC_SUPABASE_URL — your Supabase project URL (Settings → API)
• NEXT_PUBLIC_SUPABASE_ANON_KEY — anon/public key (same page)
• SUPABASE_SERVICE_ROLE_KEY — service_role key (same page, keep secret)

What to do: Add these 3 lines to .env.local in the project root. I've created the file with placeholder values — just replace them.

I will not proceed until .env.local has real values. I'll detect this by checking if SUPABASE_URL starts with "https://".
Operator → You (surfacing the block) shell-agent needs 3 Supabase credentials before it can build auth. It created .env.local with placeholders.

Go to Supabase → Settings → API and add your values. I'll watch for the file to update and unblock the agent automatically.
You Done — added all 3 keys to .env.local
Operator → shell-agent (surface:24) .env.local has been updated with real Supabase credentials. Resume Task 02 — run your credential check first, then continue.
shell-agent (resumes) ✓ SUPABASE_URL detected: https://xxxx.supabase.co
✓ ANON_KEY detected: eyJ...
Running: npx supabase gen types typescript...
Building auth flow with @supabase/ssr...
How the Agent Detects It's Missing
Agents check before they try — they don't fail silently
shell-agent credential check (part of self-inventory)
# Step 1 of self-inventory: check what I have
$ cat .env.local 2>/dev/null | grep SUPABASE

NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_ANON_KEY

→ Credentials are placeholder values. Cannot build auth.
→ Writing to BLOCKERS.md...
→ Switching to Task 01 (app shell) which has no credential dependency.
# Agent picks up unblocked work instead of sitting idle
The smart move: A good agent doesn't just block and wait. It detects the missing credential, documents exactly what's needed, then picks up a different task that has no dependencies. When the credential arrives, it picks up where it left off.
Multiple Third-Party Services
Operator collects ALL credential needs at plan time
Operator pre-flight check — before spawning any agents
OPERATOR: Scanning task list for external service dependencies...

Tasks that need credentials or accounts:
Task 02 (auth) → SUPABASE_URL, SUPABASE_ANON_KEY, SERVICE_ROLE_KEY
Task 09 (collab) → LIVEBLOCKS_SECRET_KEY
Task 14 (email) → RESEND_API_KEY
Task 18 (storage) → SUPABASE_STORAGE_BUCKET (name only)

Tasks with NO external dependencies (can run immediately):
Task 01, 03, 04, 05, 06, 07, 08, 10, 11, 12, 13

→ Created .env.local.template with all required keys
→ Spawning agents for credential-free tasks now (11 tasks in parallel)
→ Surfacing credential list to user in one message
Error Type 1
Agent drifts — says "done" but produced no commits
canvas-agent (surface:24) Task 03 complete. I've implemented the canvas with pan/zoom, grid background, and Fabric.js integration. All components are in place and the implementation is ready.
Operator (drift check) Running drift check on agent/canvas branch...
Operator — DRIFT DETECTED git log main..HEAD --onelineempty
git status --shortnothing to commit

Agent claimed completion but produced ZERO commits. It may have:
• Written only to .ay/ files (gitignored — won't appear in git)
• Described what it would build without actually building it
• Had its changes lost due to a context reset
Why this happens: Agents near context limit sometimes start describing work instead of doing it. Or they write extensively to .ay/tracking/AGENTS-LOG.md (which feels like "being productive") but that file is gitignored — so git sees nothing.
Operator — spawning verifier agent cmux new-split right --workspace workspace:9
cd .claude/worktrees/canvas && claude --permission-mode bypassPermissions
Operator → verifier-agent (surface:60) — first message You are a VERIFIER. canvas-agent claimed to complete Task 03 (Canvas + pan/zoom). Your job is to find out what they actually built — and what's missing.

Check these specifically:
1. Does src/components/Canvas.tsx exist?
2. Does it import and initialize Fabric.js?
3. Is there pan logic (spacebar + drag)?
4. Is there zoom logic (ctrl+wheel)?
5. Run git log main..HEAD --oneline — any commits?
6. Run npx tsc --noEmit — any type errors?

Report exactly what exists and what does not. Do NOT fix anything. Just verify.
verifier-agent (surface:60) — verdict Verification complete. Here's what actually exists:

src/components/Canvas.tsxdoes not exist
git log main..HEAD0 commits
package.json — fabric@6.3.0 IS listed (was added)
❌ No pan logic implemented anywhere
❌ No zoom logic implemented anywhere

What canvas-agent did: installed the package and wrote a detailed plan in .ay/plans/03-canvas-plan.md. The plan is good. Nothing was built.
Operator → canvas-agent (re-dispatch) Verifier found zero code was written. You have a good plan in .ay/plans/03-canvas-plan.md — follow it.

Required output: src/components/Canvas.tsx must exist with a real Fabric.Canvas instance, pan handler, zoom handler. Commit this file before reporting done.

Do NOT write to .ay/ files. Only code counts. Run git log main..HEAD --oneline yourself before telling me you're done — if it's empty, you're not done.
Error Type 2
Agent built something — but it's wrong
toolbar-agent — done report Select tool implemented. Objects can be selected and moved. Resize handles appear on selection. Task 07 complete.
Operator — spawning adversarial verifier Not using the same agent to self-check. Spawning an independent verifier whose job is to find problems, not confirm success.
Operator → verifier-agent (surface:60) toolbar-agent says Task 07 (select/move/resize) is done. Your job is to find everything wrong with it. Try to break it:

1. Select an object — does a blue outline appear?
2. Move it — does it snap back or drift?
3. Resize from corner — do all 8 handles work?
4. Multi-select (shift+click) — does it work?
5. Select then delete — does it clear correctly?
6. Does it use object.id or object index? (canvas-agent's HANDOFF said use .id)

Find bugs. Don't confirm success — assume there are problems until proven otherwise.
verifier-agent — bugs found Found 3 issues:

🔴 BUG 1 — Using canvas.getObjects()[idx] instead of object.id. canvas-agent's HANDOFF explicitly said to use .id because indices change on reorder. This will break when layers are reordered.

🟡 BUG 2 — Multi-select (shift+click) selects correctly but delete key only removes the first selected object, not all selected.

🟡 BUG 3 — Resize handles are visible but the bottom-right corner handle has a 2px offset — feels wrong on interaction.

✓ Move works. ✓ Single select works. ✓ Blue outline correct.
Operator → toolbar-agent (with exact fixes) Verifier found 3 issues before I accept this task. Fix these specifically:

1. Replace all canvas.getObjects()[idx] with target by object.id
2. Delete key should call canvas.getActiveObjects().forEach(o => canvas.remove(o))
3. Debug the bottom-right resize handle — 2px offset

Run your own tests after. Verifier will check again before this merges.
The Power of Independent Verification
Why a separate verifier agent beats self-review every time
Advantage 1
No rationalization
The agent that built something has context about WHY it did things. A verifier has none of that context — it just sees what's there. It doesn't "know" the intent so it can't rationalize away a bug.
Advantage 2
Fresh context window
A builder agent near context limit starts making assumptions. The verifier starts completely fresh. It re-reads everything from scratch with full attention.
Advantage 3
Adversarial by design
You tell the verifier to FIND problems, not confirm success. This is the opposite instruction. A self-reviewing agent is told "check your work" — which tends to confirm what it already believes.
Advantage 4
Parallel to building
While agent A is verifying agent B's work, agent C can already be building task 2. Verification doesn't add to the wall-clock time when you have parallel agents available.
The loop: Build → Verifier → Bugs found → Fix → Verifier again → Pass → Operator merges. This is not slower than "build and trust" — because "build and trust" produces bugs that are found much later, at integration time, when they cost 10× more to fix.
Complete Error Handling Matrix
Every error type and what the operator does
Operator error-handling playbook
ERROR: Agent drifts (no commits)
detect: git log main..HEAD --oneline == empty
fix: spawn verifier → confirm nothing built → re-dispatch with explicit output path

ERROR: Agent stuck on permission prompt
detect: fleet-scan exit 2, screen shows "Do you want to proceed?"
fix: cmux send --surface X "2" (allow all edits, no enter needed)

ERROR: Agent blocked on missing credential
detect: BLOCKERS.md has new entry, agent switched to other task
fix: surface to user in one message → user provides key → unblock agent

ERROR: Agent produced wrong output
detect: verifier finds bugs or spec mismatch
fix: send exact bug list to original agent → re-verify after fix

ERROR: Agent context exhausted mid-task
detect: agent wrote checkpoint.md and went idle, or session ended
fix: claude --resume {session-id} in same worktree pane

ERROR: Two agents edited same file (conflict)
detect: git merge conflict markers in PR
fix: check locks/ — one agent violated scope. Resolve manually. Update scope boundaries.

ERROR: Agent's subagent failed silently
detect: agent reported success but verification fails on a subsystem
fix: read agent's subagent transcript at ~/.claude/projects/.../subagents/agent-N.jsonl