Graph Engineering Cheat Sheet
By the Syncfusion DevTutorials team · Last updated
One page to come back to: loop or graph, the graph file format, the five patterns, state contract rules, exits, caps, join policies, gates, the scorecard, graph pack files per tool, and an error index for every linter rule.
How to Use This Page
This page collects everything from the course in one place: the decision, the file format, the patterns, the rules, the files for each tool, and every message the Graph Lab can show you. Keep it open while you build.
Tool paths and commands change often. The tool table below was checked against the official docs on 19 Oct 2026. Check again before you rely on a path.
Loop or Graph?
Start with one loop. Add nodes only when you can name what they buy.
| Ask | If yes | Pattern |
|---|---|---|
| Does the work split into independent parts? | Run them at the same time | Parallelization |
| Does the work mix risk levels? | Send risky work down a careful path | Routing |
| Does one step leave dead ends the next should not see? | Hand over a clean, typed result | Prompt chaining |
| Is the work list only found while running? | Start capped workers for it | Orchestrator-workers |
| Is there a clear pass test, and fixes need several tries? | Fix and check until it passes | Evaluator-optimizer |
| Is it a strict step-by-step plan one agent does well? | Keep one loop | — |
Research on 260 agent setups: extra agents helped independent, parallel work by up to 80.8%, made strict step-by-step planning 39–70% worse, and hurt tasks one agent already did well.
The Five Patterns
Every graph in the course is built from these five patterns.
| Pattern | What it buys | How it fails | The fix |
|---|---|---|---|
| Prompt chaining | Clean context for the next step | Forwarding everything | Writer reads only a typed handoff |
| Routing | Cost matched to risk | Quiet misroutes | Strong router, confidence threshold, careful else |
| Parallelization | Less waiting | Races; a hung branch | One field per branch; join timeout and policy |
| Orchestrator-workers | Work found at run time | No limits | Caps on width, depth and spend; topology record |
| Evaluator-optimizer | Fix until a test passes | Weak exits | Oscillation, no progress, flat score, budget |
Graph File Template
Each node on one line. Copy this and change it.
graph: my-graph
checkpoint: after_each_node
kill_switch: GRAPH_PAUSED
nodes:
- { id: route, kind: router, model: large, min_confidence: 0.7, reads: [ticket], writes: [route, risk_reason] }
- { id: quick_fix, kind: agent, model: small, reads: [ticket], writes: [diff], exit: { checker: npm test, max_iterations: 4 } }
- { id: careful_fix, kind: agent, model: large, on_bound: escalate, reads: [ticket], writes: [careful_diff, test_result], exit: { checker: npm test, max_iterations: 8, oscillation: true, no_progress: 2, flat_score: 3, budget: 20000 } }
- { id: approve, kind: gate, evidence: [careful_diff, test_result, risk_reason], reads: [careful_diff, test_result, risk_reason], writes: [approval] }
- { id: merge, kind: function, reads: [diff, careful_diff, approval], writes: [merged] }
edges:
- { from: route, to: quick_fix, when: risk == low }
- { from: route, to: careful_fix, when: risk == high }
- { from: route, to: careful_fix, when: else }
- { from: careful_fix, to: careful_fix }
- { from: quick_fix, to: merge }
- { from: careful_fix, to: approve }
- { from: approve, to: merge }
state:
route: { type: "low | high", writer: route }
careful_diff: { type: diff, writer: careful_fix }
test_result: { type: "pass | fail", writer: careful_fix }
approval: { type: "approve | reject", writer: approve }
Node Kinds
| Kind | What it is |
|---|---|
function | Plain code: tests, a schema check, a build |
model | One prompt in, one answer out |
agent | A full agent running its own loop |
router | Picks the next edge |
join | Waits for branches and merges them |
orchestrator | Finds work and starts workers |
gate | Waits for a person |
Edge Kinds
| Written as | Kind |
|---|---|
{ from: a, to: b } | fixed |
{ from: a, to: b, when: risk == high } | conditional (when: else is the fallback) |
{ from: a, to: a } | loop-back |
{ from: a, to: [b, c, d] } | fan-out (each target then has an edge to a join) |
State Contract Rules
One writer per field. Every node lists the fields it reads.
- Every field has one writer.
- Every node lists the fields it reads. Nobody reads
*. - Fields another node decides with get a small, exact type.
- Handoffs between chained nodes are typed shapes, like
"{cause, steps, file}". checkpoint: after_each_nodesaves the state, so a crash resumes and a gate can wait.
Limits at a Glance
| Where | Setting | Typical value |
|---|---|---|
Fixer exit: | oscillation | true |
Fixer exit: | no_progress | 2 |
Fixer exit: | flat_score | 3 |
Fixer exit: | budget | tokens for this node |
Fixer exit: | max_iterations | 12 or less (backstop) |
| Fixer | on_bound | escalate |
| Router | model, min_confidence | large, 0.7 |
| Join | timeout, policy | 10m, all / quorum / partial |
| Each parallel branch | timeout | shorter than the join's, e.g. 5m |
| Join after an orchestrator | check_plan | true |
Orchestrator caps: | width, depth, spend | at or below the API limit, 1, sized for the biggest job |
| Orchestrator | record_topology | true |
| Graph | checkpoint, kill_switch | after_each_node, GRAPH_PAUSED |
| Gate | evidence | diff, test result, risk reason |
The Scorecard
A graph is graded on its run, never on its drawing.
| Number | Bar |
|---|---|
| Correct outcomes | 36 of 40 or more |
| Races | 0 |
| Hung joins | 0 |
| High-risk on the cheap path | 0 |
| False "done" | 0 |
| Size (nodes + edges) | no more than the reference |
Graphs in Each Tool
Graph Pack Files
| File | Claude Code | Codex | Cursor |
|---|---|---|---|
| The plan | graph.md | graph.md | graph.md |
| One subagent per node | .claude/agents/<node>.md | .codex/agents/<node>.toml | .cursor/agents/<node>.md |
| Required fields | name, description | name, description, developer_instructions | name, description |
| Per-node model | model: (sonnet, opus, haiku, a full id, inherit) | model, model_reasoning_effort | model: |
| Shared state | graph-state.json + state-contract.yaml | same | same |
| Supervisor | supervisor subagent | supervisor agent | supervisor subagent |
None of the three tools has a visual graph runtime. The main agent follows graph.md and calls one subagent per node.
Debugging a Run
- Find a run that worked on the same kind of input.
- Read both logs from the top: node, inputs, tokens, time, verdict.
- The first row that differs is the cause. Rows after it are effects.
- Fix that node only, and run again.
Practise finding the broken node
Human Gates
- Gate the paths with a large blast radius: auth, payments, data migrations.
- Show the diff, the test result and the risk reason.
- Never gate the quick path. Too many approvals means nobody reads them.
- Ticket text never decides whether it needs review.
First Real Run Checklist
- Run the test command by hand once.
- Create
GRAPH_PAUSED, start the graph, check that it stops. Delete the file. - Run the graph on 2 or 3 small tickets.
- Change a state field by hand between two nodes. The supervisor must stop the graph.
- Check that
graph-state.jsonupdates after every node. - Read every diff on the first day.
Keep the graph's health numbers each week: tokens per node, which exits fire, how many tickets reach a gate, and how many changes were later reverted. A number that moves is your cue to diff a run.
Error Index
The Graph Lab's linter rules and the common check messages, what causes them, and the fix. A check message starts with the version it failed on, for example "Version 2: …".
Graph Linter
| Rule | Cause | Fix | Lesson |
|---|---|---|---|
router-no-fallback | A router has no when: else edge | Add when: else to the careful path | Routing |
two-writers | Two nodes write the same field | Give each field one writer | Nodes, Edges, Shared State |
join-no-timeout | A join waits forever | Add timeout: 10m | Parallelization |
orchestrator-no-caps | No limits on workers | Add caps: { width, depth, spend } | Orchestrator-Workers |
loop-back-no-exit | A loop-back node has no exit: | Add exits and a turn cap | Evaluator-Optimizer |
whole-state | A node reads * | List the fields it needs | Nodes, Edges, Shared State |
risk-no-gate | A high-risk path has no gate | Add a kind: gate node before the merge | Human Gates |
could-be-loop | The graph does one loop's job | Use one loop | Loop or Graph? |
no-checkpoint | No checkpoint: | Add checkpoint: after_each_node | Nodes, Edges, Shared State |
no-kill-switch | An orchestrator with no stop switch | Add kill_switch: GRAPH_PAUSED | Orchestrator-Workers |
Check Messages
| Message starts with | Cause | Fix |
|---|---|---|
| "… has 2 writers" | Two nodes own a field | One writer per field |
| "… reads the whole state" | reads: ["*"] | List the fields |
| "The final status is …" | A result was lost or never read | Let one node read both results and decide |
| "After the crash …" | No checkpoint | checkpoint: after_each_node |
| "The script writer's context is …%" | The writer reads too much | Give it only findings |
| "The handoff is not typed" | findings is plain text | Give it a shape in state: |
| "The router is right on …%" | A weak router | model: large |
| "… high-risk ticket(s) took the cheap path" | Misroutes, no safe fallback | min_confidence and a careful else |
| "Only … low-risk tickets took the quick path" | Threshold too high | Lower min_confidence |
| "Branches write the same field" | A race | One field per branch |
| "One branch hung …" | No join timeout | timeout: on the join |
"The join has no policy:" | No rule for missing branches | policy: all, quorum or partial |
"Branch … has no timeout: of its own" | Only the join has a timeout | A shorter timeout: on each branch |
| "The join trusted the results …" | Silent worker failures | check_plan: true on the join |
| "Only … of 10 scripts test the real cause" | Dead ends still reach the writer | Give the writer only the typed findings |
| "No width cap …" / "… more than the API allows" | Too many workers at once | width at or below the limit |
| "No depth cap …" | Workers start workers | depth: 1 |
| "The spend cap … stopped the run" | Spend cap too low | Size it for the biggest job |
| "The realized topology is not recorded" | No record of what ran | record_topology: true |
| "The fixer went back and forth …" | No oscillation exit | oscillation: true |
| "Only the budget stopped it …" | Exits missing | Add the exit for that stuck state |
| "A stop on a bound is reported as plain failure" | No escalation | on_bound: escalate |
| "… merged with no human gate" | No gate on the risky path | Gate before the merge |
| "A gate shows no evidence" | Reviewer sees nothing | evidence: [careful_diff, test_result, risk_reason] |
| "… the reference does the job with …" / "… does this job with …" | The graph is bigger than needed | Remove nodes that move no number |
"The writer does not read findings" / "The investigator does not write findings" | The handoff is broken | Investigator writes findings; writer reads it |
| "The fixer made the same change again and again …" | No no-progress exit | no_progress: 2 |
| "The score stayed flat while each try cost more …" | No flat-score exit | flat_score: 3 |
"max_iterations: … is a weak backstop" | Turn cap too high | 12 or less |
| "… low-risk tickets wait for a person" | Gates on the quick path | Gate only the careful path |
| "The join waits … minutes" | Join timeout too long | 30 minutes or less |
"policy: … is not known" | A typo in the policy | all, quorum or partial |
| "The join does not read …" / "The verifier does not read …" | A branch's result is never merged | List every branch field in the join's reads |
"Add a verifier …" / "The verifier join has no timeout: …" | Audits go straight to the fixer, or the verifier can hang | A kind: join with a timeout and a policy |
| "The fixer has no test checker …" | Fixes marked done with failing tests | exit: { checker: <your test command> } |
| "The … export does not validate" | A node or file is missing | Check the Export tab's message |
Glossary
- Node: one step of work: code, a model call, an agent loop, a router, a join, an orchestrator or a gate.
- Edge: a route from one node to the next: fixed, conditional, loop-back or fan-out.
- Shared state: the typed record that travels with the work.
- State contract: each field's type, its one writer and its readers.
- Handoff: the field one node writes for the next.
- Join policy: what a join does when a branch is missing: all, quorum or partial.
- Caps: limits on an orchestrator's width, depth and spend.
- Realized topology: what a run actually started.
- Stopped on bound: a loop that stopped because an exit fired, not because it failed.
- Blast radius: how much damage a wrong change can do.
- Graph pack: the exported files: graph.md, subagents, state contract, supervisor, notes.
Further Reading
- How to Implement Graph Engineering, by QA Wolf
- Building Effective Agents, by Anthropic
- Graph Engineering vs Loop Engineering, by AI Builder Club
- Prompt vs Loop vs Graph Engineering, by MarkTechPost
- graphkit, by levi-qiao
- Building Coding Agent Workflows with Loop Engineering, on GoPenAI
- Loop Engineering, by Addy Osmani
- Claude Code subagents · Codex subagents · Cursor subagents