ADR-0005: An owned auto-layout replaces dagre
Status: accepted, 2026-09-11
Context
Synth, export, materialization and the studio all fill in a position for any
action that has none, and codegen omits a document's layout when it equals
that auto-layout, so the generated TypeScript carries only hand placement.
Phase B gives a document a second authored companion, <name>.flow.tf, and
the same rule applies per action: a position block is written only where
the action sits somewhere other than where auto-layout would put it. The
Terraform provider reads those files without this package, so it has to
compute the same positions or it cannot tell an omitted position from a moved
one.
Until now the positions came from dagre, a JavaScript layout library. Its
output depends on float arithmetic, tie-breaking inside a network-simplex
ranking and an ordering heuristic that the library documents only as code.
Three ways were considered: port dagre to Go and prove the two agree to the
integer (thousands of lines, and a proof that would have to be repeated on
every dagre release), let the provider write no content.Metadata when a
position is missing (every hand-written .flow.tf would then deploy with no
console layout, which is the provider's main use), or replace dagre with an
algorithm small enough to specify and pin with fixtures.
Decision
The third. packages/core/src/layout.ts implements the layered algorithm
written down in conformance/layout/README.md: depth-first discovery from
StartAction with back edges dropped, longest-path ranks, discovery order
within a rank, unreachable actions past the deepest rank in document order,
integer coordinates from four constants. conformance/layout/<case>/ pins it,
and every implementation runs those cases.
Consequences
Every fixture and golden that carried dagre coordinates was regenerated once,
and the hosted demo's screenshot was retaken. Flows that were auto-arranged
look plainer than dagre made them, because the algorithm does not minimise
edge crossings; hand placement is untouched, and the studio still persists
it. @flow-as-code/core now has no runtime dependency at all.
Document order is part of the input: the same graph declared in a different order may lay out differently. That is deliberate, and it is what ADR-0003 already says about order everywhere else.