Skip to content

Implement a native Rust/IPC Freerouting DSN/SES bridge #337

Description

@neusse

Problem

Konnect intentionally removed the old autoroute tool in #253 because it advertised a workflow that could never succeed. The cleanup was correct, but Konnect still has no supported end-to-end path from a live KiCad board through Freerouting and back.

This should remain a new implementation issue rather than reopening #253. That issue established the engine/plugin distinction and removed a false public capability; this issue proposes the actual bridge.

Important discovery: Freerouting already provides the engine-side MCP server

Freerouting 2.3.0 includes a native Java MCP server inside the Freerouting JAR. In local/offline mode it runs headlessly over stdio and exposes the complete routing-job state machine:

create_session -> enqueue_job -> upload_job_input_from_local_file -> update_job_settings -> start_job -> get_job_details -> download_job_output_to_local_file

Official documentation: Freerouting v2.3.0 MCP guide.

This removes the need for Konnect to implement another error-prone Freerouting process protocol, job/session manager, settings transport, progress poller, log/cancellation layer, or artifact upload/download mechanism. Local mode needs neither the Node public-API bridge nor cloud access.

The native Freerouting MCP is an engine interface, not a KiCad bridge. It accepts an already-created Specctra DSN and returns a Specctra SES. It does not:

  • bind the job to the exact live KiCad document and revision;
  • convert a KiCad IPC board snapshot into DSN;
  • parse and validate SES against the exported board;
  • apply tracks, arcs, and vias through a KiCad transaction; or
  • provide rollback, read-back, candidate-save, or DRC evidence.

Those remain Konnect's responsibility. The existing Freerouting KiCad ActionPlugin proves the workflow using legacy Python/SWIG pcbnew.ExportSpecctraDSN and pcbnew.ImportSpecctraSES, but it is suitable only as a parity oracle or manual fallback. It is not the production architecture for a Rust/IPC bridge and is not forward-compatible with KiCad 11's removal of SWIG.

Revised feasibility finding

A native bridge is feasible against stock KiCad 10.0.5 without Python, SWIG, GUI automation, or a modified KiCad build:

  1. bind and read the exact live board through KiCad IPC;
  2. generate a deterministic Specctra DSN in Rust;
  3. delegate the routing job to the discovered local Freerouting JAR through its native MCP server;
  4. parse the returned SES in Rust; and
  5. apply the accepted tracks, arcs, and vias through one KiCad IPC transaction.

KiCad 10 IPC exposes the core routing model needed for that conversion: open-document identity, board layers and stackup, nets and effective netclasses, footprints and pads, board shapes and zones, existing tracks/arcs/vias, flashed pad polygons, transactions, item creation/deletion, zone refill, and save-copy operations.

It does not expose native Specctra DSN export or SES import. kicad-cli does not provide those commands, and copying KiCad 11 protobufs cannot add handlers to a stock KiCad 10 server. The remaining work is therefore a real interchange adapter, but Freerouting's native MCP removes the need to reimplement the router-control side.

Primary evidence:

Revised architecture

KiCad IPC immutable snapshot
  -> Rust normalized routing model + reverse manifest
  -> deterministic Rust DSN
  -> local Freerouting JAR, controlled through native MCP over stdio
  -> SES artifact
  -> strict Rust SES import plan + manifest resolution
  -> revision-bound KiCad IPC transaction
  -> non-overwriting candidate board + read-back/DRC evidence

1. Bind one immutable live-board snapshot

  • Require exactly one responsive PCB Editor and prove the requested board path through GetOpenDocuments.
  • Capture SaveDocumentToString and hash it as the source revision.
  • Read all required typed board data against the same document.
  • Revalidate path, token, and revision before import.

Exporting one revision and importing into another must be a structured, non-mutating conflict.

2. Normalize the routing model and write DSN in Rust

Create a deterministic internal model plus a reverse manifest containing board identity, layer mapping, net mapping, footprint/pad identities, permitted via padstacks, resolution, origin, and transforms.

Emit the supported structure, placement, library, network, and existing wiring scopes in stable order. Unsupported boundaries, padstacks, keepouts, rules, or transforms must fail before routing; nothing may be silently dropped or approximated.

topola_specctra may be evaluated as a tokenizer/syntax dependency, but only after corpus testing proves compatibility with Freerouting output. Konnect must retain strict lowering and manifest resolution even if that crate is used.

3. Delegate engine control to Freerouting MCP

  • Discover an explicit JAR, a standalone installation, or the Freerouting PCM-bundled JAR on each platform.
  • Report engine_found, native_mcp_available, and bridge_available separately.
  • Launch the discovered JAR as an owned, headless local MCP child over stdio.
  • Perform MCP initialization and tools/list; validate the required tool names and schemas before uploading a board.
  • Use Freerouting's documented session/job workflow with bounded time and passes.
  • Retain DSN, SES, logs, and structured diagnostics on failure.
  • Own and terminate the child on success, cancellation, timeout, or Konnect shutdown.
  • Do not expose an unauthenticated network listener beyond loopback.

