Projected git graph planning
A mildly retrocausal planning notation — pin the next minor release and it reaches back to decide which feature you commit today.
A project plan should look like the history you want to read after the work
ships. So I’ve started giving coding agents a small text notation that draws a
plan as a projected git graph: the future git log, written before the work
exists — real commits, branch structure, and one next step.
It borrows most of its structure. Engineers already carry the model — commits in
order, branches as lanes, merges as joins — so what’s new is four state glyphs,
defined below. It is plain text, so it renders in a terminal, an issue body, a
TASKS.md, or a diff — wherever the work happens.
The plan is executable
Every commit line in the graph is a conventional commit subject:
◇ v0.3.0 (next)
│
○ feat(auth): session refresh
◉ feat(api): token endpoint PR #2 · in review
● feat(db): user table a1b2c3d
◇ v0.2.0 — 2026-07-10
The symbols carry state:
○is planned.◉is in flight on a branch or pull request.●is shipped on main.◇is a release boundary.
Time flows upward, like git log. The graph is mildly retrocausal: you pin a
point in the future — the next minor release — and it reaches back to decide
which feature you’re allowed to commit this afternoon.
The next unit of work is the lowest open circle — the one with nothing unfinished beneath it. That rule matters when several agents are available: it tells them what is ready without turning every unfinished idea into an equally urgent checkbox.
Because the lines are real commit subjects, there is no translation step at the end. The plan line becomes the commit message verbatim.
Plans advance instead of being rewritten
The graph changes through promotion. A planned commit becomes in flight when its pull request opens, then shipped when it lands. Planned lines can be split, reordered, or dropped; shipped lines are history.
This matters with agent-written code: an agent can produce a large diff quickly, but speed does not make it reviewable. Planning in commits forces an earlier question — what are the smallest pieces that can land independently, and in what dependency order?
Branches remain branches
Independent pull requests occupy separate lanes. Dependent pull requests form a stack. The topology stays visible in plain text.
ASCII is the default because both humans and agents can read and update it in place. Mermaid renders the same graph where a diagram helps, like a pull request body.
The landing agreement
A projected graph is a claim about what main will look like. The claim is only
worth making if the planned commits are the ones that land — so the graph
carries an agreement: every ○ becomes its own ●.
This rules out squash-merging. Squash has a real use: a branch of wip, fix typo, address review should collapse, because those commits were never
chosen. A planned span is the opposite — every line was chosen before it was
written, and squashing discards what the graph encoded: the dependency order,
the reasoning per step, and the ability to bisect or review one piece at a time.
A large pull request is not a reason to squash; it is the reason not to.
The clean landing is a fast-forward. Rebase the branch onto main, then move main to it. The push creates nothing — the commits you pushed are the commits on main, with no merge commit and no rewriting on the way in:
git fetch origin
git rebase origin/main
git push origin HEAD:main
Stacked pull requests are the motivating case. Each is one clean span, and
squash-merging them individually yields one commit per pull request while losing
what is inside. Let the stack accumulate on a branch and fast-forward main to
its tip. A long-lived milestone branch works the same way, or takes a --no-ff
merge when the boundary is worth a marker in the log.
Settle this before the graph is drawn. A repository configured squash-only cannot hold a multi-commit plan; find that out while grouping the work, not at merge time. I learned it by squash-merging a stack I had just finished planning, then unpacking it back out of main.
Why the shape of history is the point
A squashed branch records that a merge happened. It does not record the work. History documents itself only when its commits are units someone chose — which is the argument for projecting the graph up front. The plan and the history stop being two artifacts that drift apart; they are the same artifact, read at different times.
What the graph does not prove
The graph organizes delivery; product judgment still has to decide whether the feature should exist at all. It makes the proposed path to main specific enough to inspect.
The obvious objection is that you often cannot know the decomposition until you
have written some of it. True — and the graph is built for that, since ○ lines
are the ones you are free to split, reorder, and drop. What you cannot redraw
are the ●s. Once a line becomes a real commit, the repository — not the prose
around it — is the authority.
This started as a set of instructions for my own coding agents — the notation,
the drawing rules, and the templates are all there. Not a spec to adopt; just
where the idea came from and the version that happens to be running:
project-planning.