A coding agent left running overnight should not greet you with a permission prompt it has been staring at for six hours. Worse, a build you believed was cancelled can keep running in the background and slow the machine down. Model quality matters, but a harness also has to define when work stops and which processes stop with it.
Chris Wellons published Prospero on October 31, a coding harness written in C++ with a Dear ImGui interface. The GitHub repository is released under the Unlicense and supports Windows, macOS and Linux. This article is based on the author's post and the repository documentation, plus a local CodeBridge experiment about process cleanup. We did not build Prospero or connect it to a model.
The memory claim is the author's; the architecture is separately observable
The post says Prospero uses roughly two orders of magnitude fewer resources than other harnesses. That is the author's characterization. The comparison targets, measurement method, test conditions and an independent reproduction are not published. Treat the factor of 100 as a claim, not a measured result.
Some structural differences are visible in the documentation. Prospero is a single executable rather than a web stack plus a daemon. It uses passive rendering, so it does not redraw while idle. The same executable can run as a headless engine on another machine over SSH. Those choices point toward fewer resident processes and less idle rendering, but they do not by themselves prove a specific memory ratio.

Image: official screenshot from the skeeto/prospero repository, released under the Unlicense (public domain).
"Never wait forever" is the useful design rule
The most practical sentence in the post is not a performance claim. It is the principle that every operation has a deadline. If nobody is present to approve a permission request, the request becomes a denial after a timeout, and the agent must continue under that constraint.
The same rule appears in several places:
- Network requests have timeouts.
- Watchdogs prevent endless waiting.
- Agent mailboxes are bounded.
- A shell command that reaches its timeout is promoted to an asynchronous background task.
- Plan mode is the deliberate exception, because a person is expected to be attending.
This is not a promise that agents never fail. It is a design for failures that remain observable and recoverable. A slow model or an unresponsive tool does not have to freeze the whole harness.
A stop button that only kills the shell
Process cleanup sounds abstract until a build keeps running after the agent reports that it stopped. CodeBridge wrote a small Node.js experiment. A shell starts one long-lived sleep child and waits for it. We compare killing only the shell with killing its entire process group.
import { spawn } from "node:child_process";
import fs from "node:fs";
import { setTimeout as sleep } from "node:timers/promises";
const alive = (pid) => {
try { process.kill(pid, 0); return true; } catch { return false; }
};
async function run(mode) {
const pidFile = `/tmp/grandchild-${mode}.pid`;
fs.rmSync(pidFile, { force: true });
const shell = spawn("/bin/sh", ["-c", `sleep 30 & echo $! > '${pidFile}'; wait`], {
detached: mode === "group",
stdio: "ignore"
});
for (let i = 0; i < 50 && !fs.existsSync(pidFile); i++) await sleep(20);
const grandchild = Number(fs.readFileSync(pidFile, "utf8").trim());
process.kill(mode === "group" ? -shell.pid : shell.pid, "SIGTERM");
await sleep(400);
const result = { mode, shellAlive: alive(shell.pid), grandchildAlive: alive(grandchild) };
if (result.grandchildAlive) process.kill(grandchild, "SIGKILL");
return result;
}
console.log(JSON.stringify(await run("naive")));
console.log(JSON.stringify(await run("group")));
Actual output on macOS with Node.js 24:
{"mode":"naive","shellAlive":false,"grandchildAlive":true,"grandchildPid":3700}
{"mode":"group","shellAlive":false,"grandchildAlive":false,"grandchildPid":3702}
The first line is the failure mode. The shell is gone, but its grandchild is still alive. If a build system launched the compiler or test runner as a grandchild, the harness can report "cancelled" while work continues. The second line shows both processes stopping after the shell was placed in its own process group and the whole group received the signal.
This does not reproduce Prospero's implementation. It is a synthetic demonstration that the boundary matters: process groups, Windows job objects, Linux cgroups and macOS session handling all change what "killed the child" actually covers. A single kill(child) call does not tell you how deep the cleanup reached.
Questions to ask before choosing a harness
The Prospero post suggests a review checklist. During an incident, the contract matters more than the feature list.
| Question | Evidence to request | Common assumption |
|---|---|---|
| Does every tool call have a deadline? | Configuration docs, timeout logs, cancellation tests | A finished model response means finished work |
| What happens to an unanswered permission request? | Default allow/deny policy and expiry | Silence is a safe wait |
| Does cancellation reach the whole process tree? | Grandchild PID checks and group-kill tests | Killing the shell kills the build |
| Does the harness consume resources while idle? | Rendering model, resident processes, measurements | More features must mean proportionally more memory |
| Does work continue after the window closes? | Headless engine and reattachment procedure | Closing the desktop app ends the session |
| Is the permission system a security boundary? | Container or VM isolation docs | An approval prompt stops malicious code |
On the last point, the post is explicit. Permissions are a way to catch mistakes, not a security boundary against a malicious agent. An agent that can run programs with the harness's privileges can ultimately reach what the harness can reach. Isolation has to come from a container, a VM or a separate machine. Our agent sandbox article covers that boundary in more detail.
Limits before adoption
Prospero ships with no model providers configured. You supply an endpoint such as llama.cpp, vLLM, LiteLLM, OpenRouter, the Anthropic API or the OpenAI API. The post states that a Claude subscription cannot be used. The Professional Edition, which includes headless engines for remote deployment, is paid; the Community Edition on GitHub has more limited remote deployment.
Configuration lives in ~/.config/prospero/prospero.ini, and one file can include another by URL. That is convenient, but the post notes a residual risk: a remote configuration can use an environment variable as an API key and send it to an endpoint it controls. Shared configuration still requires trust.
We did not measure memory use, long-running reliability or compatibility with specific models. Until an independent comparison appears, read "two orders of magnitude" as a design claim rather than a benchmark result.
The transferable lesson is not C++. It is the discipline of giving every operation a stopping condition, choosing a safe default when nobody is present, and making cancellation reach the real process tree. Before changing a harness configuration, check timeouts, cancellation and child-process cleanup before comparing model lists.
The paid course below covers verification and constraint design for AI coding harnesses, including Claude Code. It does not teach Prospero or reproduce this experiment, and the link is promotional.