The documented headless Freerouting CLI may remain a narrow fallback, but Konnect should not duplicate Freerouting's MCP job-management implementation.

4. Parse SES into a strict import plan

Parse the Freerouting-emitted session subset: route padstacks, network_out, wires (path/qarc), vias, and optional placement. Resolve every name through the export manifest.

Reject the complete session on an unknown net, layer, component, pin, or via padstack; an unrepresentable width/drill/span; an invalid transform; unexpected placement; or source-revision mismatch. Do not partially import an unfamiliar session.

5. Apply one undoable IPC transaction

  • Revalidate board identity and revision.
  • Begin one KiCad commit.
  • Preserve locked/fixed routing and delete only routing explicitly selected for replacement.
  • Create typed tracks, arcs, and vias; update placement only when explicitly enabled.
  • Check every per-item result, not only request-level status.
  • Drop the transaction on any mismatch or item failure.
  • Commit once, refill zones, save a non-overwriting <stem>.freerouted.kicad_pcb candidate, and report read-back inventory, remaining unrouted connections, and direct KiCad DRC evidence.

The source board must remain untouched until explicit acceptance; one undo must reverse the complete import.

Deliberately narrow first profile

The first end-to-end profile should support:

  • KiCad 10.0.5;
  • two-layer boards;
  • ordinary plated-through pads and through vias;
  • one simple closed outline;
  • no placement changes;
  • no custom-rule coverage claim; and
  • no existing unlocked routing.

Reject unsupported geometry and pre-routed boards before writing DSN. Expand support only through fixture parity tests, not silent approximation.

Revised focused PR series

Freerouting's native MCP removes most of the former runner PR and lets the proposed series shrink from five PRs to three. One large PR is technically possible but would combine three independent review and failure boundaries, making the safety-sensitive conversion and IPC behavior difficult to review or bisect.

  1. Deterministic DSN export: immutable snapshot identity, normalized routing model, reverse manifest, supported-profile validation, deterministic Rust DSN writer, capability reporting, and semantic parity fixtures. Reviewable outcome: a live supported KiCad board produces a revision-bound DSN or a structured refusal.
  2. Strict SES import: parser, manifest resolution, complete dry-run plan, required IPC wrappers, per-item checks, one undoable transaction, rollback, zone refill, non-overwriting candidate save, and read-back evidence. Reviewable outcome: a compatible SES is safely applied as one unit or nothing changes.
  3. Public autoroute workflow: existing JAR discovery extended with provenance/native-MCP capability, owned local MCP child, session/job orchestration, public tool registration, acceptance reporting, documentation, and end-to-end KiCad/Freerouting evidence. Reviewable outcome: the two reviewed adapters are composed with Freerouting's existing engine protocol.

Public tool names and schemas should be agreed before PR 1 under docs/NAMING_CONVENTIONS.md. Each PR should keep fixtures and tests with the code they validate.

Acceptance criteria

  • No Python, SWIG, GUI automation, or direct editing of a board owned by KiCad.
  • Reuse Freerouting's native local MCP for engine job/session control rather than implementing a parallel protocol.
  • Local/offline routing requires neither the Node public-API bridge nor cloud access.
  • Engine discovery, native MCP availability, and end-to-end bridge readiness are reported as separate facts.
  • DSN export is deterministic, revision-bound, and fail-closed.
  • SES parsing produces a complete dry-run plan or a structured refusal.
  • Import is one atomic KiCad transaction with per-item result checking and rollback.
  • Locked routing and all board content outside the import plan are preserved.
  • The original board file is not overwritten; the candidate copy is explicit.
  • Route/via inventory, remaining unrouted connections, and direct KiCad DRC are reported as acceptance evidence.
  • One undo reverses the complete import.
  • Fixtures come from real KiCad output plus KiCad/Freerouting-owned DSN/SES corpora.
  • Unsupported features fail before mutation.
  • The owned Freerouting child is cleaned up on success, failure, cancellation, timeout, and Konnect shutdown.
  • Tool counts, directory documentation, compatibility contracts, and all required Rust CI gates remain synchronized.

Design questions for agreement

  1. Is the narrow first profile acceptable, or should the initial release support existing unlocked routing?
  2. Is the three-PR split acceptable now that Freerouting MCP owns engine orchestration?
  3. Should the documented headless CLI remain as a fallback adapter, or should the first implementation support local MCP only?
  4. Is evaluating topola_specctra acceptable, provided unsupported syntax causes complete refusal and the dependency passes the required corpus tests?
  5. Should final DRC use Konnect's existing kicad-cli path on KiCad 10 while all board reads and writes remain IPC-only?

Related: #253, #257, the roadmap section “Autorouting: a real Freerouting bridge,” and Discussion #165.

Activity

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

Metadata

Metadata

Assignees

Labels

P1High-value workflow reliabilityarea:routingTraces, autorouting, Specctra/FreeroutingclaimedSomeone has claimed this; check the assignee before starting

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions