Skip to content
Small team, full backlog, zero orders dropped. Support replies are slower than we’d like. Read our status update → Zero orders dropped. Status → 📬 Check your spam folder — most of our replies land there. We do answer. Status update → 📬 Check your spam folder. Status →

Agentic Engineering / Rung 5

Graph Engineering

Many agents as a dependency graph. Nodes are work. Edges are what has to finish first. The artifact stops being an answer and becomes a project.

Where The Ladder Stops Being About One Agent

A loop improves one artifact. A graph moves a project.

Every rung below this one answers a question about a single worker. Prompt engineering writes the instruction. Context engineering decides what the agent knows. Harness engineering picks which agent, model, tools and permissions run the job. Loop engineering turns one turn into an act, observe, correct cycle that stops on its own. This page assumes all four.

Graph engineering is what comes next, once you have several trustworthy workers and a job bigger than any one of them can hold. The shift is sharp and most people feel it as a loss first: you stop writing prompts and start designing the relationships between work. Each node’s prompt gets shorter. The interesting decisions move into the shape.

Two words carry the model. A node is one unit of work with an owner, a defined input, a defined output and a condition that says when it is done. An edge is a dependency: B may not start until A produced what B needs. Draw enough of both and you have a graph. If no chain of edges leads back to where it started, the jargon for that shape is a directed acyclic graph, or DAG. You do not need the word to use the idea, only to read the tools that already solved this.

1976
Year Make first scheduled work from a dependency graph
16
Concurrent agents one Claude Code workflow run allows
1000
Agent cap per run, so a runaway graph stays bounded
10x
Upper bound Anthropic reports for multi-agent token cost

Real Projects Are Not Lines

Watch work arrive on a real change and you see four shapes.

One task unlocks another. The schema decision lands before the migration is written, and the migration runs before the backfill means anything. A chain is the only shape a linear script gets right.

Some investigations are independent. Three scouts reading three subsystems never need to talk. Running them in sequence does not make the answers better, it makes them later. Anthropic reported cutting research time by up to ninety percent on complex queries by moving that breadth-first work into parallel subagents.

Some outputs need a second pair of eyes. A build nobody independent has looked at is a proposal, not a result. Review is a node with inputs and a verdict.

Some failures must route backwards. A rejected build goes back to a builder, not forward to delivery. A pipeline that only moves forward turns every rejection into a stall or a shipped defect.

None of this is new. Stuart Feldman built Make at Bell Labs in April 1976 because a colleague fixed a bug in a file that never got recompiled. His answer was to stop describing a sequence of commands and start describing targets and dependencies, then derive the order from that. Everything since refines the same move, from Bazel and Gradle to Airflow, Argo, Dagster and Temporal: walk the graph in topological order, start each task the moment its own inputs are ready. The LLM part is new. The scheduling part is fifty years old, and that literature is better than most of what has been written about agents.

A Graph Written Down

One hardware family, re-rendered across a site, split across scouts, builders, a reviewer and a human. The file is the design.

bash
$ cat missions/s21-hub.graph.yaml

exit_conditions:
  - schema validator reports 0 errors on 12 sample URLs
  - reviewer verdict is accept
  - human co-sign recorded

nodes:
  scout-templates:
    role: scout
    model: local/qwen3-coder
    task: list every template that renders an S21 spec block

  scout-schema:
    role: scout
    model: local/qwen3-coder
    task: map the schema emitters that touch S21 SKUs

  build-renderer:
    role: builder
    needs: [scout-templates, scout-schema]
    claims: [inc/miner-data.php, inc/schema-markup.php]
    worktree: true

  build-tests:
    role: builder
    needs: [scout-schema]
    claims: [tests/schema/]
    worktree: true

  review-render:
    role: reviewer
    needs: [build-renderer]
    context: fresh
    may_reject: true
    on_reject: build-renderer
    max_rounds: 3

  gate-human:
    role: approval
    needs: [review-render, build-tests]

  integrate:
    role: delivery
    needs: [gate-human]
    join: all_success

Read it as sentences. The scouts have no needs, so they start together. build-tests needs only the schema map, so it starts when that scout returns instead of waiting for a sibling it does not depend on. The reviewer gets fresh context, may say no, and its rejection points at one node rather than the whole run. The human is an actual node between review and integration. The exit conditions sit at the top, written before the run.


