# Will Chen - Full Content > Expanding human agency through computational thinking. ## About Will Chen builds tools and frameworks that expand human agency through computational thinking. The core thesis: computation is the universal language for describing mechanisms—like category theory for mathematics, but for mental models and thought. Not metaphor, but fundamental substrate. If a system transforms inputs to outputs through rules, we have access to the entire toolkit: decomposition, abstraction, state management, resource allocation. These principles apply whether the system is silicon, neurons, organizations, or your own behavior. ## Current Work Running Idyllic Labs (https://idyllic.so): an independent research lab for programmable intelligence. Designing composable primitives and legible representations for malleable AI systems. ## Key Theses - Computation as Core Language: Why computational thinking applies to everything from physics to personal behavior (https://mechanisticmindset.com/wiki/computation-as-core-language) - Ladder of Agency: How agency climbs through demonstrated mastery, not granted permission (https://mechanisticmindset.com/wiki/ladder-of-agency) - Moralizing vs Mechanistic: Why "lazy" is debugging failure, not character flaw (https://mechanisticmindset.com/wiki/moralizing-vs-mechanistic) - Signal Boosting: The fundamental algorithm: generate → evaluate → select (https://mechanisticmindset.com/wiki/signal-boosting) ## Projects ### Active - Idyllic Labs: Independent research lab for programmable intelligence (https://idyllic.so) - Elements of Agentic System Design: Decomposing intelligent behavior into code primitives (https://wcdc.io/projects/elements) ### Personal - Mechanistic Mindset: A wiki on computational self-engineering (https://mechanisticmindset.com) - AI Organs: Local-first AI tools and agentic workflows for human augmentation (https://wcdc.io/projects/ai-organs) ### Background - Head of DevX @ Terra - Terran One WASM Research Lab (founder) - Compilers, smart contracts, developer tools ## Links - Website: https://wcdc.io - Writing: https://wcdc.io/writing - RSS Feed: https://wcdc.io/feed.xml - GitHub: https://github.com/ouiliame - Twitter: https://x.com/stablechen - LinkedIn: https://www.linkedin.com/in/will-chen-3a71ba16b/ ## Contact - Twitter DM: https://x.com/stablechen - Email: will@wcdc.io --- # Writing --- ## OpenClaw Through the 10 Elements Lens URL: https://wcdc.io/writing/openclaw-vs-cc Date: 2026-01-31 Description: A technical decomposition of the viral AI agent. ## What Is OpenClaw? ![OpenClaw header](/assets/openclaw-header.png) OpenClaw (previously Moltbot, previously Clawdbot) is an open-source AI agent created by Peter Steinberger. It runs as a persistent daemon on your machine, connecting to messaging apps like WhatsApp, Telegram, Discord, iMessage, and Slack so you can interact with it from your phone. It can execute shell commands, browse the web, manage files, and run scheduled tasks autonomously. The project exploded in popularity in January 2026 after Federico Viticci's MacStories piece went viral. It's been credited with a 14%+ spike in Cloudflare stock when they released infrastructure to support it. It now has a Wikipedia page and has been covered by Fast Company, Fortune, Platformer, and IBM. When asked why OpenClaw is better than just using Claude Code or another tool, one early adopter put it this way: > "I've been battling people about this opinion and I got to a point where like, fuck it. If you don't see it, you don't see it. Like there are people who've been trying to shove this in people's faces like, look at this, it's so productive. But for some people I think the FOMO is preventing them from seeing it clearly and they're going to find these nitpicks like 'oh I can just do this with Claude Code.' And the answer is like, fine, do it in the worst way. Like I don't know what I can say... If someone listens to all of this and concludes that this is not worth it, there's nothing you can say to convince them." > — Kitze (@thekitze) on the Startup Ideas Podcast That's a bit vague. This post tries to explain what's technically different about OpenClaw through the 10 Elements—a design space map for agentic systems that covers Context, Memory, Agency, Reasoning, Coordination, Artifacts, Autonomy, Evaluation, Feedback, and Learning. --- ## The Core Difference from Claude Code | | Claude Code | OpenClaw | |---|---|---| | **Lifecycle** | Invoked per task, then exits | Runs continuously as daemon | | **Persistence** | Session-based | Files + transcripts persist across restarts | | **Interface** | Terminal | Messaging apps (WhatsApp, Telegram, etc.) | | **Proactivity** | None | Heartbeat checks, cron jobs, webhooks | | **Installation** | `npm install` | LaunchAgent (macOS) or systemd (Linux) | Claude Code is a tool you invoke. OpenClaw is a background process that stays running and can act without being prompted. --- ## Why This Architecture Matters The capability list (shell access, web browsing, file editing, scheduling) isn't novel. You could build all of this with Claude Code plus cron plus some scripts. The difference is activation energy. ### Lowered Activation Cost You'll have random automation ideas throughout your day: - "At 2pm, remind me to go to the gym" - "At 5pm, if I haven't gone yet, escalate and hold me accountable" - "Collect daily summaries of what I worked on" - "Monitor this RSS feed and alert me when competitors ship" With Claude Code, you'd need to spin it up, write a script, set up cron, manage state, handle errors. Each automation is a small project. With OpenClaw, you just tell it. The agent figures out the implementation, persists the state, handles the scheduling, and self-heals if something breaks. This is "vibe automations." You throw ideas at the system without caring about longevity or robustness. The heartbeat catches failures. The transcripts provide audit trails. The multi-agent spawning handles parallelism. You don't architect; you accumulate. ### Unified Control Plane All your automations live in one system. The cron jobs, the heartbeat checklist, the scheduled tasks, the message handlers are all visible in the same place. Even though OpenClaw operates in terminal mode, your `~/.openclaw/` directory gives you a userland view of your entire automation surface. Compare this to having 15 Zapier zaps, 3 n8n workflows, 2 cron scripts, and a Make.com automation, all with different interfaces, different failure modes, different mental models. ### Self-Healing and Parallel Capacity The agent can spawn sub-agents for parallel work. The heartbeat system catches when things drift. The memory flush prevents context window death mid-task. This isn't about capability; it's about not babysitting. With Claude Code, you're watching the terminal. With OpenClaw, you check your phone when it messages you. ### Meta-Optimization Some users are experimenting with giving OpenClaw autonomy to design its own enforcement strategies. For example: "Your goal is my gym adherence. Evaluate yourself based on how well I comply and how annoying I find your reminders. Optimize." The agent becomes an embodied forcing function that iterates on its own approach. --- ## The 10 Elements Decomposition The 10 Elements of Agentic System Design is a framework for understanding the design decisions that shape any AI agent. Each element represents a distinct capability or subsystem that agents need to operate effectively. By examining how a system implements each element, you can quickly understand its architecture and tradeoffs. ### Element 1: CONTEXT *What information does the agent have access to during each reasoning step? Context is everything the model "sees" when making decisions.* The system prompt is assembled dynamically from multiple sources: - Base system prompt (hardcoded) - Identity files: `SOUL.md`, `USER.md`, `AGENTS.md`, `TOOLS.md` - Skills prompts from workspace directories - Session history from JSONL transcripts - Memory files: `MEMORY.md` and `memory/YYYY-MM-DD.md` Here's the code that loads context files (`src/agents/system-prompt.ts`): ```typescript const contextFiles = params.contextFiles ?? []; if (contextFiles.length > 0) { const hasSoulFile = contextFiles.some((file) => { const normalizedPath = file.path.trim().replace(/\\/g, "/"); const baseName = normalizedPath.split("/").pop() ?? normalizedPath; return baseName.toLowerCase() === "soul.md"; }); lines.push("# Project Context", "", "The following project context files have been loaded:"); if (hasSoulFile) { lines.push( "If SOUL.md is present, embody its persona and tone. Avoid stiff, generic replies; follow its guidance unless higher-priority instructions override it.", ); } lines.push(""); for (const file of contextFiles) { lines.push(`## ${file.path}`, "", file.content, ""); } } ``` The identity files are modular system prompt fragments. `SOUL.md` defines personality and tone, `USER.md` describes who you are, `AGENTS.md` contains operating instructions, and `TOOLS.md` provides tool usage guidelines. **Pre-compaction memory flush:** When approaching context limits, the system prompts the model to write important information to disk before compaction. This addresses the "forgetting mid-conversation" problem. Default trigger is 4000 tokens before the limit. The check (`src/auto-reply/reply/memory-flush.ts`): ```typescript export function shouldRunMemoryFlush(params: { entry?: Pick; contextWindowTokens: number; reserveTokensFloor: number; softThresholdTokens: number; }): boolean { const totalTokens = params.entry?.totalTokens; if (!totalTokens || totalTokens <= 0) return false; const contextWindow = Math.max(1, Math.floor(params.contextWindowTokens)); const reserveTokens = Math.max(0, Math.floor(params.reserveTokensFloor)); const softThreshold = Math.max(0, Math.floor(params.softThresholdTokens)); const threshold = Math.max(0, contextWindow - reserveTokens - softThreshold); if (threshold <= 0) return false; if (totalTokens < threshold) return false; const compactionCount = params.entry?.compactionCount ?? 0; const lastFlushAt = params.entry?.memoryFlushCompactionCount; if (typeof lastFlushAt === "number" && lastFlushAt === compactionCount) { return false; } return true; } ``` --- ### Element 2: MEMORY *How does the agent persist information beyond a single context window? Memory bridges the gap between sessions and enables long-term continuity.* OpenClaw uses the filesystem as its source of truth: ``` ~/.openclaw/workspace/ ├── MEMORY.md # Long-term curated notes ├── SOUL.md # Agent personality/tone ├── USER.md # Information about you ├── AGENTS.md # Operating instructions ├── TOOLS.md # Tool usage guidelines ├── HEARTBEAT.md # Checklist for periodic checks └── memory/ ├── 2026-01-28.md # Daily log (append-only) ├── 2026-01-29.md └── ... ``` Session transcripts are stored separately as JSONL: ``` ~/.openclaw/agents//sessions/.jsonl ``` **Vector search:** Memory is indexed via SQLite with the `sqlite-vec` extension. Hybrid search combines semantic (vector) similarity with BM25 keyword matching. Here's the hybrid merge (`src/memory/hybrid.ts`): ```typescript export function mergeHybridResults(params: { vector: HybridVectorResult[]; keyword: HybridKeywordResult[]; vectorWeight: number; textWeight: number; }): Array<{ path: string; startLine: number; endLine: number; score: number; snippet: string; source: HybridSource; }> { const byId = new Map(); for (const r of params.vector) { byId.set(r.id, { /* ... */ vectorScore: r.vectorScore, textScore: 0 }); } for (const r of params.keyword) { const existing = byId.get(r.id); if (existing) { existing.textScore = r.textScore; } else { byId.set(r.id, { /* ... */ vectorScore: 0, textScore: r.textScore }); } } const merged = Array.from(byId.values()).map((entry) => { const score = params.vectorWeight * entry.vectorScore + params.textWeight * entry.textScore; return { /* ... */ score }; }); return merged.sort((a, b) => b.score - a.score); } ``` The database is stored at `~/.openclaw/memory/.sqlite`. --- ### Element 3: AGENCY *What actions can the agent take in the world? Agency is the transition from producing text to causing effects—the tools and capabilities available.* OpenClaw's action space includes: - **Shell execution**: bash commands with PTY support - **Browser control**: dedicated Chromium instance, screenshots, form filling, navigation - **Canvas**: local HTTP server (port 18793) for HTML output - **Nodes**: device capabilities from connected phones/laptops (camera, screen recording, location, notifications) - **Messaging**: send messages to any connected channel - **Cron/webhooks**: schedule tasks, receive external triggers - **Session coordination**: spawn sub-agents, send messages between sessions **Three-layer security model:** 1. **Sandbox runtime**: Controls WHERE tools run - Modes: `off`, `non-main`, `all` - Default: main session runs on host, group chats run in Docker 2. **Tool policy**: Controls WHICH tools are available - Allow/deny lists per agent, per profile 3. **Elevated approvals**: Exec escape hatch for sandboxed sessions - Stored in `~/.openclaw/exec-approvals.json` Here's how sandbox mode is resolved (`src/agents/sandbox.ts`): ```typescript export function resolveSandboxRuntimeStatus(params: { cfg?: OpenClawConfig; sessionKey?: string; }): { agentId: string; sessionKey: string; mainSessionKey: string; mode: SandboxConfig["mode"]; sandboxed: boolean; toolPolicy: SandboxToolPolicyResolved; } { const sessionKey = params.sessionKey?.trim() ?? ""; const agentId = resolveSessionAgentId({ sessionKey, config: params.cfg }); const sandboxCfg = resolveSandboxConfigForAgent(params.cfg, agentId); const mainSessionKey = resolveMainSessionKeyForSandbox({ cfg: params.cfg, agentId }); const sandboxed = sessionKey ? shouldSandboxSession( sandboxCfg, resolveComparableSessionKeyForSandbox({ cfg: params.cfg, agentId, sessionKey }), mainSessionKey, ) : false; return { agentId, sessionKey, mainSessionKey, mode: sandboxCfg.mode, sandboxed, toolPolicy: resolveSandboxToolPolicyForAgent(params.cfg, agentId), }; } ``` [![OpenClaw conversation demo](https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4FxN935AgINZ8953WSswKB/e52d3eb268aa0732c5e6aa64a8e2adba/image6.png)](https://blog.cloudflare.com/moltworker-self-hosted-ai-agent/) *OpenClaw conversation interface. Source: [Cloudflare Blog](https://blog.cloudflare.com/moltworker-self-hosted-ai-agent/)* --- ### Element 4: REASONING *How does the agent handle multi-step problems? Reasoning is the control flow—whether it plans, searches, or iterates.* OpenClaw uses a standard agent loop: 1. Receive message 2. Assemble context (system prompt + history + memory search results) 3. Call model 4. If tool call: execute tool, add result to context, go to 3 5. Stream response to user 6. Persist to transcript The core execution happens in `src/agents/pi-embedded-runner.ts`. The `runEmbeddedPiAgent` function handles queueing, model resolution, authentication, context window management, and failover. No tree search, no explicit planning phase, no generation/evaluation split. The model decides what to do, does it, observes the result, and decides again. --- ### Element 5: COORDINATION *How do multiple agents or components work together? Coordination covers delegation, communication, and shared state.* OpenClaw supports hierarchical multi-agent coordination: - Parent spawns child via `sessions_spawn` tool - Child runs in isolated session with its own transcript - Child announces result back to parent's channel when done - `sessions_send` enables peer-to-peer messaging between sessions **Constraint: Sub-agents cannot spawn sub-agents.** Here's the enforcement (`src/agents/tools/sessions-spawn-tool.ts`): ```typescript const requesterSessionKey = opts?.agentSessionKey; if (typeof requesterSessionKey === "string" && isSubagentSessionKey(requesterSessionKey)) { return jsonResult({ status: "forbidden", error: "sessions_spawn is not allowed from sub-agent sessions", }); } ``` This prevents runaway agent multiplication. The coordination topology is always a tree, never a graph. Agents can also coordinate through shared filesystem access—one agent writes to workspace files, another reads. --- ### Element 6: ARTIFACTS *What structured outputs does the agent produce? Artifacts are persistent, addressable objects the agent creates—files, documents, visualizations.* OpenClaw produces several types of persistent outputs: - **Canvas**: HTTP server serves HTML/CSS/JS from workspace with live-reload. Each session gets its own directory. - **Workspace files**: Any file the agent writes becomes a persistent artifact - **Session transcripts**: JSONL files preserving full interaction history No built-in versioning—files are files. If you want version history, use git. [![OpenClaw canvas and file output](https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1phWt3cVUwxe9tvCYpuAW3/97f456094ede6ca8fb55bf0dddf65d5b/image10.png)](https://blog.cloudflare.com/moltworker-self-hosted-ai-agent/) *Canvas output with live preview. Source: [Cloudflare Blog](https://blog.cloudflare.com/moltworker-self-hosted-ai-agent/)* --- ### Element 7: AUTONOMY *Can the agent act without being prompted? Autonomy is self-directed operation—scheduled tasks, proactive monitoring, event-driven triggers.* OpenClaw provides three mechanisms for self-directed operation: **1. Cron (precise scheduling)** Jobs stored at `~/.openclaw/cron/jobs.json`: ```json { "id": "daily-standup", "schedule": "0 9 * * 1-5", "task": "Check my calendar and Slack for today's priorities", "session": "isolated" } ``` Standard cron expressions with timezone support. Can run in main session (with full context) or isolated session (fresh start). **2. Webhooks (external triggers)** HTTP endpoints for external systems to trigger agent runs. Token authentication required. Enables integration with GitHub, Zapier, home automation, etc. **3. Heartbeat (context-aware periodic checking)** Default interval of 30 minutes (`src/auto-reply/heartbeat.ts`): ```typescript export const DEFAULT_HEARTBEAT_EVERY = "30m"; ``` The heartbeat runs in the main session with full context. The agent reads `HEARTBEAT.md` and decides: does anything need attention? If nothing needs attention, it returns `HEARTBEAT_OK` silently (no notification). If something does, it alerts you through your configured channel. The distinction between cron and heartbeat: cron runs isolated tasks at exact times, heartbeat runs with full context and makes judgment calls about what matters. **4. Lobster (workflow DSL)** For more complex deterministic workflows, OpenClaw includes Lobster, a DSL for multi-step pipelines with explicit approval gates and resumable states. You define pipelines in YAML/JSON as `.lobster` files, making workflows auditable and replayable. Useful when you want predictable execution rather than LLM improvisation. [![OpenClaw task execution example](https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6nJY7GOCopGnMy4IY7KMcf/0d57794df524780c3f4b27e65c968e19/image5.png)](https://blog.cloudflare.com/moltworker-self-hosted-ai-agent/) *Task execution via messaging interface. Source: [Cloudflare Blog](https://blog.cloudflare.com/moltworker-self-hosted-ai-agent/)* --- ### Element 8: EVALUATION *How does the system measure whether it's working correctly? Evaluation covers quality metrics, success criteria, and verification.* Minimal built-in evaluation: - Token usage tracking - Cost tracking when available - Vitest test suites for system-level testing No LLM-as-judge. No automated quality metrics. No A/B testing of prompts. The operating assumption: for a personal assistant where you see every output, you are the evaluation pass. You correct mistakes directly. --- ### Element 9: FEEDBACK *How do user signals drive improvement? Feedback is the mechanism for capturing preferences, corrections, and satisfaction.* Minimal and file-based: - Reaction handling (thumbs up/down) exists but is primarily social signaling - Preferences stored when agent explicitly writes them to memory files - No centralized feedback collection for response quality If you want the agent to remember a preference, you tell it directly. The agent writes to `MEMORY.md`. On future sessions, memory search retrieves relevant context. --- ### Element 10: LEARNING *How does the system improve over time? Learning is accumulated optimization—whether automatic or manual, implicit or explicit.* The agent can modify its own behavior through workspace file edits: - Agent writes observations to memory files during sessions - Agent can edit `SOUL.md`, `AGENTS.md`, and other identity files directly - Since these files are loaded into the system prompt, this is effectively self-modification - Memory search retrieves relevant context on future sessions - You can also edit workspace files manually The agent has write access to the same identity files that define its system prompt. If it edits `SOUL.md`, the next session loads the modified version. This is a form of persistent self-modification. From the docs: `SOUL.md` is described as a file "the agent should read and update, as it represents its continuity and memory." There's also a `soul-evil` hook (`src/hooks/soul-evil.ts`) that can swap `SOUL.md` content with `SOUL_EVIL.md` in memory before prompt assembly—presumably for testing adversarial personas. No automatic prompt *tuning* (like optimizing based on feedback metrics), but the agent can and does modify the files that constitute its prompt. --- ## Architecture Summary | Element | OpenClaw Implementation | |---------|------------------------| | **Context** | Modular files (`SOUL.md`, etc.) + session history + memory search | | **Memory** | Filesystem (markdown) + SQLite vector index + hybrid search | | **Agency** | Shell, browser, canvas, nodes, messaging, cron, webhooks | | **Reasoning** | Standard ReAct loop, no planning phase | | **Coordination** | Hierarchical spawning, no recursive delegation | | **Artifacts** | Canvas, workspace files, JSONL transcripts | | **Autonomy** | Cron, webhooks, heartbeat | | **Evaluation** | Minimal (token/cost tracking only) | | **Feedback** | Manual via memory file edits | | **Learning** | Agent can edit identity files (self-modification of prompt) | --- ## Deployment Options **Local (Mac Mini / laptop):** - Runs as LaunchAgent (macOS) or systemd service (Linux) - Full filesystem access - Direct messaging app integration **Cloud (Cloudflare Workers):** - Cloudflare released moltworker reference implementation - Uses Sandbox SDK (containers), R2 (storage), Browser Rendering (Chromium), AI Gateway - ~$5/month Workers paid plan + API usage [![Moltworker architecture on Cloudflare](https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3OD2oHgy5ilHpQO2GJvcLU/836a55b67a626d2cd378a654ad47901d/newdiagram.png)](https://blog.cloudflare.com/moltworker-self-hosted-ai-agent/) *Moltworker architecture. Source: [Cloudflare Blog](https://blog.cloudflare.com/moltworker-self-hosted-ai-agent/)* --- ## Security Considerations OpenClaw requires significant system access to function. Security researchers have noted: - Full shell access on host (when not sandboxed) - Prompt injection surface via any content the agent reads - Credentials stored in local config files - Persistent memory enables delayed-execution attack patterns The three-layer security model (sandbox runtime, tool policy, elevated approvals) provides controls, but the default for main sessions is full host access. This is intentional—power in trusted contexts, safety in untrusted ones (group chats, external triggers). [![Zero Trust access configuration for moltworker](https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1MDXXjbMs4PViN3kp9iFBY/a3095f07c986594d0c07d0276dbf22cc/image3.png)](https://blog.cloudflare.com/moltworker-self-hosted-ai-agent/) *Cloudflare Zero Trust access configuration. Source: [Cloudflare Blog](https://blog.cloudflare.com/moltworker-self-hosted-ai-agent/)* As security researchers have noted, OpenClaw represents a "lethal trifecta": access to private data, exposure to untrusted content, and the ability to communicate externally. ### The Permission Question The real thing to watch isn't capability; it's permission. OpenClaw runs with your user permissions. It can read your files, send messages as you, execute arbitrary code, and communicate externally. The Vectra "lethal trifecta" framing is accurate. We're at an inflection point where users are granting agents permissions that would've seemed insane two years ago. And the systems work well enough that people keep expanding the grant. The question isn't whether this architecture is technically sound. It is. The question is whether the permission model scales. Every automation you add increases the attack surface. Every sub-agent you spawn increases the blast radius. The heartbeat that's supposed to catch drift could itself be compromised. OpenClaw and systems like it are a preview of what's coming: agents with persistent access, accumulated context, and the autonomy to act without confirmation. How much permission we allow, and how we audit it, will define the next phase of personal computing. --- ## Who's Using It Use cases from the community: - Car negotiation: searched Reddit pricing, contacted dealers, negotiated via email - Production bug fix: detected overnight, fixed before team woke up - Smart heating: checks weather patterns, decides when to heat based on logic - Insurance appeal: interpreted policy, found loophole, drafted appeal - Mass refactoring: orchestrated sub-agents for large codebase changes 700+ community skills on ClawdHub covering Reddit moderation, WordPress automation, recruitment, financial research, ClickUp integration, and more. --- ## Prior Art zo.computer launched in November 2025 with a similar thesis: give everyone a personal AI server. Their tagline: "They made vibe servering into a product." The key differences: - Zo is cloud-hosted (their servers); OpenClaw runs locally or on your own Cloudflare - Zo has a polished GUI; OpenClaw is terminal-first with messaging app frontends - Zo is a product; OpenClaw is open-source infrastructure Both represent the same insight: a persistent AI server with your context, accessible from anywhere, running automations in the background. The architecture converges because the problem is the same. Activation energy for personal automation is too high, and one-shot AI assistants don't accumulate. --- ## Related Projects - **Moltbook**: Social network where OpenClaw agents interact with each other - **Cloudflare moltworker**: Official reference deployment on Cloudflare infrastructure - **ClawdHub**: Community skill marketplace --- ## Links **Official:** - [OpenClaw GitHub](https://github.com/openclaw/openclaw) - [OpenClaw Documentation](https://docs.openclaw.dev) - [Cloudflare moltworker](https://github.com/cloudflare/moltworker) **Press Coverage:** - [Wikipedia: OpenClaw](https://en.wikipedia.org/wiki/Moltbot) - [MacStories: Clawdbot Showed Me What the Future of Personal AI Assistants Looks Like](https://www.macstories.net/stories/clawdbot-showed-me-what-the-future-of-personal-ai-assistants-looks-like/) *(the article that started the hype)* - [Fast Company: OpenClaw is cool, but it gets pricey fast](https://www.fastcompany.com/91484506/what-is-clawdbot-moltbot-openclaw) - [Fortune: Moltbook, a social network where AI agents hang together](https://fortune.com/2026/01/31/ai-agent-moltbot-clawdbot-openclaw-data-privacy-security-nightmare-moltbook-social-network/) - [Platformer: Falling in and out of love with Moltbot](https://www.platformer.news/moltbot-clawdbot-review-ai-agent/) - [IBM: The viral "space lobster" agent testing the limits of vertical integration](https://www.ibm.com/think/news/clawdbot-ai-agent-testing-limits-vertical-integration) - [Vectra: When Automation Becomes a Digital Backdoor](https://www.vectra.ai/blog/clawdbot-to-moltbot-to-openclaw-when-automation-becomes-a-digital-backdoor) **Community:** - [Kitze (@thekitze) on Startup Ideas Podcast](https://youtu.be/YRhGtHfs1Lw?si=BD29GFvlssSue9zh&t=442) --- ## State Space Explosion URL: https://wcdc.io/writing/state-space-explosion Date: 2026-01-31 Description: The combinatorial unease that builds when AI expands possibilities faster than you can prune them. There's a feeling I've had trouble naming. You open Claude to solve a specific problem. Thirty minutes later, you're looking at five different approaches, each with three variants, each variant raising two new questions. The original problem is buried somewhere. Your mind is racing. It feels like suffocation, a dire need to collapse the superposition. I've started calling this **state space explosion**: the combinatorial unease that builds when AI expands possibilities faster than you can prune them. ## The Phenomenon 1. You start with a forcing function: curiosity about a specific problem, a question you want answered 2. AI responds with branching possibilities ("Here are five approaches...") 3. Each branch spawns sub-branches ("For approach 2, you could either...") 4. Your working memory fills with options you can't evaluate 5. The original forcing function gets buried under the load 6. You're now "exploring" without anchor The issue is structural: AI generates options faster than human working memory can evaluate them. Your brain holds roughly 7 items in working memory. Evaluating 20 options against each other is O(n²), meaning 190 pairwise comparisons on hardware limited to 7 slots. ## The Corrupted EV Sensor Normally you run an implicit expected value calculation on possible actions: some weighted function of reward, probability, effort, and time. You don't consciously compute this. You just *feel* which paths seem promising. AI interferes by generating options whose value you can't estimate. You can see twenty options but you have no experiential data on any of them. The paths are visible but unweighted. When enough options are unpriced, your decision process degrades. You're looking at a list with no way to rank it. ## The Depth-First Trap AI tends to go depth-first when exploring solutions. It picks one approach and dives deep before you've had a chance to survey the landscape. This makes state space explosion worse. The trap: you typically want breadth-first pruning before going deep. Survey the options, eliminate the obvious non-starters, then commit to exploring one path. But AI's default interview style pulls you into the weeds of option #1 before you've even seen options #2 through #5. This triggers scarcity processing: "I must search all paths or miss the best one." Wrong algorithm for an abundance problem. When options were scarce, exhaustive search was correct. But AI creates abundance. There are always more approaches, more variants, more frameworks. The reframe: you're not searching for the optimal path. You're looking for *a path that works*. There are many. If the first one fails, there are others. ## The Cybernetic Frame I like looking at this from a cybernetic third-person view: human as information agent, AI as compute amplifier. What's happening information-theoretically? The failure mode of "using AI incorrectly" is usually invisible. You don't know what you don't know. But state space size gives you a visible heuristic. If your possibility space is exploding and you can't collapse it, something is wrong with the interaction pattern. This isn't about what the AI knows. It's about what *you* know. The state space exists in your head, not the model's. The model can generate infinite branches. Your working memory cannot hold them. ## What's Helped Me ### Backwards Chaining Before engaging with AI-generated options, establish what you actually want, even provisionally. The goal doesn't have to be final, but it has to be clear enough to evaluate candidates against. Then backwards chain: What leads to that goal? What leads to that? Find the nearest node to your current position that's on the chain. Ignore everything else. The goal functions as a query. Queries enable filtering. Without a query, you just accumulate options. ### Breadth-First Pruning Resist the depth-first pull. Before diving into any single approach, force a survey: "What are all the major approaches? Don't elaborate, just list them." Then prune. Eliminate the obvious non-starters. *Then* go deep on one. Quick heuristics for pruning: cluster the options, filter by gut sense of which feel central to the problem, pick the top three and do a quick sanity check on each. A biased filter that moves is better than a perfect filter that's still loading. ### Graduating to Management This is a confession: I've been slow to adopt multi-agent thinking. Stuck in the midwit trap where I feel like I need to micromanage the AI, verify every step, maintain control over the microstates. But this doesn't scale. A manager doesn't care about microstates. A manager samples the macrostate, performs verification at checkpoints, and trusts the execution layer to handle details. We select managers who know how to do the job themselves because they might need to enter kernel mode to debug. But they don't live in kernel mode. The pattern: let AI explore the state space while you verify. Generation can be noisy if verification is cheap. AI can generate twenty approaches, most of them mediocre, as long as you have a way to identify the good ones. This is hard if you're technically sophisticated. You *know* how to do the thing, so delegating feels like loss of control. But when exploration is cheap, the leverage is in specifying what "good" looks like, not in doing the exploration yourself. ## Parsing the Unease When I feel that suffocating dread working with AI, I label it: state space explosion. Treating it as a structural problem rather than a personal failing changes how I respond. Then I ask: - **Do I have a query?** If not, define at least a provisional goal. - **Am I being pulled depth-first?** If so, force a breadth-first survey before committing. - **Am I micromanaging?** If so, step back to the manager seat. Specify verification criteria. Let the AI generate candidates. The computational frame helps because it re-expresses "I'm overwhelmed" as "O(n²) comparisons on 7-slot hardware." Working memory capacity is fixed. The algorithm isn't. --- *State space explosion is the combinatorial unease that builds when AI expands possibilities faster than you can prune them. The failure mode is usually invisible, but state space size makes it visible. The response isn't to evaluate faster. It's to install queries that filter, force breadth-first pruning before depth, and graduate from micromanager to manager.* --- ## The era of intelligence design URL: https://wcdc.io/writing/intelligence-design Date: 2026-01-30 Description: From prompt engineering to system architecture: a two-year journey. The system's behavior has always been determined by code, but we've often treated the model rather than the architecture as the central artifact. After two years of building AI systems, I've started describing a shift in focus as **intelligence design**: treating architecture, not the model alone, as the main design surface. This aligns with what Andrej Karpathy calls context engineering, the industry's move from crafting prompts to designing systems. | Prompt Engineering Era | Intelligence Design Era | | --------------------- | ---------------------- | | Intelligence lives inside the prompt/model | Intelligence lives in the system architecture | | Work: craft prompts, tweak temperature | Work: design context, state, memory, routing, loops | | Primary artifact: the prompt string | Primary artifact: type system + primitives | | Wait for better models | Design better systems | **Six key shifts**: LLMs demoted to stateless primitives. Primitives over prompts. Horizontal over vertical. Context over chat. Designed loops over human-fixes-blob. Theory over framework. You don't get reliability by making the model smarter; you get it by treating the model as a fallible component and wrapping it in contracts, loops, and checks. Many advanced AI systems will adopt coding-agent architectures because code offers high expressivity and adaptability. The challenge is productization: moving from kernel mode (full system access) to user mode (constrained, trustable systems). This includes giving agents domain-specific languages to generate code in, structured programs instead of scattered tool calls. --- ## Part 1: My Journey ### Late 2023: Excitement ChatGPT had just launched. The demos were compelling: code generation, writing assistance, reasoning that felt almost human. I started building immediately, chatbots, assistants, automations. Within weeks, specific problems with reliability and maintainability showed up. ### Early 2024: Prompt Engineering Disillusionment The process lacked clear abstractions, invariants, or tests. It reduced to ad-hoc prompt adjustments. I'd write a prompt, test it, tweak a word, test again. Add "think step by step." Remove "think step by step." Try "You are an expert." Try "You are a helpful assistant." Change the temperature from 0.7 to 0.3. Back to 0.7. Maybe 0.5? There was no clear set of abstractions or principles for predicting or explaining model behavior across tasks. The best practices were folklore: things that seemed to work for someone, sometime, on some model. Nothing composed. Nothing generalized. Every new task meant starting from scratch. Compared with conventional software engineering, there were no types, tests, or composable abstractions to reason about behavior. Prompt engineering relied on trial-and-error heuristics rather than explicit models, guarantees, or repeatable methods. ### Mid-2024: Framework Disillusionment I evaluated common frameworks to see whether their abstractions improved reliability and structure. I dove into LangChain. Then LangGraph. Then DSPy. I read the docs, followed the tutorials, built the example apps. The frameworks advertised composable abstractions and repeatable patterns for building LLM applications, but in practice their boundaries didn't match the systems I needed to build. This frustration is widespread. LangChain gave me abstractions like `Chain` and `Agent` and `Memory`, but the boundaries did not align with concrete system boundaries I needed to model. The framework's concepts did not map to anything fundamental. They reflected one author's opinionated theory about how to organize code. LangGraph was worse. The graph abstraction forced me to think in nodes and edges, but the real question, *how do I know when to split a node?*, had no principled answer. It was the framework author's intuition, encoded as API. DSPy promised automatic optimization, but optimized for metrics that didn't capture what I actually cared about. The optimizer would find prompts that scored well on benchmarks but failed on the edge cases that mattered. The frameworks encoded their authors' ad-hoc design choices as APIs without grounding them in shared underlying principles. They standardized APIs around abstractions that, in my experience, didn't yet reflect well-understood design tradeoffs. The problem went beyond weak or leaky abstractions: many of the boundaries felt arbitrary, reflecting local conventions rather than shared underlying principles. ### November 2024: Semantic Objects Turn The standard framing for agent design is tool-centric: "What tools can the agent use?" You give the agent a list of functions (`search_web()`, `read_file()`, `send_email()`) and it decides which to call. The tools float free, unattached to any domain structure. I started thinking differently: "What can the agent manipulate?" Instead of tools, give the agent *objects*. A `Document` with sections. A `Codebase` with modules and functions. A `Conversation` with history. The operations are methods on the objects, not free-floating functions. I called these **semantic objects**: typed entities with operations attached. ```typescript // Tool-centric: "What can the agent do?" const agent = new Agent({ prompt: "You are a code assistant...", tools: [read_file, write_file, search, run_tests] }) await agent.chat("Update the login function") // Object-centric: "What can the agent manipulate?" const agent = new Agent({ prompt: "You are a code assistant...", objects: [codebase, testSuite] // operations are attached to objects }) await agent.chat("Update the login function") ``` In the first pattern, `read_file` and `write_file` float free. In the second, the `codebase` object exposes `.getModule()`, `.getFunction()`, `.updateImplementation()`. The operations are scoped to what they affect. This is object-oriented agent design. The shift matters because: **The objects define what's manipulable.** If the agent has a `Document` object, it can manipulate sections, paragraphs, citations. If it only has `read_file` and `write_file`, it manipulates bytes. The object's interface constrains the space of valid operations. **The tools belong to the objects.** Instead of a flat list of tools, operations are namespaced to the entities they affect. `project.addFunction()` is clearly about the project. `add_function(project_id, ...)` is not. **Text becomes serialization.** The text representation is how objects get passed to the model; the object is what the agent is actually manipulating. In most systems, components operate on structured representations rather than raw strings. I wrote a spec for semantic objects: rich, manipulable objects with data, operations, relationships, and visualization. But semantic objects alone weren't enough. Where do the objects live? How do they persist? Agents need environments with shared storage and lifecycle management, not just isolated objects. ### Late 2024: Tool Calling Problem Around this time, I started hating tool calling. Most frameworks treat tool calling as a single primitive: the model decides to call a tool, you execute it, you return the result. This conflates distinct concerns. Tool calling actually conflates two completely different operations: **Decision making**: The model choosing what action to take. "I should search the web for X." This is a reasoning operation, analyzing context, weighing options, committing to a course of action. **Context manipulation**: How the tool's results transform the system's state. The search results get added to context, or stored in memory, or used to update an artifact. This is a state management operation. It's about information flow, not reasoning. These should be separate abstractions: one for control and decision-making, and another for how tool outputs update system state. But most frameworks collapse them into one "tool call" concept, making systems hard to reason about, hard to debug, hard to compose. They were crystallized into APIs before anyone understood the underlying structure. ### Early 2025: Visual Grammar Attempt I needed a representation that made the system's data flow and control structure visible, not just textual logs. I started designing a visual grammar for agent systems. Boxes for components, arrows for data flow, colors for different types of operations. The goal was to make context composition visible, to see how information assembled for each call, how state flowed through the system, where decisions were made. The visual models revealed control-flow and data-flow issues, like unnecessary loops and redundant context, that I had overlooked in code. The grammar let me reason about structure in a way that prose and code alone couldn't. But I hit limits. Visual representations constrain you. Every design choice in the grammar is a commitment about what matters. Some things that were easy to draw were hard to build; some things that were easy to build were hard to draw. The visual was good for understanding, but code was still where the building happened. In practice, I use visualization for exploration and rely on code for implementation. Diagrams made call sequences and data dependencies easier to inspect than log output alone. ### Mid-2025: XJSN Exploration Around this time I started exploring constrained output languages for agents. Consider why tools like Bash and Grep are so expressive for LLMs. Models have seen thousands of examples of shell pipelines in their training data. They can generate correct Bash quickly because they understand the syntax patterns deeply. The same is true for JavaScript. The insight behind XJSN: leverage the model's prior understanding of JavaScript syntax, but don't actually execute it as JavaScript. Instead, parse it into an AST and write a custom interpreter. ```javascript Pipeline([ Filter({ status: "active" }), Map({ extract: ["id", "name", "email"] }), GroupBy({ field: "department" }), Aggregate({ count: Count(), average_age: Average("age") }) ]) ``` This looks like JavaScript function calls, so the model generates it fluently. But it's not meant to be executed as JS. You parse it, and each function call becomes a node in your AST. `Filter`, `Map`, `GroupBy`, `Aggregate` are node types you define, not JavaScript functions. The power is in the AST phase. You control the grammar: what node types exist, what arguments they accept, what can appear inside them. You can define an `If` construct but constrain what's allowed in its branches. You can allow `Filter` but restrict what predicates it accepts. The validation happens at parse time, before any interpretation. ```typescript // You define the grammar const grammar = { Pipeline: { children: ['Filter', 'Map', 'GroupBy', 'Aggregate'] }, Filter: { args: { status: 'string' } }, // ... } // Agent generates XJSN const output = await agent.generate("Transform this data...") // Parse and validate against grammar const ast = parse(output) const errors = validate(ast, grammar) if (errors) return retry(errors) // Your custom interpreter executes the validated AST const result = interpret(ast, data) ``` Businesses can define their own grammars for their domain. A compliance team defines audit node types. A data team defines transformation primitives. The agent generates code in familiar JavaScript syntax, but only the node types in your grammar are valid. Verification is tractable because you validate the AST against a known grammar, not arbitrary code. I didn't take XJSN all the way at the time, but the concept shaped how I think about constrained generation: leverage existing syntax knowledge, parse into an AST, validate against a grammar, interpret with custom semantics. ### Fall 2025: Coding Agents Arrive Claude Code shipped. Cursor exploded. Devin made waves. Coding agents went from research demos to production tools. They were effective enough to complete nontrivial coding tasks end-to-end, with some failures. These systems could take a task, explore a codebase, write code, run tests, fix errors, iterate until done. They exhibited more autonomous problem-solving than many earlier "agent" demos I had seen. Why did coding agents work when so many other agent applications flopped? The answer started crystallizing everything I'd been thinking: coding agents work because they're well-architected systems implementing real primitives. **Context**: Intelligent assembly of relevant code, documentation, error messages, history. Not just "dump everything in", careful selection of what the model needs. **Memory**: Persistent understanding of the codebase. Files, structure, patterns, conventions. **Agency**: Real effects in the world. File writes, command execution, test runs. Not just text generation, actual action. **Reasoning**: Verification loops. Write code, run tests, see errors, fix, repeat. The loop structure turns unreliable generation into reliable convergence. **Evaluation**: Test suites, linters, type checkers. External verification that doesn't depend on the model's self-assessment. Coding agents made this architecture concrete and showed that it can work in practice. The intelligence isn't in the prompt. It's in the system architecture. The model is a component. The orchestration is the product. ### November 2025: Mechanistic Mindset Connection Meanwhile, a parallel thread was running that I hadn't fully connected. For years I'd been building a wiki on what I call the "mechanistic mindset": the idea that cognition is computation. Not as metaphor but literally. Thinking is information processing. Behavior is the execution of algorithms. The mind is a machine, and understanding the machine is understanding the mind. The wiki had grown to 95 articles covering everything from motivation to laziness to willpower, all reframed through a computational lens. Instead of "I procrastinate because I'm lazy," you ask: What are the competing reward signals? What's the latency and friction of switching tasks? How is uncertainty about success suppressing action? This isn't everyone's view. But it's mine, and I published it as an Obsidian site. At that point, I connected this view of cognition with how I was structuring AI systems. If cognition is computation, then designing cognition is designing computation. The question "how do I build an agent?" becomes "how do I design a computational system that exhibits intelligent behavior?" This is software engineering: inputs, outputs, state, control flow, abstraction, composition. In this framing, intelligence design reduces to code design. The code is where the intelligence lives. The model is one component, a stateless text transformer. The intelligence emerges from how you orchestrate context, state, memory, and loops. ### December 2025: Elements of Agentic System Design I started writing *Elements of Agentic System Design*. The goal was to describe the design space from first principles, rather than documenting a particular framework. I landed on 10 elements that capture every design decision you make when building intelligent systems: 1. **Context**: What information is assembled for each call 2. **Memory**: What persists across calls 3. **Agency**: Capacity to produce effects in the world 4. **Reasoning**: Structure of model calls (loops, planning, reflection) 5. **Coordination**: How multiple components communicate 6. **Artifacts**: Structured outputs that evolve over iterations 7. **Autonomy**: What triggers computation 8. **Evaluation**: How you measure success 9. **Feedback**: Signals that flow back to improve the system 10. **Learning**: How the system gets better over time These aren't new inventions for AI. They're distributed systems primitives, programming language concepts, standard software engineering applied to a context where one of your components is an LLM. The model is a stateless text transformer. You control everything else. ### January 2026: Intelligence Design On January 1st, 2026, I finished the book and connected the strands of the previous work into a single framework. | Experience | Insight | | --------- | ------- | | The prompt engineering frustration | the primitives are missing | | The framework disillusionment | abstractions before understanding | | The semantic objects work | agents need typed environments | | The tool calling critique | operations are conflated | | The visual grammar | code is the final artifact | | The XJSN branch | give agents languages to think in | | The mechanistic mindset | cognition is computation | | The coding agents success | the paradigm is proven | **The system's behavior has always been determined by code, but we rarely treated it as the primary locus of intelligence.** I started calling this "intelligence design": the discipline of designing systems that think with you, where the code architecture is the central artifact rather than the prompt. --- ## Part 2: The Core Reframes **1. LLMs demoted: From "smart teammate" to "stateless text transformer"** The model has no memory. No identity. No continuity between calls. Each invocation is pure function over its input, text in, text out. The "agent" that feels continuous is your architecture, not any capability inherent to the model. This demotion changes the design focus from eliciting "understanding" to engineering around known limitations. You stop relying on the model to infer everything correctly and instead design systems that tolerate and correct its errors. **2. Primitives over prompts: From prompt engineering to system primitives** Instead of focusing on phrasing a single prompt, focus on how the system constructs context for each model call. Instead of tweaking temperature, design verification loops. Each of the 10 elements is a category of decisions you're making, whether explicitly or by default. **3. Horizontal over vertical: From apps to horizontal primitives** Don't build a "coding agent." Don't build a "writing agent." Build context construction primitives, reasoning loop primitives, verification primitives. The horizontal primitives compose into any vertical application. The coding agents that work look more like orchestration systems applied to code than like monolithic "coding AIs." Many of the same patterns also carry over to writing, research, and data analysis. **4. Context over chat: From chat agents to context machines** The context window IS the agent's "mind" for that call. What you put in the window determines what the model can do. Context engineering, deciding what goes in, what stays out, how it's structured, is the primary leverage point. A 10-line prompt with perfect context beats a 1000-line prompt with wrong context. Every time. **5. Designed loops: From human-fixes-blob to draft→critique→revise** The old pattern: model generates, human evaluates, human fixes, repeat. The model produces blobs; humans are the loop. The new pattern: model generates, model (or tool) critiques, model revises, system evaluates, repeat. The loop is designed into the system. Humans set the termination condition, not the iteration. Draft → critique → revise turns unreliable generation into directed search. The model doesn't need to be right, it needs to be improvable. **6. Theory over framework: From framework-driven to theory-first** Build on distributed systems thinking. Build on programming language theory. Build on state machine formalism. The primitives of intelligent systems are the primitives of computation, applied to a new domain. Don't learn LangChain. Learn context construction. Don't learn LangGraph. Learn control flow patterns. The framework is temporary. The theory persists. ### Reliable Systems from Unreliable Components > You don't get reliability by making the model smarter; you get it by treating the model as a fallible component and wrapping it in contracts, loops, and checks. The model will hallucinate. The model will misunderstand. The model will go off the rails. This isn't a bug to be fixed, it's a feature to be designed around. **Typed interfaces.** Every LLM boundary is a contract. Structured outputs, not blob-of-text. Schemas that enforce valid structure. Instead of emitting free-form JSON, the model fills fields defined by a schema, and the runtime enforces structural validity. **Narrow primitives.** Single-responsibility operations. Each model call does one thing. Compose small reliable calls, don't build large unreliable monoliths. **Verification loops.** Draft, then verify. Generate, then check. The loop structure means errors get caught and corrected. Self-evaluation, tool-based verification (run the tests, check the types), cross-checks against external sources. **Graceful degradation.** When verification fails repeatedly, don't crash, escalate. Ask the user. Fall back to simpler approaches. Admit uncertainty. Reliability includes knowing when you can't succeed. Spotify's engineering team describes coding workflows that use similar draft-run-revise loops. They draft code, run tests, see failures, revise, repeat. The loop turns an unreliable generator into a reliable problem-solver. --- ## Part 3: What Comes Next ### Convergence Toward Coding Agent Patterns Many sophisticated AI systems converge toward coding agent patterns. Why? Because code is a highly expressive medium. A coding agent isn't limited to predefined tools, it can write new tools. It isn't limited to predefined workflows, it can create new workflows. The ability to write and execute code lets the system define new tools and workflows on demand. Coding agents can reach high levels of agency and adaptability when given enough access and tooling: - **Maximum expressivity**: Any computable operation is reachable - **Maximum adaptability**: The system can modify its own behavior - **Maximum causality**: Real effects in real systems This is why coding agents work better than most domain-specific agents. They're not constrained by the tools some framework author imagined. They have the full power of computation. I expect many workflows that can be expressed as code-generation loops to shift toward coding-agent-style architectures. Writing assistance becomes "write code that produces documents." Data analysis becomes "write code that analyzes data." Research becomes "write code that searches and synthesizes." ### How Do We Productize It? Productizing these systems raises a constraint: Current coding agents operate in what I call **kernel mode**: full access to everything. - Full file system access - Arbitrary code execution - Unconstrained tool use - Complete system control This is necessary for capability. You can't build a real coding agent without letting it read and write files, run commands, make network requests. But kernel mode isn't productizable. You can't ship a system that has arbitrary access to users' systems. You can't deploy an agent that might do anything. For products, you need constraints. You need safety. You need trust. One useful framing is a shift from "kernel mode" systems toward more constrained "user mode" designs: still capable, but bounded in ways that enable trust. ### Prediction 1: File Systems → Object Systems **Current state**: Agents operate on raw files with free-floating tools. ```typescript // Tool-centric agent construction const agent = new Agent({ prompt: "You are a coding assistant...", tools: [readFile, writeFile, search, runCommand] }) // Agent can access any path, write any content ``` Arbitrary paths. Arbitrary content. The file system is the interface. This works, but it's maximally unconstrained. The agent can read anything, write anything, delete anything. **Future state**: Agents operate on semantic objects with attached operations. ```typescript // Object-centric agent construction const agent = new Agent({ prompt: "You are a coding assistant...", objects: [project, testSuite] // project exposes: .getModule(), .getFunction(), .updateImplementation() // testSuite exposes: .run(), .getFailures(), .getCoverage() }) // Agent can only manipulate what the objects expose ``` This is the semantic objects pattern applied to production systems. The operations are methods on the objects, not free-floating tools. The file system becomes an implementation detail. The agent sees "functions" and "modules" and "tests", not paths and bytes. Why this matters: **The objects define what's manipulable.** You can't accidentally delete system files if your interface is "modify this function." Invalid operations become impossible, not just forbidden. **Operations have semantic preconditions.** "Update this function" can verify that the new code type-checks, has tests, doesn't break callers. "Write arbitrary bytes to path" can't. **Objects enforce their own manipulation.** Today, "agent skills" are prompts explaining how to use scripts: when to invoke them, what they do, how to call them. This is weak because it's just instructions; the agent can still do anything. With object systems, the object's interface IS the enforcement. You don't write prompts explaining how to manipulate a `Module`. The `Module` exposes `.getFunction()`, `.updateImplementation()`, and that's what's possible. The abstraction enforces itself. **Higher-level structures become possible.** When objects enforce their interfaces, you can build on top of them. A `Project` contains `Modules` which contain `Functions`. You stop re-reading files to understand what's possible. The types tell you. Agents at different levels compose because the interfaces guarantee what each level can do. **Builders can refine object types.** Domain experts create `ReportSpec`, `DataPipeline`, `AuditTrail` objects with operations tailored to their domain. These aren't prompts explaining file formats. They're typed abstractions that enforce how they're used. The ecosystem grows because people share object definitions that carry their own guarantees. ### Prediction 2: Constrained Code Generation **Current state**: Sandbox everything, hope for the best. Agents generate arbitrary code. We contain the damage with sandboxes, isolated environments, limited permissions, restricted networks. The model can do anything; we just limit the blast radius when it goes wrong. Sandboxing limits the blast radius of mistakes but doesn't address whether the generated code is appropriate in the first place. **Future state**: Constrain the type of code agents can generate. Instead of "generate arbitrary Python," generate "data transformations in this DSL." Instead of "write any code," write "workflow steps using these primitives." The output language constrains what's possible, not just what's allowed. ```typescript // Arbitrary generation: anything goes const code = await model.generate("Write Python to process this data") await sandbox.execute(code) // Hope it's safe // Constrained generation: valid by construction const transform = await model.generate(schema) const result = interpret(transform, data) // Safe by design ``` Why this matters: **Verification becomes possible.** A DSL with limited operations can be verified exhaustively. Arbitrary code can only be contained. **Safety becomes design.** You're not defending against bad code, you're making more classes of harmful outputs unrepresentable. The grammar of valid outputs excludes harmful outputs. **Reliability improves.** Constrained generation is easier than arbitrary generation. The model has less rope to hang itself with. Constrained generation reduces the need to rely solely on sandboxing by making more classes of harmful outputs unrepresentable. ### Prediction 3: Domain-Specific Languages for Agents **Current state**: Agents express computation through scattered tool calls. Each tool call requires a full model invocation. The agent can't express "search for X, filter by Y, summarize Z" as a single thought. It must interleave execution with decision-making, rebuilding context at each step. This is analogous to a system that recomputes its control state from scratch after every step, instead of maintaining an explicit program. **Future state**: Businesses create domain-specific languages with JavaScript-like syntax, and agents generate code in those languages. The XJSN approach: leverage the model's existing understanding of JavaScript syntax, but constrain the valid constructs via a grammar. A compliance agent generates audit expressions that look like JavaScript function calls: ```javascript AuditProcess({ scope: RecursiveWalk({ root: EntityGraph("subsidiaries"), filter: And([ Revenue(GreaterThan(10000000)), Jurisdiction(In(["EU", "US", "UK"])) ]) }), decision: Switch({ cases: [ { when: Above(Percentile(95)), then: Escalate({ to: "CFO" }) }, { when: Above(Percentile(80)), then: Schedule({ review: "quarterly" }) }, { default: Archive({ retention: Years(7) }) } ] }) }) ``` The model generates this fluently because it looks like JavaScript. But only the function names and argument structures defined in your grammar are valid. You validate the output against the grammar and retry with error messages if it fails. Why this matters: **Leverages existing knowledge.** Models already know JavaScript syntax deeply. You don't need to teach them a new language from scratch. **Grammar-based validation.** You define which constructs are valid. Validation is fast because you check against a known grammar, not arbitrary code. **Domain-specific constraints.** A compliance team defines audit operations. A data team defines transformation primitives. The grammar constrains what operations are expressible. **Safety by design.** If "delete all files" isn't in the grammar, it can't be generated. The grammar is the security boundary. --- ## Part 4: The Thesis ### The Core Claim During the prompt-engineering phase, most effort went into writing instructions for the model instead of designing system structure. Intelligence design shifts the focus to architecting with models: designing context construction, loop structures, verification layers, and state management. The model is a component; the system is the product. ### The 10 Elements A practical design space for intelligent systems can be organized as follows: | Element | What It Is | Standard CS Analog | | ------- | ---------- | ------------------ | | **Context** | What information is assembled for each call | Inputs & representations | | **Memory** | What persists across calls | Data structures & storage | | **Agency** | Capacity to produce effects in the world | I/O & side effects | | **Reasoning** | Structure of model calls (loops, planning, reflection) | Control flow & algorithms | | **Coordination** | How multiple components communicate | Distributed systems | | **Artifacts** | Structured outputs that evolve | Data models & versioning | | **Autonomy** | What triggers computation | Event systems & scheduling | | **Evaluation** | How you measure success | Testing & observability | | **Feedback** | Signals that flow back to improve | Monitoring & logging | | **Learning** | How the system improves over time | Online learning & adaptation | These aren't new inventions for AI, they're the same primitives we use for any distributed system. The model is just one component. You control the other nine. ### The Vocabulary **Intelligence Design**: The discipline of designing systems that think with you. It's more specific than prompt engineering, which tunes one component, and narrower than generic "AI development." Intelligence design: the systematic practice of building computational systems that exhibit intelligent behavior. The term "agent" is overloaded. Focusing on system design clarifies the underlying concerns: context construction, state management, control flow, verification. The 10 elements provide a vocabulary to discuss these systems precisely. ### The Consequences **Context construction over prompt phrasing.** The prompt is one line. The context is everything. What information does the model need? How is it assembled? How is it structured? This is where leverage lives. **System design over model capability.** Model quality will improve over time, but in many projects I see larger near-term gains from improving system architecture. You control nine of the ten elements of any intelligent system. **Primitives over frameworks.** It is more durable to understand architectural primitives than to depend on the APIs of any single framework. LangChain will change. LangGraph will evolve. DSPy will iterate. The 10 elements won't. **System thinking over agent thinking.** Focusing on the system's architecture, context, loops, and state, clarifies design choices more than the generic label "agent." What's my context strategy? What's my loop topology? What's my state model? What's my verification approach? These questions have answers. They lead to code. They compose into systems that work. --- ## Conclusion Over two years, I iterated across many systems and frameworks, discarding abstractions that did not support reliable behavior. In practice, intelligence design reduces to code design. The code is where the intelligence lives. The model is stateless, a function from text to text. Everything that makes a system "intelligent" is your architecture: what context you construct, what loops you run, what state you maintain, what verification you perform. Work is shifting from prompt-centric experimentation toward system-level intelligence design. The system's behavior has always been determined by code. Now we recognize that code as the primary artifact. --- ## Prose is free now URL: https://wcdc.io/writing/substantives Date: 2026-01-30 Description: How to make sure people get value from your AI-generated content. AI can generate fluent, grammatical, well-structured text on any topic. The marginal cost of generating 2,000 words of coherent AI-generated text is negligible. As a16z puts it: the marginal cost of creation is approaching zero. This changes which parts of content creation are scarce and valuable: {/* SUBSTANTIVE: reframe table */} | Era | Scarce | Abundant | Value lives in | |---------|-------------------------|-------------------------|---------------------------| | Pre-AI | Prose | Ideas, facts, artifacts | The writing itself | | Post-AI | Ideas, facts, artifacts | Prose | What the prose carries | Prose is the medium, and the value lies in specific elements the reader can extract and use. I call those elements **substantives**. --- ## Substantives {/* SUBSTANTIVE: definition */} A substantive is a discrete, reusable unit of content that carries standalone value for the reader. Examples include definitions, checklists, code samples, and diagrams that can be reused or referenced without the surrounding explanation. {/* SUBSTANTIVE: the extraction test */} **The extraction test:** Assume a reader skims your piece in 30 seconds and extracts 3-5 things to save or share. Identify what they would extract. - A checklist: extractable, substantive - A screenshot: extractable, substantive - A paragraph of explanation: usually not, tissue - A definition: extractable, substantive - A transition sentence: not extractable, tissue Pieces built only from connective prose are harder to reuse or reference later. --- ## Why This Matters Now Readers quickly filter out low-value content. The backlash is real: "AI slop" entered the lexicon, and mentions increased ninefold in 2025. Platforms are responding: YouTube stopped paying for AI slop, and Pinterest added filters to hide AI content. Readers ignore writing that shows these patterns: - Generic claims without specific evidence - Fluent prose without anchoring details - Structure without substance Many AI-generated listicles, self-described comprehensive guides that say nothing, and posts with interchangeable advice contain no specific, reusable elements. --- ## The Taxonomy {/* SUBSTANTIVE: taxonomy table */} Substantives can be grouped by what the reader gains from them: | Category | Reader gets | Examples | |-----------------|------------------------|---------------------------------------------------------------| | **Utility** | Something to use | Code snippet, checklist, template, prompt, procedure | | **Knowledge** | Something to know | Definition, taxonomy, framework, distinction | | **Proof** | Reason to believe | Screenshot, benchmark, citation, timestamp, specific name | | **Perspective** | New way of seeing | Reframe, analogy, decomposition, contrast | | **Connection** | Orientation in space | External link, comparison table, prerequisite pointer | | **Experience** | Felt understanding | Worked example, failure story, interactive demo | | **Shortcut** | Compressed wisdom | Heuristic, pattern, anti-pattern, threshold, trade-off | | **Vocabulary** | Words to think with | Coined term, precise definition, distinction pair | Not every piece needs all eight. Most pieces have 2-3 primary value types. --- ## The Workflow {/* SUBSTANTIVE: workflow contrast */} One common workflow: 1. Define the main message 2. Outline the structure 3. Draft and refine the prose 4. Add supporting evidence Substantive-first: 1. Define what the reader should have when they finish 2. Identify substantives that provide that value 3. Order them into a sequence 4. Add prose that connects them Identify and design the substantives first, then write the prose that presents and connects them. --- ## Checklist Before Publishing {/* SUBSTANTIVE: checklist */} Before publishing, scroll through looking only at visual breaks and extracted elements: - Is there a substantive every 300-500 words? - Could someone extract 5+ useful things? - Is there at least one visual (screenshot, diagram, table)? - Is there at least one structural (checklist, framework, definition)? - Are there inline substantives (links, specific names, numbers)? If the piece contains mostly transitions and explanation with few concrete elements, revise it by adding more substantives or removing nonessential prose. --- ## How Substantives Differ from Arguments {/* SUBSTANTIVE: contrast table */} Traditional essays rely on arguments: claims supported by reasoning that the reader is expected to follow. Substantive-first content is built around artifacts, like checklists or definitions, that a reader can use without reading the rest of the piece. | Argument | Artifact | |-------------------------|-------------------------------| | "X is true because Y" | Screenshot showing X | | "You should do X" | Checklist for doing X | | "X works better than Y" | Benchmark comparing X and Y | | "X means Y" | Definition: X = Y | Arguments depend on the reader accepting your reasoning. Artifacts like checklists or definitions provide value directly. --- ## The Reusable Artifact Test {/* SUBSTANTIVE: criteria list */} To make a document reusable as a reference, ensure that it is: 1. **Extractable** - Elements can be pulled out and used independently 2. **Stable** - Content doesn't rely on context that will change 3. **Addressable** - Specific sections can be pointed to 4. **Machine-readable** - AI can parse and reference it Treat the document as a reusable artifact when automated tools can reliably parse its structure, you can copy sections into other documents without rewriting, and you can link directly to specific sections. --- {/* SUBSTANTIVE: core reframe, repeated */} The shift: start with what the reader should HAVE, not what you want to SAY. Design the substantives first, then write the prose, and test the result by checking what can be extracted. --- ## Reference: Exhaustive Substantive Types ### Utility — Reader Can Use This | Substantive | What Reader Gets | When To Use | |-------------|-----------------|-------------| | **Code snippet** | Executable capability | Any technical content where reader might implement | | **Template** | Fill-in-the-blank starting point | Process or document the reader will create themselves | | **Checklist** | Verification tool, nothing forgotten | Any multi-step process with failure modes | | **Procedure** | Step-by-step execution path | "How to" content of any kind | | **Prompt text** | Ready-to-use AI invocation | AI-related content, workflow content | | **Configuration** | Working setup they can copy | Technical content with setup requirements | | **Query/command** | Executable one-liner | Technical content, data content | | **Calculation/formula** | Computable relationship | Quantitative decisions, estimation | | **Decision tree** | Navigation through choices | Complex decisions with branches | ### Knowledge — Reader Now Knows This | Substantive | What Reader Gets | When To Use | |-------------|-----------------|-------------| | **Definition** | Precise meaning of term | Any time you use a term that could be ambiguous | | **Taxonomy** | Classification system | Domains with multiple types, options, or categories | | **Framework** | Multi-part mental model | Complex domains requiring structured thinking | | **Fact** | Verified true statement | Foundational claims, surprising truths | | **Distinction** | Difference between two things | Commonly confused concepts | | **Relationship** | How X connects to Y | Systems, dependencies, causes | | **Counter-example** | Case where intuition fails | Correcting common misconceptions | ### Proof — Reader Is Convinced | Substantive | What Reader Gets | When To Use | |-------------|-----------------|-------------| | **Screenshot** | Visual evidence something exists | Any claim about a system, UI, output | | **Benchmark** | Measured performance data | Claims about speed, quality, comparison | | **Log output** | Machine-generated evidence | Claims about system behavior | | **Repo link** | Inspectable source of truth | Any claim about code you've written | | **Citation** | Authority backing | Claims reader might doubt, contested topics | | **Testimonial/quote** | Another person's attestation | Claims about user experience, reception | | **Replication** | Someone else got same result | Scientific or technical claims | | **Timestamp** | When something happened | Narrative claims, incident reports | | **Specific name** | Who/what specifically | Any claim that could be vague | ### Perspective — Reader Sees Differently | Substantive | What Reader Gets | When To Use | |-------------|-----------------|-------------| | **Reframe** | "Not X, but Y" shift | Correcting common framing errors | | **Analogy** | Understanding via familiar domain | Complex concepts, cross-domain transfer | | **Lens/model** | Way of analyzing things | Recurring situations reader will face | | **Contrast** | Before/after, old/new | Change, improvement, evolution | | **Decomposition** | Breaking whole into parts | Complex wholes that seem monolithic | | **Synthesis** | Combining parts into whole | Scattered elements that form pattern | | **Scale shift** | Zooming in or out | When reader is stuck at wrong altitude | ### Connection — Reader Is Oriented | Substantive | What Reader Gets | When To Use | |-------------|-----------------|-------------| | **External link** | Path to deeper/related content | Any mention of external work | | **Comparison table** | Relative positioning of options | Decisions between alternatives | | **Influence chain** | Where ideas came from | Novel concepts, intellectual positioning | | **Contrast with alternative** | How this differs from X | Competitive positioning | | **Prerequisite pointer** | What to learn first | Content with dependencies | | **Next step pointer** | Where to go after this | Content that's part of larger journey | | **Ecosystem map** | How this fits in larger landscape | Tools, frameworks, communities | ### Experience — Reader Felt This | Substantive | What Reader Gets | When To Use | |-------------|-----------------|-------------| | **Interactive demo** | Learning by doing | Concepts that must be experienced to understand | | **Worked example** | Watching the process | Skills, procedures, problem-solving | | **Narrative incident** | Vicarious experience | Lessons that come from living through something | | **Failure story** | Learning from pain (yours) | Warnings, cautionary knowledge | | **Dialogue excerpt** | Witnessing an exchange | Interpersonal dynamics, debates | | **Sensory detail** | Grounding in physical reality | Abstract content that needs anchoring | | **Exercise** | Active practice | Skills that require repetition | ### Shortcut — Reader Has Compressed Wisdom | Substantive | What Reader Gets | When To Use | |-------------|-----------------|-------------| | **Heuristic** | Rule of thumb | Repeated decisions, common situations | | **Pattern** | Recurring structure with name | Situations reader will encounter multiple times | | **Anti-pattern** | What to avoid and why | Common failure modes | | **Threshold** | When X becomes Y | Decisions that depend on quantity | | **Priority order** | What to do first | Resource-constrained situations | | **Trade-off** | What you give up for what | Decisions with real costs | | **Warning sign** | Indicator of problem | Diagnostic situations | ### Vocabulary — Reader Can Think With This | Substantive | What Reader Gets | When To Use | |-------------|-----------------|-------------| | **Coined term** | New word for previously unnamed thing | When existing vocabulary fails | | **Precise definition** | Sharpened meaning of existing term | When term is used sloppily | | **Distinction pair** | Two words that mark important difference | When one word conflates two things | | **Acronym/abbreviation** | Compressed reference | Concepts that will be referenced repeatedly | | **Catchphrase** | Memorable crystallization | Core insight you want reader to retain and repeat | --- ## How I Broke Down the Process of Writing a Book with LLMs URL: https://wcdc.io/writing/how-i-broke-down-writing-with-llms Date: 2026-01-21 Description: What compilers can teach us about AI generation workflows. Generation is compilation: the quality of your output is limited by the quality of your intermediate representation. It's 2am and I'm staring at a wall of text that reads like cardboard. Three hundred pages of technically coherent prose, and every paragraph has the same hollow aftertaste. "It's important to note." "Let's delve into." "This approach offers several advantages." The words are all correct, the sentences parse, but reading it feels like shaking hands with someone who's smiling without their eyes. A verbal uncanny valley: close enough to human to feel wrong. The project was ambitious: *Elements of Agentic System Design*, a 300-page technical book about building AI applications. Not a blog post, not a tutorial series. A book. And I was writing it with AI, because the recursive irony felt appropriate: dogfooding the methodology while documenting it, using agents to write about agents. What I discovered had nothing to do with better prompts. The solution came from an unexpected domain: compiler design. **Generation is compilation. The quality of your output is limited by the quality of your intermediate representation.** Here's how I got there. --- ## Prompt Harder First attempt: a "clarity bear" interview prompt, a structured conversation that extracted ideas through targeted questions. Hours of back-and-forth about agents, state management, orchestration patterns, tool design. The transcript accumulated into fifty pages of raw thought, organized by topic, timestamped and indexed. I dropped the whole thing into Claude's context window and asked it to generate the book. The result wasn't "needs editing." It was fundamentally wrong. Entire chapters materialized about topics barely mentioned in the interview, while core insights I'd spent an hour articulating got a single paragraph buried in section four. The context window contained all the words but none of the intent, like a student who memorized the textbook but can't solve the exam. I threw it away and started over. **The problem wasn't the prompt. The problem was the architecture.** --- ## 80% Second attempt: procedural generation. Outline first, then chapter by chapter, section by section. Each chapter got a detailed spec before generation: topics to cover, sequence to follow, examples to include. Generate a chapter, review it, move to the next. The assembly line approach. This got to roughly 80%. Prose was coherent. Ideas flowed in approximately the right sequence. I could see the shape of a book emerging from the noise. But 80% isn't publishable. Two problems surfaced. First: AIisms everywhere. "It's important to note" in every third paragraph, "Let's delve into" opening each section, the unmistakable AI aftertaste baked into the texture of every page. Second: hallucinated emphasis. Not factual hallucinations, but something subtler. The model wasn't making things up; it was inventing *nuance*, flattening distinctions I cared about while emphasizing points I'd barely mentioned. The outline said "explain context construction," and the output explained something *adjacent* with confident authority. The outline captured hierarchy and topics, but it didn't encode the actual ideas exhaustively. The specific framings, the precise distinctions, the rhetorical shape I was reaching for: none of that was written down anywhere. It existed only in my head, and the model couldn't read my head. I was decomposing the problem, but into what? --- ## Detection, Not Prevention I attacked the AIisms first, writing style guides in the system prompt: "Never use 'delve.' Avoid 'it's worth noting.' Don't start paragraphs with 'Additionally.'" I added examples of good prose and bad prose, specified voice with increasing precision, enumerated banned phrases, provided before-and-after rewrites. None of it stuck. The model would comply for a few paragraphs, then drift back to defaults, like a student who nods along in class and immediately reverts to old habits on the exam. The style guide was in the context window; it just wasn't in the output. The insight came sideways: stop trying to *prevent* the problem and instead detect and fix it *after*. Separate the loop. Let the model generate freely, content first, style be damned, then run a second pass to identify violations and rewrite offending sentences. Two distinct operations instead of one conflated mess. But detection had to be deterministic. "Sounds too AI-like" is a feeling, not a rule, and I needed something mechanical. So I went through my own edits, every angry margin note: "NO." "This is generic." "Sounds like a robot." "Nobody talks like this." I collected them, and patterns emerged: specific phrases, structural tics, telltale rhythms. I had an AI analyze my complaints, extract the rules, and build a linter. The linter didn't need to understand good writing; it just needed to catch bad writing reliably. Generate, lint, rewrite flagged passages, repeat until clean. **Separate content generation from style verification. Make verification deterministic.** The style problem was solved. --- ## Prose Is Compiled Output With style solved, I expected the rest to fall into place. It didn't. Editing was still exhausting. I'd generate a chapter, run the linter, fix flagged patterns, read through the result, and something would still be off. Not style, but substance. This explanation doesn't land. That example feels disconnected from the point. Rewrite the paragraph, regenerate, lint again, read again. Progress measured in inches. The frustration had a familiar shape. It reminded me of debugging assembly code years ago, tracing registers and memory addresses to find a logic error that would've been obvious in the source. I was working at the wrong layer. The realization hit: prose is compiled output. The sentences, the word choices, the rhythm: these are *microstates*, countless variations that can express the same underlying idea. When I edited a sentence, I was fighting at the microstate level. One paragraph can be phrased a hundred different ways; fix one phrasing, the model generates a different one next time, and the cycle repeats forever. But here's the insight: a paragraph is a *macrostate*. It has a rhetorical function: to claim something, explain a mechanism, provide an example. The specific sentences are just one instantiation of that function. If the macrostate is right, microstates can vary freely. If the macrostate is wrong, no amount of sentence editing will fix it. **I was debugging assembly when I should have been fixing the source.** Writing in the AI age has to change, but not how people think. The goal isn't to stop writing; you still have to articulate your ideas, shape your argument, fight for your voice. What AI accelerates is the expansion from structure to prose, the compilation step. But that only works if you're operating at the right layer. The right layer isn't sentences. It's moves. --- ## What Outlines Throw Away Reading through chapters, I couldn't tell if my ideas were complete. The outline said "Section 3.2: Context Construction." The output had paragraphs about context construction. But did it cover what I actually *meant*? Did it make the right distinctions? Did it land the insight I was reaching for? I couldn't tell without reading every word. At 300 pages, careful reading is its own full-time job. The outline was supposed to be my map, but staring at it, I realized it doesn't tell me what each paragraph *does*. "Introduction to X": is that a definition, a claim, or a motivation? "Explain Y": mechanism or example? The outline encoded topics, but not moves. Traditional outlines are lossy compression. They preserve hierarchy and sequence but throw away rhetorical structure, like compiling to an IR that discards type information. You can't typecheck downstream if types were never preserved. **Traditional outlines don't encode rhetorical structure, which is what actually matters.** --- ## Paragraphs Have Types I stepped back and asked a different question: how do you notate idea flow? Not topics, not structure, but the actual movement of a reader's understanding from paragraph to paragraph. What does each chunk *do* to the reader's mental state? Down a research rabbit hole, I found Swales (1990), a linguist who studied how academic papers work. His insight: texts are composed of "rhetorical moves." A move isn't what a paragraph is *about*. It's what a paragraph *does*. The core vocabulary: - **CLAIM**: assert a position - **MECHANISM**: explain why/how something is true - **EXAMPLE**: make abstract concrete - **CONTRAST**: distinguish from alternatives - **QUESTION**: open a tension - **ANSWER**: resolve it Every paragraph has a type. The type constrains what it can contain. The outline transformed. Instead of topic labels, move annotations: ```yaml Context Construction: p1: CLAIM: Context is the only input to the model KEY: "Everything must fit inside that call" p2: MECHANISM: Why this constraint exists CONCRETE_DETAIL: The model has no persistent memory p3: CONSEQUENCE: What this means for system design PRACTICAL: You must reconstruct state every time ``` Looking at a move-annotated outline, I could see structure at a glance. QUESTION without ANSWER? Structural bug. CLAIM without MECHANISM? Missing explanation. MECHANISM without EXAMPLE? Too abstract. I could verify content completeness at the outline level because each move type has a semantic contract: MECHANISM must explain *why*, EXAMPLE must be *concrete*. Violations are visible in the notation itself, before any prose exists. **The IR must preserve the signal you care about verifying.** --- ## The Pattern Months in, the pattern crystallized. I'd been treating generation as monolithic: prompt in, prose out. But it's actually compilation. Source material (ideas, research, intent) transforms through intermediate representations into final output (prose), and the quality of each stage depends on what the previous stage preserved. Why different approaches fail or succeed: **One-shot generation fails** because there's no intermediate state to verify. Ideas in, prose out, and if the result is wrong, you can't isolate *where* it went wrong. No breakpoints, no stack traces. **Multi-step with fuzzy IRs** (summaries, traditional outlines) fails subtly. You have stages, but the representations don't preserve what matters. The outline *looks* like structure, but it's lossy, and critical information evaporates at the boundary. **Multi-step with typed IRs works.** Move notation preserves rhetorical structure, the style linter preserves voice constraints, and each pass has a verifiable invariant. You can inspect each IR and know whether it's correct before compilation continues. **Multi-step AI generation works when each step produces an intermediate representation with verifiable invariants.** The general pattern for any AI generation task: 1. Identify what signal you need to preserve 2. Design an IR that encodes that signal explicitly 3. Make verification deterministic where possible 4. Separate concerns into dedicated passes This is why "better prompts" is the wrong frame. It's not about prompting. It's about architecture. --- If you're writing with AI and fighting the same battles: start with verification, not generation. Build a style linter from your own complaints. Annotate one existing piece with move notation to see if structure becomes visible. Generate per-move instead of per-section. **Start with verification. The generation will follow.** The book isn't done, but the system works. Move notation doesn't eliminate editing; it localizes errors. The style linter doesn't prevent AIisms; it catches them mechanically. Each tool solves one problem cleanly instead of all problems poorly. The goal was never "AI writes for me." It's "AI generates, I verify at the right abstraction level." You're not writing. You're compiling. --- ## What is an Agent? URL: https://wcdc.io/writing/what-is-an-agent Date: 2026-01-19 Description: Agent is whatever you define it to be, because it's actually all just system design. I've spent the last two years building AI systems, and in that time I've watched the same argument loop endlessly on Twitter, in Slack channels, at conferences: what counts as an "agent"? Some say an agent is an LLM that can use tools. Others insist it needs memory. Others say it needs to plan, or to be autonomous, taking actions without human approval. The discourse is infinite and circular: AI Twitter argues about definitions, frameworks ship "agent" features, companies rebrand their chatbots as "agentic." And the whole conversation misses the point. **"Agent" is not a technical primitive. It's a marketing term that gestures vaguely at "AI that does stuff."** When you peel back the abstraction, there's no fundamental unit called an "agent." There's just code: context construction, model calls, tool execution, state management, control flow. The patterns you choose to compose from these primitives determine what your system can do. The word "agent" obscures more than it reveals, and the debate about what qualifies as one is the wrong debate entirely. The useful question is different: **what are the actual engineering primitives for building intelligent systems?** --- ## The Illusion of the Entity When people say "agent," they typically imagine something like a persistent entity: a digital mind that remembers you, pursues goals, and takes actions in the world. When you talk to "the agent," you're talking to *it*, a coherent self that exists across your interactions, accumulating context and refining its understanding of you over time. This intuition is incomplete. The entity is real, but it doesn't live inside the model. It's distributed across four components: 1. **The model**: Stateless text transformation. Given input, produce output. No memory, no identity, no continuity between calls. 2. **The context**: The input constructed for each call. This is where "memory" lives during a conversation: the system prompt, the message history, retrieved documents, tool results. Everything the model knows comes from what you put in front of it. 3. **The storage**: Persistent state across sessions. Databases, vector stores, file systems. What the system "remembers" between conversations lives here, not in the model. 4. **The orchestration**: Control flow logic that decides when to call the model, what context to construct, which tools to invoke, when to stop. This is the glue that turns stateless calls into coherent behavior. The "agent" is the composite of all four. The illusion of a continuous entity emerges from your system design, not from any capability inherent to the model itself. You can prove this to yourself: build a chatbot that randomly switches between Claude, GPT-4, and Gemini on each turn, while maintaining the same context window. The user experiences one coherent assistant. The "agent" is your architecture, not any particular model. This reframe matters because it shifts where you look for leverage. If you think the agent *is* the model, you wait for better models. If you understand the agent is the composite, you realize something important: you control three of the four components entirely. Context construction, storage design, and orchestration logic are all your code. The model is a capability you rent. --- ## Agent Is the Wrong Primitive So if "agent" means "the composite system," why do we need the word at all? Mostly, we don't. When someone says "I'm building an agent," what do they actually mean? In my experience, it's usually one of four things: - A chatbot with tool use - An autonomous loop that runs until a task is done - A system with multiple specialized LLM calls coordinated together - Something that remembers context across sessions These are four completely different architectures with almost no shared implementation patterns, yet the word "agent" lumps them together as if they were the same thing. The frameworks don't help clarify matters. LangChain alone has `Agent`, `AgentExecutor`, `create_react_agent`, `create_openai_functions_agent`, `create_tool_calling_agent`: a proliferation of abstractions that reify "agent" as a thing you instantiate, rather than a pattern you implement. The abstraction becomes the territory, and you lose sight of what's actually happening underneath. Strip it down. At the core of every system, regardless of what you call it, there are two fundamental operations: 1. **Context construction**: Deciding what information to put in front of the model for a given call. 2. **Control flow**: Deciding what to do with the model's output: call again? execute a tool? return to user? give up? Everything else, memory, tools, planning, multi-agent coordination, is a specific pattern built on top of these two primitives. When you say "agent," you're usually pointing at a particular combination of context construction and control flow patterns. The useful conversation is about those patterns directly, not about whether something qualifies for the label. --- ## The Reasoning Loop The core pattern that people gesture at with "agentic" is the **reasoning loop**: the model is called repeatedly, with the output of each call informing the input to the next, until some termination condition is met. This is distinct from single-call usage, where you construct a context, call the model once, and return the result. Single-call is stateless request-response; the reasoning loop is iterative search. The simplest reasoning loop looks like this: ```python while not done: response = model.call(context) if response.wants_tool: result = execute_tool(response.tool_call) context.add(tool_result=result) else: done = True return response.text ``` This is the ReAct pattern that underlies most "agentic" frameworks: the model reasons, decides to act (tool call), observes the result, and reasons again. But it's just one loop topology among many. **Reflection loop**: The model generates output, then critiques its own output, then revises. The "tool" is self-evaluation. ```python draft = model.generate(context) critique = model.critique(draft) final = model.revise(draft, critique) ``` **Planning loop**: The model decomposes a task into steps, executes each step, and potentially re-plans if a step fails. ```python plan = model.plan(task) for step in plan: result = execute(step) if failed(result): plan = model.replan(task, progress, failure) ``` **Search loop**: The model explores multiple branches, evaluates them, and selects the best path. Monte Carlo Tree Search with an LLM as the evaluation function. **Hierarchical loop**: An outer loop calls an inner loop. A "manager" model decomposes work and delegates to "worker" models, each running their own reasoning loops. The point is that "agentic" isn't a binary property. It's a spectrum of loop complexity. A single ReAct loop is weakly agentic. A hierarchical planner with reflection and search is strongly agentic. But they're all compositions of the same underlying primitives: context construction, model calls, and control flow. --- ## Why Multiple Calls? A reasonable question: why make multiple LLM calls if you could fit everything in one? After all, context windows are enormous now. GPT-4 handles 128k tokens. Claude handles 200k. Why not just dump everything in and let the model figure it out? The answer is that a single call has fixed information. The call boundary is where three critical things happen: **New information enters.** You can't know what a web search returns until you call it. You can't know what's in a file until you read it. Tool results add information that simply wasn't available when you constructed the original context. **Real verification happens.** The model can "reason" about whether code is correct, but only execution proves it. The model can "think" about what a user wants, but only asking confirms it. Verification requires leaving the model and returning with evidence. **Actions execute.** Sending an email, writing a file, making an API call: these happen at the boundary between model and world. The model produces intent; the system produces effect. A single call is a pure function over its input. Multiple calls let you interleave computation with observation, verification, and action. The loop is what makes the system *actually do things* in the world, not just talk about doing things. This is why the reasoning loop is the fundamental unit of "agency." It's not about whether the model is "smart enough." It's about whether the system architecture allows for incorporating new information, verifying beliefs against reality, and producing effects in the world. --- ## The Anthropomorphization Trap Now there's a tempting move here: if agents are composite systems, and systems can have multiple components, why not design "multi-agent" systems where multiple agents collaborate? You've probably seen the demos. A "researcher agent" and a "writer agent" work together. A "manager agent" delegates to "worker agents." A whole "society of agents" debates and votes. The framing is seductive because it maps to how humans organize: specialized roles, delegation, collaboration, consensus. I want to argue this framing is usually wrong, and often counterproductive. The problem isn't that multiple LLM calls are bad. It's that anthropomorphizing them as "agents" with identities, roles, and social dynamics imports a ton of assumptions that don't actually hold. Consider what's actually happening when you split work across two LLM calls, say a "planner" and an "executor": you're constructing two different contexts (one optimized for planning, one for execution), potentially using different prompts or even different models, and defining an interface for how information passes between them. This is just **function decomposition**. It's the same pattern as splitting a monolithic function into two functions with a defined interface. Calling them "agents" that "collaborate" adds nothing but confusion. The anthropomorphic framing creates specific problems: **Implicit statefulness.** When you think of agents as entities, you expect them to "remember" their interactions. But each call is stateless unless you explicitly persist and reconstruct state. The "researcher agent" doesn't remember what it found unless you build that memory yourself. **Social dynamics that don't exist.** "Debate" between agents isn't actually debate. It's two context constructions that include each other's outputs. There's no persuasion, no updating of beliefs, no compromise. Just text concatenation. **Reliability collapse.** Each agent boundary is a point of potential failure. If one agent misunderstands the task, the cascade fails. Multi-agent systems compound unreliability rather than improving it. The unsexy truth is that for most applications, you want the *simplest* loop that accomplishes the task. Usually that's a single ReAct loop. Sometimes it's a reflection loop. Rarely is it a "society of agents." The multi-agent paradigm has its place: in research, in exploring novel architectures, in cases where context isolation genuinely helps. But it's not a default. It's not the future of all AI. It's one pattern among many, and often not the right one. --- ## System Design, Not Agent Design Here's the reframe that dissolves the whole debate: **agent design is system design.** More specifically, it's a branch of distributed systems engineering. The problems you face building intelligent systems are the same problems you face building any distributed system: - **State management**: Where does state live? How is it persisted? How is it accessed? - **Coordination**: How do different components communicate? How do they agree on shared state? - **Failure handling**: What happens when a call fails? How do you retry? How do you recover? - **Observability**: How do you know what the system is doing? How do you debug it? - **Consistency**: How do you ensure different parts of the system have coherent views? The fact that one of your components is an LLM doesn't change the fundamental nature of the engineering. When you adopt this lens, the "what is an agent?" question dissolves. You stop asking whether something counts as an agent and start asking concrete questions: What's my state model? What's my control flow? How do I construct context? How do I handle failures? How do I observe behavior? These questions have concrete answers. They lead to actual design decisions. And they generalize across every system you'll build, whether you call it an "agent" or not. --- ## The 10 Elements of Intelligent System Design So what *are* the fundamental primitives? After two years of building AI systems, and after writing a book-length treatment trying to derive them from first principles, I've landed on 10 elements that I believe capture the complete design space. Each element is a **category of design decisions** you face when building any intelligent system. ### 1. Context Every model call receives a context: the text that constitutes its input. Context construction is the most leveraged decision you make. What goes in, what stays out, how it's ordered, how it's formatted: these choices determine what the model can do. Context is bounded (the window has a limit) and expensive (more tokens means more cost and latency). Managing this constraint, deciding what to include, what to summarize, what to retrieve on demand, is a core design problem. ### 2. Memory The model is stateless, but systems need to remember. Memory is the persistence layer: databases, vector stores, file systems, caches. The design questions are: what gets stored? How is it indexed? How is it retrieved? Memory systems range from simple (append messages to a list) to complex (hierarchical summarization with semantic retrieval). The right choice depends on your access patterns. ### 3. Agency Agency is the capacity to produce effects in the world: to call tools, write files, send messages, query APIs. A model without agency can only talk about the world. A model with agency can change it. The design questions: what actions can the system take? What constraints apply? How are actions authorized? How are results observed? ### 4. Reasoning Reasoning is the structure of model calls. Single call? Iterative loop? Reflection? Planning? The reasoning architecture determines what kinds of problems the system can solve. More complex reasoning (planning, search, verification) enables harder tasks but costs more and introduces more failure modes. The design question: what's the minimum reasoning complexity that accomplishes the task? ### 5. Coordination When a system has multiple components making model calls (whether you call them "agents" or just "functions"), they need to coordinate. How does information flow between them? How is shared state managed? What's the topology? Coordination patterns include: sequential (pipeline), parallel (fan-out/fan-in), hierarchical (delegation), and cyclic (iterative refinement). ### 6. Artifacts For complex tasks, the output isn't a message but an artifact: a document, codebase, analysis, or plan. Artifacts are structured, versioned, and evolve over multiple iterations. The design questions: what's the structure of your output? How do intermediate states get represented? How do multiple components contribute to the same artifact? ### 7. Autonomy Autonomy is about who initiates action. In a chatbot, the user initiates and the system responds. In an autonomous system, the system initiates on its own: scheduled tasks, triggered workflows, proactive notifications. The design question: what triggers computation? User messages? Schedules? Events? Conditions in the environment? ### 8. Evaluation How do you know if your system is working? Evaluation is the practice of defining and measuring success. Without evaluation, you're flying blind. Evaluation ranges from vibes ("this response looks good") to rigorous (automated evals on test suites with quantitative metrics). The design question: how do you measure quality, and how does that measurement inform iteration? ### 9. Feedback Feedback is information that flows back into the system to improve it: user corrections, thumbs up/down signals, evaluation results, explicit preferences. Feedback is what closes the loop between deployment and improvement. The design questions: what signals do you collect? How do they influence future behavior? Online learning, batch retraining, or just prompt iteration? ### 10. Learning Learning is the capacity for the system to improve over time. Not the model weights (those are frozen), but the *system*: better prompts, better retrieval, better tools, better control flow. The design question: where is learning localized? Is it manual (you edit prompts) or automatic (the system self-improves)? What's the feedback loop? --- ## Using the Framework Use the 10 elements as a **design space map**, not a checklist. Every intelligent system makes choices in each dimension, whether explicitly or by default. When you're designing a system, walk through each element: - **Context**: What information does the model need? How will I construct it? - **Memory**: What needs to persist across sessions? How will I store and retrieve it? - **Agency**: What actions can the system take? What are the boundaries? - **Reasoning**: What's the loop structure? Single call, iteration, planning? - **Coordination**: Is this one component or multiple? How do they communicate? - **Artifacts**: What's the output structure? How does it evolve? - **Autonomy**: What triggers the system? User-initiated or self-initiated? - **Evaluation**: How will I know if it's working? - **Feedback**: What signals will inform improvement? - **Learning**: How does the system get better over time? Each question has many valid answers. The framework doesn't tell you what to build. It tells you what decisions you're making, even when you don't realize you're making them. --- ## Beyond Agents I've been working on a longer treatment of these ideas: a book called *Elements of Agentic System Design* that derives each element from first principles and shows the patterns in code. The framework provides a vocabulary for intelligence design that's more precise than "agent" and more general than any particular framework's abstractions. It maps the space of choices, so you can make them deliberately rather than defaulting to whatever the framework gives you. --- What is an agent? Wrong question. The right questions are: What's my context construction strategy? What's my reasoning loop topology? What's my state model? How do I handle coordination? How do I evaluate success? These questions have answers. They lead to code. They compose into systems that actually work. The word "agent" will keep being useful as a marketing term, as a shorthand, as a way to gesture at "AI that does stuff." That's fine. But when you're building, think in systems, not agents.