Beyond Linear Chat: Building Editable Context DAGs for Multi-Turn LLM Reasoning
The Fundamental Flaw in the Scrollback Buffer
If you’ve spent more than ten minutes debugging a complex system with an LLM, you know the pain. You start with a simple prompt. The model gives a plausible but wrong answer. You correct it. It over-corrects. You try a different approach in a new chat window, losing all prior context. You copy-paste fragments between windows. You have three tabs open, each representing a different hypothesis branch. You are, effectively, manually managing a version control system for your thoughts using Ctrl+C and Ctrl+V.
This is the linear chat trap. Modern LLM interfaces—ChatGPT, Claude, Gemini—are built on a fundamental data structure: the monolithic array of messages. Every turn appends a user or assistant message to a list. The entire list is shipped to the model on the next request. This is simple, stateless, and works beautifully for casual Q&A. But for non-linear reasoning tasks—debugging a distributed system, designing a multi-step architecture, or performing a root-cause analysis—a linear list is a blunt instrument.
The source project ThoughtDAG crystallizes the alternative: an editable Directed Acyclic Graph (DAG) as the native data structure for LLM conversations. Instead of a single thread, you have nodes. Instead of scrolling back, you traverse edges. Instead of starting over, you branch.
Enter the Context DAG: Nodes, Not Messages
A standard chat history looks like this under the hood:
[
{ role: "user", content: "Why is the auth service returning 403?" },
{ role: "assistant", content: "Check the JWT expiry..." },
{ role: "user", content: "Expiry is fine. What about the API gateway?" },
{ role: "assistant", content: "The gateway might be stripping headers..." }
]
Every message is a sibling in a flat list. The only relationship is temporal order. If you want to explore the "API gateway" hypothesis deeply without polluting the main thread, you can’t. If that hypothesis dead-ends, you have to manually instruct the model to "forget the last few messages"—a prompt engineering hack that works inconsistently.
A DAG-based system reimagines this. Each node is a discrete unit of context—a user prompt, an assistant response, a code snippet, or even a system directive. Edges define the lineage: "this node was informed by that node." The context window for any given node isn’t the entire history; it’s the transitive closure of its ancestors, walked via the DAG.
Here’s the mental model:
You can edit any node without invalidating downstream branches that don’t depend on it. You can prune a dead-end branch and the context window shrinks, saving tokens and reducing distraction. You can merge two independent analysis branches into a synthesis node that sees both.
This isn’t just a UI gimmick. It’s a fundamental shift from temporal context management to structural context management.
Why This Architecture Matters for Forward-Deployed Engineers
Forward-Deployed Engineers (FDEs) live in the messiest part of the problem space. You’re dropped into a customer’s environment, asked to understand a broken pipeline, a misbehaving model, or an integration that “works on our machine.” The work is inherently investigative and branching. Linear chat is your enemy here.
Consider a typical FDE scenario: a customer reports that their RAG pipeline is returning irrelevant chunks. You start a chat with the LLM, pasting error logs. The model suggests checking the embedding model. You do. Not the issue. You ask about chunk size. The model gives a generic answer. You need to explore three hypotheses simultaneously: chunk overlap strategy, the retrieval query rewriting step, and a possible regression in the embedding model version. In a linear chat, you’re forced to serialize these. By the time you’re on hypothesis three, the context window is bloated with the dead ends of hypotheses one and two. The model starts to lose the plot.
A DAG lets you fork at the point of divergence. Each hypothesis gets its own branch, sharing the common root context (the error logs, the system architecture description) but not polluting each other. When you identify the true root cause—say, a silent change in the embedding model’s tokenizer—you merge that finding back into a summary node that informs your final recommendation to the customer.
This pattern also maps cleanly to the FDE workflow we’ve described in The FDE Weekly Rhythm: Embed, Ship, and Expand in a Live Customer Environment. The “Embed” phase is all about context gathering—building the initial DAG. “Ship” is about executing on the highest-confidence branch. “Expand” is about revisiting pruned branches once the immediate fire is out, turning them into product improvements.
The Engineering Anatomy of a Thought Graph
Let’s get concrete. What does a context DAG actually look like in code? The ThoughtDAG implementation provides a clean reference architecture. At its core, you need:
- A Node Model: Each node has a unique ID, a content payload (the prompt or response), and a list of parent node IDs. Optionally, a node can carry metadata like a title, tags, or a “pinned” flag that prevents it from being pruned.
- A Context Window Builder: Given a target node, this function walks the DAG backwards, collecting all ancestor nodes in topological order. This ordered list becomes the
messagesarray sent to the LLM API. - A Merge Strategy: When you want a node to synthesize information from multiple parents, you need a deterministic way to interleave their contexts. A simple approach is to concatenate ancestor chains, but smarter implementations might summarize each branch first to save tokens.
Here’s a minimal Python sketch of the context builder:
from collections import deque
def build_context(node_id, graph):
"""
graph: dict mapping node_id -> Node object with 'parents' list
Returns ordered list of node contents for the LLM context window.
"""
visited = set()
order = []
queue = deque([node_id])
while queue:
current = queue.popleft()
if current in visited:
continue
visited.add(current)
node = graph[current]
# Recurse into parents first (post-order for causal flow)
for parent_id in node.parents:
if parent_id not in visited:
queue.append(parent_id)
order.append(node)
# Reverse to get chronological order from root to target
order.reverse()
return [node.content for node in order]
This is a simplified post-order traversal. In practice, you’d want to handle cycles (which shouldn’t exist in a true DAG but might creep in), enforce a maximum context length by pruning the oldest or least-relevant ancestors, and cache context windows for nodes that haven’t changed.
The real power move is making the graph editable. If you realize a node’s prompt was poorly worded, you edit it in place. All downstream nodes that depend on it will see the corrected context on their next run. This is like a reactive spreadsheet for reasoning. Change a cell, and the cells that reference it recalculate.
How to Prototype This Pattern Today
You don’t need to wait for OpenAI or Anthropic to ship a DAG-native UI. You can bolt this pattern onto existing APIs today. Here’s a pragmatic path:
1. The Notebook Approach (Jupyter + Python)
Jupyter notebooks are already a DAG of execution cells. You can mimic the pattern by using a dictionary to store nodes and manually specifying which cells feed into which LLM call. This is great for solo debugging sessions where you want to keep a clean record of your investigative path.
2. The LangGraph / LangChain Route
LangGraph explicitly models agent workflows as stateful graphs. While typically used for multi-agent orchestration, you can co-opt it for human-in-the-loop reasoning. Each user input becomes a node. You define conditional edges based on the user’s intent (“branch to hypothesis A”, “merge”). This is heavier but gives you checkpointing and persistence for free. For an example of graph-based agent orchestration, see our deep-dive on building a multi-agent research assistant.
3. The Custom React/TypeScript UI Route
If you want the full interactive experience shown in ThoughtDAG, you’re building a custom frontend. The key libraries are a DAG visualization tool like React Flow or Cytoscape.js, and a backend that persists the graph (SQLite with a JSON column for node data is sufficient for a single-user tool). The backend’s only job is to manage CRUD operations on nodes and edges, and to proxy LLM API calls with the dynamically built context windows.
4. The Low-Code Automation Route
For FDEs who need to ship a solution into a customer environment fast, you can wire this logic into an automation platform. Imagine an n8n workflow where each node is a separate “Webhook” or “Manual Trigger” step, and the context assembly logic is handled by a central “Code” node that queries a simple database of nodes. This isn’t as fluid as a visual canvas, but it lets you operationalize a branching investigation process that a team can follow. The pattern is similar to how we automate daily Slack channel summaries with n8n—structured steps that compose into a larger intelligence pipeline.
The Trade-offs: When DAGs Get Messy
This architecture is not a silver bullet. Engineers should be clear-eyed about the failure modes.
Cognitive Overhead: A linear chat is simple because it’s a single stream of consciousness. A DAG forces you to think structurally about your own reasoning process. For quick, one-off questions, this is overkill. You’re adding a database schema to a conversation.
Context Window Inflation: It’s tempting to create a sprawling graph with dozens of nodes. But if you merge branches naively, you can easily stuff the context window with redundant or contradictory information. A naive merge of two 4k-token branches gives you 8k tokens of context before you even ask the next question. You need aggressive summarization at merge points, which introduces its own risk of information loss.
The Edit Propagation Problem: If you edit a root node that hundreds of downstream nodes depend on, do you automatically re-run all of them? That’s computationally expensive and might not be what the user wants. If you don’t, the downstream nodes are “stale”—their responses were generated with a now-outdated context. Managing this staleness is an unsolved UX problem. It’s a bit like cache invalidation, one of the two hard problems in computer science, now applied to your thought process.
API Cost Non-Linearity: The pay-per-token model of LLM APIs incentivizes tight context windows. A DAG, by making it easy to pull in rich context from multiple branches, can silently increase your token consumption per turn. You need to build in token accounting and budget alerts from day one. This mirrors the cost engineering discipline we discuss in Claude Code session economics—structural context management requires structural cost management.
FAQ: Context DAGs for LLM Reasoning
Q: Is this just a fancy way to do prompt chaining?
Not quite. Prompt chaining is a linear sequence of LLM calls where the output of one is the input to the next. A context DAG allows non-linear relationships: multiple inputs to one node (merging), one input to multiple nodes (branching), and the ability to edit upstream nodes without re-running the entire chain. It’s prompt chaining with version control and branching.
Q: How does this differ from the “Projects” or “Spaces” features in ChatGPT/Claude?
Those features add persistent custom instructions and file storage to a linear chat. They don’t change the fundamental data structure. You still have a single, sequential message history. A DAG changes the data structure itself from a list to a graph.
Q: Can I use this with any LLM API?
Yes. The DAG is a client-side data structure. The LLM API still receives a flat list of messages. The magic is entirely in how you build that list from the graph before making the API call. This makes it provider-agnostic.
Q: What happens when my DAG gets too big for the context window?
You need a pruning or summarization strategy. Common approaches: (1) set a maximum node depth from the current node, (2) use an LLM to summarize entire branches into compressed context nodes, (3) let the user manually “archive” or “pin” nodes to control what’s in scope. This is an active area of experimentation.
Q: Is this just for solo work, or can teams use it?
A shared, editable DAG is a powerful collaborative reasoning tool. Imagine a war room during an incident where multiple engineers are exploring different hypotheses in parallel on a shared graph, then merging findings into a timeline. The concurrency control (who edits what) becomes the hard problem, but the payoff is a shared, structured artifact of the investigation rather than a chaotic Slack thread.
Want to build like a Forward Deployed Engineer?
FDE Coach is a cohort-based program in frontend, backend, AWS, and AI. Build real products and get referred to 200+ hiring partners.
Explore the program