Skip to content

Port D2's Dagre compatibility surface to Dagre 3.1.1 - #3

Draft
alixander wants to merge 5 commits into
mainfrom
agent/dagre-3.1.1-d2-surface
Draft

Port D2's Dagre compatibility surface to Dagre 3.1.1#3
alixander wants to merge 5 commits into
mainfrom
agent/dagre-3.1.1-d2-surface

Conversation

@alixander

@alixander alixander commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

This ports Dagro's D2-used default layout profile from Dagre 0.8.5 / Graphlib 2.1.8 behavior to Dagre 3.1.1 / Graphlib 4.0.5.

  • adopt current dummy-node, ordering, acyclic, positioning, compound, parallel-edge, and self-loop behavior
  • preserve caller-owned named multiedges when internal reversed-edge names collide
  • retain every opposite-direction dummy partner when several map to one forward edge, avoiding missing coordinates on valid compound multigraphs
  • preserve JavaScript root-child insertion and missing-map semantics in the native Go port
  • pin the official JavaScript oracle, a reproducibly source-built compatibility oracle, and a content-addressed D2 corpus in CI
  • document the exact source revisions, compatibility boundary, and D2/MPL versus synthetic/MIT fixture provenance
  • update the module and CI toolchain to Go 1.26.5

Compatibility boundary

The verified surface is deliberately limited to D2's directed, compound, named-multiedge profile: rankdir, nodesep, edgesep, ranksep; node IDs, parents, width, and height; and named edges with width, height, and labelpos.

This is not a claim to implement every Dagre 3.1.1 or Graphlib 4.0.5 feature. Dynamic remembered state, per-cluster direction, custom/manual ordering and ranking, constraints, and rankalign remain outside the contract.

Differential evidence

The checked corpus covers 313 D2 Dagre E2E cases, 349 ordered adapter calls, and 311 unique raw layouts.

  • 308 outputs match finite official Dagre 3.1.1 results bit-for-bit
  • one official result contains non-finite geometry
  • two official layouts throw during rectangle intersection
  • all three invalid captured cases use narrow, documented finite compatibility expectations
  • a fourth synthetic compatibility case pins the composition of the reversed-edge ordering corrections

The compatibility patch pairs only opposite reversal states, preserves all partners for a shared forward dummy, and keeps degenerate rectangle intersections finite. The builder fetches the exact upstream commit, verifies its lockfile and patch, and pins the resulting CommonJS oracle by SHA-256.

A deterministic 300-case generator restricted to D2's profile matches that compatibility oracle exactly. It includes the minimized regression behind the natural D2 source a.b -> c -> a.b <- c, which passed in Dagro v0.1.1, panicked during this upgrade, and is finite and connected after the fix.

Validation

  • pinned Dagre 3.1.1 / Graphlib 4.0.5 official Node oracle
  • reproducibly source-built, SHA-pinned compatibility oracle
  • complete 311-input corpus with exact float-bit comparisons
  • 300 / 300 deterministic D2-profile layouts exact against the compatibility oracle
  • go test ./...
  • go test -race ./...
  • go vet ./...
  • native and js/wasm builds
  • executed 300-case finiteness gate under js/wasm
  • local corpus verification on arm64, amd64, and js/wasm

The linked D2 integration PR, d2lang/d2#2844, carries the intentional E2E layout-output update, downstream self-loop normalization, and natural-source crash regression.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant