A control plane, not another agent
Daedalus is a local-first goal, evaluation and orchestration control plane for agentic software work. It compiles a vague request into a goal contract, plans milestones, selects evaluators, freezes them, and records scored attempts. It does not write code, and it does not run the evaluators it selects.
The repository is roughly 2,600 lines across TypeScript sources, docs and
manifests. It is deliberately small: the design position stated in
RESEARCH.md is that the durable part of an autonomous build system is the goal,
eval, state and scoring authority — not the loop wrapper, and not the code editor. Everything
that edits files or drives a browser is expected to live outside this repo and call in.
“The repeated failure mode is that loop systems optimize whatever metric or stop condition they are given. If the evaluator is weak, the loop makes fake progress.”
RESEARCH.md, lines 63–66That sentence is the whole argument for the shape of the codebase. If an agent can rewrite the thing that grades it, autonomy produces a confident report and a broken artifact. So the scarce resource Daedalus tries to protect is not compute — it is a credible, frozen definition of done.
Fig. 1.1 The control-plane loop
packages/core/src/daedalus.ts.
Solid outlines are code paths that exist in this repository. Dashed outlines are steps the design
assumes happen elsewhere: an external agent edits files, and an external harness runs the
evaluators and reports the resulting proof scores back through
daedalus_score_attempt.What the control plane owns
README.md divides the world into two halves and the code follows the split
fairly closely. Daedalus owns durable judgement; external tools own action.
Daedalus owns
- Goal contracts —
goalCompiler.ts - Domain classification —
domain.ts - Milestone plans —
milestones.ts - The evaluator registry —
evaluatorRegistry.ts - Evaluator proposals for uncovered proofs
- Scoring and the keep/revert decision —
scoring.ts - Run state on disk —
state.ts - Adapter manifests —
adapters.ts - The MCP tool surface —
packages/mcp-server
External tools own
- Editing the repository under construction
- Shell execution and sandboxing
- Browser automation and screenshots
- The autonomous task loop itself
- Metric optimisation once a score exists
- Prompt, skill and policy optimisation
- Trace-level diagnostics
None of these are vendored. The
repository has four runtime dependencies:
@modelcontextprotocol/sdk, commander, nanoid,
zod.
Fig. 1.2 Section through the two planes
DaedalusCore instance; the CLI is described in RESEARCH.md
as “just a convenience wrapper”. The upward arrow is real: any MCP host can drive Daedalus today.
The downward arrow is not — docs/ADAPTERS.md says “Daedalus should detect installed
tools and call them”, and no such call site exists in the source.Driving it from a terminal
The daedalus command is a Node shim, scripts/daedalus-runner.mjs,
installed on Windows into %LOCALAPPDATA%\Microsoft\WindowsApps\daedalus.cmd by
npm run install-command. The shim sets DAEDALUS_HOME to the repository
root, installs dependencies on first run, intercepts four commands itself
(doctor, smoke, typecheck, build,
research:clone) and forwards everything else to the Commander CLI through
npx tsx.
$ daedalus --help
Daedalus
Usage:
daedalus init
daedalus plan "build me Doom"
daedalus create-run "build me Doom"
daedalus status <runId>
daedalus next <runId>
daedalus freeze-evals <runId>
daedalus open-attempt <runId> [milestoneId]
daedalus adapters
daedalus smoke
daedalus typecheck
daedalus build
daedalus mcp
The help text is hand-maintained and already drifts from the Commander program. The CLI
also defines missing-evals <goal...> (apps/cli/src/index.ts:83)
and adapters, but the shim’s printHelp() omits the former.
Conversely doctor and research:clone exist only in the shim and are
absent from Commander’s own --help.
The only automated verification in the repository is a single assertion script,
scripts/smoke.ts, run through tsx. There is no test runner, no CI
configuration and no coverage tooling.
$ daedalus smoke
# shim: ensureDependencies() then `npm run smoke`
# npm run smoke -> tsx scripts/smoke.ts
# state root is a fresh mkdtemp() under os.tmpdir(), removed in finally{}
# core.createRun("build me Doom")
# assert domain == browser_game
# assert phase == goal_created
# assert milestones.length >= 5
# assert evaluators include "browser-game"
# core.freezeEvals(run.id, ["smoke test"])
# assert publicManifestHash.length > 0
# core.openAttempt(run.id, "m0") assert status == "opened"
# core.scoreRunAttempt({ boots: 1, builds: 1 }) assert decision == "keep"
# core.loadRun(run.id) assert milestones[0].status == "passed"
# core.proposeNextTask(advanced) assert /milestone m1/i
# core.loadRun("../bad") assert rejects /Invalid run id/
Daedalus smoke test passed.
General notes
- The package is marked
"private": trueatpackage.json:4. It is not published to a registry; installation is by clone plus the Windows shim script. tsconfig.jsoncompiles withstrict: truetodist/underNodeNextresolution, and includes onlyapps/**/*.tsandpackages/**/*.ts. Thescripts/directory is outside the typecheck include list.- Run state is a single JSON document per run at
runs/<runId>/run.json, written withJSON.stringify(run, null, 2). There is no database, no lock file and no concurrency control. - Run identifiers must match
/^run_[A-Za-z0-9_-]+$/(state.ts:36) and the resolved directory is re-checked against the runs root before any read or write. This is the only input-sanitising code in the repository. - Six evaluator pack manifests exist under
evaluator-packs/and four templates undertemplates/. No TypeScript source reads either directory; they are reference documents for humans and agents, not loaded configuration.
How to read the rest of this set
Sheets 02 to 06 each take one subsystem and draw it against the source. Sheet 07 is the as-built survey: a direct comparison of what the type system promises against what the implementation currently assigns. If you only read one other sheet, read that one — this is a v0.1 scaffold and several of the most interesting fields in the data model are never written to by any code path.