Fan-Out Is Easy. The Join Is Where It Goes Wrong.

Fan-out is one node producing many. Six files, six readers. That is the half every framework demos. The join is where the branches come back, and the default nearly everywhere is a barrier: nothing past the line starts until everything before it has finished.

Barriers are old and well understood. Valiant’s bulk synchronous parallel model formalised them in 1990, Google’s Pregel built large-scale graph processing on supersteps separated by them, and LangGraph’s execution model is explicitly Pregel-inspired, advancing one super-step at a time with parallel nodes inside each step. The payoff is real: a barrier makes state merging deterministic.

The cost is the straggler, and it is worse with agents than with threads because agent runtimes vary wildly. One node reads four files in twenty seconds. Its sibling hits an unexpected dependency and takes eleven minutes. A barrier charges you eleven minutes for both, and three barriers charge you three times.

Use a barrier when the next node genuinely reads every upstream output: synthesis, ranking, deduplication, merge. Use a plain dependency edge everywhere else, because most nodes need two upstreams and not twelve. Airflow has carried this distinction for years as trigger rules, and the vocabulary transfers straight across.

Join Rules Worth Knowing
Join rule Releases the next node when Right when
Barrier Every branch in the fan-out has finished The next node truly reads all outputs: synthesis, merge, ranked summary
Plain dependency edge That node's own named inputs are ready Your default. Most nodes depend on two upstreams, not the whole layer
All success Every upstream finished without failing Integration and delivery that must never run on a broken branch
All done Every upstream finished, pass or fail Cleanup, teardown, cost accounting, run reports
Any failure At least one upstream failed Escalation, rollback and alert paths
Quorum N of M independent nodes agree Cross-checked research where any single reader can be wrong

Pipelines Beat Phases

When the same work happens to many items there are two shapes and only one is good. Phases: audit all four hundred files, then fix all four hundred, then verify all four hundred. Three barriers, three stragglers, nothing finished until the end, nothing shippable if the run dies at eighty percent.

A pipeline sends each item through audit, fix and verify on its own. File 1 is done while file 399 is still being read, and one file that cannot be fixed does not hold the other 399. Claude Code’s dynamic workflows expose exactly this, with agent() spawning one worker and pipeline() running one per item.

Small nodes buy something less obvious. In that runtime a resumed run replays in start order: cached results stop at the first agent that did not finish, and every agent that started after it runs again even if it completed. The documented conclusion is that many small agents preserve more progress than a few long ones. Node size is your unit of recovery.


Routing, Retry, Escalation

An edge does not have to be unconditional. A conditional edge carries a question, evaluated against the state the graph has accumulated, and LangGraph implements it as routing functions that inspect state and name the next node. The value is that the failure path lives inside the model. If retrieval returns nothing, widen the search. If that returns nothing, escalate to a human. As edges those are visible and reusable. As improvisation inside one agent’s turn they are invisible and different every run.

Retry

Same node, different input. The failure and what was already tried get appended before it runs again. A retry that changes nothing is a coin flip and keeps landing on the same face. Cap the rounds in the graph file, not in the prompt.

Escalate

Different node. A stronger model, a wider tool set, a specialist role, or a human. Escalation stops a bounded retry from becoming a dead end. It has to change something structural, not ask again more loudly.

Abandon

Mark the branch failed and let the graph continue. This must be a declared outcome, or every unsolvable node becomes an infinite retry and the run ends when someone gets tired rather than when the work is done.

Termination inside one node belongs to loop engineering. What belongs here is the budget across nodes, because a graph can be well behaved at every node and still run forever if rejections bounce between a builder and a reviewer.

The Six Primitives You Actually Need

Node

One unit of work with an owner, named inputs, a named output and a done condition. If you cannot say when it is finished, it is not a node yet.

Edge

Must finish before. The only place ordering should live. Ordering that lives in the order you typed things is ordering nobody can review.

Claim

An exclusive lease on files or resources, held by one node while it works, so two agents never write the same bytes from two different plans.

Worktree

Physical isolation. A separate checkout per builder over one repository, so parallel work cannot collide on disk before anyone has reviewed it.

Gate

A node whose job is to say yes or no. Reviewers and humans belong in the graph with inputs and verdicts, not as a step someone remembers.

