Repository navigation
Releases
v0.17.0
π·οΈ v0.17.0 β every worker gets a name tag
This one was learned in the field, not designed at a desk. A real /dobby:execute run hit a wall on its very first dispatch, the operator patched the protocol by hand mid-build, and /dobby:learn turned that session into the kit. The headline: the build loop's workers are now addressable per task, and you can watch the whole plan move, step by step, while it runs.
π Per-task worker names
The dispatch protocol used to tell the Architect to name every worker dobby:<role>. The Agent tool's name field rejects a : outright, so that first dispatch failed with an InputValidationError every single time, and even if it hadn't, one name per role collides the moment two tasks run in parallel. Workers are now dispatched as test-author-t<id> / implementor-t<id> / qa-t<id> (the subagent_type stays dobby:<role>), every dispatch opens with the task's roster so a worker knows exactly who its siblings are, and a task's three workers are dispatched once: every later round reaches the same name by SendMessage, which resumes the agent with its context intact. No more fresh workers re-reading the world mid-task. dobby spec lint now also checks that a task's # cell is a legal name fragment, so an address can never be malformed by construction.
π A live status table after every step
Ask any operator watching a multi-task run what they want and it is this: after every worker verdict and every fix round, the Architect prints one row per task, one column per step, one emoji per cell (βͺ not started Β· π in progress Β· β failed, in fix Β· β passed Β· β n/a), with the transitions spelled out so two runs never show the same state two different ways. The closing summary table is unchanged; this is the running view that was missing.
π Fix rounds close themselves
A round used to have a hole in it: QA reported a defect, the implementor fixed it, and nobody said who woke QA back up. Now the fixer messages back by name with the round number. And a corrected test contract always returns through the implementor's Exit gate before QA resumes, because QA never runs the suite, so a contract fix that skipped the gate would have been an unproven done. The test-author agent gained SendMessage to hold up its end of that conversation.
ποΈ Affected areas that match how apps are actually built
spec lint rejected TanStack-style $param route directories as "not a real path", and accepted a not-yet-existing area only when its direct parent already existed. The field workaround was to name whole directories, which made every task overlap and serialised the entire plan. Both rules are relaxed: $ is a legal path character, and a planned path just needs some existing ancestor below the repo root. task-decomposition.md now says it plainly: name files, not directories, from the first draft. Parallelism is back.
β οΈ Two things this release asks of you
- Reload before your next execute.
test-author's tools changed, and Claude Code registers agent tools at session startup. Run/reload-plugins(or open a fresh session), or your test-authors will be mute. - A spec can go red.
dobby spec lintnow rejects a#cell that is not a short[A-Za-z0-9_-]token. Renumber the tasks1, 2, 3β¦and it passes.
The full walkthrough lives in plugin/skills/upgrade/references/v0.17.md, and /dobby:upgrade walks it for you.
β¬οΈ Upgrade
bun update @kvnwolf/dobbyThen /reload-plugins, and run bunx dobby spec lint on any live STATE.md before your next /dobby:execute.
v0.16.0
π The worktree is yours. dobby works wherever you already stand.
Until now /dobby:scope opened its own worktree for every goal β a kit-named branch under .claude/worktrees/, guarded by a scope preflight that computed slug collisions and nesting before anything ran. That ceremony duplicated what every host already does on its own β claude --worktree, Claude Desktop, t3 code, your IDE, a bare git worktree add β and it made a plain checkout with one goal per branch a second-class citizen: the lifecycle simply refused to run there.
v0.16.0 drops the ceremony. Scope grounds the goal where the session already stands, and finish cleans up exactly what it finds there β a worktree somebody opened, or a branch on a plain checkout. Same skills, same bunx dobby up/down, one path fewer to maintain. This very release was built that way: a branch on the main checkout, no worktree, closed by the new finish. It worked the first time.
π§ /dobby:scope no longer creates anything
Run it in a worktree you opened β with any tool β or on a branch in a plain checkout. It writes STATE.md where you are, and brings the run up with bunx dobby up exactly as before (the setup phase, .worktreeinclude re-materialization, the two-step bring-up β all unchanged). The goal's slug is basename(workroot) everywhere: the Neon branch, the cmux panes, the workspace rename. There is no nesting guard left because there is nothing left to nest β whoever made the worktree owns that judgment, not dobby.
π§Ή /dobby:finish is symmetric: it tears down what it stands in
The merge gate is the same as ever β pr watch says merge-ready, you pick "Merge & finish", the squash is pinned to the validated SHA. After bunx dobby down --json, teardown now branches on where the session stands:
- In a linked worktree (whoever made it): finish offers to remove it and its branch β native
ExitWorktreefirst, so the cwd comes back to the main checkout; on refusal, raw git frommainRoot, never from inside the directory it's about to delete. - On a plain checkout: there's no worktree to remove. Finish switches back to the goal's base branch, force-deletes the goal's branch (
-D, because a squash-merge leaves a tip git will always call "unmerged"), and pulls.
Which branch is "back"? That single field took five review rounds, and the answer is now a decision, not a heuristic. Finish returns you to the branch the goal's PR was merged into β its baseRefName, reported as defaultBranch with defaultBranchSource: "pr-base". A goal opened against develop belongs back on develop; dragging you to a repository-wide trunk would move you away from where you were working. Only a branch that never had a PR falls through to the git cascade β a validated origin/HEAD (a dangling symref counts as unset), then origin/main/origin/master, then local main/master, then null β and null stops with the three commands for you to run, rather than guessing at a branch the checkout can't reach. If the base exists only on the remote, finish creates it with git switch --track. The reasoning lives in ADR-0033 so the next reviewer finds the decision, not the question.
π dobby finish --preflight reports where you stand
The payload is rebuilt around the current workroot: inWorktree, worktreePath, mainRoot, branch, defaultBranch + defaultBranchSource, pr (state/mergedAt/url), dirty (count + files, untracked included), dobbyInstalled β now probed at the workroot, not the main root, so a worktree missing its install can't hide behind a healthy main checkout β branchDeleteSafe, reasons[], and the unchanged verdict set (safe / blocked / confirm-required). Nothing in it removes anything; the skill branches on it and asks you at every destructive gate.
β οΈ Breaking changes β read before you upgrade
/dobby:scope opens no worktree. Want the goal isolated? Open one yourself first β claude --worktree, or git worktree add from the main checkout β or just work on a branch. If your CLAUDE.md tells scope to expect or create a worktree, drop that line.
dobby scope preflight is gone. The command, its collision/nesting detection and its suggestedSlug are removed with no replacement. A collision is now whatever your host or git reports.
dobby finish --preflight changed shape. --slug and candidates[] are gone (there is no kit-assigned slug to disambiguate). mode (same-session/orphan) and removeMechanism (ExitWorktree/raw-git) are replaced by inWorktree, worktreePath and mainRoot β the caller derives the removal path from those. defaultBranch and defaultBranchSource are new. Anything scripting against the old keys needs the new payload.
Finish on a plain checkout may stop. When no base branch can be resolved (defaultBranch: null), finish leaves the PR merged and the run torn down, prints git switch <trunk> / git branch -D <branch> / git pull, and ends. That's on purpose β it will not guess.
π¦ Upgrading
bun update @kvnwolf/dobby/dobby:upgrade walks this note (v0.16.md) if you're jumping several versions. The decision and the two rejected alternatives β keep-behind-a-flag, and never-touch-worktrees β are in ADR-0033.
v0.15.0
π§ dobby stops driving your environment and starts telling the model how to.
v0.14.0 put the host-specific mechanics behind one seam β open a cmux pane, spawn a detached process, open a browser, rename the workspace, tear it all down. That worked exactly as long as a CLI could control the host. Claude Desktop and t3 code drive their surfaces through MCP tools that only the model can call, so on those hosts the seam had nothing to execute and no way to say so.
v0.15.0 turns every adapter into a catalogue of instructions. dobby still executes what only it can execute β the setup phase, the liveness probe, pidfile registration, Neon branches, your setup[]/teardown[] extras. Everything host-shaped it now describes, per topic, and the model carries out. One shape everywhere, and two new hosts for free.
π§ dobby instructions <topic> β the catalogue, readable on its own
Four topics: start, stop, browser, rename. Ask for one and you get the instruction for the environment you're actually in, as text or --json ({environment, topic, applies, text}). A topic that doesn't apply β rename on a plain terminal, which has no workspace to rename β answers applies: false with exit 0. That's a valid answer, not an error; you never have to know the environment/topic matrix to avoid a legitimate red. An unknown topic exits 1 and names the four.
π₯οΈ Four adapters, one file each
cmux, terminal, t3-code, claude-desktop, under cli/src/adapters/. Detection is an ordered cascade with terminal as the floor: CMUX_WORKSPACE_ID β cmux; CLAUDE_CODE_ENTRYPOINT β {claude-desktop, claude-desktop-3p, local-agent} β Desktop; __CFBundleIdentifier starting with com.t3tools.t3code β t3 code; anything else β terminal, whose instructions (a background Bash, claude-in-chrome falling back to curl) work on any host. A new SDK host you've never heard of degrades instead of breaking.
The t3 code adapter was written against tool schemas loaded live in a t3 code session and verified against the vendor's source (v0.0.38). Its browser guide follows the vendor's own order β preview_status first, so the model reuses a tab instead of opening a second one β and names all 14 mcp__t3-code__preview_* tools. The Claude Desktop adapter is written from a field study and marked unverified in code until someone runs it against a live Desktop; public docs confirm the entrypoint values and the server name Claude Browser, not its method names. The study itself now lives in docs/field-studies/, so the adapter's provenance survives.
π up hands you what to do next β and does it in two steps
bunx dobby up --json no longer starts the server. It runs setup, probes the devUrl once, and returns live plus instructions[] β rename under cmux, start when nothing answers. You carry them out, run up again, and it sees the registered start in flight, waits in its liveness loop, and comes back live: true. A returned instruction is ok: true; it is the normal path, not a failure, and /dobby:scope's abort-or-degrade gate now fires only on real failures.
That two-step protocol is written once, in plugin/skills/execute/references/bring-up.md, and cited by every skill that used to promise "up ensures the run is up" β eight of them. If the contract changes again, one file changes.
bunx dobby down --json is new and symmetric: {cmux, instructions, ok, reason, slug, workroot}, with a stop instruction naming the exact panes to close when there are any.
πͺͺ dobby dev owns its pidfile β and survived six review rounds proving it
bunx dobby dev now registers itself in .dobby/dev.pid on startup, refuses a live twin (already running (pid N) β dobby down stops it, exit 1, nothing spawned), and clears it on teardown β so down finds the process no matter who launched it: the model, you by hand, or nobody. Greptile then spent six rounds on that registry, and it earned its keep: registration is an atomic O_EXCL create; a stale file is reclaimed by atomic rename with re-classification of what was actually captured; an ownership probe that can't answer (a failing ps, unparseable timing) never deletes and never reclaims β inconclusive is not dead; and down sweeps the dev.pid.stale.* sidecars a failed restore leaves behind. What's deliberately still open β a live registration stranded in a sidecar during a three-process race, visible to up only after down β is recorded in ADR-0032 as accepted residual risk, with the alternative that was rejected and why.
One more thing from that review, worth saying plainly: a repository path containing $(β¦) executed when the cmux start instruction was pasted into a shell. Every dynamic argument on every cmux command line is now single-quoted, nested where it has to nest, and the tests prove it by running the emitted lines through sh against a recording stub.
π§ͺ CI runs the real dev now
The real-dev tests used to pass on CI by accident β portless wants sudo for port 443 and dies without a TTY, and the old test only waited for the pidfile to exist, which it did for an instant. They now run against a portless on an ephemeral unprivileged port with CI=1, and wait for the app to actually start.
β οΈ Breaking changes β read before you upgrade
dobby env --json no longer carries browserGuide. Anything that read it gets undefined. The guide moved to dobby instructions browser --json; dobby:qa already reads it there and now declares the three MCP tool families (claude-in-chrome, Claude_Browser, t3-code).
up no longer starts the dev server. It returns instructions[] and live. Any prompt or CLAUDE.md of yours that says "bunx dobby up ensures the run is up" is stale β point it at bring-up.md's two steps.
dev-start-failed is gone from reason; neon-branch-failed is new. A failed start is now diagnosed by the model that ran the command and can see its stderr β more than the old boolean ever told you.
dobby dev exits 1 when a live registered twin exists. dobby down stops it.
The Environment interface is reshaped β ensureRunSurface, ensureBrowserSurface, closeSurfaces, renameWorkspace and browserGuide are gone; what's left is id, cmux, discoverPanes and instruction(topic, ctx). Only relevant if you wrote an adapter.
Restart your session after upgrading. The dobby:qa agent's tools: frontmatter changed, and Claude Code pins the agent registry at startup.
Known limits. t3 code detection is macOS-only (the bundle id is an Info.plist artefact; t3 code injects no variable of its own β on Linux/Windows it falls to terminal and loses its browser guide). The Claude Desktop adapter is unverified against a live Desktop. .claude/launch.json is deliberately neither read nor written: dobby owns the devUrl model through portless.
π¦ Upgrading
bun update @kvnwolf/dobbyThen restart your Claude Code session before the next /dobby:execute. /dobby:upgrade walks the full v0.15 note if you're jumping several versions.
The decision, the four rejected alternatives and the accepted residual risk are in ADR-0032; ADR-0031's vendored cmux protocol is unchanged in content and changed in channel.
v0.14.0
𧦠The build loop stopped being a script.
Until now, /dobby:execute handed your whole plan to a native Claude Workflow: a JavaScript program that sequenced waves, counted retries and classified failures. It worked, and then a real run showed what it cost β 15 tasks, 1h43m, 1.75M tokens, 3 finished. One task ran out of its two attempts and the script switched off ten dependents in a cascade. Ten of that run's verdicts were environment β a dead browser β and each one burned an attempt exactly like a genuine bug would.
v0.14.0 takes the script out. The Architect now dispatches named subagents itself, and they talk to each other.
π Tasks start when they're ready, not when a batch says so
Waves are gone. A task begins the moment its own dependencies are done β no barrier waiting for a group to finish. Two exceptions, both deliberate: a destructive task runs alone, and two tasks whose Affected areas overlap never run at once (compared as real, canonical repository paths, which dobby spec lint now enforces).
And when a task dies, only its genuine dependents stop. Everything independent keeps going. No more five empty waves printing β.
π¬ Workers now message each other
This is the one that changes how a fix feels. When QA finds a defect, it no longer ends the round so a fresh implementor can re-read the task, the spec and the code from nothing. It writes directly to the implementor that still has all of it loaded β and to the test-author when the test contract itself is the problem, described as behaviour only, never implementation, so the tests stay honest.
QA numbers every message it sends ("round 2 of 5") so the count lives in the text instead of somebody's memory. Five rounds, then it stops and reports. An environment failure goes to the Architect and doesn't spend a round β a dead browser is not a bug in your code.
π§± Quality split into three layers, one owner each
Nothing told the old verifier what its job was, so it grabbed the widest thing available: it ran the entire quality gate and then discarded the lint findings by hand, and triaged a 3130-test suite to work out which reds were even its own.
Now:
- The edit hook applies Biome's safe fixes and reports type errors β scoped to the file you just edited. On TypeScript 7's native compiler that costs about 0.15 s, so a type error surfaces as you write it instead of ambushing you at commit.
- The implementor runs the full gate itself before handing off β the Exit gate, serialised so two of them never judge each other's half-written code.
- QA proves behaviour and nothing else: the browser where there's an app, exercising the artefact for real where there isn't.
- The PR review judges the finished change, tests included.
Tests deliberately did not move into the hook. vitest related pulls in 24 of 29 suites and ~48 s from any file in a densely-imported repo β and since the test-author writes the contract before implementation, a task's tests are red by design the whole time. Type checking moved; the suite stayed.
π― No more hand-written KNOWN-RED lists
Tasks used to carry prose telling agents which failures to ignore. One agent cloned main into /tmp just to be sure. Now a green baseline records what was already failing before the work began β by test identity, not by file β and the gate reports only what newly broke. A file with duplicate test names is marked ambiguous and exempts nothing, and says so, because a gate that guesses is worse than one that shouts.
π QA stops rediscovering how to drive a browser
The cmux browser protocol now ships inside the CLI, with its provenance recorded, and dobby env hands QA the instructions for the environment it's actually in. No lookup, no reconstruction from memory.
π And the ground for what's next
Environment coupling moved behind an adapter seam (cli/src/environment.ts) with two real implementations β cmux and plain terminal. Four scattered CMUX_WORKSPACE_ID reads became one. Supporting another host runner is now writing an adapter instead of editing the CLI. (Refs #27 β the Claude Desktop adapter is still ahead.)
β οΈ Breaking changes β read before you upgrade
dobby:verifier is now dobby:qa. This is the one that will bite. Claude Code pins its agent registry at session startup, so a session you already had open keeps the old one and dobby:qa won't resolve. Run /reload-plugins, or start a fresh session β and on a host without that command, a fresh session is the only way through. If /dobby:execute reports an unknown agent type right after upgrading, this is almost certainly why.
The edit hook now reports type errors, not just Biome findings. Expect to see more at edit time β that's the point, and it's scoped to the file you touched.
The implementor now runs the gate itself before handing off, inverting the old rule that forbade it from running any check.
dobby env no longer reports workflowRecipe. Agent model and effort live in each agent's frontmatter, full stop. dobby build-plan no longer emits waves[][] β it returns tasks with their dependsOn and destructive flags.
π¦ Upgrading
bun update @kvnwolf/dobbyThen restart your Claude Code session (or /reload-plugins) before running /dobby:execute.
/dobby:upgrade walks the full v0.14 note if you're jumping several versions.
Three architecture decisions came out of this one, if you want the reasoning: ADR-0029 (subagent dispatch, superseding ADR-0023), ADR-0030 (the three-layer quality model), and ADR-0031 (vendoring the browser protocol). ADR-0029 also records the residual risks that were accepted rather than hidden β worth reading if you run plans with many parallel tasks.
v0.13.1
π§Ή The trim sweep can actually finish now
/dobby:trim-context had a humiliating secret: point it at a real repository and it would never finish. A field run against a 948-file codebase reached 1.2% coverage and could not write a single line of its ledger. Not slowly β never. Three rules in the sweep contract guaranteed it, and this release replaces all three with what actually worked when the same repository was swept by hand: β263,238 bytes across 165 files, gate green, zero executable lines touched.
π Judgment, not inventory
The old contract demanded one inventory row per eligible unit. On that repo, that meant 24,113 rows β the entire budget spent writing a table before a single judgment call happened.
Now the inventory is one row per file, ranked by comment-byte weight, and per-unit decisions happen inside the lot that touches the file. The Tree-sitter extractor stays exactly where it was (it was never the bottleneck β it ran once, cheaply, in seconds), but it is now the targeting input that ranks files into lots rather than the source of an exhaustive census.
β Approve once, then walk away
The old contract wanted every batch approved before any worker wrote a byte. At repository scale that gate can never clear β you'd be clicking approvals until the heat death of the universe.
Now you approve scope and aggressiveness once, up front: which tracks run, how aggressive, what's off-limits. Lots then run unattended. The sweep comes back to you only when the policy needs to change, or something blocks. Go make coffee.
πΎ Progress that survives
The worst of the three: a single parse-error voided the entire sweep. 43 unparseable .sql migrations permanently vetoed coverage genuinely earned on ~900 other files, and every rerun started from zero.
Coverage is now incremental and checkpointed per lot. A file that couldn't be resolved simply carries no sub-key β which already meant "not covered", so there's no schema change β and the savings report names it with its reason. An interruption, a blocked lot, an unparseable migration: each costs only itself. Everything already verified stays verified.
The honesty invariant is untouched, and it's the whole point: the ledger never claims coverage it doesn't have. What changed is that missing coverage no longer erases real coverage.
π Nothing gets certified without being validated
Four rounds of adversarial review chased one invariant through this design, and each round found it hiding a level deeper. All four were real:
- Comment lots could skip semantic review whenever the mechanical diff came back clean β but deleting a
biome-ignore, a licence header or a security warning changes no executable line either. The exemption is gone: every lot gets the cheap mechanical proof and an independent semantic review. - The checkpoint writer mutated the canonical ledger before validation. Now it stages to
.dobby/sweeps.json.pending, the reviewer validates that staging file alone, and only the coordinator promotes it. /dobby:anti-slopshares that same ledger file β so it got the same staged-then-promoted ordering. Nothing else about it changed.- Nothing bound a checkpoint to the lot that just passed, so a stale staging file could be promoted as someone else's coverage. The reviewer now verifies the increment's paths match the lot's file set exactly.
π― The recipe that worked, encoded
Lots of 6-8 files by weight (a 47-file lot exhausted one implementor at 13 files, 430K tokens, 257 tool calls). One track per brief β comment-trim or prose densification, never both, after a mixed brief produced a proud β186 bytes. Levers carried forward between lots, worth a steady ~β12% once discovered. And every brief now states its expected yield (~5% on comments, 12-14% on prose) with "leaving a file untouched is a correct outcome" said out loud, because an agent measured on bytes removed will find bytes to remove.
β οΈ One thing to know
rulesVersion moves from human-text-v1 to human-text-v2 β the preservation rules genuinely changed. If you somehow have an existing sweep ledger, its entries no longer certify, and the next run re-sweeps from scratch. In practice nobody does: writing a ledger is precisely what the old contract could never manage. π
π¦ Upgrading
bun update @kvnwolf/dobbyThe CLI itself has no changes this release β everything above lives in the plugin, which autoUpdate carries to you from the marketplace. Bump the devDependency to keep the two surfaces in step.
Worth saying plainly: this is methodology, validated by 1,676 tests and four review rounds, but nobody has yet run the new sweep against a large repository end to end. That's the real test. If you point it at something big, the interesting question is whether it now reaches ledger finality β and the savings report will tell you exactly what it couldn't cover.
v0.13.0
Your repository has been writing to your model's context window this whole time, and nobody has been reading the bill. π§Ύ
v0.13.0 adds two sweeps that read it for you β and they start by counting, not by guessing.
π /dobby:trim-context β find out what your comments actually cost
Point it at a repository and the first thing it does is boring on purpose: it enumerates every tracked file and parses the comments out of them with real grammars. No regex, no vibes. On one production app that census came back with 24,113 comments across 1.83 MB β roughly 434,000 tokens sitting in code that every agent reads past on its way to the actual logic.
Only after that does any judgment happen. The skill proposes what to cut, in cost order, and waits for you: text that restates what the code plainly says goes, and the load-bearing stuff stays. Shebangs stay. biome-ignore directives stay. The comment explaining why you round half-up because the tax authority rejects banker's rounding β that one stays, because deleting it is how someone reintroduces the bug in eight months. Generated files are never touched at all.
It owns comments exclusively. No other sweep in the kit will go near them.
βοΈ /dobby:anti-slop β the voice pass, judged in context
The other sweep asks a different question: does this prose fabricate significance? Throat-clearing openers, the "it's not just X β it's Y" reversal that distinguishes nothing, bold bullet triads that define each term with itself.
It judges contextually and in any language, and that word is doing real work. Measuring this very repository is what settled the design: leverage and harness sit on every banned-word list on the internet, and both are domain terms in our own glossary. A skill that banned words by occurrence would have started its career by mangling the architecture vocabulary. So it doesn't ban anything β every finding names the pattern, quotes the exact text, and offers the smallest fix that works. It never claims a human or a model wrote something, and it never touches code comments.
Its taxonomy adapts Peter Yang's no-ai-slop, MIT attribution intact.
π Both sweeps remember what they already read
Coverage lives in a tracked .dobby/sweeps.json, keyed per file and per skill, hashed against exact final bytes. Your first run is a full sweep; every run after that only looks at what actually changed. Neither sweep can certify the other's work.
Everything is approval-gated, written by disjoint workers, and independently reviewed before any coverage is recorded. No new CLI command, config key, check, hook, or gate β these are inference, not enforcement, and your quality gate stays exactly as mechanical as it was.
π What the smoke tests caught
Both skills were driven end to end against throwaway fixtures before shipping, and that found two things static review had waved through. The ledger would never have become tracked in any real consumer, because .dobby/ is ignored β every run would have redone the full sweep, forever. And the two skills had incompatible ledger shapes, so whichever wrote second would have made the first fail closed. Both fixed here. The external reviewer then caught a third: an unbounded worker fan-out that could blow past the concurrency cap. Also fixed.
Worth saying plainly, because it's the useful part: none of these were found by reading the diff.
β οΈ One honest limitation
The bundled SQL grammar can't parse Postgres RLS or ACL statements, and Tree-sitter's error recovery poisons the whole file when it trips β so on a Supabase-style migrations directory, expect a large chunk of .sql to come back uncounted (43 of 58 files in the repo we measured). We evaluated the alternative grammar and rejected it: no WASM prebuild, identical failures when compiled by hand, and a lost provenance chain.
It's declared in the contract, in PROVENANCE.md, and in the extractor's own output. Coverage fails closed rather than pretending.
π¦ Upgrade
bun update @kvnwolf/dobbyNothing is required of you β both skills are opt-in and user-invoked. When you want both, run /dobby:trim-context first, then /dobby:anti-slop. And be ready for the first sweep of a mature repository to propose more than you expect. That's the point.
v0.12.1
π©Ή The cask release that couldn't read its own version
v0.12.0 shipped Homebrew cask releases. It just couldn't finish one β because the pack gate asked Tauri for a DMG and then went looking for an app bundle Tauri had never been told to keep. This release makes that whole path actually work.
π¨ The pack gate now builds what it inspects
The version gate's whole job is paranoia: before anything is tagged, notarized or pushed, it opens the artifact you just built and asks it what version it thinks it is. That answer lives in the .app's Info.plist β but the build only requested --bundles dmg, so the .app was a leftover from whatever happened to be on disk before, or nothing at all.
The build now asks for --bundles app,dmg. The .app supplies the embedded version the gate reads; the DMG is still the one artifact that ships. Same gate, same guarantees β except now it's inspecting the build in front of it instead of an artifact that may never have existed. π
And because a fixture that quietly does the right thing is how a contradiction like this hides for a whole release cycle, the test builder now honors the bundle formats it was actually handed. Ask it for dmg and you get a dmg β nothing more. The bug can't come back wearing a green test suite. β
π¦ Getting it
bun update @kvnwolf/dobbyNo config changes, no migration, nothing to re-declare. If your project has a release.type: "homebrew-cask" target, this is the version where bunx dobby release gets past the pack gate. Everyone else is unaffected β the npm release path never touched this code.
v0.12.0
π§ One recipe, one build run, one trustworthy review boundary
Dobby 0.12.0 replaces execution-time guesswork with a fixed, inspectable workflow recipe. Every plan now enters one native Claude Workflow with the same agent roles, limits, and verification postureβso runs are easier to understand, compare, and improve from evidence instead of configuration theory.
π§ͺ A baseline you can actually measure
The new baseline-v1 recipe is exposed by bunx dobby env --json with a deterministic fingerprint. It pins the five worker roles to their intended model and effort, caps outer attempts and concurrency at two, and requires mechanical-first verification.
That contract is checked before any worker starts. If the recipe drifts between the plugin, agent definitions, and CLI, execution refuses early rather than silently running a different experiment. Dobby also records the evidence it can observeβattempts, retries, outcomes, role calls, and verification sourcesβwhile leaving unavailable provider and cost data honestly marked as unknown.
ποΈ A leaner build loop
/dobby:execute now runs the whole approved plan through a single native Workflow. Tasks move through optional spec-only test authoring, implementation, and independent verification; a failed verification gets at most one bounded fix-and-retry when another verifier attempt remains.
The duplicate per-task reviewer loop is gone. That saves context and calls without weakening the boundary: implementors still cannot verify their own work, terminal fixes are forbidden, blocked dependents do not run, and a write-capable worker with no usable work log triggers an independent safety review before the task is marked needs-human.
π Review the complete change, not fragments
Holistic review now lives where the full story exists: the external PR boundary. dobby pr watch requires review evidence for the current PR HEAD, including Greptile's Last reviewed commit marker when Greptile is the configured gate. A stale summary or an old green review no longer makes a newer push look merge-ready.
This gives each layer one job: the build loop proves task behavior, the PR reviewer judges the complete diff, and merge readiness fails closed when either proof is missing.
π Upgrade
bun update @kvnwolf/dobbyReload the plugin after updating so the revised worker definitions are active:
/reload-plugins
v0.11.0
π One plan, one run, and a chat that finally talks back.
/dobby:execute used to slice your plan into one workflow launch per wave and go quiet until the very end. v0.11.0 replaces that with the build run: a single workflow that carries the whole approved plan β waves sequenced inside it, tasks inside each wave still fully parallel, every safety the waves encoded (area-disjointness, destructive tasks isolated in their own wave) preserved byte-for-byte.
π¦ Live progress, where you actually are
The run narrates itself as it goes β a terminal line the moment each task lands (T3 β verified β β¦ (2 loops)), retry lines the moment a review or verify pushes back, and a summary at every wave close. Verbosity is adaptive: a healthy task stays terse, and the first failed review or verify flips that task into full milestone narration while its siblings stay quiet. The detail shows up exactly where the trouble is.
And you don't have to keep the /workflows widget open anymore: the coordinator now relays the run into the chat β one quiet watcher over the run's journal, waking only when something terminal happened, one batched message per wakeup with everything new. No filler, no per-event spam; the chat lines are what survives, the widget stays the live view.
β Blocked, not burned
When a task exhausts its caps and lands needs-human, everything that depends on it β directly or transitively β is now skipped as blocked: zero agents spawned, the blocker named in its row, independent tasks in the same wave untouched. No more burning a whole loop's worth of agents building on a foundation that never landed. (Powered by build-plan now exposing each task's dependsOn β an additive payload change.)
β»οΈ A dead run resumes, it never rebuilds
If the run dies mid-plan β or comes back empty β the play is resumeFromRunId with the same script and args: every completed agent replays instantly from cache and the script re-derives the full, authoritative result. The journal read is a forensic fallback, not the recovery path.
π§ͺ The script is no longer prose-only
The build-run script that lives inside references/build-workflow.md is now pinned by a committed harness (cli/src/build-workflow.test.ts, 38 tests) that extracts the fenced script from the markdown and executes it under stubbed runtime globals β wave sequencing, blocked skipping, the narration lines, the args guards (including duplicate task ids and a task literally named __proto__) all proven, mutation-checked, before any real agents are involved. All recorded in ADR-0021.
β¬οΈ Upgrading
bun update @kvnwolf/dobby
Nothing else to do: no new config keys, no new hooks. The plugin side rides your marketplace auto-update β running Claude Code sessions need a /reload-plugins to pick up the new execute. /dobby:dispatch and /dobby:address-review keep their exact contracts (single-task tasks[], done status) β no consumer change there either.
v0.10.0
πͺ v0.10.0 β the interview learns to juggle
One thing changed for the better, and you'll feel it in the first minute of your next /dobby:scope session: the interview stops dripping questions one per turn and starts asking in frontier rounds β every question that's answerable right now travels together, and only the ones that genuinely depend on an open answer wait their turn.
β Frontier rounds in /dobby:interview
The old interview was thorough but slow on purpose: one focused question per turn, always. That rule was right about focus and wrong about cadence β when four open questions don't depend on each other, four round-trips are three too many.
The interview now models your task as a design tree and asks the frontier β every open decision whose prerequisites are already settled β in rounds:
- πͺ Popup rounds first: up to 4 independent questions in one AskUserQuestion call, each still fully self-contained (its own 1β3 lines of context inside the question β the popup covers the turn's text, so nothing load-bearing lives outside it), each with a recommended answer.
- βοΈ Text rounds after: open-ended questions arrive as their own numbered
β **Q1**β¦/β‘οΈ recommendedlist, so you answer several in one typed message. - βοΈ Dependent chains still arrive one at a time β a question whose premise hangs on an open answer belongs to a later round. The old one-question interview didn't die; it's the degenerate case of the new mechanism.
- π¬ Fact-finding no longer stalls the conversation: when a claim needs checking against the code, a researcher is dispatched and only the questions downstream of that fact wait β the rest of the frontier ships now.
- β»οΈ And if an answer invalidates a sibling answered in the same round, the orphaned answer is discarded and the question re-asked with the corrected premise β no decision ever stands on a premise that moved under it.
The closing machinery you rely on β the litmus test, the infra-assumption gate, the glossary term offer, the STATE.md persistence β is untouched. frontier, round, and design tree joined the glossary, ADR 0020 records the why, and the mechanism is adapted from mattpocock/skills' productivity/grilling (the attribution now points at the skill's current home β it was renamed upstream from batch-grill-me).
π€ /dobby:finish closes the deal β literally
The other hop that stopped hurting: finish no longer makes you detour to GitHub to press the merge button. When your PR is merge-ready and the tree is clean, the finish gate now offers "Merge & finish" β it re-verifies merge-readiness (dobby pr watch), merges squashed, re-runs the preflight, and rolls straight into the teardown: dobby down, worktree + branch removed, main pulled. Your explicit selection is the merge decision β no path merges on its own, feedback-present still routes to /dobby:address-review, and /dobby:commit still never merges. The commit β (merge on GitHub) β finish lifecycle is now just commit β finish.
β¬οΈ Upgrading
Nothing is asked of you: no new config keys, no hook changes, no rules that can go red. Update the devDependency and enjoy the shorter conversations:
bun update @kvnwolf/dobbyThe plugin side ships with your marketplace auto-update; the batched interview and the merging finish are live the next time the skills load.