The converged variant read as two arrows feeding one point; the two-
complete-arrows variant as two arrows next to each other, not coherent.
Owner withdrew the double-shaft idea entirely (2026-09-15); dashed was
already rejected 2026-09-14. Both relation types draw the same plain
single arrow again.
Kept: CanvasEdge.type plumbing from the snapshot (the next
differentiator will need it) and the ArrowGeom named type. Removed:
followsEdgeGeometry, the gap constants, the zoom-drift edge-path
rebuild (edges are back to a single loop in buildGeometry), the ⟹
legend entry, and the glyph tests. Vision doc records the withdrawal
and keeps the still-valid constraints.
Gates: tsc, 429/429 tests, lint, build. Live: canvas coverage 0.477,
no console errors, no legend remnants.
Owner rejected the first cut: two tails converging on one shared head
reads as two arrows feeding a single point, not as parallel strokes.
- followsEdgeGeometry now returns two COMPLETE regular arrows (own shaft
+ own head each), translated ±half-gap along the edge normal —
parallel shafts AND parallel heads, nothing converges. Offset shafts
never clip the source disc; offset tips land a hair outside the target
disc edge (accepted price of parallel heads).
- Gap floors: screen-anchored 3x edgeWidth (dominates at fit/zoomed-out,
unchanged look) OR head-anchored 1.2x arrowLen (world) — without the
head floor, the world-scaled heads merge into one blob at deep zoom
(measured: one 10px band at 5x). max() keeps >= 0.4 arrowLen of clear
space between the heads past the ~1.5x crossover.
- Vision doc revised (2026-09-15) to record the rejection.
Live-verified at 5x fit zoom: two shafts (11.5px gap = widened floor),
two separate ~8px-base heads with clear space between. At fit: 3px gap,
two heads, unchanged. Gates: tsc, 435/435 tests, lint, build.