Skip to content

Architecture mode: connections can route through unrelated components — render/validate accept it silently #24

Description

@hee-c

Summary

In architecture mode, the built-in router can produce a path that passes through an unrelated component's bounding box. Both render and validate accept the result without any error or warning.

Because arrows are painted before component boxes (correct per the design system), the offending segments hide behind the obstacle component and the arrow visually appears to originate from the obstacle, misrepresenting the topology.

Workflow mode already guards against this since v2.8 ("workflow rendering now rejects edges that cross through non-endpoint nodes" — status update in #10). Architecture mode has no equivalent check. This report is a concrete, minimal real-world case for the "shared obstacle detection across architecture/dataflow/lifecycle" follow-up mentioned there.

Repro

repro.architecture.json — 3 components, 1 connection, no route hints:

{
  "schema_version": 1,
  "diagram_type": "architecture",
  "meta": {
    "title": "Repro",
    "subtitle": "arrow passes through an unrelated component",
    "output": "repro.html"
  },
  "components": [
    { "id": "api", "type": "backend", "label": "API", "pos": [400, 280], "size": [160, 76] },
    { "id": "cache", "type": "database", "label": "Cache", "pos": [645, 130], "size": [130, 60] },
    { "id": "queue", "type": "cloud", "label": "Queue", "pos": [880, 130] }
  ],
  "connections": [
    { "from": "api", "to": "queue", "variant": "dashed" }
  ],
  "cards": []
}
node bin/archify.mjs render architecture repro.architecture.json repro.html   # succeeds
node bin/archify.mjs validate architecture repro.architecture.json --json    # passes
node bin/archify.mjs check repro.html                                        # ok: true

Actual

The only connection in the file is api → queue, but it renders as if it were cache → queue:

api → queue rendered as if cache → queue

Generated path:

M 560 318 L 712 318 Q 720 318 720 310 L 720 168 Q 720 160 728 160 L 880 160

The vertical run at x=720 (y 310 → 168) and the horizontal run at y=160 (x 728 → 880) both cross cache's bounding box x 645–775, y 130–190. The dashed arrow disappears behind the Cache box and re-emerges from its right edge, so it reads as Cache → Queue instead of API → Queue.

How we hit this in practice: in a 12-component diagram, an API → SQS connection rendered exactly like a Redis → SQS connection (the router chose a detour whose corner sat inside the Redis box). The first person who saw the diagram asked "why is Redis connected to SQS?" — i.e. the failure mode is silent and actively misleading, unlike a validation error.

Expected

Same behavior as workflow mode since v2.8: fail fast with an actionable hint (e.g. suggest via waypoints or different fromSide/toSide), or at minimum a warning from validate.

The check itself is cheap for orthogonal paths: axis-aligned segment vs. component rects (minus the two endpoint components, with a small margin). We ran exactly that as an external post-check to find all occurrences in our diagram.

Workaround

Manually routing with via waypoints around the obstacle works fine:

{ "from": "api", "to": "queue", "variant": "dashed",
  "fromSide": "right", "toSide": "top", "via": [[620, 318], [620, 100], [940, 100]] }

workaround: routed above the Cache box

Environment

  • archify 2.11.0 (installed via npx skills add tt-a1i/archify -g)
  • Node v24.11.0, macOS
  • Found while driving the skill from Claude Code — model claude-fable-5, reasoning effort max

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions