DESIGN TRADE-OFFS · SIDE BY SIDE
A 30-line agent loop is not wrong—it answers a smaller question
A tiny loop is ideal for learning tool calls. A harness addresses failure, recovery, governance, replacement, and long-term maintenance. Complexity moves into contracted boundaries.
while (true) {
const reply = await model(messages, tools)
messages.push(reply)
if (!reply.toolCalls) break
for (const call of reply.toolCalls) {
messages.push(await tools[call.name](call.args))
}
}Local causality is clear. Add persistence, approval, cancellation, recovery, and surfaces, and every responsibility crowds the loop.
profile / bundle / patch
↓ compose
Context + scoped effects
↓ drive
Inbox → Turn → Step
↓ publish
SessionEvent + agent/*
↓ project
Web · CLI · ACP · SDKThe value is not fewer lines, but ownership, lifecycle, and test boundaries for each kind of change.
ConcernMinimal agentDeepSeek Harness
State truthIn-memory messages arrayAppend-only SessionEvent + projection
Execution unitOne loop iterationInbox → Turn → 1..n steps → settlement
Tool resultPush text directlyGuard pipeline + paired debt + settlement
PermissionIf inside each toolPreset → approval → monotonic guard
Long contextDrop old messagesCompaction + surface + spill + pair balance
Replace implementationEdit call sitesService definition → provider → consumer
Background workBuild another queueSubagent / Job / Workflow / Schedule contracts
Product compositionCopy config or forkProfile + Bundle + Patch + scoped disposal
USE THE SMALLEST SYSTEM THAT ANSWERS YOUR QUESTION
When should you graduate to a harness?
When you need cross-process recovery, multiple clients, auditable approval, replaceable capabilities, or parallel maintenance, explicit boundaries matter more than local line count.
Start from first principles