
Ducklab
io.github.jrullan由 GitHub 帳號 jrullan 發布v0.7.0更新於 Nov 10, 2026
Multi-LLM dev harness, MCP-operable: bugs, cycles, gates. Verdicts are exit codes, never opinions.
概覽
Ducklab 是自架的多模型開發流程工具,其 MCP 伺服器讓外部模型在人工簽署的測試閘門下操作從需求到發布的完整循環。
- 可以做什麼
- 讀取執行結果與下一步清單,決定閘門並附上記錄在案的理由,回答待處理問題、提交缺陷、修訂計畫並啟動任務,決策記為來自 MCP 用戶端而非人工。讀取附加到階段的參考文件,包括摘要背後的全文。列出、讀取並執行專案技能,其中只有實作者席位可以執行可執行技能。所有操作都受與人工操作者相同的權限約束,判定始終是命令結束碼而非模型意見。
- 適用情境
- 當你已在某個專案上執行 Ducklab,並希望另一個模型或代理來驅動開發循環時使用:分流執行、帶理由核准或拒絕閘門、提交缺陷、啟動任務。它不是通用編碼助理,沒有設定好的 Ducklab 引擎和專案時無法做任何事。
- 執行需求
- 以本機 stdio 程序執行,透過 ducklab mcp serve 命令啟動。需要 Ducklab 引擎、CLI 以及每個專案的 git 儲存庫,桌面端還需 Go 1.25+ 和 Node 22+;Linux 是經過測試的平台。模型供應商金鑰由引擎從環境變數按名稱讀取,引擎僅綁定回送位址。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Ducklab,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
Ducklab
A self-hosted harness that runs a project's full development cycle with several LLMs in fixed roles, under test gates that only you sign.
In one block: self-hosted development harness (Go engine + CLI + desktop,
Linux first) · brief → requirements → spec → plan → build → review → release ·
verdicts are exit codes, never model opinions · local models first (llama.cpp,
vLLM) beside any OpenAI-compatible or Anthropic endpoint · operable by humans
or by other agents over MCP with recorded, attributed decisions ·
Apache-2.0 · develops itself (the run records in .ducklab/ are the
receipts). Agents: start at AGENTS.md and llms.txt.
You give it a brief. It writes requirements, a spec and a plan; builds tasks with one model or several arguing; runs your project's real test gate; and stops for you before anything is committed. Every model call is logged. No model ever decides a verdict.
[A live council intake: the architect streams a requirements draft, the reviewer approves, and the run stops at a human gate]
A real council intake, recorded live and sped up: the architect streams the draft, a different model reviews it, the budget ticks in cents — and the run stops at your gate. Total cost of what you just watched: $0.07.
It was built for local models first. Two of the seats that built most of it are a vLLM box on the LAN and a llama.cpp server on localhost, both priced at zero; hosted models sit beside them in the same roster, measured by the same evidence.
Why this exists
Most agentic coding tools assume one strong model and trust it. Ducklab assumes several cheap models and trusts none of them:
- The gate decides, never a model. A verdict is a command's exit code. A test-first run measures a green baseline before any test is written, the red over the new test after, and every accept reproduces the gate from a clean checkout of the committed sha — nothing lands that did not reproduce, and an accept whose reproduction fails takes its own commit back.
- Decorrelation everywhere. A different model reviews; a reviewer never learns who wrote the code (absent from the payload, not hidden in the UI); tournament judges choose blind; council critics read the draft, not each other.
- Work is a contract. A task's deliverables are the implementer's
numbered checklist; it reports on each by number, the reviewer checks each
against the diff, and an undelivered item summons the rubber duck — an
advisor seat that wakes only on measured distress (brake refusals, failure
streaks, red gates) and answers
none, a note that sends the implementer straight back to work, orstop. - Seats are chosen on evidence. Every duckling carries a scorecard — in-seat pass rate from your own runs, cost per run, coding index — and the roster board suggests seats from it, with the ranking criteria yours to reorder. Suggestions are rare and justified: pass rates rank by their Wilson lower bound, three runs minimum, locals never win on a $0 price.
- Nothing is unbounded. Turns, tokens, cost, wallclock, tool output, shell commands — every ceiling visible and liftable mid-run, on the record.
- Your documentation is not bounded by the model's window. Attach a wiki
to a stage and a big seat reads it whole; a small seat gets each document
digested to fit, the full text one
ref_readcall away, and the gate names any document nobody opened. A 32k local model can be briefed by a quarter-million characters of reference material — the harness carries the working memory.
[The run record: 358 runs with verdicts, costs, and reproduced-green accepts]
The record does not round up: every run with its verdict, its cost, and whether its accept reproduced green from a clean checkout.
Ducklab is developed inside ducklab. The plan, the bugs, the releases and the accepted tasks went through its own loop, driven by the same local and hosted models it measures; recent features (per-run worktrees, the merge-proof accept, the acceptance receipts, the governance write guard) were built by the duck and gated by a person. To check the claim yourself:
Receipts ship with every accept since v0.7.0: the committed sha, the gate command, its exit code, and the clean-checkout reproduction verdict — facts a third party re-derives, never assessments.
Status
v0.7.0 plus the phase-3 work now on main: every build and test run
executes in its own git worktree (your checkout is never touched),
acceptance rebases the run branch, re-runs the gate on the rebased commit
and merges fast-forward only, and an operator can re-close a finished run
as landed when its work reached main outside the engine. Before that:
seven stages, five modes, the roster board with measured scorecards,
reference documents with automatic digestion, skills managed from the
desktop, a seated consultant chat (vision verified before images are
sent), bug reports with screenshot evidence, adopt surveys with a
deterministic coverage check, provider-aware queueing that states why a
run waits, escalation suggestions when a seat measurably hits its
ceiling, acceptance receipts (ducklab proof verify), releases,
autopilot, a CLI, a desktop app, and an MCP server — in the
official MCP registry as
io.github.jrullan/ducklab — so another model can operate the loop with
recorded, attributed decisions.
docs/status.md tracks all acceptance criteria and does
not round up. Where code and spec differ, the difference is recorded in
docs/decisions/.
Install
Needs Go 1.25+, Node 22+ for the desktop, and git.
Linux
The CLI and engine are pure Go. The desktop is a Wails v3 app and needs the GTK/WebKit development packages:
On Ubuntu 24.04+ the desktop also needs an AppArmor profile — see
decision 0003 and
packaging/apparmor/.
macOS
Honesty note: ducklab is developed and exercised daily on Linux. The CLI and
engine compile-check for darwin/arm64 on every make cross, but no desktop
build has been verified on a Mac yet — the first person to try it is the
test, and make install gives you the CLI and engine either way. Please
report whatever breaks.
Both
make install installs to ~/.local/bin — make sure it is on your PATH.
It warns when the desktop binary predates frontend/src, because it will
happily install a stale one.
Frontend development without the desktop
To exercise the frontend in a browser, run the engine and Vite in separate terminals, then open the browser with its connection details. The fake engine is the quickest option; the same flow can use a real engine with its opt-in CORS flag:
The real engine remains same-origin restricted by default. --allow-origin
enables exactly one browser origin and is intended for local frontend development
and visual audits; it does not change authentication or the loopback bind. Without
this flag, a browser's cross-origin failure can look like a dead session.
The engine and token query parameters are available only in Vite dev
builds. They can also be supplied as VITE_DUCKLAB_ENGINE and
VITE_DUCKLAB_TOKEN environment variables. The desktop shell continues to use
its injected window.ducklab connection.
Three binaries
Provider keys come from the engine's environment at call time — export them before it starts, or launch the desktop through a wrapper that loads them from your keyring. The app tells you when the engine it adopted is missing a key this app has, with the restart button beside the words.
A cycle, end to end
From the desktop: on first launch, Now → Create your first project; afterwards Settings → Projects → New project, then Work → Documents → Intent → Add intention → Draft it. From a terminal:
Your words are part of the record: every brief is kept verbatim as an
INT-nnn entry before any model reads it, and the requirements it added or
changed point back to it — so a requirement can always answer "who asked for
this, and in what words".
Each stage writes a .proposed file first and waits for you. accept
promotes it; reject restores exactly what the run wrote and nothing else;
"request changes" sends any draft — spec, plan, release notes — back with
your note. Nothing is committed without you (or without the autonomy level
you explicitly granted).
Reference documents ride any stage: --ref ~/wiki/product/ (or the
attach door in the desktop) loads files or whole directories as background
for the architect — grounded by two rules the prompt states outright: the
approved requirements own the scope, and where a reference and the code
disagree, the code is the truth. When the corpus outgrows the seat's
context, each document is digested once (cached by content hash), the full
text stays reachable through the ref_read tool, and the proposal card
lists any document no seat ever opened.
Adopting an existing codebase works the same way: intake reads the code
and writes as-built requirements, the spec marks its sections as-built, and
the plan stays deliberately empty — new work then enters through bug reports
and plan amendments, which is how ducklab itself is developed.
Your project declares its own truth in .ducklab/project.toml: the gate
([verify] — with link_deps and setup for what a clean checkout needs),
how the app launches ([run] with a preflight), and how the project's own
binaries are rebuilt ([install]) so the whole loop runs without leaving
ducklab. Stack capabilities propose run.command choices from build metadata
(for example Meson executables, Cargo bins, Go main packages, or Node scripts),
but a person must adopt one. Once configured, every otherwise-green build gate
executes run.smoke when declared, otherwise it falls back to run.command.
This lets an interactive application declare a headless smoke without opening
its real UI during every gate. run.smoke_expect = "exit" | "live" defines
success: exit requires status 0 inside run.smoke_timeout_s; live requires
the process to survive the whole observation window. When omitted, an explicit
run.smoke defaults to exit, while a run.command fallback or an app with a
URL/health endpoint defaults to live. The gate and UI always state the
effective expectation, so an interactive program that exits cleanly in 20 ms
cannot masquerade as a booted product. An intentional Stop sends a polite
termination to the whole app process group, waits run.stop_grace_s seconds
(3 by default), and only then force-kills it; the same graceful boundary is
used when a live product smoke finishes its observation window.
Gate and shell process trees always receive DUCKLAB_RUN_ID and DUCKLAB_PROJECT_ID. For example, excercise-tracker can use DATABASE_URL=test_db_${DUCKLAB_RUN_ID} in [verify].tests, and a compose preflight can use ${DUCKLAB_PROJECT_ID} as its per-run project name. Ducklab guarantees identity only; provisioning and teardown remain the project's.
Adding a model
--key-env is the name of an environment variable, never a key. No key
is written to config, sent over the API, or kept in shell history.
A ChatGPT / Codex subscription
A ChatGPT plan's Codex allowance can seat ducklings too: an opt-in provider beside the keyed ones, never a default.
- Sign-in: the engine runs OpenAI's device-code sign-in and keeps the
tokens in its own file (
~/.config/ducklab/codex/<id>.json, owner-only), refreshing them as they expire. It never reads or writes the Codex CLI's~/.codex/auth.json: a refresh token rotates on use, so sharing one would sign the other program out. The desktop's Providers panel has the same Sign in button; signing in or out is the person's alone. - Models: whatever the plan includes; the provider's live model list
(the desktop's model picker) shows them. A
:effortsuffix on the model id (low…max) sets the reasoning effort; without one the model's default applies. - Cost: no dollars per token, so leave the costs at zero. That makes these ducklings rank as the cheapest in automatic stand-in choices, and USD budgets never cap them — token caps do. When a usage window runs out, the run pauses as provider weather with the reset time in the reason; resume it after the reset.
- Not sent: the subscription takes no temperature, top-p, output cap or stop sequences, so a duckling's settings for those have no effect there.
[The roster board: flock with scorecards, seats per mode, engine suggestions]
Seat suggestions come with their evidence: pass rates from your own runs, cost per run, coding index. You decide.
The desktop's Roster view is where seats are assigned: drag from the Flock onto a mode's seat, globally or per project, with each duckling's evidence on the card and the engine's suggestions beside the seats. Coding / intelligence / agentic indices come from OpenRouter's benchmarks endpoint when a duckling lives there; your own runs supply the rest.
The fleet that built this repo
This is not a recommendation list. It is this repository's own run record (454 recorded runs, ~2,300 seat assignments as of 2026-08-24), so you can see what actually held which seat. Any OpenAI-compatible endpoint slots in the same way.
Two notes for accuracy. First, "built with local models" here means the
local seats held judgment and documentation roles (judge, reviewer,
scribe, architect) while cheap hosted models did most of the typing;
about a third of all seat assignments ran on hardware in this room.
Second, the pass rates above are measured on my runs (ducklab duckling scorecard, Wilson lower bound). Yours will differ, and that is the
point: the roster works from your record, not from a leaderboard.
[A council run mid-flight on ducklab's own spec]
The same machinery on real work: a council revising ducklab's own spec, 4.5M tokens in, paused once on a budget it asked to lift.
The five modes
ducklab run T-001 --mode <mode>
What it will not do
Invariants, enforced in code:
- A model never decides a verdict. A gate is a command's exit code.
- A green candidate is applied byte-for-byte; nothing is re-generated after it passed.
- A reviewer never learns who wrote the code.
- Nothing lands that did not reproduce from a clean checkout of the commit being merged.
- A reject undoes what the run wrote, and nobody else's work.
- Every budget (tokens, cost, turns, wallclock) has a ceiling you can see.
- Secrets never touch project state.
- The engine is loopback-only. There is no remote mode.
Skills
A skill is a directory with a SKILL.md — under .ducklab/skills/ for one
project, or in the machine-wide skills directory to serve every project
(project shadows global on a name collision). The documentation-only form
has no script and is the default: a recipe a model reads and follows. The
architect reads survey guides before an adopt (skill_list is in its
prompt), the consultant reads them in chat, and only the implementer can
skill_run an executable one.
Skills are administered from the desktop (gear → Skills): list with
scope badges and validation problems, read, edit the whole SKILL.md,
run with arguments, delete. A skill a duckling writes during a run shows
there greyed pending acceptance until its run is accepted — proposing a
skill goes through the same gate as proposing code.
The consultant
Every project seats a consultant (a Common seat on the roster board): the model behind the "chat about this" doors and the free-form chat in the guide rail. It reads the code, the runs, the boards and the skills — never writes — and takes images: paste a screenshot of a broken view and ask. Vision is verified, not assumed: a declared-vision seat is probed with a real image request once, and a text-only seat refuses the paste with words instead of hallucinating an answer.
[The consultant chat: luna answering 'convince me to use Ducklab']
The seated consultant answering a question about the repo it reads.
Operating ducklab from another model
ducklab mcp serve exposes the whole loop over stdio as an MCP server: an
external model reads each result, decides gates (with a required, recorded
reason — decisions land as approved_by: mcp:<client>, never as "human"),
answers questions, files bugs, amends plans and starts work. The engine's
next lists are the law: an operator cannot take an action a person could
not.
An operator does not have to poll. For OpenCode, the
ducklab-bell plugin wakes a
chosen session whenever a run needs a decision, and in act mode carries a
coordinator policy so that session runs errands end to end ("fix the bugs")
and escalates what is the person's.
Giving each agent its own access
By default every agent that can read engine.json shares one token with
full access to every project. Give each agent its own instead — Settings →
Agent access, or from your own terminal:
The secret is printed once; put it in that agent's MCP server environment as
DUCKLAB_TOKEN. The agent then reaches only the projects you granted —
read-only reads, full acts — and everything it does is recorded under its
own name (agent:opencode-ti36). Even full cannot change a project's
rules (its config, autonomy, gates, skills) or name files outside it. Change
grants or revoke at any time. Once
every agent has its own token, turn the shared one off
(ducklab token legacy off).
This orders, limits and audits agents; it is not a sandbox. full runs the
project's builds, tests and app — code the agent's ducklings wrote — with
your rights, so give full only to an agent you trust with that project;
read-only is the level that contains one. And an agent running as your
own OS user can read files you can, human.token included.
Contributing
See CONTRIBUTING.md — how to build, how the tests guard the architecture, how work flows through ducklab's own loop, and where to start. The short version:
License: Apache-2.0. Contributions are accepted under the same terms (§5 of the license — no CLA). The Ducklab name and the duck are the maintainer's (§6).
Specification
The code implements a written specification, in this repo:
docs/spec/ (00-VISION through 08-DESKTOP-UI) is the
normative layer — vision, invariants, protocol contracts, acceptance
criteria. What the system IS today lives in .ducklab/docs/ — the as-built
requirements, spec and plan the loop itself maintains, each version signed
at a human gate. Where the two differ deliberately, the difference is
recorded in docs/decisions/; the diff between them is
the roadmap, and the alignment stage computes it.
來源:README.md,提交 82b0aca
工具
0版本歷史
1- v0.7.0最新Sep 16, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分