A Graph of Tasks, a Loop of Learning

The workflow I use to keep a feature's design current as agents build, check, and discover things we didn't know at the start.

In this post

A task can be finished and the feature can still be heading in the wrong direction. Implementation may have exposed an assumption that doesn’t hold, a dependency we missed, or a better way to shape the work. If that discovery stays in a chat, the next task carries on as though nothing changed.

That’s the problem I’m trying to account for with Dotbrain. I want agents to build pieces we can check, then bring what we learn back into the plan. A task list can faithfully execute an out-of-date design. I’d rather give it a current one.

The important part isn’t the number of agents or skills involved. It’s where a discovery goes, and who gets to decide what changes because of it.

The plan has to hear back from the code

Before implementation, we examine an idea against the project’s vocabulary and earlier decisions, surface unknowns, and write a design. It holds what we’re building, why, what’s still uncertain, and how we’ll know it works. Then we divide the feature into slices: pieces small enough to build and check.

That sounds like a familiar planning sequence. The useful part happens on the way back.

If an API can’t support the behavior we expected, the constraint needs to become a design question before an agent adds a workaround to the next task. Once we settle the direction, the design and affected tasks change together. A discovery that only adds work, without changing the design, belongs in the tracker instead. Not every surprise needs to become architecture.

The design stays active while we build, so the next slice can find what the previous one taught us. We repeat until the agreed criteria are met or the work is blocked. The workflow guide describes the stages and handoffs.

The skill chain: find-unknowns, grill-decisions, to-design, to-issues, run-execution, review-gate, close-design. A dashed branch runs from to-issues through iterate-design to review-gate under an explicit handoff. The skill chain: find-unknowns, grill-decisions, to-design, to-issues, run-execution, review-gate, close-design. A dashed branch runs from to-issues through iterate-design to review-gate under an explicit handoff.
Skills hand work forward; discoveries feed back into the active design. The dashed path repeats execution under an approved Handoff contract.

I took the idea of small, composable skills from Matt Pocock’s skills. The ones I built ship with the Dotbrain plugin for Claude Code and Codex. Each has a bounded job. Their names are useful when running the workflow, but the reason for the split is that each stage should leave something the next stage can actually use.

find-unknowns, inspired by Anthropic’s field guide, surfaces blind spots. grill-decisions helps settle vocabulary and durable decisions. Those belong in the glossary and decision records, not buried halfway down a feature plan.

The design has a different lifetime. to-design gives the initiative a common reference across sessions. to-issues turns it into dependent slices, and manage-work-graph maintains their state in Beads. The design explains intent; the tracker records where the work stands. Project rules have their own home in agent instructions, kept usable with write-agent-docs.

I still use the agent harness’s planning features to think through an approach before editing. A shared design doesn’t need to replace every other way of planning. It needs to keep those separate sessions from quietly building different versions of the same idea.

Finished by a worker isn’t accepted by the project

Keeping the design current helps only if the result we’re learning from has actually been checked.

Suppose we’re adding pagination to an API. A query change and an SDK update might be ready together once the API contract is settled. Integration tests that depend on both must wait until those prerequisites are accepted. This is an illustration, not a report of a particular feature I shipped.

A worker returning a commit doesn’t establish that acceptance. Tests passing in its checkout don’t establish that the combined change works either.

This is why I distinguish the work graph, the issues and their dependencies, from the execution graph, how a fixed batch gets built, integrated, reviewed, and checked. The glossary defines both. Beads retains the work graph and recoverable ownership, results, blockers, and evidence. The lead’s session coordinates the execution graph.

run-execution handles one issue or a fixed batch. My main session, the lead, gives each writer clear ownership, criteria, and checks. Workers keep their issue claims through implementation and review fixes. While they’re running, only the lead changes the work graph or active design; discoveries come back to it instead of producing competing plans.

Candidates are combined on an isolated integration branch. An independent reviewer examines them, fixes return to the responsible worker, and checks cover the same integrated revision. Before delivery, independent review must cover the whole branch diff against the design, including interactions between issues. Only after required checks, review, and human decisions does the exact accepted revision move to the delivery branch and release dependent work.

The agent-team guide gives the operational detail. Dotbrain supplies focused researcher, worker, reviewer, and verifier roles; the harness launches and resumes them. The reviewer judges the change, while the verifier runs agreed checks and returns evidence tied to the revision. Those are different jobs.

Parallel work makes these boundaries more important, not less. Issues must be ready together, shared interfaces settled, and ownership clear. Concurrent writers use separate worktrees. Databases, ports, and build outputs need isolation or exclusive scheduling too. A worktree doesn’t give you a private database as a bonus.

