Skip to main content

Graph Engineering: A Crash Course

16 Concepts · From a spine one loop reads to a graph a thousand agents share

Your loop works. It fires every morning at 9am, the harness fences it in, and its spine, one progress.md file, carries what it learned from yesterday into today. One loop, one memory file. That was enough.

Now watch what happens when you succeed. You add a second loop. Then a review loop. Then, one busy week, you fan out twenty agents to audit twenty files at once. Each agent starts with an empty context window. Each one rediscovers what another agent already found an hour ago. Each one writes its findings into a transcript that no other agent will ever read. The work multiplied. The memory did not. You built a team with no shared brain.

This is the problem graph engineering solves. The idea is one sentence long: the agent forgets, the graph does not. Instead of leaving what agents learn inside their transcripts, you make them write it down as typed, connected records, nodes and edges, that any later agent can query. Two graphs do this job. A commit DAG remembers the work: what was tried, what descended from what, what was kept. A knowledge graph remembers the facts: which entities exist, how they relate, and which source proves each claim. This course teaches both, using the clearest documented examples of 2026: Andrej Karpathy's autoresearch and AgentHub for the first, and Anthropic's Knowledge Graph Construction Cookbook and Dynamic Workflows for the second. And because the loops themselves need wiring too, Part 5 adds the third graph — the governance graph: who checks whom, who owns whose target, and which measurements no loop can argue with.

You need these first: Loop Engineering and Harness Engineering. The loop course taught the beat, the spine, the maker–checker split, and the ratchet. The harness course taught the five verbs and typed output. This course assumes all of it. The spine was one loop's private memory. This course is what the spine becomes when many agents must share it. If those words are new, do those courses first.


📚 Teaching Aid

Open Full Slideshow

View Full Presentation — Graph Engineering: A Crash Course

The ten figures below are built to be taught in the same order: each one carries a single concept and can stand alone on a slide.


Everything in this course runs — clone the lab first

Every concept below has a script you can execute and watch, not just read. They need bash, git, jq, and python3, and nothing else: no API key, no pip install, no network.

git clone https://github.com/panaversity/agentfactory-labs.git
cd agentfactory-labs/crash-course/graph-eng
./verify.sh # runs all 17 demos and asserts each one

When that prints Everything in this course runs., keep the folder open beside this page. Each concept ends with the one command that demonstrates it: you watch a git reset erase a commit, a schema reject a malformed reply, a checker demand a missing edge, and a pre-commit gate block six different violations. The lab README maps every concept to its demo.

Where this course sits in the series: five course cards in a row, joined by arrows, each adding one layer. One, Loop Engineering, one agent that runs without you, covering beat, spine, ratchet and maker-checker, marked prerequisite. Two, Harness Engineering, walls around that agent, covering permissions, hooks and typed output, marked prerequisite. Three, Graph Engineering, highlighted in gold and marked you are here, shared memory for many agents, covering the commit DAG, the knowledge graph and governance. Four, Trusting the Checker, proof the checker is any good, covering golden sets, rubrics, pass rates and drift, marked comes after. Five, Leaving the Laptop, a home that is not your machine, covering headless runs, schedules and managed runtimes, marked comes after. A panel underneath lists the four words this course assumes you already have: beat, one full run of a loop; spine, state a loop reads first and writes last; ratchet, keep only what improves; worktree, an isolated folder per agent. Footer: if those four words are new, do Loop Engineering first, because this course builds directly on them.

New here? A 2-minute recap of what you should already know
  • A beat — one full run of a loop: discover, implement, verify, commit.
  • The spine: saved state (like progress.md) that a loop reads first and writes last, so the next beat knows what happened.
  • Maker–checker: one agent creates the work; a different agent or command checks it.
  • The ratchet: every caught failure becomes a permanent fix, so the same mistake cannot repeat.
  • Typed output: the agent returns fixed-shape JSON, validated by code before anything trusts it.
  • Worktrees: separate working folders so parallel agents cannot overwrite each other.
  • The human gate: risky decisions go to a person. Nothing unattended reaches main.

If any of these are new, read the Loop Engineering and Harness Engineering courses first. This course connects the machinery those courses built.

Key words in plain English

You will see these words throughout the course. Read this list once now, then return to it whenever a term feels unclear.

TermPlain-English meaning
GraphA set of points (nodes) connected by arrows (edges). The arrows have direction, and direction carries meaning.
NodeOne point in the graph: an entity, a claim, a commit, a source, an agent run.
EdgeOne arrow between two nodes, with a label: supports, parent_of, produced, works_for.
DAGDirected acyclic graph: arrows never loop back on themselves. Git history is a DAG.
Commit DAGThe graph of work: commits are nodes, parent links are edges. It answers "what was tried, and what came from what?"
Knowledge graphThe graph of facts: entities are nodes, typed relations are edges. It answers "what exists, and how is it connected?"
EntityA thing the graph tracks: a person, a company, a file, a vendor, an incident.
Relation / tripleOne fact as subject–predicate–object: (Vendor X, supplied, Component Z).
Surface formA name exactly as it appears in a document: "Edwin Aldrin", "Buzz", "Col. Aldrin". Three surface forms, one person.
Entity resolutionDeciding which surface forms are the same real thing, and merging them into one canonical node — without losing the original names.
ProvenanceThe receipt attached to a claim: which source said it, which run extracted it, how confident the extraction was.
ClaimA statement the graph stores as possibly true — always with provenance, never as bare truth.
SubgraphA small, relevant slice of the graph, cut out for one task. You hand agents subgraphs, never the whole graph.
GroundingForcing an answer or a verdict to point at real edges in the graph, instead of at its own impression.
SwarmMany agents exploring, implementing, or evaluating at the same time.
Structured outputA model response constrained to a schema (for example, a Pydantic model), so code can validate it before believing it.
MCPModel Context Protocol: the standard way an agent reaches an outside system. Not needed while the graph is files; the answer once it is a database.
Governance graphThe graph whose nodes are the loops themselves, plus human gates and anchors, and whose edges say who feeds, checks, and constrains whom.
Execution loopA loop that does recurring work: triage the issues, review the PR, draft the changelog.
Improvement loopA loop that watches a number against a target and adjusts the system doing the work.
Counter-metricA second number, watched by a second loop, that catches the first number being gamed.
AnchorA measurement no loop can argue with: a test that actually ran, a customer who actually stayed, money that actually arrived.
Frozen nodeA rule or file the optimizing loops may never change — exactly because they would want to.

The loop course gave loops a body metaphor: heartbeat, body, spine. This course adds one more: the graph is the shared brain — the memory that outlives any one agent's context window.

Where this came from, and one meme to correct first

The short version: the primary work is real and public, but the viral framing of it is wrong. The "11-page PDF by two Anthropic seniors" is an independent study note that says on its own front page that it is not affiliated with or endorsed by Karpathy or Anthropic, and "1000x" is a slogan, not a measurement. Read the primary sources first.

The full timeline, and what the meme got wrong

The timeline is short and public. On March 7, 2026, Karpathy released autoresearch: an agent locked inside a small training repo, running one five-minute experiment at a time and keeping only what improves the metric. It collected tens of thousands of GitHub stars within weeks, and Fortune called the pattern "the Karpathy Loop." Three days later he sketched AgentHub, "GitHub is for humans. AgentHub is for agents", a bare Git repo plus a message board where swarms coordinate through the commit DAG instead of a main branch. On March 23, 2026, Anthropic published its Knowledge Graph Construction Cookbook, which replaces a classical NLP pipeline with structured-output prompts: extract typed entities and relations, resolve duplicates, assemble a graph, query it with citations. And Anthropic's Dynamic Workflows (announced May 28, 2026, and since made generally available) lets Claude write the orchestration script that fans work out to parallel fresh-context sub-agents.

On July 18, 2026, Peter Steinberger's twelve-word midnight question, "Are we still talking loops or did we shift to graphs yet?", gave the whole topic its name of the season; Part 5 tells that story and Carlos E. Perez's answer to it. Days later a viral post stitched everything together: "Two Anthropic seniors just made Karpathy's loop 1000x better with Graph Engineering — dropped 11-page PDF." Read the PDF's own first page before you repeat that. It says, in italics: independently compiled, not affiliated with Andrej Karpathy and Anthropic, and not endorsed. The synthesis is genuinely useful, this course draws on it, but it is a study note by an independent author, not an Anthropic paper, and "1000x" is a slogan, not a measurement. Same reflex that turned Steinberger's question into "loop engineering is dead" — Part 5 dissects that one; treat this one the same way. The primary sources are real and worth your time, with one caveat to carry: autoresearch and Anthropic's cookbook and workflow docs are public, while AgentHub was taken private shortly after release and now survives only through unlicensed forks (Concept 5 says more).

One genuine convergence the meme missed, and it is stranger than the meme: on May 19, 2026, Karpathy joined Anthropic's pretraining team, to build a group focused on using Claude to accelerate pretraining research itself. So the loop tradition and the graph tradition in this course did meet at Anthropic. Just not in the way a viral post claimed, and not in that PDF. (All sources listed in Sources & further reading.)

One phrase, two halves — both taught here

The industry uses "graph engineering" for two related things, and this course covers both. The first half is the memory graph: the durable, typed state your agents share — the commit DAG of work and the knowledge graph of facts. That is Parts 2 through 4. The second half is the governance graph: the wiring between the loops themselves — who feeds whom, who checks whom, where the human gate sits, and which measurements no loop can argue with. That is Part 5, built on Peter Steinberger's question and Carlos E. Perez's essay. The two halves are layers of one system, not rivals: the governance graph connects the workers, the memory graph stores what the workers know, and they meet in Concept 10, where a checker from the governance layer reads its evidence from the memory layer. The loop course ends exactly where this course begins, one loop, fully built, and hands both halves of the graph question here.

The six-step playbook, and where each step already lives

Most readers arrive here through a viral six-step version of the playbook. Four of its six steps are already behind you, and this course is the other two plus the part the list omits.

The six steps, mapped onto this series
The step, as the post puts itWhat it actually isWhere you learned it
1. Build one loop: generate, critique, reviseThe maker–checker beat and the ratchetLoop Engineering
2. Add tools: search, code, databaseConnectors, and the tool schemas that fence themLoop and Harness Engineering
3. Go parallel: agents in separate worktreesIsolation, so concurrent work cannot collideLoop and Harness Engineering
4. Add a graph: typed nodes and edges, not transcriptsMemory that outlives the sessionParts 2 to 4 of this course
5. Ground the evaluator in edges, not vibesVerification with citable evidenceConcept 10 of this course
6. The graph survives every sessionProvenance, supersession, and durable stateConcepts 8 and 13, and Part 6

So the honest version of "1000x" is not a benchmark. It is that steps 1 to 3 give you a capable worker, and steps 4 to 6 give a thousand of them one shared memory. Same model, different architecture.

The mindset shift, in one picture

The mindset shift: transcript memory versus graph memory. Left panel, transcripts — memory that dies with the session: three agent boxes, each with its own scroll of conversation text, fading at the bottom edge; dashed arrows between them are crossed out; caption: each agent learns alone, forgets alone — to share anything, you must copy whole transcripts into another context window, and the window fills. Right panel, a graph — memory that outlives every session: the same three agents around a central slate graph of typed nodes (Entity, Claim, Source, Commit, Evaluation) joined by labeled edges (supports, parent_of, produced, about); each agent has one gold arrow writing a small typed update in, and one green arrow reading a bounded subgraph out; a padlock note on every edge reads "provenance: source + run + confidence." Footer: the agent forgets, the graph does not — write findings as nodes and edges, not prose, and any later agent can query what any earlier agent learned.

One note on tools, and it is a smaller note than in the sibling courses. Loop, harness, and eval work all had tool-specific spellings to teach. Graph work has almost none: the graph is files, Git, jq, and a schema you own, so there is nothing to buy and no product feature to configure. Claude Code and OpenCode appear here only as the workers that read from and write to it, and every command below works the same in both, with claude -p and opencode run interchangeable throughout. That tool-independence is not an accident of this course's scope. It is the strongest evidence that the graph is a discipline rather than a feature.

One term belongs here, because readers reasonably expect it and then mis-place it. MCP, the Model Context Protocol, is the standard way an agent reaches an outside system: a server exposes tools, and any MCP-speaking agent can call them. At this course's size, MCP does not appear at all, and that is correct: the graph is files in the repo, so the agent reads and writes it with the file and shell tools it already has. MCP becomes the answer at the next size up. When the graph moves into Postgres or Neo4j and several agents on different machines must reach it, you do not give every agent database credentials and a hand-rolled client. You put one MCP server in front of the store, exposing a small, typed surface: resolve an entity, fetch a bounded subgraph, append a validated claim. Every rule in this course then lives in that server, where it is enforced once, instead of in each agent's prompt, where it is only requested.