Exit condition

A measurable statement, written before the run, that decides whether the graph is finished. Without one a run does not end, it gets abandoned.

Two Agents, One File

The fastest way to lose a day to multi-agent work is letting two agents edit the same file. It does not announce itself. One rewrites a function, the other read the version from before that write, and you get a diff neither intended.

Use both defences. The logical one is a claim: before a node starts it takes an exclusive lease on the paths it intends to touch, and a node that cannot get its claim waits or fails. The physical one is a worktree: each builder gets its own checkout over the same repository, so simultaneous writes are not addressing the same bytes. Claude Code ships both, and is direct about the alternative, since its agent teams are not worktree-isolated and you must partition the files instead.

bash
# physical isolation: one checkout per builder, one shared .git
$ git worktree add ../wt/build-renderer -b agent/build-renderer
$ git worktree add ../wt/build-tests    -b agent/build-tests

# logical isolation: who may touch what, for how long
$ printf '%s\n' inc/miner-data.php inc/schema-markup.php > .claims/build-renderer
$ printf '%s\n' tests/schema/ > .claims/build-tests

# a claim is only real if something refuses when it is violated
$ git worktree list

Here is the caveat most guides skip. Worktrees prevent file collisions. They do not prevent logical conflicts. Two builders in perfect isolation can still make incompatible assumptions, and both branches can be individually correct and jointly broken. That is why integration is its own node with its own reviewer.


A Model Per Node, Not Per Project

Once work is nodes, the model becomes a property of the node instead of a setting for the session. Enumerating files, extracting a list and applying a mechanical transform are jobs a small local model does well, and they are usually the highest-volume nodes. Architectural decisions, adversarial review and integration are where a stronger model earns its cost, and they are usually the rarest. Getting that backwards is the most common reason a graph is expensive without being good.

The economics are not theoretical. Anthropic reports that multi-agent implementations typically use three to ten times the tokens of a single-agent approach for equivalent tasks, and that their research system used roughly fifteen times the tokens of a chat interaction. A graph that spends a frontier model on file enumeration multiplies that for nothing.

This is where local-first inference becomes arithmetic rather than ideology. DCENT_ADE discovers and manages Ollama and LM Studio, supports LAN inference and supports assigning models to roles, so the high-volume mechanical nodes can run on hardware you own. Cloud stays an explicit, consented boundary, never an invisible fallback. Which model runs where is a harness engineering decision. The graph is where you make it per node.

Finished, Not Abandoned

A graph without exit conditions is a habit. It ends when the person watching gets bored or the budget runs out, and neither is a result.

Write them before the run, in terms something other than an agent can check. A validator returns zero errors on a named sample. A suite passes. A reviewer returned accept. A human signed. Insist on external checkability, because a self-assessed exit condition is not a condition, it is a mood, and models are agreeable. Declare the failure exits too: maximum rejection rounds, maximum nodes, and what happens to an abandoned branch. A node budget is the difference between a run you can leave alone and one you have to babysit.


How DCENT_ADE Models This

DCENT_ADE is our local-first, open-source agentic development environment, and its coordination layer, Orion, exists to hold what this page describes. Boards and missions carry the shape of the work, missions decompose into trees rather than lists, claims record who may touch what, and a mailbox lets agents pass findings without shouting into a shared context. Workspace-scoped memory keeps notes as native-scoped local Markdown with bounded read, write, append, list and search.

Mission Control names the lifecycle in roles: Coordinator, Scouts, Builders, Reviewers, human approval, then integration and delivery. Read as a graph, the Coordinator owns the fan-out decision, Scouts are the parallel investigation layer whose outputs become edges into the builders, Builders hold claims and work in isolated worktrees, Reviewers receive review packets, human approval is a node rather than a notification, and MergeGate with human co-signing sits in front of integration.

Two decisions do more work than the diagram. The Tauri and Rust native host holds the sensitive authority: no general spawn, SQL or fetch authority in the WebView, a native-owned harness catalog, native-side egress controls. With many agents running, blast radius is a design parameter. The awareness layer then distinguishes busy, idle, awaiting input and finished from real terminal behaviour, because a dependency graph is only as good as its knowledge of when a node is done. An orchestrator that guesses fires joins early and hands reviewers half-written files. Hence the house rule: DCENT_ADE’s development rules reject fake agents, simulated activity, fabricated telemetry and optimistic availability indicators, and the current catalog execution layer refuses to claim a Git merge it does not perform.

