The graph, not the list
A tour of the prerequisite model that makes Tree feel different from kanban: how nodes unlock other nodes, and why fog of war reveals more than it hides.
Most project tools model work as a list. A queue, a backlog, a board with columns. Even tools that show dependencies (Asana's task dependencies, Jira's blocker links, Linear's parent-child issues) treat them as metadata attached to a list, not as the structure that organizes it.
Tree models work as a directed acyclic graph. Tasks are nodes. Dependencies are directed edges. The graph is the project. Everything else, including the list view we'll eventually ship, is a projection of it.
This post is about why that choice matters, what it lets us do that list-first tools can't, and where the model has costs we accept on purpose.
The list flattens what the graph keeps
Lists encode order. Graphs encode relationships.
If you're looking at a backlog in Linear, you can see what's at the top, what's near the top, and what's been deprioritized. The order is the information. Dependencies, when they exist, are footnotes. A task says "blocked by #142," you click #142, you scroll to find it, you check its status, you scroll back. The dependency is real but the tool doesn't act like it.
In a graph model, the dependency is the structure. #142 is the parent. The current task is its child. Both nodes appear on the canvas with an edge between them. You don't navigate to verify the relationship. You see it.
This sounds like a UI difference. It isn't. It's a data model difference that produces UI differences downstream.
A list-first tool stores tasks in a table with optional dependency rows pointing between them. A graph-first tool stores nodes and edges as first-class peers, where neither one is the primary record. Every operation in a list-first tool that involves dependencies (filtering blocked tasks, finding unblocked work, computing critical path) requires a join. Every operation in a graph-first tool that involves dependencies is a graph traversal, which is what graph databases are built for.
The architectural choice cascades. Once dependencies are first-class, you can ask the structural questions list tools can't answer cheaply. What unlocks the most downstream work? What's the longest path to a given goal? Which nodes have no dependents (true leaves)? Which nodes have no prerequisites (entry points)? These are graph algorithms, available in any DAG library, trivial to compute, and absent from every list-shaped project tool we've used.
Locked, available, in-progress, complete
Tree's status model has four states. They're not arbitrary.
A node is locked when at least one of its prerequisites is incomplete. Locked nodes are visible but greyed out. You can plan them, comment on them, add detail. You can't start them. The tool refuses, and that refusal is the point.
A node is available when all its prerequisites are complete and it hasn't been started. Available nodes are the work you can pick up right now. The set of available nodes is the actual to-do list, computed continuously from the graph. It's not a list someone curated. It's the leaves of a topological sort, freshly evaluated every time the graph changes.
A node is in-progress when someone has claimed it. The status is operational, not informational. It tells the rest of the graph that this node's children are still locked because the work isn't done.
A node is complete when it's done. It turns green, stays on the graph as foundation for everything above it, and unlocks any children whose other prerequisites were already met.
That's the entire status model. There are no custom workflows, no transition rules, no approval gates, no "in review" / "ready for QA" / "blocked" / "won't fix" states. Tools that ship with workflow engines do so because their data model can't tell what state a task is in without one. Ours can. The graph knows whether a node is locked. We don't need a workflow engine to tell us.
This is the part where Jira admins start sweating. The argument is that real projects need more states than four because real projects have more nuance than four states can capture. Our argument is that the nuance is in the graph, not in the status field. A node "blocked on review" in Jira is a node whose review prerequisite hasn't completed in Tree. Same information, different place.
Why fog of war turned out to matter
We added fog of war as a visualization toggle and discovered it was load-bearing.
The original purpose was simple. Large project trees have hundreds of nodes. Showing all of them at all zoom levels makes the canvas unreadable. Fog of war hides nodes that are far enough downstream from the user's current focus to be irrelevant. The default setting reveals the available tier (what you can start now), the in-progress tier (what's being worked on), and the next tier of locked nodes (what's coming next). Everything beyond that fades.
The behavior is editable in granular ways. Admins can configure fog of war globally or per-user. Different access levels see different visibility. Some teams turn it off entirely (everyone sees the full tree). Some teams set it tighter (one tier of lookahead, no further). Some teams configure it per group (engineers see two tiers, executives see the full graph, contractors see only their assigned branch).
We expected this to be a minor convenience feature. It turned into something else.
The graph model lets us show everything. Fog of war is the choice not to. It's the difference between a tool that exposes structure and a tool that's overwhelming. With fog of war off, a 200-node project tree is technically visible but cognitively unusable. With fog of war on, the same tree shows you exactly what to think about right now and quietly hints at what's behind the curtain.
What we didn't anticipate is how often the configuration itself becomes a planning artifact. A team that sets fog of war to "one tier of lookahead" is making a statement about how they want to work. A team that turns it off is saying the opposite. The setting carries information about the team's culture, not just their UI preferences.
This is the same dynamic as the graph itself. The structure carries information. The default behavior carries information. Configurability carries information. None of it is neutral.
What the model can't do
A graph-first project tool has costs. We accept them, but pretending they don't exist would be dishonest.
Cyclical dependencies don't exist. The model is a DAG, which is the correct shape for project work but the wrong shape for some real-world workflows. If two tasks genuinely depend on each other (mutual review, iterative refinement), Tree forces you to break the cycle by introducing intermediate nodes. This is sometimes annoying. It's also a feature, because cycles in project graphs usually mean the work isn't decomposed enough.
Free-form work is awkward. Some teams operate in continuous flow with no clear prerequisites. Designers iterating on a mood board. Researchers exploring a question. Sales teams working a pipeline. These workflows don't have the structure Tree expects, and forcing them into a graph would feel like overhead. Tree isn't the right tool for that work. We'd rather be specific about who we're for than pretend we're for everyone.
Real-time collaboration on a graph is harder than on a list. Two people editing a list can usually merge cleanly. Two people editing a graph can produce structural conflicts (one adds a dependency, the other removes the parent node) that need resolution logic. We're solving this in the product, but it's a real complication we wouldn't have on a list.
Performance at scale. A graph with 10,000 nodes and dense edges renders slower than a list of 10,000 rows. We're optimizing for the projects most teams actually run, which top out in the low hundreds of nodes. If you have a project that genuinely requires 10,000 interconnected tasks, you have either a misclassified portfolio or a different problem than Tree solves.
What this enables
The list of things the graph model lets us do, briefly:
Critical path computation, native. Show the longest dependency chain in the project. The bottleneck is structural, not subjective.
Unblocked work surfacing, native. The available tier is the answer to "what should I work on" without anyone having to triage a backlog.
Branching for exploration. Plant two parallel approaches as branches, develop them independently, prune the loser when you commit. The structure carries the comparison instead of forcing you to maintain two parallel project plans.
Prerequisite-aware notifications. When a node completes, every newly-unlocked child can ping its assignee. The notification logic is structural, not rule-based.
Honest progress tracking. Project completion is a function of the graph, not a manually-updated percentage. Mark nodes complete, watch the path to your goal shorten in real time.
None of this requires features Tree has to build separately. They fall out of the data model. List-first tools can replicate any one of these with enough plumbing, but each one is a feature ticket. In a graph-first tool, they're consequences.
The list view is coming
We're shipping a list view. It's on the roadmap under the "Templates" branch.
The point isn't that lists are bad. The point is that the list should be a projection of the graph, not the source of truth. When the list view ships, it'll show available nodes in priority order, computed from graph traversal. Mark a task complete in the list, the graph updates. Add a dependency in the graph, the list reorders.
This is the inverse of how list-first tools approach dependencies. They start with a list and bolt graph features on top. We're starting with a graph and projecting list views from it. The two architectures look similar at a glance and produce wildly different software.
If you want to see the model in action, join the waitlist. The first interactive tree drops when the build is ready, and the model described in this post is what it'll be running on.


