Sheet 01General arrangementScale: NTS

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–66

That 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

The Daedalus control-plane loop from goal text to keep/revert decision A serpentine flow diagram. The upper row runs left to right: user goal text, goal contract, milestone DAG, evaluator selection, frozen eval snapshot. The lower row runs right to left: attempt opened, builder edits the repository outside Daedalus, proof scores submitted by the caller, scoreAttempt, and a keep/revert/needs-review decision, which loops back to open the next attempt. Dimension lines above the upper row separate the pure compile stage from the persisted run stage. Three numbered callouts mark the boundaries where Daedalus stops. PURE COMPILE · domain.ts · goalCompiler.ts · milestones.ts PERSISTED · state.ts → runs/<id>/run.json goal text “build me Doom” GoalContract domain · proofs · doneWhen Milestone[] m0…m5, weighted EvalSelection + uncoveredProofs FrozenEval Snapshot + hash AttemptRecord status: opened builder edits Codex · OpenCode · Claude Code · Aider evaluators run Playwright, shell, gameplay bots scoreAttempt() mean of proofScores decision keep · revert · needs_review proposeNextTask() → next milestone 1 2 3 1 Daedalus never spawns an executor. adapterRegistry is a static array of manifests; listAdapters() returns it verbatim. 2 Daedalus never executes an evaluator either. proofScores arrive as a caller-supplied record of numbers in 0…1. 3 freezeEvals() writes a 32-bit stableHash of the public manifest. hiddenManifestHash is declared in the type and never assigned.
Fig. 1.1 — The loop as implemented in 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

Section drawing separating the Daedalus control plane from the execution plane A sectional diagram in three bands. The top band is the Daedalus control plane containing the goal compiler, milestone planner, evaluator registry, scoring, adapter registry and the state store writing run.json. The middle band is the interface cut: a Commander CLI and an MCP stdio server, both wrapping the same DaedalusCore class. The bottom band is the execution plane of external tools — Codex CLI, OpenCode, Claude Code, Aider, OpenHands, bmalph and Ralph loops, Autoloop, and Playwright evaluators. Arrows from the execution plane upward are solid, because harnesses call Daedalus. Arrows downward are dashed and marked not implemented, because Daedalus contains no code that invokes an adapter. CONTROL PLANE — packages/core goalCompiler createGoalContract milestones planMilestones evaluatorRegistry 7 manifests, static scoring scoreAttempt adapters 14 manifests, static StateStore runs/<id>/run.json class DaedalusCore 10 public methods · constructor(store = new StateStore()) · daedalus.ts:10 INTERFACE CUT Commander CLI apps/cli/src/index.ts · 10 commands tsx apps/cli/src/index.ts MCP server StdioServerTransport · 11 tools daedalus mcp EXECUTION PLANE — not vendored, not spawned Codex CLI OpenCode Claude Code Aider OpenHands bmalph / Ralph loop Autoloop · pi-autoresearch Playwright · gameplay bots GEPA · EvoSkill · AgentEvals · OpenEvolve harness calls Daedalus (MCP) Daedalus calls harness — NOT IMPLEMENTED SOLID = implemented in repo · DASHED + HATCHED = declared in docs/manifests only
Fig. 1.2 — The interface cut is where the honesty lives. Both entry points wrap the same 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.

scripts/daedalus-runner.mjs — printHelp()
$ 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.

npm run smoke — scripts/smoke.ts
$ 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

  1. The package is marked "private": true at package.json:4. It is not published to a registry; installation is by clone plus the Windows shim script.
  2. tsconfig.json compiles with strict: true to dist/ under NodeNext resolution, and includes only apps/**/*.ts and packages/**/*.ts. The scripts/ directory is outside the typecheck include list.
  3. Run state is a single JSON document per run at runs/<runId>/run.json, written with JSON.stringify(run, null, 2). There is no database, no lock file and no concurrency control.
  4. 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.
  5. Six evaluator pack manifests exist under evaluator-packs/ and four templates under templates/. 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.