The Gauntlet Graph Loop

Our own build doctrine is the two-node version, documented at gauntlet loops: a builder produces a real artifact, an independent critic gets fresh context and judges the actual result, and if it loses against the quality bar the largest gap goes back to a builder. The critic must be able to reject, and the evidence must be real: real pixels, real files, real tests, real git effects. That method is live-proven in the strongest sense available to us, because it is how DCENT_ADE itself is built.

The stated ambition is the graph version, which we call the Gauntlet Graph Loop. The gauntlet loop it extends was named publicly by Matt Shumer; the graph form is our extension of it, not a rebrand of his. Not one agent repeatedly improving one answer, but a dynamically coordinated graph of specialised agents and evaluators moving a whole project toward measurable exit conditions, with gates that can reject and route the largest gap back to a specific builder, and a human co-signing before anything is integrated. The graph itself becomes the thing you tune, rewire and re-run.

That is direction, and we are naming it as direction. The roles, claims, worktree isolation, review packets and human co-signing are how the architecture is designed and how we work today, with Claude Code, Codex, Gemini and OpenCode running side by side under one host. A fully automatic graph scheduler that rewires itself against exit conditions is not a shipped feature and we will not describe it as one. The brute-force cousin, where the same prompt runs in a loop with fresh context every iteration, is RALPH loops, and that is Geoffrey Huntley’s work rather than ours.

One last honesty note. The correct default is still the simplest thing that works. Anthropic’s guidance is that multi-agent systems suit heavy parallelisation and information exceeding a single context window, and suit tightly interdependent work far less, which describes plenty of ordinary coding. Reach for a graph when the work genuinely forks.

A loop improves one artifact by repeating act, observe and correct until a stop condition is met. A graph coordinates many units of work with dependencies between them, so different work runs in different places at once and results have to be joined. A graph usually contains loops, since a node can loop internally. The reverse is not true.
No, but read one. LangGraph, Airflow, Temporal, Argo and Make already solved dependency ordering, conditional routing, retries, checkpointing and human pauses, and the vocabulary is worth borrowing even if your first graph is a YAML file and a script. Adopt a framework when you need durable state or resumable runs.
Fewer than you think, and bounded either way. Anthropic reported early failures where a lead agent spawned fifty subagents for simple queries and workers duplicated each other’s searches. Claude Code caps a dynamic workflow at sixteen concurrent agents and a thousand total. A graph producing more output than anyone reviews has saved you nothing.
Use both defences. Claims give each node an exclusive lease on the paths it may touch, and a node that cannot get its claim should wait or fail rather than proceed. Worktrees give each agent a separate checkout over one repository so writes cannot physically collide. Neither prevents logical conflicts, which is why integration needs its own reviewer.
Yes, when the next node genuinely reads every upstream output. Synthesis, ranking, deduplication and merge all qualify. The mistake is making it the default between every stage, because then every fast branch idles until the slowest finishes and you pay that cost at every boundary. Prefer plain dependency edges and spend barriers deliberately.
Because you wrote the exit conditions before the run and something other than an agent can check them: a validator returning zero errors, a suite passing, a reviewer verdict, a recorded human signature. Declare the failure exits too, including maximum rejection rounds and what happens to an abandoned branch.

Standing On Other People’s Work

Dependency graphs were solved long before language models existed. Make, Bazel, Airflow, Argo, Dagster and Temporal did the hard thinking about ordering, retries, branches and joins. LangGraph brought that model to LLM workflows: typed shared state, conditional edges, checkpointing, and interrupts that pause a run for human input and resume it later. Anthropic published what broke in a production orchestrator-worker system, not only what worked. Claude Code, OpenAI’s Codex, Gemini and OpenCode are the harnesses under our own graphs. We invented none of it.

DCENT_ADE is free, open and as-is. No gatekeeping, no paid tier, nothing held back for a premium version. If you use it commercially or it saves you real time, a voluntary subscription or donation is expected and never enforced: d-central.tech/fund.

Design The Relationships, Not Just The Prompts

Graph engineering is rung five of seven. Climb the rest of the ladder, or see the environment we built to run these graphs locally.