If safe parallel work isn’t available, the lead explains the limit and proceeds sequentially when parallelism was optional. Missing a required capability is a blocker. Spare capacity is never permission to add unrelated work to the batch.

Delegation doesn’t decide the scope of permission

I make two independent choices: how often the agent returns to me, and how many agents write at once.

Human-in-the-Loop (HITL) returns after each bounded execution. Handoff continues through an approved active design until it finishes, reaches a decision reserved for me, or hits a hard stop. Either can use one writer or several. Research or review alongside one writer still counts as sequential execution.

HITL with sequential execution is the default. The lead proposes a dedicated branch, completes the bounded work, and returns the result. It needs delivery approval before pushing or opening a pull request; complete applicable approval can be reused rather than requested again. Several executions can build on that branch.

For Handoff, I explicitly invoke iterate-design. The contract settles scope, branch and base, checks, review, writer limits, human gates, and pull-request delivery. Required harness capabilities, provider access, and authenticated agent authority have to be available too. An execution request doesn’t supply missing permission. The lead waits for my GO when the contract isn’t already completely approved; a resume retains narrower instructions and remaining limits.

Within that contract, Handoff can push a dedicated branch, open a draft pull request at the first push, and deliver accepted work there. Once the scoped work is complete, verified, and reviewed, it can mark the pull request ready and request my review. That is not permission to merge, deploy, publish, change dependencies, or change scope or success criteria. Both workflows leave the delivery branch for my review before merge.

Acceptance criteria are agreed before implementation. Only I approve changes to them. If a criterion proves wrong or impossible, the agent stops and asks rather than lowering the requirement to match its result.

Retries need a boundary too. Repeated failed checks or review requests stop affected work for my decision, and its dependents wait. Genuinely independent work inside the same scope can continue only with demonstrated isolation. Accepted work stays on the delivery branch; held candidates remain separate. If nothing unblocked remains, Handoff reports blocked, and its pull request stays draft while any scoped issue is blocked. Missing capabilities, out-of-contract actions, or lack of progress can stop the whole run sooner. The guides specify the finite retry and no-progress limits; leaving the counts out of this explanation doesn’t remove them.

review-gate records focused reviews and their evidence. In the workflow I’ve described, I also keep a separate human gate on closing the final review record: my decision, design close-out after findings are handled, or, for code review, the lead observing my merge. That is stricter than the current documented default, which permits the lead to close reviews after the required evidence and findings are settled. Neither closing a record nor a passing agent verdict grants merge or release authority.

I want agents to work without asking about every small step. I don’t want that convenience to depend on them deciding what counts as success along the way.

A current design needs an end

A living design eventually has to stop changing. Otherwise the next session has to work out whether an old plan is still an instruction.

It begins as a draft, becomes active, and ends as shipped, abandoned, or superseded. Active means we can still learn and change direction. A terminal design freezes as a record.

Design doc lifecycle: Draft leads to Active, Active leads to Close-out, and Close-out ends in one of three frozen states: Shipped, Abandoned, or Superseded. Design doc lifecycle: Draft leads to Active, Active leads to Close-out, and Close-out ends in one of three frozen states: Shipped, Abandoned, or Superseded.
Every design ends through close-out, whichever way it ends.

close-design records the outcome, evidence, and end date. It’s manually invoked, not an automatic consequence of finishing tasks or closing reviews. For shipment, a ready pull request isn’t enough: actual merge evidence matters. Decisions worth keeping move into decision records, and settled vocabulary goes into the glossary. The design preserves what happened without pretending it is still the plan.

That’s the other return path in the loop. Implementation informs the active design; closing the design leaves useful knowledge for the next initiative.

The structure has a cost

Most of this can be done by hand: a glossary, decision records, one design per initiative, and a separate tracker. The habits matter more than the tool. Keep current intent distinct from task state, return consequential discoveries to the design, accept prerequisites before their dependents, and make human decisions explicit.

Keeping those things in sync takes work. Delegation adds assignments, integration, and review. One agent can be the better choice, and a two-line fix doesn’t need a design production. Human gates cost attention too, which is inconvenient when saving attention was part of the reason for adding them.

I built Dotbrain around bounded skills so I can use a small part without adopting the whole arrangement for every change. The test I care about is what happens when we start the next slice: does the design reflect what we’ve already learned, or are we about to explain it again?

The Dotbrain documentation covers setup and the detailed contracts.