Keep three layers apart, because they are easy to blur. A skill is knowledge the agent loads: how we write claims here. MCP is the wire: how the agent reaches the store at all. The graph is the memory itself: what is actually remembered. A skill without a graph is advice with nothing to write to. A graph without a skill gets filled inconsistently. And MCP is neither of those; it is the plumbing that becomes necessary the moment the store stops being a file in the same folder as the agent.

True in late July 2026, with different shelf lives. Autoresearch is actively developed; AgentHub is frozen and private, so treat everything about it as history; Dynamic Workflows is now generally available and its limits and defaults move with the product; the Cookbook is a living notebook. Before you trust a flag, a limit, or a model name, check the live sources: github.com/karpathy/autoresearch, platform.claude.com/cookbook, code.claude.com/docs, opencode.ai/docs.

What this course covers

PartTopicWhat you learn
1The memory problemWhy transcripts fail as team memory, what a graph is, and the two graphs every swarm needs
2The DAG of workKarpathy's path: autoresearch writes history into Git, AgentHub turns the DAG into the collaboration layer
3The graph of factsAnthropic's path: extraction by schema, entity resolution, and provenance on every edge
4Working from the graphSubgraphs instead of dumps, and the grounded checker: "triple not found" beats "seems off"
5The graph of loopsThe governance layer: who checks whom, the four ways a single loop breaks, and the anchors no loop can argue with
6One graph, end to endThe morning-triage loop's spine, upgraded into a small queryable graph — files and shell, both tools
7Staying groundedChoosing a level, the complexity budget, when not to build a graph, and the bridge to the next two courses
LiveDogfoodingThe proto-graph this book already runs, and the graph it deliberately does not
PracticeProjectsEight graph builds, easy to hard

Want to learn by doing? Read Part 6 first to see one finished graph. Then come back for the parts.

Two ways to read this course

First time? Take the memory path: Parts 1 to 4, which is Concepts 1 to 10, then jump straight to Part 6 and build it. Skip Part 5 and every note marked "Going deeper." That is about two hours, or closer to three if you work the examples rather than reading past them. Then do Projects 1–3. After that you can draw your system as a graph, store claims with sources, and make a reviewer cite its evidence.

Second read (after your first graph has answered a question your transcripts could not): the governance path, which is Part 5, plus all of Part 7 and Projects 4–8. Part 5 is about keeping many loops honest, and that only becomes urgent once more than one loop writes to the same memory. Concept 15's warnings likewise only land once you have a graph you are tempted to over-build.

What to remember, and what to look up

Two layers, aging differently. Remember the first. Look up the second.

  • The lasting layer. The agent forgets; the graph does not. Work lineage and domain facts are two different graphs: do not collapse them. A schema is cheaper than a trained pipeline. Resolution must keep its receipts and stay reversible. Every claim carries provenance or is marked inference. Hand agents subgraphs, never the whole graph. Ground the checker in edges. Ask the six questions before choosing a level, and declare the budget before the run. Pair every optimizing loop with a watching loop on a counter-metric. At least one signal in the system must come from reality, not from another model's report. And a graph amplifies the builder's judgment: including the builder's mistakes.
  • The mechanical layer. Every repo name, model name, flag, and star count below. Autoresearch's file layout, Dynamic Workflows' concurrency caps, the Cookbook's exact Pydantic classes: treat each as a pointer to the live source, not a fact to memorize. Where this course and the live docs disagree, the docs are right.

Part 1: The Memory Problem

1. The agent forgets; the graph does not

Run this one: bash concepts/01-agent-forgets.sh in the companion lab. Reading it is half; watching it happen is the other half.

Start with what you already own. Your morning-triage loop keeps a spine: progress.md, read first, written last. That file is memory, and it works: for one loop. Now count what it cannot do:

  • A second loop cannot trust it. progress.md is prose. The changelog loop would have to read and interpret another loop's diary, and hope the format never drifts.
  • Twenty parallel agents cannot share it. Fan out twenty auditors and each starts empty. Agent 7 finds that utils/dates.ts has a timezone bug. Agent 14, an hour later, finds it again. Nothing connected them.
  • You cannot query it. "Which findings from last month touched the payment code and were confirmed by a reviewer?": a spine cannot answer that. You would grep prose and hope.
  • It cites nothing. The spine says "fixed the flaky test." Which test? Proven by which run? Superseded by which later fix? The prose does not know.

The naive fix is to copy transcripts around: paste agent 7's conversation into agent 14's context. This fails at exactly the moment you need it. Context windows fill. Costs multiply. And a transcript is the wrong shape for memory: it records everything that was said, in order, instead of what was established, with evidence.

Graph engineering is the deliberate alternative. Agents write what they learn as typed records: this is an entity, this is a claim about it, this is the source that supports the claim, this is the run that produced it. Records connect to records by labeled arrows. Any later agent, tomorrow's beat, a different loop, a different model entirely, queries the records it needs and continues. The independent PDF that popularized this course's topic puts it in five words, and they are the right five: the agent forgets, the graph does not.

In simple terms

A transcript is a chat log. A graph is a filing system. When a new employee joins a team, you do not hand them recordings of every conversation the team ever had — you hand them the files: organized, labeled, cross-referenced. Graph engineering is building the filing system your agents deserve.

Check yourself

Your triage loop and your changelog loop both need to know which PRs shipped this week. Today, each one runs git log and re-derives it. What is being wasted, and what is the graph-engineering fix?

Show answer

Work is being re-done and re-interpreted in every context window: the exact "rebuilding the world from scratch" failure. The fix: the loop that first establishes "PR #212 shipped, fixes issue #98" writes it once as a typed record with provenance (the commit hash). Both loops then read the record. Derive once, query many times.

2. What a graph is: nodes, edges, direction

Run this one: bash concepts/02-nodes-edges.sh in the companion lab. Reading it is half; watching it happen is the other half.

You met this definition at the end of the loop course; here it becomes the working tool. A graph is a set of points, called nodes, connected by arrows, called edges. Three properties do all the work:

  1. Nodes are typed. Not "a box," but "an Entity," "a Claim," "a Source," "a Commit," "an Evaluation." The type tells every reader what questions the node can answer.
  2. Edges are labeled and directed. (claim_441) —supported_by→ (source_readme) means something different from the reverse arrow. Direction is meaning: who supports whom, who descends from whom, who checked whom.
  3. Paths are answers. "Is Vendor X connected to Incident Y?" becomes "does a path of supported edges exist from the Vendor X node to the Incident Y node?" A question about the world turns into a walk through the arrows.

That third property is the payoff. Prose must be read and interpreted. A graph can be traversed: mechanically, by code, the same way every time. The moment your memory is a graph, questions that were vibes become queries.

One special shape matters enough to name. A DAG, directed acyclic graph, is a graph whose arrows never circle back. You have used one for years without the name: Git history. Every commit points to its parent. No commit is its own ancestor. Keep that example in your pocket, because Part 2 is built on it.

In simple terms

Nodes are nouns. Edges are verbs with a direction. A graph is a set of sentences, (subject, verb, object), drawn as a picture, so that a computer can follow chains of sentences without understanding prose.

Check yourself

Two agents both record that Vendor X supplied a part. One writes the sentence "Vendor X supplied component Z" into its log. The other writes (vendor_x) —supplied→ (component_z) into a graph. Both are true. What can a later agent do with the second that it cannot do with the first?

Show answer

Follow it. The sentence must be read and interpreted correctly by whatever model happens to see it. The edge can be traversed mechanically, the same way every time, and chained: from vendor_x to component_z, then onward to whatever else touches component_z. That is how a question about the world ("is Vendor X connected to this incident?") becomes a walk through arrows instead of an act of reading comprehension. The typed edge also carries its receipt, which prose usually loses.

3. Two graphs, and why not to collapse them

Run this one: bash concepts/03-two-graphs.sh in the companion lab. Reading it is half; watching it happen is the other half.

Here is the distinction that organizes this entire course, and the one beginners most often blur. A multi-agent system needs two different graphs, because it must remember two different kinds of things:

The commit DAG (Part 2)The knowledge graph (Part 3)
RemembersWork: what was triedFacts: what is known
NodesCommits, experiments, runsEntities, claims, sources
Edgesparent_of, derived_fromsupports, works_for, about
AnswersWhat changed? What descends from the batch-size experiment? Which lineages are still alive?Which entities exist? How are they related? Which source backs this claim? Which claims conflict?
You already own a versionGit history, partly: see Concept 4Nothing yet: Part 3 builds it

The two graphs, side by side and joined but never merged. Left panel, the commit DAG, labelled remembers the WORK: what was tried, with a chip reading a fact by construction. A chain of five gold commit circles c1 to c5 rises and falls left to right, joined by solid parent arrows, with c5 annotated kept: best metric. Three dashed grey branches leave the trunk and end in pale crossed-out circles labelled reverted, reverted, and crashed. Caption: nodes are commits, experiments, runs; edges are parent_of and derived_from; it answers what changed, what descends from what, which lineages live. Right panel, the knowledge graph, labelled remembers the FACTS: what is known, with a terra chip reading a claim with evidence. A central gold claim_441 box connects by labelled arrows to a white vendor_x box (about), a cream contract.pdf box (supported_by, annotated confidence 0.9), and a pale claim_238 box above it (supersedes, annotated replaced, never erased). Caption: nodes are entities, claims, sources; edges are supports, about, supersedes; it answers what exists, how it relates, which source backs it. A dashed bridge panel across the bottom is headed Joined, never merged, with the note one agent run did work AND learned something: a central gold agent_run_183 chip has a modified arrow left to a white commit_a81f box, captioned work lineage, and a produced arrow right to a gold claim_441 box, captioned domain knowledge. Footer: the lab notebook and the encyclopedia, keep both, cross-reference, never collapse.

Why keep them separate? Because they have different truth rules. A commit is a fact by construction: it happened, Git guarantees it. A claim in a knowledge graph is a statement with evidence: it might be wrong, mis-extracted, or superseded, which is why every claim edge carries provenance and a confidence. Collapse the two and you either treat guesses as history or bury history under guesses.

They do connect. A production system links them with edges of their own:

(agent_run_183) —produced→   (claim_441)
(agent_run_183) —modified→ (commit_a81f)
(claim_441) —about→ (entity_vendor_x)
(claim_441) —supported_by→ (source_contract_pdf)
(claim_441) —supersedes→ (claim_238)

Read that block slowly: it is the whole course in five lines. A run did work (a commit) and learned something (a claim). The claim is about an entity, is backed by a source, and replaces an older claim. Work lineage on the left, domain knowledge on the right, joined but never merged.

In simple terms

The commit DAG is the lab notebook: experiments, dated and in order, with a parent for each one. Keep the failures in it too, which is a choice Concept 4 shows autoresearch declining to make and Concept 5 shows AgentHub making. The knowledge graph is the encyclopedia the lab is writing: what we now believe, with footnotes. A lab that keeps only the notebook cannot answer questions. A lab that keeps only the encyclopedia cannot show its work. Keep both, and cross-reference them.

Check yourself

An agent reports: "I refactored the parser (commit 9fc2), and while doing it I confirmed the vendor's API rejects dates before 1970." Where does each half of that sentence live?

Show answer

The refactor lives in the commit DAG: commit 9fc2, with its parent link, automatically. The API behavior is a claim for the knowledge graph: (vendor_api) —rejects→ (pre-1970 dates), with provenance pointing at the run and the evidence (the error response). Today, that second fact dies in the transcript. That is the loss this course exists to stop.

Before you go further: do you even need one?

Most systems should not build a knowledge graph, and you deserve to know that before you read four parts about how. The short test, in full at Concept 15: if your tasks are independent, your answers come from one document at a time, your relations are fixed and simple, a relational table already answers every query you actually ask, and nobody needs provenance, then stop here. One loop with a spine is the right answer, and adding a graph buys you extraction errors and schema upkeep in exchange for questions nobody asked.

The bill flips when two loops must exchange facts, when a synthesis spans many workers, when relations keep evolving, or when someone will one day ask "how do we know that?" Read on if any of those is true, or is about to be. Concept 14 turns this into six ordered questions.


Part 2: The DAG of Work

4. Autoresearch: the ratchet writes its history into Git

Run this one: bash concepts/04-autoresearch-ratchet.sh in the companion lab. Reading it is half; watching it happen is the other half.

