Motivation
Today we tune agents by feel — swap a model, tweak a prompt, eyeball the result. As agents get more complex (multi-tool, multi-step), that hits a ceiling fast. We want a way to treat an agent configuration as something testable, measurable, and comparable — run a config against a task, record exactly what it did, score it, and compare configs head-to-head.
Most of the foundation already exists. @maka/runtime is Electron-free and headless-capable (RuntimeRunner takes an InvocationRequest → InvocationResult), and runtime-events.jsonl is already a canonical execution trajectory. What's missing is (1) a headless process entry point and (2) the experiment layer on top.
Goal & non-goals
MVP = the shared engine: Config × Task → run in sandbox → trajectory → score → compare. First task type: coding tasks scored by running tests (SWE-bench style). This engine is the model-comparison feature for free.
Explicitly out of MVP scope (all pure additions later, no debt): self-evolution / optimization loop · LLM / rule evaluators (MVP = command/test evaluator only) · Docker backend & real benchmark task packs · Inspect AI adapter · network allowlist · any Electron UI · a general-purpose third-party headless entry point.
Design: three orthogonal contracts
The core idea is experiment = Config × Task, so the two must be separable. Three contracts in @maka/core:
Task — instruction + initial workspace + verification (test command + FAIL_TO_PASS/PASS_TO_PASS semantics). Fields mirror the SWE-bench instance schema so a SWE-bench instance maps in losslessly. Verification lives on the Task, not the Config — so a config under test cannot grade itself (essential for honest benchmarking).
Config (the variable under test) — does not invent a new format; references Maka's existing model/connection selection, toolset, and params, plus a systemPrompt override. The "toolset" is a capability set, not an interactive permission policy.
Execution — in the MVP this is not a config field, it's a single non-overridable safe default. It graduates into a contract later (a pure addition) when a Docker backend lands.
Plus ResultRecord (one per run: pass/fail + cost / duration / steps).
Safety model
The lab is a separate trust context, physically isolated from the interactive permission model. Maka's interactive ask/permission flow does not enter the lab — so "non-interactive batch" and Maka's "never auto-approve" stance don't conflict; they're two different trust models, not a relaxation of the existing one.
Inside the lab, tools auto-approve, and safety comes from isolation, not asking. The non-overridable safe default per run:
- a throwaway workspace copy (never reuse an agent-mutated dir)
- network off by default (MVP fixtures pre-vendor deps; a network allowlist is a named later addition)
- env allowlist (no host secrets)
- timeout + process-tree kill + output cap + temp cleanup
- evaluator runs separately after the agent finishes, using hidden / re-injected test assets (so the agent can't game the tests)
Packaging & output
- New package
@maka/lab (depends on core + runtime + storage) for orchestration + scoring + compare.
- A thin CLI bin with experiment-only verbs (
run, compare). Peer to desktop, no mutual dependency. (@maka/lab ≠ "headless" — headless is a property of @maka/runtime; the lab is its first consumer. A general third-party headless entry point would be a separate layer.)
- Reuses the existing run dir (
sessions/<id>/runs/<runId>/) and runtime-events.jsonl.
ResultRecord JSONL is canonical truth; MVP derives a markdown comparison table (git-diffable, shareable) + a terminal table. HTML/UI are later derived views that never touch the data.
End-to-end: maka-lab run → read Task + a set of Configs → each combo runs once in the safe sandbox (via RuntimeRunner) → trajectory to runtime-events.jsonl → evaluator produces pass/fail + metrics → write ResultRecord → derive markdown table.
Proposed first step
A walking skeleton: single Config × Task end-to-end on one hand-built fixture — the three contracts (minimal), the safe sandbox default, one RuntimeRunner run, the command evaluator, one ResultRecord. No matrix / compare / CLI polish yet. The contracts are easy; the integration (sandbox + headless RuntimeRunner + evaluator composing into one real run) is where the surprises hide, so de-risk that first.
Open questions
- Comparison axes for the first experiments —
model × systemPrompt enough, or also vary toolset/params early?
- Network-off-by-default for MVP fixtures — acceptable, or is a minimal allowlist needed sooner?
- Does mirroring the SWE-bench instance schema on
Task match how we'd want to ingest real benchmark packs later?
Prior art surveyed: jeju · SWE-bench · Inspect AI
Motivation
Today we tune agents by feel — swap a model, tweak a prompt, eyeball the result. As agents get more complex (multi-tool, multi-step), that hits a ceiling fast. We want a way to treat an agent configuration as something testable, measurable, and comparable — run a config against a task, record exactly what it did, score it, and compare configs head-to-head.
Most of the foundation already exists.
@maka/runtimeis Electron-free and headless-capable (RuntimeRunnertakes anInvocationRequest→InvocationResult), andruntime-events.jsonlis already a canonical execution trajectory. What's missing is (1) a headless process entry point and (2) the experiment layer on top.Goal & non-goals
MVP = the shared engine:
Config × Task → run in sandbox → trajectory → score → compare. First task type: coding tasks scored by running tests (SWE-bench style). This engine is the model-comparison feature for free.Explicitly out of MVP scope (all pure additions later, no debt): self-evolution / optimization loop · LLM / rule evaluators (MVP = command/test evaluator only) · Docker backend & real benchmark task packs · Inspect AI adapter · network allowlist · any Electron UI · a general-purpose third-party headless entry point.
Design: three orthogonal contracts
The core idea is
experiment = Config × Task, so the two must be separable. Three contracts in@maka/core:Task—instruction+ initial workspace + verification (test command +FAIL_TO_PASS/PASS_TO_PASSsemantics). Fields mirror the SWE-bench instance schema so a SWE-bench instance maps in losslessly. Verification lives on the Task, not the Config — so a config under test cannot grade itself (essential for honest benchmarking).Config(the variable under test) — does not invent a new format; references Maka's existing model/connection selection, toolset, and params, plus asystemPromptoverride. The "toolset" is a capability set, not an interactive permission policy.Execution— in the MVP this is not a config field, it's a single non-overridable safe default. It graduates into a contract later (a pure addition) when a Docker backend lands.Plus
ResultRecord(one per run: pass/fail + cost / duration / steps).Safety model
The lab is a separate trust context, physically isolated from the interactive permission model. Maka's interactive
ask/permission flow does not enter the lab — so "non-interactive batch" and Maka's "never auto-approve" stance don't conflict; they're two different trust models, not a relaxation of the existing one.Inside the lab, tools auto-approve, and safety comes from isolation, not asking. The non-overridable safe default per run:
Packaging & output
@maka/lab(depends on core + runtime + storage) for orchestration + scoring + compare.run,compare). Peer todesktop, no mutual dependency. (@maka/lab≠ "headless" — headless is a property of@maka/runtime; the lab is its first consumer. A general third-party headless entry point would be a separate layer.)sessions/<id>/runs/<runId>/) andruntime-events.jsonl.ResultRecordJSONL is canonical truth; MVP derives a markdown comparison table (git-diffable, shareable) + a terminal table. HTML/UI are later derived views that never touch the data.End-to-end:
maka-lab run→ read Task + a set of Configs → each combo runs once in the safe sandbox (viaRuntimeRunner) → trajectory toruntime-events.jsonl→ evaluator produces pass/fail + metrics → writeResultRecord→ derive markdown table.Proposed first step
A walking skeleton: single
Config × Taskend-to-end on one hand-built fixture — the three contracts (minimal), the safe sandbox default, oneRuntimeRunnerrun, the command evaluator, oneResultRecord. No matrix / compare / CLI polish yet. The contracts are easy; the integration (sandbox + headlessRuntimeRunner+ evaluator composing into one real run) is where the surprises hide, so de-risk that first.Open questions
model × systemPromptenough, or also vary toolset/params early?Taskmatch how we'd want to ingest real benchmark packs later?Prior art surveyed: jeju · SWE-bench · Inspect AI