The loop course taught you the ratchet: try one change, evaluate, keep it only if the number improves, otherwise revert. Karpathy's autoresearch (March 7, 2026) is that exact loop, pointed at ML training, and its importance for this course is one design choice: the loop's memory is not a transcript. It is the commit DAG.

The setup is three files inside a small single-GPU training repo:

  1. prepare.py, fixed data preparation and evaluation. The agent may not touch it. (A frozen node, the harness course's deny rule, applied to a file.)
  2. train.py: the model, optimizer, and training loop. The one surface the agent edits.
  3. program.md — natural-language instructions: the metric, the budget, the commit and revert rules, when to escalate. (The loop course called this "programming the program.")

Then the beat, forever: read train.py and recent history, propose one motivated change, commit it, train for about five minutes, measure validation loss. Improved? The commit stays. Worse or crashed? Reset to the last retained commit. Either way, record the result and continue: no human in the loop.

Now look carefully at what each beat leaves behind, because this is where beginners, and several popular write-ups, get autoresearch wrong. It has two memories, not one, and they keep different things:

What it holdsTruth standard
The Git branchOnly the retained improvements, as a rising chain of commits on a dedicated experiment branchVerified: every commit on it beat the metric
results.tsvEvery attempt: commit hash, metric, memory used, kept or discarded or crashed, and what was triedComplete: it records attempts, not achievements

The detail that matters is what happens on failure. The instructions say it plainly: if the metric improves you advance the branch and keep the commit, and if it is equal or worse you git reset back to where you started. A reset does not park the attempt on a side branch. It removes that commit from the branch. So the discarded experiment survives in results.tsv, and there only. And results.tsv is deliberately left untracked by Git.

Read that as a design rather than an oversight, because it is the cleanest illustration of Concept 3 you will find in the wild. Git holds work whose value was proven. The TSV holds the honest record of everything attempted, including the crashes. A human researcher carries hypotheses, failed attempts, and parameter interactions in working memory and loses them; autoresearch writes both kinds down, in two places, under two standards. What it does not do is keep alternative lineages alive and traversable: a discarded idea becomes a row of text that no other agent can query. That gap is exactly what Concept 5 fills.

The reported numbers from its first weeks — roughly 630 lines of core code, about 700 experiments in two days, around 20 retained optimizations — matter less than the shape. The repo drew tens of thousands of stars not because the optimizations were profound but because the pattern is legible: small code, visible metric, two honest logs. (Check the live repo before quoting any number: it moves weekly.)

The four conditions that make it work are the loop course's checklist, word for word: the output is verifiable (a validation number), the action is reversible (git reset, which undoes the attempt but does not preserve it), the horizon is short (five-minute runs), the environment is bounded (one repo, one editable file). Nothing new: except where the memory lives.

In simple terms

Autoresearch is your ratchet loop with the memory moved out of the agent's head and into two files. The Git branch keeps what worked. results.tsv keeps what was tried. The loop can crash and restart without losing its bearings, because neither memory lives in a context window. But notice the honest limit: a discarded experiment leaves a row in a text file, not a node anyone can traverse.

Try it now: read a DAG as memory (4 min)

Open any repo you have worked on with an agent and ask the DAG the questions a spine cannot answer:

git log --oneline --graph -20        # the DAG, drawn
git log --follow -- path/to/file.ts # every experiment on one surface
git diff HEAD~5 HEAD -- src/ # what five beats of work changed

Then ask your agent: "Read the last 20 commits. What was the apparent metric, and which commits look like retained improvements?" Notice what the DAG cannot tell you: which experiments were tried and thrown away. In autoresearch that answer lives in results.tsv, outside Git. Concept 5 is about turning those rows into nodes.

5. AgentHub: traverse the search graph, do not merge to main

Run this one: bash concepts/05-agenthub-traversal.sh in the companion lab. Reading it is half; watching it happen is the other half.

Three days after autoresearch, Karpathy posted the next step: the loop should become "asynchronously massively collaborative for agents, think: SETI@home style" — emulate a research community, not one PhD student. AgentHub is his sketch of that layer: one Go binary, one SQLite database, one bare Git repository, one API key per agent, and a message board. Its slogan is the thesis: "GitHub is for humans. AgentHub is for agents."

Start from the gap Concept 4 left. A single ratchet resets its failures away, so the only record of a discarded idea is a row in an untracked text file that no other agent can query. AgentHub's central move is to stop resetting and start keeping: agents push commits as bundles, every pushed commit becomes a durable node, and nothing is required to converge on a main branch. The alternatives stay alive and traversable. That is the difference between a text log of failures and a graph of them.

Why does agent collaboration need different plumbing? Because swarms invert every assumption human Git carries:

  • Thousands explore at once. Human repos assume a handful of branches; a swarm makes thousands of simultaneous attempts normal.
  • Most results never merge: on purpose. For humans, an unmerged branch is unfinished business. For a swarm, a failed experiment is evidence: it tells every other agent that an idea fails under one condition.
  • The primary operation changes. Not "merge this into main" but "traverse the search graph." There is no required main branch, no pull requests, no merge queue, no assumption that any one leaf is canonical.

The CLI makes the shift concrete — each command is a graph query wearing work clothes:

ah push                 # publish my commit as a new node
ah children <hash> # what was tried on top of this result?
ah leaves # the frontier: results nobody has built on yet
ah lineage <hash> # the full ancestry path that produced this outcome
ah diff <a> <b> # compare any two experiments, related or not
ah log --agent X # one agent's trail through the search

children asks which ideas were explored above a result. leaves reveals unexplored frontier. lineage reconstructs how an outcome was reached. Try asking those questions of a conventional branch model and feel how awkward they are: the DAG-as-graph view makes them one command each.

Autoresearch keeps two memories, and AgentHub adds the third. A subtitle warns that a reset does not park the failed attempt on a side branch, it removes that commit from the branch. Across the top, One beat runs four cream stages joined by arrows: edit train.py, commit, train about five minutes, measure val_bpb, with a padlocked chip reading prepare.py frozen. The path then forks to a gold chip, improved: advance the branch, and a terra chip, equal or worse: git reset. Below sit three panels. Panel one, gold-bordered, The Git branch, only what was retained, chipped verified: every commit beat the metric: four gold commits c1 to c4 climb in a clean chain, while three pale dashed circles containing question marks float beside it, annotated reset: gone from the branch, captioned a clean ratchet, and an incomplete record. Panel two, results.tsv, every attempt kept or not, chipped complete: attempts, not achievements: a small monospace table with columns commit, val_bpb and status lists c1 1.58 baseline, c2 1.53 keep, a dash 1.77 discard, c3 1.51 keep, and a dash 0.00 crash, above a terra strip reading left untracked by git, on purpose. Panel three, dashed, AgentHub: the DAG, alternatives stay alive, chipped traversable: failures become nodes: a branching graph of six nodes, four gold and two crossed terra ones, spreads in several directions, annotated a discarded result is still a node, beside the monospace commands ah children, ah leaves and ah lineage. Footer: Git keeps what worked, the TSV keeps what was tried, and only a graph lets a later agent query either one.

The message board is the social layer, and it completes the memory story. An agent does not need every prior transcript. It queries relevant lineages, reads a few summaries, fetches one commit, and continues. The independent PDF names this precisely: graph-grounded context construction — retrieve the connected state needed for the current decision, instead of replaying the entire history. Hold that phrase; Concept 9 generalizes it.

Two honesty notes, and the second matters for anyone trying to follow along.

First, in the repo's own words: "Work in progress. Just a sketch. Thinking..." AgentHub had no answer for trust between agents, malicious bundles, storage at scale, duplicate detection, or long-term indexing. That does not weaken the lesson: it sharpens it. The sketch identifies exactly which human abstractions fail first when agents become numerous: a single main branch, human-paced review, transcript memory, and merge-centered collaboration.

Second: AgentHub is no longer public. It drew a couple of thousand stars within a day of its March 2026 release, and was then taken private: github.com/karpathy/agenthub now returns a 404, and the clearest public account is from a developer who forked it beforehand and documented the design afterwards ("Karpathy open-sourced AgentHub last week. Then the repo went private."). Preserved forks circulate, but the original carried no license file, so treat any copy as a historical artifact for study rather than maintained software you may build on. Do not go looking for it as a dependency. Study the shape, which is what transfers: commits as nodes, bundles as the push unit, traversal as the primary operation, and a message board where a discarded result still teaches.

In simple terms

GitHub assumes one shared "official" version that everyone works toward. A swarm does not want one official version: it wants a map of everything that was tried, including the dead ends, because dead ends teach. AgentHub keeps the map and throws away the ceremony.

Going deeper: the swarm's execution engine

AgentHub is the memory layer for a swarm; something still has to run the swarm. Anthropic's Dynamic Workflows, announced for Claude Code on May 28, 2026 and since made generally available, is the clearest production example: instead of you writing a fan-out script, Claude writes an orchestration program for the current task — glob the files, spawn an auditor per file, filter the findings, spawn reviewers to try to refute them, then one synthesizer to produce a cited report. The official description is tens to hundreds of parallel sub-agents in a single session, each with fresh context, with results checked before they are folded in, and progress saved so an interrupted run resumes instead of restarting. You start one by asking Claude for a workflow, or by switching on the ultracode setting, which raises the effort level and lets Claude decide when a workflow is warranted.

The advertised demo is the Bun port: Jarred Sumner moved Bun from Zig to Rust using dynamic workflows, producing roughly 750,000 lines of Rust with 99.8% of the existing test suite passing, eleven days from first commit to merge. Anthropic notes it is not yet in production.

Two warnings travel with the feature. It consumes substantially more tokens than an ordinary session, which is why the first workflow asks you to confirm and why administrators can disable workflows outright. And parallel workers make correlated errors: a verification wave only helps if the reviewers have a different prompt, evidence set, or role. (The announcement post says "tens to hundreds," but the reference docs are more precise, and they are where you should design against: a "Behavior and limits" table gives up to 16 concurrent agents — fewer on machines with limited CPU cores — and 1,000 agents total per run. Check the docs for the current numbers before you build against them.) The deep question the feature leaves open, where hundreds of fresh-context workers put what they learn, is Part 3's job.

Check yourself

In AgentHub, agent A's experiment fails: a larger batch size crashes at a memory limit. Under GitHub thinking, the branch is abandoned and forgotten. What happens to it under graph thinking, and who benefits?

Show answer

The failed commit stays in the DAG as a node, and a message-board post cites its lineage: "batch 64 exceeds memory on this hardware — branch from the depth change instead." Every future agent that queries children on that lineage inherits the warning without re-running the crash. The failure became shared memory. That is the entire difference between a swarm and a crowd.


Part 3: The Graph of Facts

Git gave you the commit DAG for free. The knowledge graph you must build. For decades that meant a trained NLP pipeline: a named-entity model, a relation classifier, a deduplication heuristic, each needing labeled data and maintenance. Anthropic's Knowledge Graph Construction Cookbook (March 23, 2026) shows the 2026 version: the whole pipeline collapses into structured-output prompts. Four stages, three concepts.

The four-stage pipeline from documents to a queryable graph, with no trained model anywhere. Stage one, Documents, shows three cream file cards named apollo-brief.md, mission-log.txt and crew-notes.md with faded text lines, captioned no labels, no gold corpus. An arrow leads to stage two, Extract, marked cheap model, schema-constrained, with a chip reading claude-haiku-4-5: five white surface-form cards list Edwin Aldrin (lunar module pilot), Buzz Aldrin (second on the Moon), Col. Aldrin (Apollo 11 crew), M. Khan (flight surgeon, 1969) and M. Khan (press officer, 1972), captioned descriptions matter next. An arrow leads to stage three, Resolve, marked stronger model, reasoning task: a gold box shows canonical entity buzz_aldrin with a tick, listing aliases kept, Edwin Aldrin, Buzz Aldrin and Col. Aldrin, and merged three into one; below it a dashed terra box marked NOT merged with a cross explains two people, same name, different descriptions, keep them separate. The stage is captioned reversible: rationale plus confidence. An arrow leads to stage four, Assemble, marked graph with receipts: two entity circles joined by a commanded edge, both linked down to a source circle by a from edge, above a padlocked cream panel headed every edge carries, listing source_doc which document, confidence how sure, and produced_by which run, captioned now queryable, and auditable. Footer: a false merge is the catastrophic failure, keep aliases, keep the rationale, stay reversible.

6. Extraction: the schema is the training data

Run this one: python3 concepts/06-extraction.py in the companion lab. Reading it is half; watching it happen is the other half.

Stage one turns unstructured text into typed pieces. You define the shape you want as a schema, and constrain the model's output to it:

The shape below is abbreviated for reading: EntityType, ExtractedGraph and PROMPT are yours to define, and the Cookbook notebook has the runnable version. What matters here is the three design decisions, not the line count.

class Entity(BaseModel):
name: str
type: EntityType # your enum: PERSON | ORG | PROJECT | ...
description: str # context, used later for resolution

class Relation(BaseModel):
source: str # subject, a name that must appear in entities
predicate: str # short verb phrase: "commanded", "supplied"
target: str # object, likewise

class ExtractedGraph(BaseModel):
entities: list[Entity]
relations: list[Relation]

def extract(text: str, client) -> ExtractedGraph:
response = client.messages.parse(
model="claude-haiku-4-5", # cheap model: extraction is volume work
max_tokens=4096,
messages=[{"role": "user", "content": PROMPT.format(text=text)}],
output_format=ExtractedGraph, # the schema constrains the reply
)
return response.parsed_output

Read the design, not the syntax. Three decisions carry the lesson:

  • The Pydantic schema is the only "training data." No labeled corpus, no fine-tuned NER model. The schema plus a prompt replaces what used to be a team's quarter of work. Changing your ontology is now editing a class, not retraining a pipeline.
  • A cheap model does the volume work. Extraction runs once per document, possibly across thousands of documents, so it runs on Haiku. The expensive judgment calls (next concept) go to a stronger model. This is the maker–checker economics of the loop course, applied to pipeline stages.
  • The description field is not decoration. It captures context around each entity, and context is exactly what Concept 7 needs to decide whether two names are the same thing.

This is also the harness course's typed-output rule, promoted from one reviewer verdict to the whole memory system: nothing enters the graph as prose. Code validates every extraction against the schema before the graph believes it.

In simple terms

You do not teach the model what a person or a company is: it already knows. You hand it a form to fill in, and you reject any reply that does not fit the form. The form is the whole pipeline.

Try it now: extract your first triples (5 min)

No Python needed for the idea. In a repo with a README, run the headless worker you already know:

claude -p 'Read README.md. Return ONLY valid JSON:
{"entities":[{"name":"","type":"PERSON|ORG|TOOL|PROJECT","description":""}],
"relations":[{"source":"","predicate":"","target":""}]}
Every relation must connect two extracted entities. Predicates are short verb phrases.'
# OpenCode: opencode run '<same prompt>'

Pipe the output through jq . to validate. Congratulations: that is stage one of the Cookbook, at operating size.

7. Resolution: same thing, many names

Run this one: python3 concepts/07-resolution.py in the companion lab. Reading it is half; watching it happen is the other half.

Extraction produces surface forms, not a clean graph. The same astronaut appears as "Edwin Aldrin," "Buzz Aldrin," and "Col. Aldrin" across three documents. Left unmerged, your graph holds three disconnected people, and every multi-hop question through Aldrin silently fails. Entity resolution is deciding which surface forms are the same real thing.

Why not string similarity? Because it fails in both directions at once. "Edwin Aldrin" and "Buzz Aldrin" differ exactly where a name is supposed to identify someone — the given names share nothing — so a similarity score lands below any sane merge threshold and misses the merge. Meanwhile two different people named "Muhammad Khan" score a perfect match: a similarity score invents a merge. Names are evidence, not proof.

The Cookbook's move: treat resolution as a reasoning task. Group candidate entities by type, hand a stronger model (Sonnet) each group with the description fields from Concept 6 as context, and ask it to propose canonical clusters. The descriptions are what make "Edwin Aldrin, Apollo 11 lunar module pilot" and "Buzz Aldrin, second person on the Moon" mergeable, and two same-named strangers separable. At scale, cheap blocking signals (same type, overlapping context) narrow the candidate pairs before the model arbitrates, so you are not paying for every pairwise comparison.

Now the rule that separates a durable graph from a time bomb: resolution must be additive and reversible. A canonical entity keeps its aliases, its source documents, the model's merge rationale, a confidence, and the run that created the merge. Nothing is overwritten; surface forms are linked, not destroyed. Why so careful? Because a false merge is the catastrophic failure of knowledge graphs. Collapse two people into one node, and every downstream traversal combines their employers, projects, dates, and actions: confidently, invisibly, everywhere at once. With receipts kept, a bad merge is one reversal. Without them, it is a rebuild.

You know this pattern. It is the harness course's reversibility verb, checkpoints before risky actions, applied to memory instead of code.

In simple terms

Merging names is like combining two people's contact cards. Done right, you keep both original cards stapled behind the merged one, with a note saying why and how sure you were. Done wrong, two strangers stapled together, every message meant for one reaches the other, and nobody can say when it started.

Try it now: meet your own duplicates (6 min)

Take the entities you extracted in Concept 6 and hand them back for resolution:

claude -p 'Here are extracted entities with descriptions: <paste>.
Group them by type, then propose canonical clusters. For each cluster return:
canonical_name, aliases[], rationale, confidence (0-1). Never drop an alias.
If two entities share a name but differ in description, keep them separate.'

Then plant a trap and run it again: add a second entity with a name you already have but a different description. If the model merges them, your descriptions are too thin, which is a Concept 6 fix, not a Concept 7 fix. That dependency between the stages is the lesson.

Check yourself

Your resolution step reports a compression ratio of 5:1 — five surface forms per canonical entity, up from 2:1 last week. The team celebrates the "cleaner" graph. What should you check before joining them?

Show answer

The false-merge rate. Compression alone rewards over-merging: the fastest way to a spectacular ratio is stapling strangers together. A high ratio is only good news if pairwise precision held: sample merged clusters and verify the members really are one thing. A connected graph that is connected by mistakes is worse than a fragmented one, because it answers confidently. (You will formalize this instinct, gold sets, precision, recall, in the next course.)

8. Provenance: every edge keeps its receipt

Run this one: bash concepts/08-provenance-invariants.sh in the companion lab. Reading it is half; watching it happen is the other half.

Assembly, the third stage, is mechanically simple: the Cookbook uses an in-memory NetworkX MultiDiGraph, and the same shape moves to Postgres or Neo4j when you outgrow memory. The engineering content is not the container. It is what every node and edge must carry:

Resolution (Concept 7) hands you one extra thing: a map from every surface form to the canonical name it belongs to. Assembly is that map, applied. Note that Entity and Relation are still the plain Concept 6 schemas: the canonical identity lives in the map, not in a new field on the model's output.

# from Concept 7: {"Edwin Aldrin": "Buzz Aldrin", "Buzz": "Buzz Aldrin", ...}
alias_to_canonical: dict[str, str] = resolve(entities, client)

def add_entity(G, entity: Entity, source_doc: str) -> None:
canonical = alias_to_canonical.get(entity.name)
if canonical is None: # unresolved: do not guess
return
if canonical in G: # merge, do not overwrite
G.nodes[canonical]["source_docs"].add(source_doc)
G.nodes[canonical]["aliases"].add(entity.name)
return
G.add_node(canonical,
entity_type=entity.type,
description=entity.description,
source_docs={source_doc}, # where this entity was seen
aliases={entity.name}) # resolution's receipts, kept

def add_relation(G, rel: Relation, source_doc: str) -> None:
src = alias_to_canonical.get(rel.source)
tgt = alias_to_canonical.get(rel.target)
if src is None or tgt is None: # never invent an endpoint
return
if src not in G or tgt not in G:
return
G.add_edge(src, tgt,
predicate=rel.predicate,
source_doc=source_doc) # the receipt

Two details are the whole lesson. Re-seeing an entity adds a source document and an alias rather than replacing what was there, so the node accumulates receipts instead of losing them. And anything the resolution step did not resolve is dropped rather than guessed, which is why both functions return early on a missing key instead of falling back to the raw surface form. That strictness has a cost you should see clearly: a resolution step that misses an entity silently loses every fact about it. The gentler policy is to accept the raw name and flag it for review, and either choice is defensible. What is not defensible is the middle: falling back silently, and then telling readers you dropped it. (The Cookbook's own notebook is the reference implementation; if you want a confidence number on each edge, that is a field you add to your schema, not one the Cookbook's Relation provides.)

Provenance, the receipt, is what upgrades a claim from "something a model once said" to "a statement you can audit." The independent PDF condenses the discipline into four invariants, and they are worth memorizing as a unit. Every graph write must satisfy:

  1. Every claim has a source, or is explicitly marked as inference.
  2. Every artifact has an authoring run and a version.
  3. Every evaluation identifies its rubric.
  4. Every superseded object remains addressable: replaced, never erased.

Notice what the invariants quietly forbid: a graph is not a truth machine. It stores claims, sources, and relationships so they can be inspected; it does not convert claims into truth. A biased corpus produces a biased graph. A missing document produces a missing edge. The graph's honest promise is smaller and more valuable: nothing in it is unattributed, and nothing in it is unexaminable. That promise is what makes Concept 10's grounded checking possible: a checker cannot demand evidence from a memory that never kept any.

Try it now: audit your own receipts (4 min)

Write five claims your agents established this week into a scratch file, one JSON object each, and fill in every field the invariants demand. The exercise is not the typing. It is noticing which claims you cannot source, because those are the ones you have been carrying as facts on the strength of a model's fluency. Mark each of those "source": {"kind": "inference"} and count them. That count is your current honesty baseline.

Invariant 4 deserves one more sentence, because it is the ratchet's sibling. When new evidence overturns a claim, you add (claim_new) —supersedes→ (claim_old) — you do not delete. The graph remembers being wrong, which is exactly what lets you ask, later, "what did we believe on March 10, and why?" An audit trail is a feature you cannot retrofit.

In simple terms

A claim without a receipt is a rumor. A graph full of rumors is worse than no graph, because it looks organized. The four invariants are one habit: never write anything down without also writing down how you know it and what it replaced.


Part 4: Working From the Graph

9. The subgraph, not the graph: context construction

Run this one: python3 concepts/09-subgraph.py in the companion lab. Reading it is half; watching it happen is the other half.

You built the graph to escape context dumping. The fastest way to ruin it is to invent a new kind: serializing the whole graph into every prompt. A graph with fifty thousand edges is as useless in a context window as fifty transcripts. The discipline is bounded retrieval: each worker gets a task-specific subgraph, and nothing more.

The Cookbook's query stage shows the shape, and the PDF generalizes it into a context builder any of your loops can follow:

  1. Resolve the entities the task mentions against the graph ("the vendor incident" → entity_vendor_x, entity_incident_y).
  2. Expand one or two hops from those nodes, over allowed edge types only: not every edge, everywhere.
  3. Include current artifact versions the task touches.
  4. Prioritize recent, verified claims over stale or low-confidence ones.
  5. Include the conflicts. If two claims contradict, the worker must see both: hiding uncertainty produces confident errors.
  6. Serialize within a token budget, as plain triples the model can read.
  7. Attach stable edge identifiers, so the worker's answer can cite edge_1042 and a checker can look it up.

Bounded retrieval, in three panels. Left, The whole graph, about fifty thousand edges, drawn as a dense pale cloud of sixty small circles joined by thin criss-crossing lines, with only two nodes highlighted in gold as the entities the task mentions; captioned dumping all of this is the old failure, renamed. A terra arrow labelled resolve, expand leads to the middle panel, The task&#39;s subgraph, marked two entities, two hops, allowed edge types only: gold vendor_x and incident_y boxes and a white component_z box are joined by supplied and involved_in edges, with a cream contract.pdf source box attached, and below them a terra sticky panel headed conflicts travel too, reading two sources disagree on the date, so the worker sees both. A second terra arrow labelled serialize, in budget leads to the right panel, What the worker gets, marked plain triples, stable ids: a cream monospace block lists e1041 vendor_x supplied component_z, e1042 component_z involved_in incident_y, e1043 claim_88 from contract.pdf, and a conflict line naming e1044 versus e1045. A gold chip below reads twenty triples, not fifty thousand edges, captioned each id is citable, so a checker can look up what was used. Footer: resolve, expand, prioritise verified, include conflicts, serialize in budget, attach ids.

Steps 6 and 7 close the loop with everything this book has taught about context: the serialized subgraph is a briefing document, assembled by code, from audited memory — the context-engineering ideal, finally with a durable source behind it. And notice what became possible that transcripts could never offer: an orchestrator coordinating twenty workers no longer copies twenty outputs into its own window. Workers publish typed graph updates; a synthesizer traverses the graph and combines findings even if no single agent ever saw all the source documents. The orchestrator's context stays clean. That single property is why multi-agent systems scale with a graph and suffocate without one.

Try it now: build one subgraph by hand (7 min)

Using the entities and relations you already extracted, pick one that a real task would mention. Then do the seven steps with your own hands: write down the resolved entity, list only its one-hop neighbours over two edge types you choose, add any conflicting claim, and serialize the result as plain triples with an ID on each line. Count the lines. If a real task's briefing fits in twenty triples, you have just proved why dumping fifty thousand edges was never necessary.

In simple terms

Do not hand the new employee the whole filing cabinet. Pull the three folders their task touches, plus the sticky note saying "these two folders disagree — check." That is a subgraph: small, relevant, honest about conflicts, and citable.

10. The grounded checker: "triple not found" beats "seems off"

Run this one: python3 concepts/10-grounded-checker.py in the companion lab. Reading it is half; watching it happen is the other half.

Here the memory layer meets the governance layer, and the course pays a debt to its siblings. The loop course gave you the maker–checker split. The harness course gave the checker typed output. But the checker's verdict was still an impression: a model reading work and reporting a feeling with a schema around it. A graph changes what a checker can be: it can check claims against edges.

Walk one claim through. The maker's report says: "Vendor X supplied the component involved in Incident Y." A grounded checker does not ask "does this sound right?" It asks the graph two mechanical questions: does a supported edge (vendor_x, supplied, component_z) exist? Does (component_z, involved_in, incident_y) exist? If either is missing, the verdict is not a vibe — it is a structured, actionable demand:

{
"decision": "revise",
"claim": "Vendor X supplied the component in Incident Y",
"reason": "No supported path from vendor_x to incident_y",
"required_evidence": [
"A source-backed 'supplied' relation from vendor_x",
"A source-backed 'involved_in' relation to incident_y"
]
}

Compare the two failure messages this concept's title promises. "Seems off" sends the maker back to guess what displeased the reviewer. "Triple not found: here are the two edges you must supply" tells the maker exactly which evidence to find or which claim to withdraw. One is a mood. The other is a work order.

How a grounded checker decomposes one claim into required edges. At the top, a chip reading the maker claims sits beside the sentence Vendor X supplied the component involved in Incident Y. Two arrows fan down, labelled decompose into the edges it requires. On the left, a gold-bordered panel headed Required edge one is marked found with a tick: a gold vendor_x box connects by a supplied arrow to a white component_z box, footnoted e1041, source contract.pdf, confidence 0.94. On the right, a dashed terra panel headed Required edge two is marked missing with a cross: a white component_z box connects by a dashed involved_in question-marked arrow to a pale dashed incident_y box, footnoted no supported path exists in the graph. An arrow leads down to two contrasting verdict panels. The left, bordered terra and headed The grounded verdict: a work order, shows monospace JSON with decision revise, reason no supported path from vendor_x to incident_y, and required_evidence naming a source-backed involved_in relation, captioned the maker knows exactly what to find, or what to withdraw. The right, pale and dashed and headed The ungrounded verdict: a mood, contains only the sentence this claim seems weak, captioned the maker must now guess what displeased the reviewer, and nothing in the memory improved either. Footer: and when the maker finds the evidence, the graph gains an edge, so grounding improves the memory, not just the report. And the demand is honest in both directions: sometimes the maker finds the source and the graph gets a new edge — grounding improves the memory, not just the report.

The same grounding upgrades every workflow pattern you know. In a chain, the graph is a gate between stages: do the entities this stage produced exist upstream? In a fan-out, it is the shared surface workers publish to without overlapping. In orchestrator–workers, it is the shared memory that keeps the orchestrator's context clean. In evaluator–optimizer, it is the evidence layer under every verdict. One graph, five patterns, the same three roles throughout: shared memory, grounding layer, persistent world model.

Now the sentence that keeps you honest, and hands you to the next course. A grounded verdict is only as good as two things the grounding does not test: whether the graph's edges are themselves correct (Concepts 7 and 8 reduce that risk; nothing removes it), and whether the checker reliably finds paths that do exist. "The checker consulted the graph" is a better claim than "the checker had an impression." But it is still a claim, produced by a model. How do you know the checker is any good? That question has a whole discipline, and it is the very next course: Trusting the Checker.

In simple terms

An ungrounded reviewer is a critic: "I did not like it." A grounded reviewer is an auditor: "Line 4 claims a payment; there is no receipt in the file; produce the receipt or remove the line." You can argue with a critic forever. An auditor's demand is either met or it is not.

Try it now: make a checker demand evidence (8 min)

Write three claims about your own project into a file, two supported by something real and one you invent. Then run the checker against your hand-built subgraph from Concept 9:

claude -p 'Subgraph (triples with ids): <paste>. Claims: <paste>.
For each claim, cite the triple ids that support it. If no triple supports it,
return decision "revise" with required_evidence naming the exact missing
relations. Never approve on plausibility. Return JSON only.'

Watch what happens to the invented claim. If the checker approves it anyway, you have found something more valuable than a working checker: you have found the reason the next course exists.

Check yourself

Your grounded checker returns PASS on every claim in a report, citing edge IDs for each. A week later, one claim proves false. List the two places the failure could live, and which course fixes each.

Show answer

Either the graph was wrong — a bad extraction or a false merge put a wrong edge in memory, which is a Part 3 discipline problem (tighten resolution, audit provenance, add the failure as a gold case), or the checker was wrong, it cited an edge that does not actually support the claim, which is a checker-quality problem: measuring it is Trusting the Checker, where fluent answers citing irrelevant edges is a named failure mode. Grounding narrowed "something is wrong somewhere" to two auditable suspects. That narrowing is the win.


Part 5: The Graph of Loops

Parts 2 through 4 built graphs whose nodes are records: commits, entities, claims, sources. This part changes what a node is. Zoom out until each of your loops becomes a single point, and ask how the points connect. That is the governance graph, the other half of "graph engineering", and it decides whether a system of loops stays honest or merely stays busy.

11. The wiring: who feeds whom, who checks whom

Run this one: python3 concepts/11-wiring.py in the companion lab. Reading it is half; watching it happen is the other half.

On July 18, 2026, Peter Steinberger — the "you should be designing loops that prompt your agents" voice from the very start of the loop course — posted a twelve-word question after midnight: "Are we still talking loops or did we shift to graphs yet?" Within hours it had been turned into a slogan: about four and a half hours later Hamel Husain published an article titled "Loop Engineering Is Dead. Enter Graph Engineering," and Santiago Valdarrama posted the line that spread furthest, "Loop engineering is dead. Long live graph engineering!" Be careful about who said what, one more time. Steinberger only asked a question, and his words read like a joke about the industry's naming treadmill, not an announcement; the obituary was written by other people, and read as a joke too. But behind the noise sits a real idea, and Carlos E. Perez's essay From Loop Engineering to Graph Engineering? maps it clearly.

You know what a loop is: one agent's behavior — a heartbeat, a body, a spine. In the governance graph, the loop is the node, and the graph is the wiring between the nodes. One correction before that settles in: not every node is a loop. A human gate is a node. A ground-truth check is a node. A frozen checker is a node. The precise version: an agent loop can be a node; the other nodes are human decisions, outside systems, and the measurements everyone must accept.

If this sounds familiar, it should: you built governance edges throughout the loop course without the name. The maker–checker split is an edge: one loop's output becomes another's input. The two-routine gate is a three-node graph: Routine A drafts, a human decides, the decision fires Routine B. The dreaming loop is a graph too: one loop reads every other loop's logs and proposes changes through a gate. So read the slogan honestly: a graph is loops, composed. Remove the loops and the graph is empty boxes.

One distinction organizes everything that follows. The word "loop" covers two different machines, and they fail differently:

  • An execution loop does recurring work: triage the issues, review the PR, draft the changelog. Nearly everything the loop course taught.
  • An improvement loop watches a number against a target and adjusts the system doing the work: measure the gap, act to shrink it, go around again. The dreaming loop is the clearest one you built.

An execution loop that misses its stopping condition needs a better spec. An improvement loop that games its own number needs structure around it, and that structure is the next concept.

When you think in graphs, three practical questions move to the center: none new to you, each now asked once per edge instead of once per loop. Routing: when loop A finishes, who receives the result, loop B, a human, or nobody? Trust boundaries: the edges are standing permissions, so which loop may fire which, and under whose identity? A missing edge is work that silently never arrives. Gate placement: put the human where a wrong automatic move is costly and hard to reverse. That was a dial you set per loop; now you set it per edge, with the gate sitting between nodes rather than inside one.

In simple terms

A loop is one worker who runs without you. The governance graph is the org chart that connects the workers, plus the manager who owns the targets, the referee who settles fights, and the measurements nobody can argue with. You cannot draw a useful org chart for workers who do not exist yet, which is why the loop course came first.

Try it now: draw your own wiring (5 min)

List every loop, checker, and human gate you run today, one per line. Then draw one arrow for each relationship, and label each arrow with its verb: fires, reviews, approves, audits. Two findings are almost universal on a first drawing. Some loop has no incoming arrow from anything that checks it, and some number has nobody watching it. Both are missing edges, and the next concept names what they cost.

12. Perez's four failures of the single loop

Run this one: python3 concepts/12-four-failures.py in the companion lab. Reading it is half; watching it happen is the other half.

Perez's essay opens with a story: a support team spends a quarter building a loop that optimizes ticket-resolution rate. The number climbs. Then the renewal data arrives, and customers are leaving at twice the old rate. The bot had learned to close tickets by pushing customers away, marking abandoned problems as "solved." The loop worked perfectly; its number had quietly stopped meaning the real outcome. You met this failure in miniature already: it is why autoresearch's agent may never edit prepare.py, and why the loop course's portfolio project forbids touching check.py.

Perez names four ways a single loop breaks. Each break is fixed by an edge in the graph — never by a better loop:

How a single loop breaksWhat it looks likeThe graph's answerYou saw this already
Gaming (Goodhart's law)The loop moves its number in ways that betray the real outcomePair every optimizing loop with a watching loop on a counter-metric — resolution rate paired with renewal rateThe prepare.py / check.py rules; the read-only reviewer
Blindness upwardNothing inside a loop can ask whether its own target is rightA slower loop owns the faster loop's target, so changing targets is governed workOnly you can question what the loop is for — the loop course's context-advantage lesson
ConflictLoops built separately fight each other, and each one looks perfect aloneAn arbitration node above them, a supervising loop or a human gate, that owns the trade-offThe human gate deciding what ships
Measurement decayChecking slides from checking reality into checking one report against another reportIndependent audit loops that test whether the numbers still touch the worldThe dreaming loop; "green does not mean done"

Read the table as one sentence: every fix is an edge, never a better loop. That is the governance graph earning its name.

In simple terms

A loop can only see its own number, so it will chase the number even after the number stops meaning the real result. The fix is never "trust the loop more." The fix is structure: a watcher on a counter-metric, a slower loop that owns the target, a referee above conflicts, and an auditor that checks the numbers against reality.

Check yourself

Your triage loop's "issues closed per day" climbs for a month, and the team celebrates. Which of Perez's four failures should you rule out first, and what edge rules it out?

Show answer

Gaming. A rising close-rate is exactly the support-bot story's shape: the cheapest way to close issues is to close them badly. The edge that rules it out is a watching loop on a counter-metric: reopen rate, or reader complaints per closed issue. If the counter-metric held while closes rose, celebrate. If nobody is watching a counter-metric, the number is unaudited, and the celebration is premature by definition.

13. Anchors and frozen nodes: a graph can fool itself

Run this one: bash concepts/13-anchors-audit.sh in the companion lab. Reading it is half; watching it happen is the other half.

This is Perez's warning, and it is the part every slogan leaves out. It applies with equal force to both graphs this course teaches.

The governance version. Imagine a graph where every loop only reads other loops' reports. Loop A checks loop B's numbers. B's numbers come from C. C reads a dashboard built from A and B. Everything agrees with everything; nothing was checked against the real world. Perez calls this a circular graph. It fails exactly as the single loop failed: just later, more expensively, and with more green lights on the way down.

The memory version. Now replace "loops" with "claims." Every claim in your knowledge graph cites a source, and every source is another agent's report, whose sources are further agent reports. The four invariants of Concept 8 all pass, because the invariants enforce that receipts exist, not what the receipts are made of. Internally perfect, externally untethered: the same circle, drawn in JSON.

One failure in two costumes, so one fix, worn twice. A graph needs three things that no arrangement of arrows can supply:

  • Anchors — measurements no loop can argue with, and sources no model produced: tests that actually ran, customers who actually stayed, money that actually arrived, documents humans wrote. Be strict about the last kind. A run log is an anchor only where the cited lines are captured output from something outside the model: a test runner, a compiler, a database, an API, the operating system. An agent's own prose inside a log file is model output wearing a filename, and citing it is how a circular graph passes its own audit.
  • Frozen nodes — rules the optimizing loops are never allowed to change, exactly because they would want to: check.py, prepare.py, the held-out test set, the claims schema itself.
  • A root judgment from outside the graph: the answer to "what does better mean?" The machinery cannot produce this answer, because every loop already assumes it. It comes from people. That is the loop course's Concept 1 — intent and accountability — reached from the other direction: they are not the last things the graph automates; they are the things the graph cannot contain at all.

The audit is the same in both costumes: follow the leaves. Pick ten random verdicts (governance) or ten random claims (memory) and walk each one all the way down. Count how many bottom out in reality rather than in another model's report. That count is your system's grounding, in one number.

The loop is the node, the graph is the wiring — and the graph must touch the ground. Left panel, one loop — the loop course: a gold heartbeat chip (9 weekdays) fires a four-step cycle with arrows — 1 discover, 2 implement, 3 verify, 4 commit — labeled &quot;one agent&#39;s behavior,&quot; draining into a slate spine bar, progress.md; caption: a heartbeat, a body, a spine — and its one blind spot: a loop can only see its own metric, so it will game it, and it cannot question its own target. A terra &quot;compose&quot; arrow points to the right panel, a graph — loops watching loops, anchored, drawn in three layers separated by dashed dividers. Fast loops — optimize: two green-outlined execution loops; the triage loop (9 weekdays) fires the review loop with its PRs and escalates risky items to the gate; the review loop, labeled counter-metric on the maker, sends verdicts to the gate. Slow loops — watch the watchers: a terra audit/dreaming loop (weekly — do the numbers still touch reality?) reads both fast loops&#39; logs along dashed lines and proposes rule changes, as a PR, into a gold human-gate node that owns the targets and decides what &quot;better&quot; means. Anchors — what no loop can argue with: a slate ground bar with hatching and a shield icon — ground truth: tests that actually ran, real users, revenue that landed — with a padlock and a gold line: frozen node, check.py, the loops may never tune it. The review loop&#39;s verdicts settle against the anchor; the human gate reads real outcomes from it. A legend decodes the colors: green = fast execution loops (optimize); orange = slow governance loop (watches the watchers); gold = human decision (not a loop); slate = anchor to reality (not a loop). Panel caption: the working nodes are loops from the loop course; the gate and the anchor are not loops — they are what the loops answer to. Footer bar with a scales icon: the durable axis is not loops vs. graphs — it is grounded vs. ungrounded; a graph where every loop only reads other loops&#39; reports is mutual confirmation, consistent and verified against nothing.

What this changes for you, in practice. Nothing about your first loop: build it exactly as the loop course taught. Everything about your second. The moment two loops exchange work, share state, or trigger, review, or constrain each other, you are doing graph engineering, whether you use the name or not. The cheap starting version is two rules: every optimizing loop gets one watching loop, and at least one signal in your graph must come from reality, not from another model's report.

And one prediction, which Perez makes and this book agrees with: graphs of loops will fail too, circular, consistent, and convincing, wherever they are built without anchors. Then the discourse will jump to the next name. The lasting question was never loops versus graphs. It is grounded versus ungrounded: does your machinery, whatever its shape, still touch the reality it claims to improve?

In simple terms

Three newspapers citing each other in a ring is not three sources: someone has to have attended the event. That rule holds whether the ring is made of loops checking loops or claims citing claims. Anchors are the reporters who were actually there; frozen nodes are the ethics rules reporters may not rewrite; and "what counts as news" is decided by the editor, never by the printing press.

Check yourself

A team builds a beautiful graph: five loops, paired metrics, an audit loop, a human gate. But every loop reads only the reports the other loops produce. What is missing, and what goes wrong?

Show answer

An anchor. The graph is circular: every loop confirms every other loop, but nothing is checked against the real world. It will fail like a single loop: just later, more expensively, and with more green lights along the way. At least one signal must come from reality: a test that actually ran, a customer who actually stayed, money that actually arrived.

The naming treadmill
One caution, in this book's own spirit

One caution. The industry renames the frontier about once a season: prompt engineering, then context engineering, then harness engineering, then loop engineering — and now graph engineering, in two flavors at once. Each name is partly real and partly noise, and each layer wraps the one before it: a graph is many loops, composed and given shared memory. The idea is older than the slogan — MLOps pipelines, company governance, and the body's own regulation are all graphs of loops running at different speeds, over shared records. What became newly visible in 2026 is that capable agents can run these loops unattended, so both the wiring question and the memory question reached a much larger group of builders. The names change fast. The shape underneath grows slowly. Learn the shape, and the next rename costs you an afternoon, not a course.


Part 6: One Graph, End to End

Theory over. This part upgrades a system you have already built twice, the morning-triage loop from the loop course, fenced by the harness course, from a prose spine to a small, queryable graph. Files and shell only, no database, no framework: the graph is three JSON files in the repo, and the discipline is the schema, not the storage. Both tools run it; only the headless command differs.

The whole build on one page: three JSON files, one hook, two prompts. On the left, a panel headed the maker (one beat) explains that it does the work, then writes what it established as one claim with a source a later agent could open, noting that session chatter stays in progress.md. A gold arrow labelled writes points right to a cream panel headed graph slash, containing three white file cards: entities.json, the nodes the loops talk about; claims.json, edges with receipts, what was established; and runs.json, the work side, which beat wrote what. Beneath them a gold padlocked strip reads every claim carries source, produced_by, supersedes and created, and warns that the file is append-only, so old claims are never edited. On the right, a panel headed the reviewer explains that it cites a claim id for every factual statement, or returns REVISE naming the evidence it could not find, with the reminder that sounds right is not a citation; a dashed arrow labelled reads runs from it back to the files. Below the maker, a dashed panel headed pre-commit hook says jq validates every claim and a schema violation blocks the commit, with terra arrows labelled guards pointing up to the maker and right to the files. A gold panel across the bottom, headed what this buys, three weeks later, shows a monospace jq one-liner that collects superseded ids and then selects the still-active claims, with an arrow to the note: one line, one answer, and a receipt, so the changelog loop can report a fix it never witnessed. Footer: the reviewer edge is governance, the hook is a frozen node, the source refs are anchors.

The shape on disk

graph/
SCHEMA.md # the contract: fields, types, and the write rules
entities.json # nodes: things the loops talk about
claims.json # edges-with-receipts: what the loops have established
runs.json # the work side: which beat wrote what, and with what result
evidence/
run_2026-07-21-triage.log # raw tool output a claim can point at

Two directories, because they hold different things. graph/ is the curated memory, small and schema-checked. evidence/ is the raw output claims point back to: test runs, command output, API responses. Nothing in evidence/ is ever edited.

And when does JSON stop being enough? Later than instinct fears and sooner than this design suggests. A single file rewritten on every append is comfortable into the low thousands of claims, and starts hurting somewhere around ten thousand: jq scans slow down, the whole file is rewritten for one new row, and two loops appending at the same moment can lose a write. The upgrade path is deliberately boring, because the discipline is the schema rather than the storage. A relational table (Postgres, or SQLite on one machine) buys real ids, indexes on subject and predicate, transactions so concurrent appends stop colliding, and append-only enforced by a database trigger instead of a Git hook. A graph database (Neo4j, Neptune) buys one thing beyond that: multi-hop traversal as a query rather than as code you maintain, which starts to matter when your context builder walks three or four hops instead of one or two. None of those moves changes a single invariant. Move when a query gets slow or a write gets lost, not before.

One claim, in full — this is the whole discipline in twelve lines:

{
"id": "claim_0007",
"subject": "test_payments_flaky",
"predicate": "diagnosed_as",
"object": "tz_default_utc",
"confidence": 0.9,
"source": {
"kind": "tool_output",
"command": "pytest tests/test_payments.py -x",
"exit_code": 1,
"ref": "evidence/run_2026-07-21-triage.log#L88-L94",
"captured": "2026-07-21T09:14:22Z"
},
"produced_by": "run_2026-07-21-triage",
"supersedes": "claim_0004",
"created": "2026-07-21"
}

One thing to notice before you copy it: this claim carries a supersedes, so it assumes claim_0004 is already sitting in the file. Your first claim has no supersedes field at all. Add one that points at a claim you never wrote and the hook below will stop the commit, correctly, with supersedes points at a claim that does not exist. Note too that claims.json is an array, even when it holds a single claim: every jq query on this page starts with .[].

The other two files are smaller. An entity is an identity plus its aliases:

[
{
"id": "test_payments_flaky",
"type": "TEST",
"aliases": ["tests/test_payments.py::test_tz", "the flaky payments test"],
"first_seen": "2026-07-14"
}
]

And a run is one beat's receipt:

[
{
"id": "run_2026-07-21-triage",
"beat": "morning-triage",
"started": "2026-07-21T09:11:04Z",
"tool": "claude-code",
"evidence": ["evidence/run_2026-07-21-triage.log"],
"claims_written": ["claim_0007"],
"verdict": "PASS"
}
]

Now, two things about the claim above are deliberate, and both were nearly wrong.

There is no status field. Claims are append-only: nothing is ever edited, and nothing is ever deleted. Whether a claim is still current is not stored, it is derived at read time: a claim is active if no later claim supersedes it. One jq expression answers it, and the rule cannot drift, because there is no second place for the truth to live. This is the accounting analogy done properly. A correcting entry does not reach back and modify the original entry.

# active claims = those that nothing supersedes
jq '[.[].supersedes] as $dead
| [.[] | select(.id | IN($dead[]) | not)]' graph/claims.json

Read it as two steps: collect every id that a later claim supersedes, then keep the claims whose id is not in that list. One trap is worth naming, because the obvious one-liner is wrong: do not fold this into select(any(.[]; ...)) over .[], because inside the select the current input is a single claim, so any(.[]; ...) walks that claim's own fields and errors out. The two-step form avoids it and reads better.

The predicate is diagnosed_as, not caused_by. A failing test with exit code 1 proves that something failed. It does not prove what caused the failure: that step is the agent's reading of the output. So the claim states what it can support. Upgrading to caused_by needs more evidence than a red test — the failing assertion, the configuration line it points at, and ideally a rerun that passes after the timezone assumption is corrected. Then you append a new caused_by claim that supersedes this one, citing both runs. Choosing a predicate your evidence actually supports is the smallest, most repeatable act of honesty in this whole discipline.

The source names the tool, not the agent. "kind": "tool_output" with a command, an exit code, a timestamp, and a line range points at something no model wrote: pytest failed, and here is where it said so. Compare that with a source that pointed at a sentence the agent typed into its own log. That would satisfy the letter of invariant 1 while pointing at model output, which is Concept 13's circular graph in a single field. The rule: a run log is only an anchor where the cited lines are captured output from something outside the model — a test runner, a compiler, a database, an API, the operating system. An agent's own prose inside a log file is a claim, not evidence for one.

Check the claim against Concept 8's invariants: a real source (invariant 1), an authoring run (invariant 2), and a superseded predecessor that remains addressable because it was never touched (invariant 4). Invariant 3 arrives with the reviewer below.

The maker writes to the graph

One paragraph added to the triage skill changes what the loop leaves behind. Where the old skill said "update progress.md," the new one says:

## 5. Update the graph last

For every durable finding this beat established, append one claim to
graph/claims.json following the schema in graph/SCHEMA.md. Rules:

- claims.json is APPEND-ONLY. Never edit and never delete an existing
claim, including any of its fields. To correct a claim, append a new one
whose "supersedes" names the old id. The old claim is left untouched:
whether a claim is current is derived when the graph is read, never
stored on the claim itself.
- Every claim needs a source a later agent could open and verify. Prefer
captured tool output: save it under evidence/ and cite the command, the
exit_code, and a line range. If the finding is your own reasoning with
no external output behind it, mark it "source": {"kind": "inference"}.
- Never cite your own prose in a log as the evidence for your own claim.
- New entities go in entities.json first. Check aliases before adding:
do not create "payments-test" if "test_payments_flaky" exists.
- Session notes, dead ends, and chatter stay in progress.md. The graph
is for what was established, not what was said.

The last rule matters most. The spine does not disappear: it remains the loop's diary. The graph is the smaller, stricter record of what the diary proved. Two memories, two truth standards, exactly like Concept 3's two graphs.

And the harness makes the rules real, because a guardrail lives in the harness, never in the prompt. Three of the rules above are mechanically checkable, so the hook checks them:

#!/bin/sh
# .git/hooks/pre-commit — the graph gate (jq only, no framework)
C=graph/claims.json
fail() { echo "claims.json: $1 — commit blocked"; exit 1; }

# 1. required fields on every claim
jq -e 'all(.[]; has("id") and has("subject") and has("predicate")
and has("object") and has("source") and has("produced_by"))' "$C" \
>/dev/null || fail "a claim is missing a required field"

# 2. ids are unique
[ "$(jq 'length' "$C")" = "$(jq '[.[].id] | unique | length' "$C")" ] \
|| fail "duplicate claim id"

# 3. every supersedes target exists
jq -e --argjson ids "$(jq '[.[].id]' "$C")" \
'all(.[]; (has("supersedes") | not) or (.supersedes | IN($ids[])))' "$C" \
>/dev/null || fail "supersedes points at a claim that does not exist"

# 4. append-only: nothing already committed may change
git show HEAD:"$C" 2>/dev/null > /tmp/old.json || exit 0
jq -e --slurpfile new "$C" \
'all(.[]; . as $o | $new[0] | any(.[]; . == $o))' /tmp/old.json \
>/dev/null || fail "an existing claim was modified or removed"

Now be honest about the boundary of that gate, since this course keeps insisting on the difference between a request and an enforced rule. Those four checks are real: required fields, unique ids, resolvable supersession, and append-only history. What the hook does not check is field types, whether subject names an entity that exists in entities.json, whether a source block is well-formed, or whether the cited evidence file and line range actually exist. Each is another jq line, on the day you want it. Until you write it, that rule lives only in SCHEMA.md, which makes it guidance rather than a guardrail. Knowing which of your rules is which is the entire point of the distinction.

The reviewer reads from the graph

The reviewer's prompt gains one obligation, and its verdict gains one field. This is Concept 10 at operating size:

You are the reviewer. For every factual claim in the maker's report:

1. Find the claim in graph/claims.json that supports it. Cite its id.
2. If no active claim supports it, your verdict is REVISE, and
required_evidence must name the missing claim precisely.
3. A claim whose source.kind is "inference" cannot by itself ground a
factual assertion. Either cite a source-backed claim that supports it,
or return REVISE. An honestly recorded guess is still a guess.
4. Never approve a factual claim on plausibility. "Sounds right" is
not a citation.

Return only JSON:
{ "verdict": "PASS|REVISE|FAIL",
"grounded_in": ["claim_0007", "claim_0012"],
"missing": [],
"rubric": "reviewer-rubric-v3" }

Rule 3 is the one people leave out, and leaving it out quietly undoes the whole build. The maker is allowed to record inference honestly, which is right: a marked guess is better than a laundered one. But if the reviewer only checks that a cited claim exists, then a marked guess can ground a shipped factual statement, and the graph has laundered it after all. Honest inference and grounding evidence are different jobs. The reviewer's task is to keep them apart.

The rubric field is invariant 3, and the grounded_in field is the auditable trail: months later, you can open any shipped PR and walk from its verdict to the claims to the sources. Every important output traced to an objective, an artifact, a source, a graph path, and an evaluator decision: the PDF's closing test, passed at the scale of three JSON files.

One beat, before and after

Before (spine only). Tuesday's triage beat fixes the flaky payments test and writes prose: "fixed flaky test, was a timezone thing." Thursday, a different loop, the changelog loop, mentions the payments fix, and its reviewer approves on plausibility. Three weeks later someone asks which timezone assumption it was, and the answer is an archaeology dig through transcripts.

After (graph). Tuesday's beat writes claim_0007 above, citing the pytest output it captured under evidence/. Thursday, the changelog loop's context builder pulls the two-hop subgraph around test_payments_flaky: the claim, its source ref, the run that produced it. Its reviewer approves the changelog entry because grounded_in: ["claim_0007"] resolves. Three weeks later, the question is answered by jq, in one line, with a receipt:

jq '[.[].supersedes] as $dead
| .[] | select(.subject == "test_payments_flaky")
| select(.id | IN($dead[]) | not)' graph/claims.json

Same loops. Same model. The only change is where the memory lives, and that changed what every later agent, and every later human, can know.

One more reading of the build, now that Part 5 has given you the second lens. This tiny system carries both graphs at once. The reviewer-to-maker relationship is a governance edge (a watching loop, whose counter-metric on the maker is "every claim resolves"). The pre-commit schema hook is a frozen node: the makers may never tune the rules they are checked against. And the source blocks are anchors, but only because of what they point at: a command, an exit code, and captured output from a test runner. Point the same field at a sentence the agent wrote about itself and the anchor evaporates while the schema still passes. Three JSON files, one hook, two prompts, and every idea in this course is present in miniature.

Try it now: run one grounded beat by hand (15 min)

In a throwaway repo: create the three JSON files with one entity and zero claims. Run one maker beat headlessly, claude -p or opencode run, with the graph-writing skill above, on any small real task. Then run the reviewer prompt against the maker's report. Watch it REVISE with a missing field on the first pass, because the maker under-recorded. That failure is the lesson: the reviewer just taught the maker what the graph must contain.


Part 7: Staying Grounded

14. Choosing a level, and budgeting for it

Run this one: python3 concepts/14-choose-a-level.py in the companion lab. Reading it is half; watching it happen is the other half.

Before the case against graphs, the procedure for choosing one. Six questions, asked in order, decide how much structure a job actually needs. Each one you answer "no" to saves you a layer.

  1. Can success be verified? If not, do not begin with autonomy at all. Define a test, a rubric, a source requirement, or a human decision first. This is the loop course's first gate, and nothing later repairs a missing answer here.
  2. Are the steps stable? If yes, a chain is enough. If no, you need planning or an orchestrator.
  3. Are the subtasks independent? If yes, parallelize. If no, model the dependencies explicitly and limit how many workers may write at once.
  4. Must alternative lineages stay available? If yes, use a DAG rather than forcing every result into one branch. This is Concept 5's question.
  5. Must the facts survive the run? If yes, persist artifacts and graph state. Do not rely on a transcript summary to carry them.
  6. Can you afford the cost and the latency? Set budgets before adding workers, not after the invoice.

Answered together, they produce a level rather than a preference:

Your situationStart withWhy
Simple, low-risk questionZero-shotLowest latency, no machinery to maintain
The output can be checkedA loopRepeated feedback improves the artifact
The sequence is stableA chainPredictable, testable stages
The categories are clearA routerSeparates policies and models cleanly
The units are independentParallel workersCuts wall-clock time
The decomposition varies per taskOrchestrator-workersDynamic specialization
Alternatives must stay aliveA commit DAGPreserves experiment branches
Facts must survive sessionsA knowledge graphPersistent shared memory
Very large parallel workA dynamic workflowAutomates the fan-out and the fan-in

Notice that the graph is the eighth row, not the first. Most work stops earlier, and stopping earlier is a correct outcome, not a failure of ambition.

The nine architecture levels drawn as a rising staircase, each step costing more than the last. From bottom left to top right the rungs read: one, zero-shot, for a simple low-risk question; two, a loop, when the output can be checked; three, a chain, when the sequence is stable; four, a router, when the categories are clear; five, parallel workers, when the units are independent; six, orchestrator-workers, when decomposition varies per task; seven, a commit DAG, in gold, when alternatives must stay alive; eight, a knowledge graph, in gold, when facts must survive sessions; nine, a dynamic workflow, in terra, for very large parallel work. An upward arrow along the left edge is labelled more cost, latency, and machinery. A side panel reads: this course is rungs seven and eight, where rung seven is Part 2 keeping every lineage alive, rung eight is Parts 3 and 4 keeping the facts, rung nine needs both plus a real budget, and rungs one to six are the two courses before this. A gold chip below reads answer the six questions first. Footer: declare the budget before the run, maximum workers, tokens, cost, graph writes, and the evidence required to finish.

Whatever level you choose, declare its complexity budget before the run starts. Every run should state, in writing: maximum model calls, maximum sub-agents, maximum concurrent workers, maximum tool calls, maximum wall-clock time, maximum tokens, maximum financial cost, maximum retries, maximum graph writes, and the minimum evidence required before anything is called finished. That last item is the one people forget, and it is the one that makes the rest meaningful.

Then the rule for what happens when a budget runs out, which matters more than the numbers themselves: return the best current artifact, the work that completed, the issues left unresolved, and the reason for stopping. Do not hide partial failure behind a fluent final answer. A run that says "I stopped at 40 of 60 files because the token budget ended, and here is what the 40 showed" is worth more than a confident report that quietly covered two thirds of the job.

In simple terms

Ask the six questions before you build, and let them tell you the smallest structure that fits. Then write down your limits in advance, because a system that has never been told when to stop will stop at whatever moment its money runs out, and it will describe that moment as success.

Check yourself

A team wants a knowledge graph. Their answers: success is verifiable, steps are stable, subtasks are independent, alternative lineages do not matter, and facts do not need to survive the run. Which level do the six questions give them?

Show answer

Parallel workers over a stable chain, and no graph. Question 4 is no, so no DAG; question 5 is no, so no knowledge graph. They wanted the eighth row and the questions gave them the fifth. Building the graph anyway means paying for extraction errors and schema upkeep to answer questions nobody has asked yet.

15. When not to build a graph

Run this one: python3 concepts/14-choose-a-level.py in the companion lab. Reading it is half; watching it happen is the other half.

Concept 14 gave you the procedure. This concept gives the case against the level this course has spent fourteen concepts selling, in the book's tradition of honest grading. Do not introduce a knowledge graph merely because the system has agents. A graph is machinery with a real bill: extraction errors, resolution risk, schema maintenance, and a new thing that can silently rot. Skip it when:

  • tasks are independent and no cross-session state is needed,
  • answers come from one document at a time,
  • relations are fixed and simple: a relational table already answers every query you actually ask,
  • provenance is not required, or
  • extraction errors would outweigh traversal value.

A graph earns its cost when connected queries, evolving relations, provenance, or shared world state are central. One loop with a spine needs no graph. The moment two loops exchange facts, or twenty workers need a synthesis, the bill flips. The Part 6 build is deliberately the smallest version that clears the bar.

Then two failure modes for the graphs you do build:

The graph amplifies builder judgment: including bad judgment. A loop amplifies its objective and evaluator; you learned that lesson twice. A graph amplifies its ontology and source policy. Choose the wrong entity types, admit the wrong sources, and automation scales the error: a biased corpus produces a biased graph that answers confidently in every direction. The graph inspects claims; it does not launder them into truth.

Metrics can be gamed here too. An extraction pipeline tuned only for entity recall will happily flood the graph; a resolution step tuned only for compression will merge strangers (Concept 7's trap). Every optimization needs its counter-metric: precision against recall, compression against false merges. This is Goodhart's law knocking a course early; the next course answers the door.

And keep Concept 13's audit running on the graphs you do build: follow ten random claims all the way down to their leaves, and count how many bottom out in reality rather than in another model's report. A memory graph without anchors is the circular graph of Part 5, rebuilt in JSON.

In simple terms

A graph is a bureaucracy for facts. A good bureaucracy makes everything findable and auditable. A bad one stamps rumors with official-looking seals and files them beautifully. The stamp is not the truth: the receipt at the bottom of the file is. Check the receipts, and only build the bureaucracy when the pile of facts is genuinely too big for a notebook.

16. What the graph cannot do, and where this goes next

Run this one: bash concepts/16-limits.sh in the companion lab. Reading it is half; watching it happen is the other half.

End on the honest boundary, in three statements the graph cannot make for you:

"The checker's PASS is now trustworthy." No. Grounding upgraded the verdict from impression to audit, but the auditor is still a model: it can cite an edge that does not support the claim, miss a path that exists, and drift when the model beneath it updates. A fluent answer citing irrelevant edges is a documented failure, not a theoretical one. Measuring the checker, golden sets, calibration, pass rates, drift, is the next course: Trusting the Checker. Everything it teaches applies doubled here, because a graph system has two checkable layers: the extraction that fills the memory, and the reviewer that reads it. Its evaluation harness will even look familiar: read the extraction prompt and score history, propose one change, run against a gold set, keep or revert. The ratchet, pointed at the graph itself.

"The memory is safe where it is." Only as safe as its home. A graph in a laptop repo dies with the laptop; a swarm's shared memory must live where every worker, local, scheduled, cloud, can reach it, survive machine failure, and enforce who may write what. Autoresearch runs on one GPU precisely because bounded is safe; AgentHub is one Go binary on one server precisely because it is a sketch. Moving proven loops and their graph to a runtime you do not babysit is the course after next: Leaving the Laptop.

"Better wiring means better judgment." The oldest boundary in this book, and it does not move. The graph places memory and evaluation outside the context window — that is real, and it is the single most important insight here: the bottleneck is usually not the next model call; it is the placement of memory and evaluation. But the ontology, the source policy, the anchors, and the answer to "what does better mean?" come from outside every graph, from you. Karpathy's README jokes about "autonomous swarms of AI agents running across compute cluster megastructures in the skies." The near-term work is less cinematic and more valuable: typed contracts, preserved lineage, grounded claims, and memory that outlives the session. Intent and accountability, Concept 1 of the first course, remain the two things no arrangement of nodes and edges can contain.

In simple terms

The graph moves memory and checking out of the agent's head, and that is a real win: it is why a thousand agents can work on one problem without each starting from nothing. But it cannot decide what the memory is for. Somebody has to choose which things are worth remembering, which sources count as evidence, and what "better" means. That somebody is you, and no amount of wiring takes the job.

Where this book picks the thread up: read a system of loops sharing a graph out loud and it becomes an organization — workers with a shared filing system, reviewers who demand receipts, an owner who sets the rules. That is the Human-Agent Teams crash course, and it is what a well-built graph of loops becomes when it earns a name on an org chart: a Digital FTE with institutional memory.


Using a graph on this book (dogfooding)

Does this book practice what this course preaches? Honestly: it runs the proto-graph, and it deliberately does not run the full one.

Look at the feedback loop from the loop course's dogfooding section with this course's eyes. Every reader note is a record in a database. Notes link to the GitHub issues they open. Issues link to the pull requests that fix them. PRs link to the lessons they change, and to the human approval that shipped them. Typed records, directed links, provenance end to end: you can walk from any shipped fix back to the exact reader note that caused it. That is a graph in everything but the diagram, and it is why the same note is never worked twice: the loops query the links instead of re-reading history.

What the book does not run is a knowledge graph with model-driven extraction and entity resolution over its own content. That is Concept 15 applied to ourselves: the book's cross-session questions are still answered by the issue links and Git history, the relations are simple, and an extraction pipeline would add error surface without a query that demands it. The day a question arrives that the links cannot answer — "which lessons make claims sourced to documents that have since changed?" is the likely first one — the Part 6 pattern is the plan on file. Build the graph when a real query earns it, and not a week before.


🚀 Projects

Reading about a graph is not the same as filling one. Here are eight builds, easy to hard. Do them in either tool: the graph is files and jq, so the only thing that changes is the headless command (claude -p or opencode run).

Two rules before you start, every time:

  • Use a throwaway repo, and real documents. The graph is only interesting when the entities are ones you actually recognise, so use your own READMEs, notes, or logs rather than invented data.
  • Write the schema before the first claim. A graph/SCHEMA.md you wrote in five minutes beats one you reconstruct later from what the files happen to contain (Part 6).
Project 110-15 minDraw your systemFind the finding that dies in a transcript, and the number nobody audits.

Difficulty: easy · Uses: Concepts 2, 3, 11 (nodes and edges, two graphs, the wiring).

Build. On paper or in Mermaid, draw every loop, checker, human gate, anchor, and memory file you run today as typed nodes with labeled directed edges. Mark which nodes are loops and which are not. Then circle two things: any finding that currently exists only inside a transcript, and any optimizing loop with no watcher on its number.

Done when you can point at one circled item of each kind and say what it costs you. Almost nobody draws this and finds nothing, which is the point of doing it before you build anything.

Project 230-45 minSpine to claimsConvert ten real findings into typed records, and meet the ones you cannot source.

Difficulty: easy · Uses: Concepts 1, 8 and Part 6 (provenance, the schema).

Build. Take your real progress.md (or any loop's log) and convert its last ten durable findings into claims.json records under Part 6's schema. Fill in every field the invariants demand, including produced_by and a real source.

Done when every one of the ten either cites something a later agent could open, or is explicitly marked "source": {"kind": "inference"}. Count the inference ones. That count is the number of things you had been treating as facts on the strength of a model's fluency, and it is the most useful number this course will give you about your own system.

Project 345-60 minFirst extractionRun one schema-constrained prompt over three documents and meet your own duplicates.

Difficulty: medium · Uses: Concept 6 (extraction).

Build. Run the Concept 6 prompt headlessly over three related documents: three READMEs from one project, three meeting notes, three incident write-ups. Validate every reply with jq before you believe it. Then count how many distinct entities appear under more than one surface form.

Done when all three documents return schema-valid JSON, and you can name at least one entity that appeared under two or more names. If the count is zero, your documents are too similar: use three that were written by different people, which is when resolution becomes a real problem rather than a hypothetical one.

Project 430-45 minThe DAG speaksAnswer AgentHub's three questions using only Git, then leave the commands for your agents.

Difficulty: medium · Uses: Concepts 4 and 5 (the two memories, traversal).

Build. In a repo with real history, answer AgentHub's three questions with plain Git: what was tried on top of commit X, which tips are unexplored frontier, and what path produced the current state. Then write the three commands into a GRAPH.md so your future agents can ask them too.

Done when the three commands work and you can also state what the DAG cannot tell you: which experiments were tried and thrown away. That absence is Concept 4's correction, felt in your own repo rather than read about.

Project 545-60 minResolution drillMerge what should merge, keep apart what should not, and keep every receipt.

Difficulty: medium · Uses: Concept 7 (resolution).

Build. Take twenty surface forms from Project 3, grouped by type and carrying their descriptions, and ask a stronger model for canonical clusters with a rationale and a confidence for each, keeping every alias. Then plant a trap: add two genuinely different entities that share a name, and run it again.

Done when the real duplicates merged, the two same-named strangers stayed apart, and every canonical entity still lists the surface forms it came from. If the strangers merged, do not fix the resolution prompt first: go back and make the descriptions richer, because that is where the evidence has to come from.

Project 61-2 hrs, plus five beatsThe grounded reviewerMake a checker demand an edge instead of offering an opinion.

Difficulty: hard · Uses: Concept 10 and Part 6 (grounding, the reviewer).

Build. Wire Part 6's reviewer into one loop you already run. Verdicts must carry grounded_in with resolvable claim ids, a factual statement with no supporting claim forces REVISE with missing filled in, and a claim whose source is inference cannot ground anything on its own. Run five real beats.

Done when at least one beat came back REVISE with a named missing edge, and the maker's next attempt either produced the evidence or withdrew the claim. Read the five verdicts together afterwards: what the maker learned to record is the actual output of this project.

Project 72-3 hrsA gold set for extractionMeasure the pipeline that fills your memory, one course early.

Difficulty: hard · Uses: Concepts 6, 7 and the next course.

Build. Hand-label the entities and relations in five documents. That is the tedious part and there is no way around it. Then score Project 3's prompt against your labels: precision, recall, and the schema-valid rate. Change exactly one line of the prompt, re-score, and keep or revert on the number.

Done when you have run the ratchet at least three times on the prompt itself and kept a record of every attempt, including the reverted ones. You have just built graph autoresearch: same loop, different artifact. Trusting the Checker is where this becomes a discipline.

Project 8Capstone: a weekend, then a week of beatsTwo loops, one graphMake one loop report a fix it never witnessed, because the graph told it.

Difficulty: capstone · Uses: everything.

Build. Two loops over one graph. The triage loop writes claims with real tool-output sources. The changelog loop reads them through a two-hop context builder rather than a file dump. Both reviewers ground their verdicts. The pre-commit hook guards the schema and the append-only rule. And the triage loop's throughput number gets one counter-metric that the review loop watches (Concept 12).

Done when the changelog loop correctly reports a fix that it never witnessed, because the graph carried it, and you can walk from that changelog line back through the claim, the run, and the captured tool output to something no model wrote. When that walk succeeds, you have built shared memory with anchors, and this course has nothing further to teach you.


Sources & further reading

Inside this book

  • Loop Engineering: the ratchet, the spine, the maker–checker split, the dreaming loop, and the two-routine gate — every governance edge in Part 5 was first built there, one loop at a time.
  • Harness Engineering: typed output and the reversibility discipline, both promoted here to memory-scale.
  • Trusting the Checker: the next course — how to know the grounded reviewer, and the extraction pipeline, are any good.
  • Leaving the Laptop: where the graph and its loops go to live when the laptop closes.
  • Human-Agent Teams: what a graph of loops becomes on an org chart.

Primary sources

  • Andrej Karpathy, autoresearch (released March 7, 2026): https://github.com/karpathy/autoresearch. The three-file harness, the ratchet, and the reported results. Read program.md itself before quoting the loop anywhere: it is the source for Concept 4's two-memories correction, specifying that experiments run on a dedicated branch, that the branch advances only on improvement, that anything equal or worse is git reset away, and that results.tsv records every attempt while being deliberately left untracked by Git. — the three-file harness, the ratchet loop, and the commit-DAG memory. Star counts and experiment numbers in this course reflect its first weeks; check the live repo.
  • Andrej Karpathy, AgentHub (published around March 9-10, 2026; no longer public): the agent-first collaboration layer, with a bare Git repo, SQLite, a message board, and the children / leaves / lineage CLI. Explicitly "just a sketch. Thinking..." The original repository was taken private within weeks and carried no license file. Preserved community forks are the only way to read the code now: treat them as historical artifacts for study, not as software to depend on. The AgentHub integration thread in the autoresearch repository remains public and is the better primary reference. For the repository's removal, see the contemporaneous write-up by a developer who forked it before it went private: https://dev.to/alireza_rezvani/karpathys-agent-native-infrastructure-working-python-agent-template-2o9d (March 2026).
  • Anthropic, Knowledge Graph Construction with Claude (Cookbook, March 23, 2026): https://platform.claude.com/cookbook/capabilities-knowledge-graph-guide — extraction with structured outputs on Haiku, resolution as reasoning on Sonnet, NetworkX assembly, and subgraph querying with citations. The source for Part 3.
  • Erik Schluntz and Barry Zhang, Building Effective Agents (Anthropic Engineering, December 2024): the five composable workflow patterns Concept 10 grounds.
  • Anthropic, Introducing dynamic workflows in Claude Code (May 28, 2026; the page now carries an update noting general availability): https://claude.com/blog/introducing-dynamic-workflows-in-claude-code — the source for Part 2's deeper note: generated orchestration, tens to hundreds of parallel fresh-context sub-agents, checked results, resumable progress, the ultracode setting, the token warning, and the Bun port. Availability spans the Claude Code CLI, Desktop and the IDE extensions on all paid plans (on Pro you switch it on from the Dynamic workflows row in /config), plus the Anthropic API, Amazon Bedrock, Google Cloud's Agent Platform and Microsoft Foundry. The reference docs, not the announcement, carry the real limits: 16 concurrent agents and 1,000 agents per run. Reference docs: code.claude.com/docs/en/workflows.
  • Peter Steinberger, post of July 18, 2026 ("Are we still talking loops or did we shift to graphs yet?"), on X (x.com/steipete/status/2078277297791189132, posted 00:34 UTC): the twelve words that named the season. The obituary is not Steinberger's: Hamel Husain published "Loop Engineering Is Dead. Enter Graph Engineering" about four and a half hours later, and Santiago Valdarrama (@svpino) posted the widely-quoted "Loop Engineering is dead. Long live Graph Engineering!". Both read as jokes about the naming treadmill.
  • Carlos E. Perez (Intuition Machine), From Loop Engineering to Graph Engineering? (July 19, 2026) — the source of Part 5: the support-bot story, the four failures of the single loop and their structural fixes, the circular-graph warning, anchors and frozen nodes, and the grounded-versus-ungrounded conclusion. https://medium.com/intuitionmachine/from-loop-engineering-to-graph-engineering-d3ebeb08511c
  • Graph Engineering: The Karpathy Loop, Improved 1000x by Itself (independent synthesis PDF, July 2026): the staged build path, the two-graphs distinction, the four invariants, and the closing traceability test. By its own front page, not affiliated with or endorsed by Karpathy or Anthropic: read it as a useful study note, and read its primary sources first.
  • Fortune, coverage of autoresearch ("the Karpathy Loop"), March 2026.
  • TechCrunch and others, on Karpathy joining Anthropic's pretraining team, May 19, 2026, with a mandate to build a team using Claude to accelerate pretraining research: https://techcrunch.com/2026/05/19/openai-co-founder-andrej-karpathy-joins-anthropics-pre-training-team/

All links current as of late July 2026. Every repository, preview feature, and number here moves fast; confirm against the live source before you rely on it.


The one-line summary

The agent forgets; the graph does not. Keep two graphs of memory, a DAG for the work, a knowledge graph for the facts, fill the second by schema, merge names reversibly, staple a receipt to every edge, hand workers subgraphs instead of dumps, and make every checker cite the edge or demand it. Then wire the loops themselves: a watcher on every optimizing number, a slower loop owning every target, a gate between nodes, and anchors no loop can argue with. A graph stores claims, not truth: grounded versus ungrounded is the axis that outlives every rename.

Flashcards Study Aid


Test Your Understanding

Checking access...