
Test4.routed
pginfinitylab-ai/forge
It combines deterministic EDA analysis, native KiCad tooling, structured engineering knowledge, multi-plane design graphs, vector retrieval, and controlled execution to provide an AI-assisted hardware engineering…

It combines deterministic EDA analysis, native KiCad tooling, structured engineering knowledge, multi-plane design graphs, vector retrieval, and controlled execution to provide an AI-assisted hardware engineering…
Test3 is an open-source printed circuit board by pginfinitylab-ai, from the pginfinitylab-ai/forge repository on GitHub. It is a 2-layer board of 50 × 40 mm with 30 parts on 15 nets, routed with 17 tracks and 17 vias.
It is built around the B340A-13-F-MS, SMAJ36A and USB4125-GF-A. Its parts include 9 capacitors, 7 resistors, 4 diodes, 2 connectors and 1 IC.
UST Forge (formerly CircuitMind) is an engineering-grade AI-powered, end-to-end product engineering platform covering Electronics · Firmware · Mechanical · Simulation · Validation, designed to operate alongside KiCad 10.x, ngspice, Renode, FreeCAD, and Freerouting.
Using the application? The end-user guide for hardware engineers is in
docs/user-guide/(PDF:CircuitMind-User-Guide.pdf).
It combines deterministic EDA analysis, native KiCad tooling, structured engineering knowledge, multi-plane design graphs, vector retrieval, and controlled execution to provide an AI-assisted hardware engineering environment.
The primary design principle is:
AI may reason about the design, but engineering facts must come from deterministic sources.
UST Forge therefore separates:
Part I — Overview
Part II — Core Capabilities
Part III — Technology Selection and Benchmarks
Part IV — Installation and Configuration
Part V — Operation
Part VI — Reference
CircuitMind is composed of two primary applications:
CircuitMind
├── frontend/
│ └── React + Vite + Tailwind engineering workstation
│
└── backend/
└── FastAPI + PostgreSQL + KiCad integrationThe backend provides:
The frontend provides the engineering workstation interface:
UST Forge allows designing complete boards starting from natural-language engineering requirements. You can paste these directly into the Design Pipeline (Stage 1 — Requirements > Describe tab):
Design a 12V to 5V step-down buck converter power supply capable of supplying 1A continuous output current.
Input requirements:
- Nominal input voltage: 12V DC (acceptable range 9V to 16V).
- Protection: Reverse polarity protection diode, over-current fuse, and input TVS surge diode.
Output requirements:
- Output voltage: 5V DC at 1A continuous.
- Delivery interface: Power-only USB-C receptacle output (sink/source advertisement via CC pins).
- Diagnostics: Power indicator LED with series resistor, and an output test point.
PCB & Mechanical requirements:
- PCB stackup: 2 layers.
- Board dimensions: 50 mm width by 40 mm height.
- Mounting: 4x M3 mounting holes (3.2 mm diameter).
- Operating temperature range: -20°C to 70°C.Design an STM32 microcontroller board powered from a 5V USB-C input.
Power requirements:
- Input: 5V DC via USB-C receptacle (4.75V to 5.5V VBUS).
- Regulation: 3.3V low-dropout regulator (AP2112K-3.3 or equivalent) with 100mA continuous load capability.
Microcontroller requirements:
- MCU: STM32F407 MCU (LQFP-100) operating at 3.3V.
- Clock: 8 MHz HSE crystal oscillator with 27 pF load capacitors.
- Power integrity: 6x 100 nF VDD decoupling capacitors, 1x 4.7 µF bulk capacitor, and 2x 2.2 µF VCAP capacitors.
- Boot & Control: 10 kΩ BOOT0 pull-down resistor, tactile reset push button connected to NRST.
Interfaces & Diagnostics:
- Debug: Standard 5-pin SWD debug header (3V3, SWDIO, SWCLK, NRST, GND).
- User interface: User status LED connected to GPIO with a current-limiting resistor.
PCB & Mechanical requirements:
- PCB stackup: 2 layers.
- Board dimensions: 60 mm width by 40 mm height.
- Mounting: 4x M3 mounting holes (3.2 mm diameter).See docs/EXAMPLE_PROMPTS.md for full stage-by-stage pipeline outcomes and engineering calculation details.
CircuitMind is intentionally designed to avoid treating an LLM as the source of truth.
CircuitMind follows this general hierarchy:
KiCad Project Files
│
▼
Native KiCad CLI / Parsers
│
▼
Deterministic Engineering Facts
│
▼
Canonical EDM Graph
│
├──────────────► Deterministic Calculation Engine
│
├──────────────► Verification Engine
│
└──────────────► Retrieval / Knowledge Layer
│
▼
AI Reasoning
│
▼
ChangeSet
│
▼The AI layer can propose, explain, compare, and reason.
It should not invent:
Those facts must originate from deterministic engineering sources.
CircuitMind follows several safety principles.
Use native EDA data before asking an LLM to infer design relationships.
Do not infer electrical identity solely from human-readable error descriptions.
Use DSM hashes and project state checks to detect stale information.
Represent modifications as ChangeSets rather than implicit AI mutations.
A successful file modification does not mean the engineering change is correct.
Run deterministic verification after changes.
Engineering findings should retain their source and supporting evidence wherever practical.
CircuitMind is evolving toward a platform where:
KiCad provides the facts. The EDM graph provides the structure. Deterministic engines provide the calculations. Qdrant provides engineering knowledge. AI provides reasoning. DSM provides state integrity. KCAA provides controlled execution. Verification provides evidence.
The objective is not simply to build an AI chatbot for electronics.
The objective is to build an engineering system in which AI reasoning is constrained by deterministic design facts, explicit evidence, controlled execution, and repeatable verification.
CircuitMind is an active engineering-development project. Some subsystems are production-oriented while others remain experimental.
backend/app/main.pyimports cleanly. Runpytestfrom the repository root for the hermetic suite (1018 tests collected; live-service tests are marked and skipped unless configured — see §22).
| Subsystem | Readiness | State | Description |
|---|---|---|---|
| KiCad 10 Headless DRC/ERC | 100% | LIVE | Executes kicad-cli ERC/DRC, parses JSON violations into findings, computes verification deltas |
| Smart Verification Cache | 90% | LIVE | DRC/ERC are skipped while every schematic sheet, the board, the .kicad_pro, any .kicad_dru and the kicad-cli install are unchanged and the last result was usable; the status shows STALE as soon as one of them changes, and Run Full Verification always reruns (§5.1). Pipeline stages run their own checks every time |
| PCB Layer SVG Visualizer | 100% | LIVE | Generates multi-layer SVG visualization with coordinate crosshair support |
| Schematic Vector Visualizer | 100% | LIVE | Multi-sheet SVG export via KiCad CLI with calibrated reticle overlay for cross-probing |
| BOM & Netlist Extraction | 90% | LIVE | Native BOM/netlist extraction via kicad-cli and a real s-expression parser, feeds the EDM graph hydration pipeline |
| 5-Plane Canonical EDM Graph | 40% | LIVE/WIP | Only LOGICAL and PHYSICAL planes are populated; INTENT, FUNCTIONAL, and VERIFICATION planes are not yet implemented |
| Visual IR → KiCad schematic (§6) | Beta | LIVE/HYBRID | Image/PDF schematic → components, pins, geometry-proven connection suggestions → engineer review → ERC-gated KCAA build verified by netlist read-back. Op-amp polarity and unnamed parts always need an engineer (§6.6) |
| Visual retrieval (ColQwen) indexing (§6.7, §13.5) | Beta | LIVE/OPTIONAL | Rendered pages are embedded through vLLM /pooling (chat request; server needs --chat-template deploy/colqwen/chat_template.jinja) into Qdrant, observable per job and never fatal. R@1 92.9 vs 64.3 for OCR text on a 14-query schematic test (§11.4); evidence only |
| Deterministic Calculation Engine | 85% | LIVE/HYBRID | Buck (with TPS54360 support parts), voltage-divider, inductor/capacitor sizing, MLCC DC-bias derating, LDO dissipation, boost sizing, IPC-2152 trace width, tolerance, thermal and op-amp headroom math, wired into the design pipeline's component resolver and contract; RS-485 and other interface topologies are not yet implemented |
| Design Sync Manager (DSM) | 75% | LIVE | SHA-256 multi-file hashing used for cache invalidation across PCB/schematic/EDM; gating is hash-equality, not a rich dependency gate |
| Qdrant Engineering Knowledge | 80% | LIVE/HYBRID | Full upload → PDF extraction → embedding → indexing → semantic search → delete pipeline is implemented and wired; documents are now Global (org-wide library) or Project-private, with global docs applyable per-project |
| KCAA Safe Execution Sandbox | 55% | EXPERIMENTAL | Sandbox clone/commit, MCP stdio driver, and LLM-driven repair-loop orchestrator exist with a full /api/repair/* session lifecycle |
| Design Pipeline (Requirements → Gate 3 release) | Beta | LIVE/HYBRID | Stages 0–8 with gates G0–G5: requirements, architecture, components validated against the KiCad libraries, contract with a connectivity plan, schematic synthesis and verification (ERC, ngspice DC operating point), placement, Freerouting routing with ground plane, DRC repair loop, and the Gate 3 release package. The D9 end-to-end run released both reference boards (12 V→5 V buck, STM32F407) on real KiCad 10 (docs/D9_PC_RESULTS.md); routing is not yet repeatable on every run |
| Firmware Simulation (STM32F4-Discovery, Renode) | Beta | LIVE/HYBRID | Editable architecture canvas, Simulation Bench with pin bindings, Renode runs replayed on the canvas (LED, button, UART), firmware build worker on a fixed skeleton, AI-written firmware and tests the engineer reviews, bench bindings checked against the KiCad netlist (docs/simulation-spike.md); one target, Windows-verified |
| Mechanical Stage (board 3D, enclosure, fit, GM) (§9A) | Beta | LIVE/HYBRID | kicad-cli STEP/GLB export with a model-status manifest, enclosure intent in the contract, deterministic two-piece case generated by headless FreeCAD, box + exact fit tiers, gate GM and enclosure files in the Gate 3 release |
| Reference Library & AI Design Comparison | 100% | LIVE | Architecture > Reference Library runs an LLM-graded, RAG-grounded functional/physical block comparison of the live design against a chosen reference document, utilizing multi-modal VLM circuit graph extraction and evidence fusion. |
| Remote Project Access (ZIP Upload/Download) | 70% | LIVE | Upload a ZIP of a KiCad project over HTTP to open it on a server the browser isn't running on, and download the live project folder back as a ZIP (see §19); no authentication yet |
Readiness percentages represent the current development state and are not production-readiness certifications.
a6c8e0f2b4d5); the application only verifies at startup that the database is at head (§14.3).torch. Live suites are opt-in (§22).docs/D9_PC_RESULTS.md) ┌───────────────────────┐
│ CircuitMind UI │
│ React / Vite / │
│ Tailwind │
└───────────┬───────────┘
│
│ REST API
▼
┌───────────────────────┐
│ FastAPI Backend │
└───────────┬───────────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌──────────────┐
│ KiCad CLI │ │ PostgreSQL │ │ Qdrant │
│ │ │ │ │ │
│ DRC/ERC │ │ Canonical │ │ Engineering │
│ BOM │ │ EDM Graph │ │ Knowledge │
│ Netlist │ │ │ │ │
│ SVG │ │ │ │ │
└──────┬──────┘ └─────────────┘ └──────────────┘
│CircuitMind targets KiCad 10.x.
The live verification and export pipeline relies on KiCad 10.x CLI functionality, including:
Older KiCad versions are not currently supported by the v6 verification pipeline.
CircuitMind is intentionally conservative when interacting with design files.
Analysis operations should be read-only.
AI-generated modifications are represented as proposed ChangeSets rather than immediately modifying the KiCad project.
The intended workflow is:
AI Proposal
│
▼
ChangeSet
│
▼
DSM Preconditions
│
▼
Controlled Execution
│
▼
KiCad Re-analysis
│
▼
DRC/ERC VerificationDirect uncontrolled modification of the user's KiCad project is intentionally avoided.
CircuitMind does not use textual descriptions from DRC/ERC messages as the primary identity of design objects.
For example, a description such as:
Symbol R3 [R1206_10K_1%_0.25W_100PPM]is not sufficient to uniquely identify the electrical context of a component.
Component identity, connectivity, pins, nets, coordinates, and relationships should instead be derived from:
This prevents fragile regex-based reverse engineering of engineering relationships.
CircuitMind uses a canonical Engineering Design Model (EDM) to represent the hardware design independently from individual UI representations.
The graph is intended to represent multiple engineering planes, including:
Conceptually:
┌─────────────────┐
│ Requirements │
└────────┬────────┘
│
▼
┌────────────┐ ┌──────────────┐ ┌─────────────┐
│ Components │────►│ Canonical EDM│◄────│ Nets │
└────────────┘ │ Graph │ └─────────────┘
└──────┬───────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
Physical Verification Evidence
PCB DRC/ERC KnowledgeThe database currently uses composite identity structures such as:
(project_id, entity_id)to prevent collisions between entities belonging to different projects.
The Design Synchronization Manager (DSM) protects against reasoning against stale design data.
DSM tracks project state using SHA-256 hashes across relevant project files.
Conceptually:
Project Files
│
▼
SHA-256 Hashing
│
▼
Current Design State
│
├── unchanged ──► cached verification may be reused
│
└── changed ────► re-analysis requiredThe DSM is also used as a pre-condition gate before controlled design mutations.
A proposed ChangeSet should not execute if the underlying design state has changed unexpectedly.
CircuitMind integrates with the KiCad CLI for deterministic verification.
Typical verification flow:
KiCad Project
│
▼
kicad-cli
│
├── DRC
├── ERC
├── BOM
├── Netlist
└── SVG
│
▼
Structured Results
│
▼
CircuitMind Verification Model
│
▼
Frontend FindingsThe verification cache uses file hashes to avoid unnecessary DRC/ERC execution (dsm.get_verification_keys). A result depends on two keys: every schematic sheet plus the .kicad_pro, and the board plus the .kicad_pro and any .kicad_dru. Local UI state (.kicad_prl) and KiCad backups do not count.
Conceptually:
Project Hash
│
├── unchanged ──► reuse previous verification
│
└── changed ────► execute KiCad verificationA result of ERR, UNKNOWN or a missing tool is never reused. Run all tests reuses a current result; Run Full Verification (DRC/ERC) on the Verification dashboard always reruns (POST /api/tests/run-all?force=true).
CircuitMind supports cross-probing between verification findings and visual design representations.
Example workflow:
This allows deterministic coordinates from KiCad verification output to be connected directly to the visual engineering workstation.
The Visual IR pipeline turns a schematic image or PDF into a structured, reviewable graph — components, pins, wires, junctions, connection evidence and coordinates — and, after an engineer reviews it, builds a KiCad schematic through KCAA that is checked by netlist read-back.
services/visual_ir/decisions.py writes ENGINEER_OVERRIDE, and only from an explicit human action.ORPHANED (re-bind or discard)..kicad_sch. Pin numbers and names come only from KCAA get_symbol_pins.UNPLACED or SUGGESTED with a reason code, never silently promoted.Every stage is persisted as a VisualJobStage row (status, timing, error, model versions), so a job can be inspected, retried or cancelled. Stages 4–7 run once per page. Order (services/visual_extraction.py):
| Order | Stage | What it does |
|---|---|---|
| 1 | ingest | Register the PDF/image and its pages |
| 2 | analyze | Route each page by content type (VECTOR, RASTER, VECTOR_MIXED): does it carry usable vector drawings and a text layer? |
| 3 | render | Render pages at 300 DPI; optionally index them for visual retrieval (§6.7) |
| 4 | objects | YOLO component detection; text from the PDF text layer, or — when the vector text is empty (stroked/outlined text, as in the multichannel fixture) — PyMuPDF OCR (get_textpage_ocr, Tesseract, 300 DPI); YOLO text boxes are cropped and OCR'd |
| 5 | associate | Globally consistent text↔component assignment (Hungarian); references, values and net labels are matched in separate passes |
| 6 | wires | Wires and junctions from vector drawings and/or raster masks, merged by confidence |
| 7 | vlm | VLM circuit description — evidence only |
| 8 | normalize | Normalized coordinates and statuses |
| 9 | symbols, pins | Symbol resolution (exact part-number match only), pin hydration and geometry |
| 10 | connectivity | Strict/loose net graph, ConnectionSuggestion rows, dangling-end evidence |
| 11 | graphs | Visual circuit graphs for the inspector |
NEEDS_SYMBOL..kicad_sym geometry (parsed by symbols/geometry.py) mapped onto the detection box, then cross-checked against the KCAA pin set. The detection box covers the symbol body; pins lie outside it, so the scale is taken from the body.+/−) the fit reports ambiguity instead of guessing.placement.py): a part is PLACED only if all four hold — detector confidence ≥ place_min_confidence (0.5), box inside the page, aspect ratio within place_aspect_tolerance (±35 %) of the symbol body, and overlap with other detections ≤ place_max_overlap (0.3). Otherwise it is UNPLACED with a reason code: NEEDS_SYMBOL, LOW_CONFIDENCE, BBOX_OUT_OF_PAGE, BAD_ASPECT, OVERLAP or NO_BBOX. VLM-only components are always UNPLACED / NO_BBOX.Thresholds live in VisualIRSettings (database columns with the defaults above). There is no Settings control or API for them yet; change them in the database.
DETERMINISTIC_JUNCTION, LIKELY_JUNCTION, LIKELY_CROSSING, AMBIGUOUS); a wire that merely crosses at a node is not joined.GEOMETRY_CONFIRMED) and a loose graph (SUGGESTED).VisualDanglingEndpoint (NO_PIN_IN_TOLERANCE, AMBIGUOUS_PIN, OFF_PAGE) rather than dropped.UNPLACED tray with reason codes, assign-symbol, add-pin, net naming, accept/reject suggestions (also batch-accept of geometry-confirmed ones), manual pin-to-pin connections, re-bind or discard of orphaned decisions.reconcile_component_proximity 0.04, reconcile_pin_proximity 0.02); each endpoint of an orphaned connection is re-pointed independently, and an ORPHANED connection heals to APPROVED automatically when both ends match again. A .cmi snapshot/restore keeps the review state with the project.exporter/erc_lite.py): errors block the export — floating pins, single-pin nets, duplicate references, UNPLACED or NEEDS_SYMBOL parts, missing references, shorted rails. Only PLACED, symbol-resolved parts and APPROVED + ENGINEER_OVERRIDE connections are exported.exporter/export_job.py, kicad_builder.py): a scratch folder through KCAA; positions come from a page→mm scale derived from the symbol body, never from normalized values.connect_points_with_wire only if its polyline is orthogonal, on the 1.27 mm grid and on real pin coordinates; the netlist is re-extracted after each wire, and a wire that shorts is deleted and replaced by connect_pins_with_wire. Each connection is tagged ROUTING_REPRODUCED or ROUTING_FALLBACK.kicad-cli sch erc; only on success is the project schematic replaced (the previous file is kept as .bak) and the .net written by kicad-cli. The report (blockers, routing tags, topology diff, ERC) is stored on the job and shown in the inspector.Main API (all under /api/visual-ir):
| Endpoint | Purpose |
|---|---|
POST /upload, POST /jobs, POST /jobs/{id}/run | Create and run a job |
GET /jobs/{id}, /stages, /graph, /evidence, /ir | Job state, stage rows, the typed review graph |
POST /jobs/{id}/re-extract | Re-run with reconciliation |
POST /jobs/{id}/components/{cid}/place, /symbol, /pins | Place a part, assign a symbol, add a pin |
POST /jobs/{id}/suggestions/{sid}/accept, /reject; /connections/batch-accept-geometry, /connections/manual | Create engineer decisions |
POST /jobs/{id}/connections/{cid}/rebind, /disconnect; /nets/rename; /reset-decisions | Maintain decisions |
POST /jobs/{id}/export/check, /export/kicad; GET /jobs/{id}/export/report | ERC-lite preview, gated export (422 with the report when blocked or unverified), stored report |
MVP note: the Visual IR screen no longer shows an export panel. The review workflow ends at an approved netlist (pins joined by accepted connections, grouped into named nets), shown in the right-hand pane. The export endpoints above and their tests are unchanged and still available through the API; the UI control returns when KiCad export is part of the supported workflow.
+/− connections stay SUGGESTED until an engineer accepts them.resistor-adjustable, and over-counts ICs; the engineer's UNPLACED tray is the safety net. It runs at its training size (1024 px).501 rather than returning invented data, and /conflicts returns an empty list: conflict detection is not implemented.Visual retrieval is an optional part of the render stage. It is auxiliary evidence: nothing in connectivity depends on it.
ColQwenClient ──► ColQwenProvider ──► vLLM POST /pooling (task: token_embed, multi-vector)
│
└────────► VisualRetrievalService ──► Qdrant (multivector, MaxSim)services/colqwen_service.py); ColQwenClient adds a per-page timeout, a sync bridge and an honest result per page. There is no /embed or /retrieve./pooling request (messages with an image_url). The vLLM server must be started with --chat-template deploy/colqwen/chat_template.jinja (§13.5); the client sends no template of its own. Sending the image in multi_modal_data is silently ignored by the server and gives every page the same placeholder-only vectors (§11.4).max_model_len 1024); each page becomes about 694 vectors of 320 values.job_id. A job's old vectors are deleted immediately before it is indexed again.DISABLED, SUCCESS, PARTIAL, FAILED, TIMEOUT) is stored on the render stage row (model_versions["colqwen"]) and shown in the inspector.Setting (Settings page / AppSettings) | Default | Meaning |
|---|---|---|
colqwen_enabled | true | Master switch |
colqwen_index_pages | true | Index rendered pages during extraction |
colqwen_timeout_ms | 30000 | Per-page timeout |
colqwen_collection | (null) | Qdrant collection; null uses circuitmind_reference_visual |
Run on page2_multichannel_sch.pdf with the real YOLO weights, real KCAA and real kicad-cli, no engineer action (this first run used the detector at 640 px; at the trained size 1024 px it finds many more parts — §11.3 — and the real-service acceptance tests still pass):
UNPLACED with reason codes)After an engineer-style review of the suggestions: the export succeeded with 15 connections, the topology read-back matched exactly, KiCad ERC ran and passed (14 non-blocking pin_not_connected notices), the .net was produced, and one replayed wire that shorted two nets was caught and rolled back. Decisions survived a re-extract with 0 orphaned.
The final check — opening the generated .kicad_sch in the KiCad GUI and running ERC — is a manual step.
| Package | Scope | State |
|---|---|---|
| W0 | Foundation and honesty: Alembic baseline + migrations, no hardcoded toolchain paths, authority hygiene, hermetic tests | Done |
| W1 | Pipeline end to end: symbols, pins, pin geometry, auto-placement rule, geometry-proven suggestions, dangling-wire evidence | Done |
| W2 | Reconciliation on re-extract, wire/junction/dangling overlays, re-bind / rename / add-pin / assign-symbol controls | Done |
| W3 | ERC-lite export gate, wire-routing replay with tags, KiCad ERC and report stored on the job | Done |
| W4 | Acceptance tests and a run on the multichannel schematic against real KCAA + KiCad | Done (final KiCad GUI check is manual) |
| W5 | ColQwen: one implementation over /pooling, optional and observable page indexing, job-scoped cleanup, settings | Done (verified against the real server) |
Qdrant is intended to provide retrieval over engineering documentation.
Potential knowledge sources include:
Engineering Standards
│
Manufacturer Datasheets
│
Application Notes
│
Internal Design Guidelines
│
Test Specifications
│
PDFs
│
▼
Document Ingestion
│
▼
Chunking
│
▼
Embeddings
│
▼
Qdrant
│This pipeline is implemented and wired end-to-end: upload a PDF, it's chunked and embedded into Qdrant, and both semantic search and the AI Engineering Review draw on it. Knowledge documents are either Global (visible to every project — standards, generic datasheets) or Project-private (this project's own reference material); a global document can be applied to a project from the Knowledge view. See §7.2 for the Reference Library, which uses one chosen document for a targeted design comparison rather than open-ended retrieval.
CircuitMind can compare the live design against one specific reference document (a reference design, EVM guide, or similar), distinct from the open-ended "Engineering Audit" AI review (§7.1) that retrieves across the whole knowledge base rather than one chosen document.
Every document in the Knowledge view (datasheets, standards, app notes, reference designs) is one of:
A global document can be applied to the open project from the Knowledge view without duplicating the file or its Qdrant vectors; applying/un-applying just links/unlinks it.
Architecture > Reference Library lists the open project's linked documents of type Reference Design or EVM Design (its own private ones, plus any applied global ones). "Add Reference" uploads a new project-private Reference Design directly from there.
Selecting Run Comparison on a linked reference document runs a comprehensive evidence fusion pipeline:
Reference Graph containing discrete components, net aliases, functional blocks, and qualitative physical rules (e.g. proximity/thermal claims).Reference Graph into a highly-contextual knowledge payload scoped strictly to the chosen document.U1 to a live U3).REVIEW instead of guessing when evidence is missing or ambiguous.PASS/WARNING/REVIEW), a note, and exact provenance pointers (evidence_ids) tracing back to the reference source.Results persist per (project, document) pair and are cleared automatically if that document is later un-applied or deleted from a project.
A schematic PDF alone can be reviewed against a datasheet or a reference design from the Visual IR screen (Review against a reference or datasheet). GET /api/visual-ir/jobs/{id}/compare/documents lists the indexed reference/EVM designs, datasheets and application notes; POST /api/visual-ir/jobs/{id}/compare ({doc_id}) runs run_reference_comparison with the job's parts and engineer-approved nets as the design (job_design_facts, no layout, so physical rows are a layout checklist marked Not available (schematic only)); GET /api/visual-ir/jobs/{id}/comparison returns the stored Functional and Physical rows. Rows are stored under the key visual-ir-job:<id>, so the Architecture view of a project never shows them. The same tables component (components/ComparisonTable.tsx) is used in both places. LaTeX in datasheet text is shown as symbols (_plain).
A Visual IR job becomes a reference design with Use as reference design (Visual IR, under Netlist (approved); API POST /api/visual-ir/jobs/{id}/publish-reference). It writes a global Reference Design document, a ReferenceExtractionRun and its ReferenceComponent/ReferenceNet rows (components that have a reference designator, nets from engineer-approved connections only) and links the document to the open project. Parts without a designator are skipped (skipped_unnamed_components in the response). A job can be published more than once.
Run Comparison then adds deterministic rows from services/visual_ir/comparison/anchor_matcher.py to the LLM rows, all as Functional results:
IC2A pairs with the design's IC2), then by type and value. Values are compared by meaning (parse_value): 10k, 4k7, 100nF, 0.1uF and library part names such as R1206_10K_1%_0.25W_100PPM read as ohms or farads, so a plain value matches a library name; a real difference is reported with both values.PIN -> NET relationships, _net_connections). The two ends of a resistor, capacitor or inductor are interchangeable. A missing part makes the net WARNING ("cannot be checked completely"); pins split over several live nets are a REVIEW row naming where each pin is.The Architecture view shows one Functional and one Physical table per reference document, with a pass/warning/review summary.
Current state (2026-10-06): step 1 above names ColQwen3.5 as the extractor, but nothing in the backend calls ColQwen page search today. The comparison's visual evidence comes from stored
VisualEvidencerows of a Visual IR job (services/evidence_fusion.py), and the only production ColQwen call is the optional page indexing of §6.7, which is currently ineffective (§11.4).
CircuitMind contains a deterministic calculation layer intended to keep engineering calculations independent from LLM reasoning.
Implemented and wired into the design pipeline's component resolver (feeds Architecture > Generate Components, with passing integration test coverage):
Not yet implemented:
See §3 (System Readiness) for the current readiness percentage.
The intended architecture is:
Engineering Inputs
│
▼
Deterministic Calculation
│
├── Formula
├── Inputs
├── Assumptions
└── Units
│
▼
Result + EvidenceThe AI layer can explain the calculation, but the calculation itself should be reproducible.
The Requirements view (sidebar: Requirements) tracks the open project's engineering requirements and their verification status — the left-hand side of the Requirement → Implementation → Test → Status traceability chain described in §2's Engineering Truth Hierarchy.
A table of requirements, each with:
REQ-001, ...), unique within the project.Pass / Warning / Fail / Unknown, shown as an icon.Below the table: coverage percentage and verified/partial/unverified counts, computed live from the current requirement statuses.
Category column; click the × on the active category chip to clear it.Click Upload CSV and select a CSV with these exact headers:
ID,Category,Description,Target_Implementation,Test_Method,StatusID — required; rows with a blank ID are skipped. Uploading a CSV with an ID that already exists updates that requirement in place rather than duplicating it, so re-uploading a revised CSV is a safe way to update the set.Category — free text; use Electrical, Functional, EMC/EMI, Mechanical, or Environmental to match the sidebar's filter categories (anything else still works, it just won't have a matching sidebar shortcut).Status — one of Pass, Warning, Fail, Unknown (anything else is stored as Unknown). Leave it Unknown on upload — statuses are normally set by Run All Tests (§9.2) or the AI Engineering Audit, not hand-entered.A ready-to-use example is at samples/requirements.csv (see §9.3).
The Tests view (sidebar: Verification > Automated Tests) runs and tracks the test cases that verify requirements (§9.1) against the live design.
A table of test cases: ID, Type (Functional / Performance / Stress / ...), Title & Setup, Expected Outcome, and Status (Pending / Passed / Failed). When a test's title/setup/expected text names a net or a RefDes pin N that has a simulated DC operating-point voltage (from a live Ngspice run), that measured value is shown inline next to the expectation.
Click Upload Test CSV and select a CSV with these exact headers:
ID,Type,Title,Setup,Expected,StatusLike Requirements, ID is required and re-uploading an existing ID updates that test case in place. Status on upload should normally be Pending — it's set by running the tests, not hand-entered.
A ready-to-use example is at samples/testcases.csv (see §9.3).
Run All Tests evaluates every test case for the open project:
samples/testcases.csv's TC-105 demonstrates a genuinely deterministic, KiCad-grounded result rather than a simulated one.TC-001, TC-002) and specific setup text (+12V, IC1 pin 3) are special-cased to look up particular net names (+12V, vbias) from an older demo board — this only matters if a CSV happens to reuse those exact IDs or phrases; the sample files intentionally avoid them.Passed, and if its text names a net/pin with a simulated voltage, that value is shown in the result (actualResults) for reference. There is no physical bench in the loop — this reflects what the deterministic/simulated data supports, not a hardware test result.A single test can also be run individually via its row's Run action, which marks it Passed directly (used for manually-verified test cases rather than the automated DRC/Ngspice-driven evaluation Run All Tests performs).
samples/requirements.csv and samples/testcases.csv are a small, generic example set (12V input rail, 3.3V regulator, CAN bus, reverse-polarity protection, DRC/ERC cleanliness, thermal, mechanical fit) for exercising the Requirements (§9.1) and Automated Tests (§9.2) workflows end-to-end on any opened project — they aren't tied to any specific board's actual net names, so every requirement/test uploads and displays correctly regardless of which project is open, though the DRC-titled test case (TC-105) and any Ngspice-matched net voltages will only reflect real project data once the project has KiCad files and (optionally) Ngspice available.
To try them:
samples/requirements.csv.samples/testcases.csv.Run All Tests marks every requirement Pass directly (see routers/tests.py::run_all_tests) rather than deriving it per-requirement from its linked test's result.The Mechanical tab sits beside the design pipeline. It becomes available once the board is routed
(the session reached STAGE7_ROUTING, i.e. G3 passed) and has its own gate, GM. It never moves the
session status and never rewinds it: every result records the hashes it was made from (PCB, board
export, contract, enclosure spec, plan, clearance classes) and is shown stale when one changes.
One board frame. Every mechanical file uses millimetres with the origin at the lower-left corner of
the board outline, +X right, +Y up and Z = 0 at the PCB bottom. kicad-cli writes STEP and GLB straight
into it (--user-origin); FreeCAD's glTF (metres, Y up) is converted for the viewer.
Flow
mechanical_export): kicad-cli writes board.step, board.glb and a
render, and mechanical_manifest.json is built from the .kicad_pcb and the export (JSON Schema in
backend/app/services/mechanical/schemas/). kicad-cli exits 0 when models are missing, so its
warnings are counted: a part whose model is missing (or that has none) is UNVERIFIED, never PASS.
Part heights are measured from the GLB, whose nodes kicad-cli names by reference designator.enclosure (type, manufacturing method fdm / sla /
cnc, max_length / max_width / max_height, connector_faces such as J2:right, mounting)
become the contract's mechanical.enclosure block, inside the contract hash (editing one rewinds the
session past Gate 2 like any requirement). A preflight checks the board against it: outline in the
envelope, Z stack-up, mounting holes, connector faces, part-height limit, each with the measured value
and the limit.EnclosureSpec (two-piece rectangular case with a screwed lid: walls, clearances,
standoffs, lid lip, screws, cutout overrides; ranges per manufacturing method) becomes a deterministic
plan (enclosure_plan.py): standoffs at the mounting holes, lid bosses and through-screws, cutouts from
the contract's connector faces, the committed connector_table.json, or draft cutouts for unknown edge
connectors. The worker runs the versioned FreeCADCmd script (cad/enclosure_cad_v1.py), which builds
base, lid and screws, places the board STEP, saves enclosure.FCStd and exports STEP / STL / 3MF per
part and GLBs, then probes the solids to confirm the geometry matches the plan.distToShape per placed model, lid closure, solid validity), against the engineer's editable
clearance classes. Failing parts are red in the 3D view, unverified ones amber.mechanical_gate; NOT_REQUIRED otherwise).mechanical/ (FCStd, STEP,
STL / 3MF, manifest, render, spec, plan, fit report, hashes.json); an existing candidate is amended
on approval. The release history flags packages whose board, contract or enclosure changed. PCB-side
suggestions (missing models, a connector recessed behind the wall, the part that sets the case height,
parts in the standoff keep-out) are findings only.AI drafts. "Describe the enclosure" turns one sentence into a draft spec with design decisions via
LLMGateway.generate_structured; "Ask AI to fix" proposes a spec for the failing checks. Drafts are
validated against the method ranges, keep their origin (llm, engineer, llm+engineer) and only take
effect when the engineer accepts them; the AI can never change the checks or the clearance classes.
Tools. kicad-cli (KiCad 10) and FreeCAD 1.1 FreeCADCmd, both installed by the engineer and found by
locators (FREECAD_CMD, FREECAD_PATH or PATH for FreeCAD; §13.3.1). Run the worker
(python -m app.worker, or CIRCUITMIND_SIMULATION_WORKER=inprocess) for exports and CAD jobs.
Status per milestone: docs/enclosure/STATUS.md.
KCAA is intended to provide a controlled execution mechanism between AI-generated engineering actions and the local development environment.
The long-term execution model is:
AI
│
▼
Proposed Action
│
▼
Validation
│
▼
Permission / Safety Gate
│
▼
KCAA
│
▼
Controlled Tool Execution
│
▼
Result
│
▼
VerificationThe AI should not receive unrestricted filesystem or shell access merely because it is capable of generating commands.
This part records why each component of the extraction pipeline was chosen, and how well it actually works. Every number below was produced by a script in benchmarks/ and its raw output is committed in benchmarks/results/. Measurements were taken on 2026-10-06.
Read this first. The benchmarks are small, single-design measurements and they found real problems, not only confirmations. Two were fixed on 2026-10-06 and are kept in the record: ColQwen page indexing embedded only placeholder tokens (§11.4), and the detector ran at 640 px although it was trained at 1024 (§11.3). §11.6 lists what was decided and what is still open.
| Item | Value |
|---|---|
| Machine | Windows 11, NVIDIA RTX 5070 (12 GB), Python 3.12.10 |
| Libraries | PyMuPDF 1.28.2 (the version the backend pins), pymupdf4llm 1.28.2, Tesseract 5.5.0, Ultralytics 8.4.171 / torch 2.7.0+cu128, scikit-learn TF-IDF |
| Models | YOLO26m best.pt; ColQwen athrael-soju/colqwen3.5-4.5B-v3 on vLLM 0.30.0 (--max-model-len 1024 --gpu-memory-utilization 0.85 --enforce-eager --limit-mm-per-prompt '{"image":1,"video":0}' --chat-template deploy/colqwen/chat_template.jinja); nomic-embed-text; VLM qwen3.5:9b-Q4_K_M on Ollama |
| Ground truth | Read from source files, never from the extractor under test (the KiCad .kicad_sch, the dataset's label files, the datasheet's known page numbers) |
| Dataset | What it is | Size |
|---|---|---|
| D1 — TL07x datasheet | TL072CD.pdf (TI), 40 lookup queries with ground-truth pages (pinouts, tables, graphs, schematics, package drawings) | 89 pages, 40 queries |
| D2 — KiCad schematic | board/test1 multichannel mixer: ground truth = every visible reference designator and field value in the KiCad source, resolved through the sheet hierarchy. Two renderings: a fresh kicad-cli PDF export (text layer present) and the project fixture multichannel_sch.pdf (text outlined, no text layer) | 5 pages, 114 designators, 69 values |
| D3 — CGHD test split | Roboflow cghd-zenodo-11-all v4, the detector's training distribution (hand-drawn circuits), held-out test split | 252 images, 20,172 instances, 56 classes present |
| D4 — Schematic page retrieval | The 5-page fixture multichannel_sch.pdf; 14 queries generated from the KiCad source (4 page descriptions, 10 designator queries) with ground-truth pages | 5 pages, 14 queries |
Ground rules: sample sizes are shown next to the numbers; negative and disappointing results are kept; what was not measured is listed in §11.8.
Question. Where does the text of a schematic page come from, and what is it worth?
Schematic pages (D2) — recall of visible reference designators and values, 5 pages, 300 DPI for OCR:
| Source PDF | Extractor | Designators (of 114) | Values (of 69) | s / page |
|---|---|---|---|---|
kicad-cli export (text layer) | PyMuPDF text layer | 114 (100 %) | 69 (100 %) | 0.007 |
PyMuPDF OCR (get_textpage_ocr, what the pipeline uses) | 72 (63 %) | 42 (61 %) | 0.81 | |
| Tesseract, psm 11 (sparse text) | 81 (71 %) | 65 (94 %) | 1.12 | |
| Tesseract, psm 6 (block) | 68 (60 %) | 38 (55 %) | 1.18 | |
| pymupdf4llm | 0 (0 %) | 0 (0 %) | 0.51 | |
Fixture multichannel_sch.pdf (text outlined) | PyMuPDF text layer | 0 (0 %) | 0 (0 %) | 0.003 |
| PyMuPDF OCR (pipeline) | 72 (63 %) | 49 (71 %) | 0.58 | |
| Tesseract, psm 11 | 92 (81 %) | 41 (59 %) | 0.96 | |
| Tesseract, psm 6 | 77 (68 %) | 40 (58 %) | 0.89 | |
| pymupdf4llm | 3 (3 %) | 4 (6 %) | 0.99 |
Notes: multi-unit parts are plotted as IC2A, IC2B; IC2A counts as IC2. The fixture was exported earlier than the current source, so its recall is measured against the current source's designators (page i ↔ sheet i) and is indicative only.
Datasheet pages (D1) — extractor cost, and the downstream task that matters: lexical page retrieval (TF-IDF) over each extractor's text with the 40 queries:
| Extractor | s / page | Total (89 pp) | Empty pages | Tables found | R@1 | R@5 | R@10 | MRR |
|---|---|---|---|---|---|---|---|---|
PyMuPDF get_text | 0.008 | 0.7 s | 7 | — | 67.5 | 92.5 | 95.0 | 0.777 |
| pymupdf4llm (Markdown) | 0.87 | 77 s | 0 | 37 | 65.0 | 92.5 | 95.0 | 0.771 |
| Tesseract 200 DPI (psm 3) | 1.17 | 104 s | 0 | — | 57.5 | 92.5 | 95.0 | 0.718 |
What this shows
analyze routes by content type — §6.2).Model. best.pt: Ultralytics YOLO26m (21.9 M parameters, 42 MB), 57 classes, trained 150 epochs at 1024 px on the Roboflow CGHD export (1,767 train / 493 validation images). The weights embed the training-time validation metrics (best epoch: P 0.946, R 0.808, mAP50 0.833, mAP50-95 0.721).
Held-out test split (D3) — 252 images never used for training or model selection, imgsz 1024:
| Precision | Recall | mAP50 | mAP50-95 | Inference | Post-process | Peak VRAM |
|---|---|---|---|---|---|---|
| 0.956 | 0.797 | 0.828 | 0.708 | 14.5 ms | 3.9 ms | 978 MB |
Per-class AP50 on the classes a schematic is built from: integrated_circuit 0.93, diode 0.91, inductor 0.91, transistor-bjt 0.90, capacitor-unpolarized 0.88, terminal 0.87, resistor 0.87, text 0.86, voltage-dc 0.84, junction 0.82, gnd 0.81, operational_amplifier 0.80, capacitor-polarized 0.77, crossover 0.73. Weak classes: xor 0.44, circuit-drawing 0.38, and mechanical, microphone, unknown at 0.00 (their instance counts were not examined).
The domain gap — KiCad-rendered schematics. D3 is hand-drawn circuits; CircuitMind feeds the detector KiCad renders. The only check available without labelled KiCad images is a count comparison against the KiCad source (5 pages; expected 47 R, 16 RV, 40 C, 5 IC; a count check, not a localisation-aware metric, so it can over-credit):
| Input | imgsz | Components found (matched) | Count recall | Count precision |
|---|---|---|---|---|
kicad-cli export | 640 (the old pipeline default) | 40 of 108 | 37 % | 85 % |
| 1024 (training size; now used) | 92 of 108 | 85 % | 81 % | |
Fixture multichannel_sch.pdf | 640 | 36 of 108 | 33 % | 90 % |
| 1024 | 92 of 108 | 85 % | 77 % |
What this shows
YoloClient now runs at the size recorded in the checkpoint (train_args.imgsz, overridable). On the fixture page page2_multichannel_sch.pdf (confidence 0.5) this changes component-like detections from 5 to 26 and total detections from 55 to 152; the real-service acceptance run (§6.8) still passes.RV) are never detected as resistor-adjustable (0 of 16 at either size) and ICs are over-counted (14–15 found for 5). Both likely reflect the hand-drawn training set; the highest-value upgrade is training data rendered from KiCad (§3.3). Until then, the review UI's UNPLACED tray and the placement thresholds (§6.3) are the safeguard — the detector's output is never trusted for connectivity.Question. Does visual retrieval (ColQwen) find the right page better than text retrieval?
The first measurement (D1, the 89-page datasheet, 40 queries) gave ColQwen R@1 2.5 / R@10 35.0 — and the reason was not the model:
3 × 320 matrix). Re-indexing at another resolution gave the exact same metrics.multi_modal_data, a field vLLM's /pooling completion request does not accept, so the server embedded only the three placeholder tokens <|vision_start|><|image_pad|><|vision_end|> and ignored the image. The project's earlier benchmark (2026-10-02; fused R@1 22.5) shows the same constant ranking: it measured this defect, not the model.messages with an image_url). This vLLM 0.30.0 deployment cannot resolve the model's own chat template (unhashable type: 'dict', also logged as "Chat template warmup failed" at every start), so the server is started with a plain template file, --chat-template deploy/colqwen/chat_template.jinja (§13.5). After the fix a page embeds to 694 tokens × 320 and different pages get different vectors.test_the_embedding_depends_on_the_page_image (real-server, marked colqwen).Historical result of the broken run (kept in benchmarks/results/retrieval_before_fix_placeholder_embeddings.json): ColQwen alone R@1 2.5 / R@5 20.0 / R@10 35.0 / MRR 0.115; RRF fusion with it cut text retrieval's R@1 from 47.5 to 22.5, which shows that rank fusion with an unvalidated retriever is dangerous.
The datasheet was not re-run (the 89-page benchmark is slow and the page-level question is better asked of schematics). Each system ranks the 5 pages for each query; chance for R@1 is 28.6 %.
| System | R@1 | R@3 | MRR | Notes |
|---|---|---|---|---|
| Text: Tesseract OCR of the fixture + TF-IDF | 64.3 | 85.7 | 0.770 | What the pipeline has for this PDF (no text layer) |
Text: PDF text layer of a fresh kicad-cli export + TF-IDF | 71.4 | 85.7 | 0.806 | Upper bound for text; the fixture itself has no text layer |
| ColQwen (production client, ≤ 0.7 MP pages) | 92.9 | 100.0 | 0.964 | 4.5 s per query, 7.8 s per page to index |
| Query type (n) | OCR text R@1 | Text-layer R@1 | ColQwen R@1 |
|---|---|---|---|
| Page descriptions (4), e.g. "power input connector and audio output jack" | 75.0 | 100.0 | 100.0 |
| Designators (10), e.g. "R19 and RV6", "op-amp IC4" | 60.0 | 60.0 | 90.0 |
All 14 queries produced 14 different ColQwen rankings, and the 5 pages have 5 distinct embeddings.
What this shows
Question. Can a VLM look at a candidate page and tell whether it answers the query? Setup: for each query, the text retriever's top-5 pages (200 query–page pairs) are shown to qwen3.5:9b-Q4_K_M as images with a structured-JSON prompt (CONFIRMED / REJECTED / UNCERTAIN); pages are then re-ranked by the VLM's confidence. Same prompt and ordering rule as the original benchmark; temperature 0.
| Reasoning | Verdict precision | recall | false acceptance | false rejection | uncertain | Latency / verdict |
|---|---|---|---|---|---|---|
Off (think: false) | 43.0 % | 97.6 % | 26.5 % | 0.5 % | 0 % | 5.3 s (2 requests in flight) |
| Ranking | R@1 | R@5 | R@10 | MRR |
|---|---|---|---|---|
| Text (dense; MRR over the top 10) | 47.5 | 82.5 | 92.5 | 0.624 |
| Text → VLM re-rank, reasoning off | 65.0 | 82.5 | 92.5 | 0.735 |
What this shows
| Decision | Evidence | Status |
|---|---|---|
| Read the PDF text layer first; use OCR only when there is none | 100 %/100 % recall at 0.007 s/page vs 71–81 %/71–94 % at 0.6–1.2 s/page (§11.2) | In the pipeline |
| Treat OCR'd designators and values as evidence; block unnamed parts until an engineer names them | Best OCR per page type recovers ≤ 81 % of designators and ≤ 94 % of values; no setting wins on both (§11.2) | In the pipeline |
| Do not use pymupdf4llm for schematic pages | 0 of 114 designators recovered; keeps only the title block (§11.2) | Decided; revisit only for datasheet-table ingestion |
| PyMuPDF for text, vector drawings and the OCR wrapper | Best retrieval (R@1 67.5, MRR 0.777) at the lowest cost (§11.2) | In the pipeline |
| YOLO26m for component boxes, never for connectivity | mAP50 0.828 / P 0.956 / 14.5 ms; but 85 % count recall at best on KiCad renders, no potentiometer class (§11.3) | In the pipeline, now at the trained size (1024 px, fixed 2026-10-06) |
Engineer-gated review with UNPLACED reason codes | Detector domain gap and OCR limits above | In the pipeline |
| ColQwen as page-level visual evidence, not in text ranking | After the fix: R@1 92.9 vs 64.3 (OCR text) on 14 schematic queries; before it, a constant ranking and RRF cut R@1 47.5 → 22.5 (§11.4) | Fixed 2026-10-06 (chat request + server-side --chat-template); validate on a larger set before any fusion |
| VLM as re-ranker/evidence, never a gate | +17.5 pts R@1 but 43 % verdict precision (§11.5) | In the pipeline |
| Lexical (TF-IDF) retrieval deserves a place next to dense retrieval for datasheet lookups | 67.5 vs 47.5 R@1 on the same queries (§11.2) | Not adopted — a measured opportunity (hybrid retrieval was not tested) |
# CPU only (needs PyMuPDF, pymupdf4llm, pytesseract + Tesseract, scikit-learn; KICAD_CLI_PATH or kicad-cli on PATH)
python benchmarks/bench_text_extraction.py
# needs the vLLM ColQwen server (started with --chat-template deploy/colqwen/chat_template.jinja) and Qdrant
COLQWEN_API_BASE_URL=http://localhost:8101 QDRANT_URL=http://localhost:6333 python benchmarks/bench_retrieval_schematic.py
# the 89-page datasheet version additionally needs Ollama (nomic-embed-text) and is slow
COLQWEN_API_BASE_URL=http://localhost:8101 QDRANT_URL=http://localhost:6333 python benchmarks/bench_retrieval.py
# stop the ColQwen container first (it holds ~10.7 of 12 GB): the VLM and YOLO need the GPU
python benchmarks/bench_vlm.py --think off # `--think default` (reasoning on) takes hours
YOLO_DATASET_DIR=<path to the CGHD export> python benchmarks/bench_yolo.pyInputs that are not in the repository and must be supplied locally: TL072CD.pdf (the TI datasheet), board/test1 (the KiCad source used as ground truth), the CGHD detector dataset (YOLO_DATASET_DIR) and the weights best.pt. Results land in benchmarks/results/*.json. retrieval_before_fix_placeholder_embeddings.json is the datasheet run that exposed the ColQwen defect (§11.4) and is kept as evidence; retrieval_schematic.json is the measurement after the fix.
vlm stage), wire/junction extraction accuracy against the source, and end-to-end connectivity beyond the single acceptance run in §6.8.| Component | Requirement |
|---|---|
| OS | Windows 10/11 64-bit, or Ubuntu 24.04 LTS 64-bit |
| KiCad | KiCad 10.x |
| Python | Python 3.12+ 64-bit |
| Node.js | Node.js 20.x or 22.x |
| npm | Included with Node.js |
| PostgreSQL | PostgreSQL 15+ |
| Qdrant | Qdrant server |
| KCAA | KCAA executable / MCP bridge — installed via the kicad_ai_assistant KiCad plugin, see §13.4 |
Sections 13–14 below give setup steps for both Windows and Ubuntu 24.04, including KCAA (§13.4), which is installed as a KiCad plugin and runs on both platforms.
Recommended service ports:
| Service | Port |
|---|---|
| CircuitMind Backend | 8000 |
| PostgreSQL | 5432 |
| Qdrant HTTP | 6333 |
| Qdrant gRPC | 6334 |
CircuitMind uses PostgreSQL for the canonical EDM graph and application state.
A PostgreSQL instance may be provided by:
If Docker is permitted:
docker run -d `
--name circuitmind-postgres `
-e POSTGRES_USER=circuitmind `
-e POSTGRES_PASSWORD=circuitmind `
-e POSTGRES_DB=circuitmind `
-p 5432:5432 `
postgres:16-alpinepsql -U circuitmind -h localhost -d circuitmindIf psql is not available in PATH, use the PostgreSQL installation directory or configure PostgreSQL client tools separately.
sudo apt update
sudo apt install -y postgresql postgresql-contrib
sudo systemctl enable --now postgresql
sudo -u postgres psql -c "CREATE USER circuitmind WITH PASSWORD 'circuitmind';"
sudo -u postgres psql -c "CREATE DATABASE circuitmind OWNER circuitmind;"sudo docker run -d \
--name circuitmind-postgres \
-e POSTGRES_USER=circuitmind \
-e POSTGRES_PASSWORD=circuitmind \
-e POSTGRES_DB=circuitmind \
-p 5432:5432 \
postgres:16-alpinepsql -U circuitmind -h localhost -d circuitmindQdrant is used for engineering knowledge retrieval, including:
CircuitMind communicates with Qdrant through HTTP on port 6333.
Download the appropriate Qdrant Windows release and extract the executable.
.\qdrant.exedocker run -d `
--name circuitmind-qdrant `
-p 6333:6333 `
-p 6334:6334 `
qdrant/qdrantOn company-managed machines where Docker is not approved, use the standalone Qdrant binary or an approved remote Qdrant instance.
Download the appropriate Qdrant Linux release (qdrant-x86_64-unknown-linux-gnu.tar.gz or similar) and extract it:
tar -xzf qdrant-x86_64-unknown-linux-gnu.tar.gz
./qdrantsudo docker run -d \
--name circuitmind-qdrant \
-p 6333:6333 \
-p 6334:6334 \
qdrant/qdrantQdrant should then be available at:
http://localhost:6333The circuitmind_knowledge collection's vector size must match whatever embedding model EMBEDDING_MODEL (§14.2) is configured to use — different models produce different-sized vectors:
| Embedding model | Dimension |
|---|---|
nomic-embed-text | 768 |
qwen3-embedding | 1024 |
CircuitMind handles this automatically: ensure_qdrant_collection (backend/app/services/vector_service.py) probes the configured model's actual output dimension on first use and recreates the collection if it doesn't match what's already there — so switching EMBEDDING_MODEL and restarting the backend is normally enough. Recreating the collection re-embeds nothing automatically, though, so existing Knowledge documents (§7.1) will need to be re-uploaded (or re-indexed) after switching models, since their old vectors are gone with the old collection.
To inspect or manually (re)create the collection instead of waiting for CircuitMind to do it on next use:
Check the current collection's vector size:
curl http://localhost:6333/collections/circuitmind_knowledgeManually create/reset it for a specific model (example: switching to nomic-embed-text, 768 dimensions). If the collection already exists with a different size, delete it first — this is the same delete-then-create sequence ensure_qdrant_collection runs automatically on a dimension mismatch:
Linux / macOS:
curl -X DELETE http://localhost:6333/collections/circuitmind_knowledge
curl -X PUT http://localhost:6333/collections/circuitmind_knowledge \
-H "Content-Type: application/json" \
-d '{"vectors":{"size":768,"distance":"Cosine"}}'Windows (Command Prompt):
curl -X PUT http://localhost:6333/collections/circuitmind_knowledge ^
-H "Content-Type: application/json" ^
-d "{\"vectors\":{\"size\":768,\"distance\":\"Cosine\"}}"Windows (PowerShell):
curl.exe -X PUT http://localhost:6333/collections/circuitmind_knowledge `
-H "Content-Type: application/json" `
-d '{\"vectors\":{\"size\":768,\"distance\":\"Cosine\"}}'This deletes any existing vectors in the collection (not the KnowledgeDocument rows in Postgres, which just become un-indexed) — only do this deliberately, and plan to re-upload documents afterward.
CircuitMind expects KiCad 10.x to be installed.
& "C:\Program Files\KiCad\10.0\bin\kicad-cli.exe" --versionIf KiCad is installed elsewhere, configure the path through KICAD_CLI_PATH / KICAD_APP_PATH (§14.2).
KiCad 10.x is not yet in Ubuntu 24.04's default repositories at the time of writing — use the official KiCad PPA:
sudo add-apt-repository ppa:kicad/kicad-10.0-releases
sudo apt update
sudo apt install -y kicad
kicad-cli --versionExpected output should identify a KiCad 10.x installation on either platform.
The Mechanical stage builds enclosures with FreeCAD's headless FreeCADCmd (FreeCAD 1.1). Install
FreeCAD and point CircuitMind at it with FREECAD_PATH (the FreeCAD folder; its bin/ holds
freecadcmd) or FREECAD_CMD (the executable itself); otherwise freecadcmd must be on PATH.
CIRCUITMIND_CAD_TIMEOUT_S (default 600) limits one CAD job. Without FreeCAD the board export still works
and CAD jobs end with a clear "FreeCAD is unavailable" error.
KCAA (paul356/kcaa) provides the controlled execution bridge used by CircuitMind's repair sandbox (§10). It ships as a KiCad plugin (kicad_ai_assistant) whose setup script creates a Python virtual environment and installs the kcaa executable into it — it is not a single downloadable binary.
Only needed to build the plugin from source or contribute. Skip this if installing the pre-built plugin from the Releases page.
git clone https://github.com/paul356/kcaa.git
cd kcaaDownload kicad_ai_assistant.zip from the Releases page.
Ubuntu:
KICAD_PLUGIN_DIR=~/.local/share/kicad/10.0/scripting/plugins
mkdir -p "$KICAD_PLUGIN_DIR"
unzip kicad_ai_assistant.zip -d "$KICAD_PLUGIN_DIR"Windows (PowerShell):
$KICAD_PLUGIN_DIR = "$env:USERPROFILE\Documents\KiCad\10.0\scripting\plugins"
New-Item -ItemType Directory -Force -Path $KICAD_PLUGIN_DIR
Expand-Archive -Path kicad_ai_assistant.zip -DestinationPath $KICAD_PLUGIN_DIROr build from source (from the cloned kcaa repository root):
make dist-plugin # produces dist/kicad_ai_assistant.zip
# Ubuntu:
KICAD_PLUGIN_DIR=~/.local/share/kicad/10.0/scripting/plugins
mkdir -p "$KICAD_PLUGIN_DIR"
unzip dist/kicad_ai_assistant.zip -d "$KICAD_PLUGIN_DIR"kcaa executable)Run the setup script from inside the installed plugin directory — it creates a .venv, installs kcaa from PyPI into it, and downloads the Freerouting JAR. uv installs the required Python version automatically. KiCad already provides the environment variables the script needs (KICAD_VERSION, KICAD*_DIR, etc.), so no .env file is needed for this step.
Ubuntu:
cd ~/.local/share/kicad/10.0/scripting/plugins/kicad_ai_assistant
./setup_plugin.shWindows (PowerShell):
cd "$env:USERPROFILE\Documents\KiCad\10.0\scripting\plugins\kicad_ai_assistant"
.\setup_plugin.ps1Windows (Command Prompt):
cd /d "%USERPROFILE%\Documents\KiCad\10.0\scripting\plugins\kicad_ai_assistant"
setup_plugin.batThe script installs kcaa inside the plugin's .venv. Set KCAA_PATH (§14.2) accordingly:
~/.local/share/kicad/10.0/scripting/plugins/kicad_ai_assistant/.venv/bin/kcaa%USERPROFILE%\Documents\KiCad\10.0\scripting\plugins\kicad_ai_assistant\.venv\Scripts\kcaa.exeVerify it runs:
# Ubuntu
~/.local/share/kicad/10.0/scripting/plugins/kicad_ai_assistant/.venv/bin/kcaa --help# Windows
& "$env:USERPROFILE\Documents\KiCad\10.0\scripting\plugins\kicad_ai_assistant\.venv\Scripts\kcaa.exe" --helpThe actual location is configurable either way — CircuitMind should not assume every developer or deployment machine uses the same username, KiCad version, or filesystem layout.
CircuitMind uses athrael-soju/colqwen3.5-4.5B-v3 for visual page retrieval (Reference Library, Visual IR page indexing — §6.7). ColQwen is a multi-vector late-interaction model, not a chat model and not a single-vector embedder: it acts as a dedicated visual-evidence retrieval engine, and it is evidence only (measured quality in §11.4).
Run it with Docker and vLLM, which serves ColQwen3.5 natively and exposes the multi-vector POST /pooling endpoint (task token_embed). A vLLM server has no /embed and no /retrieve: a 404 on those paths in the server log means a client is calling the wrong API.
(Windows: install WSL2 with Ubuntu. Docker Desktop with the WSL2 backend supports GPU pass-through natively.)
# 1. Install Docker and NVIDIA Container Toolkit (if not already installed)
sudo apt update
sudo apt install -y docker.io nvidia-container-toolkit
sudo systemctl restart docker
# 2. Start vLLM serving the ColQwen model on host port 8101
sudo docker run --gpus all \
--name circuitmind-colqwen \
-v ~/.cache/huggingface:/root/.cache/huggingface \
-v ~/.cache/vllm:/root/.cache/vllm \
-v "$PWD/deploy/colqwen:/templates:ro" \
-p 8101:8000 \
--ipc=host \
vllm/vllm-openai:latest \
--model athrael-soju/colqwen3.5-4.5B-v3 \
--max-model-len 1024 \
--gpu-memory-utilization 0.85 \
--enforce-eager \
--limit-mm-per-prompt '{"image":1,"video":0}' \
--chat-template /templates/chat_template.jinja--chat-template is required. Page images are sent as chat requests. This vLLM version cannot resolve the model's own (dict-valued) chat template: every start logs Chat template warmup failed ... unhashable type: 'dict' and an image request fails with 400 unhashable type: 'dict'. deploy/colqwen/chat_template.jinja is a plain Qwen turn template; mounting it and passing --chat-template fixes both. The client sends no template of its own (and reports this hint if the server rejects a page request).--limit-mm-per-prompt '{"image":1,"video":0}' stops vLLM from profiling video input (ColQwen only embeds images), which cuts the peak by more than a gigabyte: 0.85 then leaves 1 GiB for the KV cache, whereas 0.90 without it needed about 10.8 GiB free and failed whenever another program held more than ~1.2 GiB (Free memory ... is less than desired GPU memory utilization). Close other GPU users first (Ollama models, a running YOLO job). On a 12 GB card the engine once died with CUDA error: unknown error during a request; restart the container if that happens.--enforce-eager skips torch.compile and CUDA-graph capture, so the container is ready in about 3 minutes instead of 6–15; a ColQwen embedding is not latency-critical, so the eager-mode inference cost is irrelevant here. Wait for /v1/models to answer before running anything against it.Docker Desktop keeps its WSL data disk (31 GB with the vLLM image) on C: by default, and a bind-mounted Hugging Face cache can be large (the model repository holds three 9 GB copies of the weights; only the root model.safetensors is needed).
wsl --shutdown, move %LOCALAPPDATA%\Docker\wsl to the target drive (for example D:\docker\wsl), unregister the stale distro with wsl --unregister docker-desktop, and set "CustomWslDistroDir": "D:\\docker\\wsl" in %APPDATA%\Docker\settings-store.json; then start Docker Desktop. (A directory junction does not work: WSL refuses disks behind a mount point.) Alternatively use Settings → Resources → Advanced → Disk image location.$env:HF_HUB_DISABLE_SYMLINKS_WARNING = "1"
python -c "from huggingface_hub import snapshot_download as d; d('athrael-soju/colqwen3.5-4.5B-v3', cache_dir=r'D:\docker\huggingface\hub', ignore_patterns=['v1/*','v2/*','results/*'])"
docker run -d --gpus all --name circuitmind-colqwen -e HF_HUB_OFFLINE=1 `
-v "D:\docker\huggingface:/root/.cache/huggingface" -v "D:\docker\vllm-cache:/root/.cache/vllm" `
-v "$PWD\deploy\colqwen:/templates:ro" `
-p 8101:8000 --ipc=host vllm/vllm-openai:latest `
--model athrael-soju/colqwen3.5-4.5B-v3 --max-model-len 1024 --gpu-memory-utilization 0.85 --enforce-eager `
--limit-mm-per-prompt '{\"image\":1,\"video\":0}' --chat-template /templates/chat_template.jinjaOn Windows the Hugging Face cache stores symlinks, which a Linux container cannot follow through a bind mount; replace the links in ...\snapshots\<rev> with hard links to the files in ...\blobs (same drive, no extra space) before starting the container.
CERTIFICATE_VERIFY_FAILED. Downloading on the host (above) avoids this; or export the interception root certificate and point REQUESTS_CA_BUNDLE / SSL_CERT_FILE at a bundle that includes it.Do not overwrite your existing VLM_API_BASE_URL (reasoning engine). Configure the dedicated ColQwen endpoint in backend/.env:
# Existing reasoning / VLM endpoint
VLM_API_BASE_URL=http://localhost:xxxx/v1
VLM_MODEL=<your-current-reasoning-model>
# Dedicated visual retrieval endpoint (vLLM serves multi-vector embeddings on /pooling)
COLQWEN_API_BASE_URL=http://localhost:8101
COLQWEN_MODEL=athrael-soju/colqwen3.5-4.5B-v3CircuitMind configures the Qdrant visual index for 320-dimensional multi-vector embeddings (MaxSim late interaction). Visual IR indexing is switched from the Settings page (colqwen_enabled, colqwen_index_pages, colqwen_timeout_ms, colqwen_collection — §6.7).
curl http://localhost:8101/v1/models
COLQWEN_API_BASE_URL=http://localhost:8101 QDRANT_URL=http://localhost:6333 \
pytest backend/tests/integration/test_colqwen_real.pyThe two tests embed real pages through /pooling, check that different pages get different embeddings, index one into a throwaway Qdrant collection, retrieve it, delete it by job, and drop the collection.
Open PowerShell and navigate to the backend:
cd backend
python -m venv .venv
.\.venv\Scripts\Activate.ps1Install dependencies:
pip install -r requirements.txtcd backend
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtIf python3-venv or build tools (for psycopg2-binary) are missing:
sudo apt install -y python3-venv python3-dev build-essential libpq-devIf the repository has no requirements.txt yet, install the minimum set directly on either platform:
pip install fastapi uvicorn sqlmodel psycopg2-binary httpx qdrant-client pydanticCreate backend/.env. The variable names are the same on both platforms — only the path syntax differs.
# PostgreSQL
DATABASE_URL=postgresql://circuitmind:circuitmind@localhost:5432/circuitmind
# Qdrant
QDRANT_URL=http://localhost:6333
# KiCad
KICAD_APP_PATH=C:\Program Files\KiCad\10.0
KICAD_CLI_PATH=C:\Program Files\KiCad\10.0\bin\kicad-cli.exe
# KCAA (installed via the kicad_ai_assistant plugin, see §13.4)
KCAA_PATH=C:\Users\<USERNAME>\Documents\KiCad\10.0\scripting\plugins\kicad_ai_assistant\.venv\Scripts\kcaa.exe
# Ngspice
NGSPICE_PATH=C:\Users\<USERNAME>\ngspice\ngspice_fftw.exe
# AI Gateway (OpenAI-compatible — Ollama, vLLM, OpenAI, ...)
AI_PROVIDER=ollama
AI_BASE_URL=http://localhost:11434/v1
REASONING_MODEL=gemma4:26b
EMBEDDING_MODEL=nomic-embed-text# PostgreSQL
DATABASE_URL=postgresql://circuitmind:circuitmind@localhost:5432/circuitmind
# Qdrant
QDRANT_URL=http://localhost:6333
# KiCad
KICAD_APP_PATH=/usr/share/kicad
KICAD_CLI_PATH=/usr/bin/kicad-cli
# KCAA (installed via the kicad_ai_assistant plugin, see §13.4)
KCAA_PATH=/home/<USERNAME>/.local/share/kicad/10.0/scripting/plugins/kicad_ai_assistant/.venv/bin/kcaa
# Ngspice
NGSPICE_PATH=/usr/bin/ngspice
# AI Gateway (OpenAI-compatible — Ollama, vLLM, OpenAI, ...)
AI_PROVIDER=ollama
AI_BASE_URL=http://localhost:11434/v1
REASONING_MODEL=gemma4:26b
EMBEDDING_MODEL=nomic-embed-textThese match the variables read by backend/app/core/config.py — if a variable is omitted, that file's built-in default is used instead (and today defaults to a Windows-shaped path), so set every path explicitly on Ubuntu rather than relying on defaults.
Do not commit machine-specific .env files containing:
Add .env to .gitignore if it is not already ignored.
Schema changes are managed by Alembic (backend/alembic, run from backend/):
alembic upgrade head # apply
alembic current # show the revision (head: a6c8e0f2b4d5)pg_dump before upgrading a database you care about.CIRCUITMIND_DEV_SCHEMA=1 re-enables the old create_all schema sync for throwaway SQLite databases (the hermetic tests use this).From the backend directory (same command on Windows PowerShell and Ubuntu's bash):
uvicorn app.main:app --host 127.0.0.1 --port 8000 --reloadThe API should then be available at:
http://127.0.0.1:8000FastAPI documentation is typically available at:
http://127.0.0.1:8000/docsRemote access (§19):
127.0.0.1only accepts connections from the same machine. To reach CircuitMind from another machine on the network, bind to0.0.0.0instead —uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload— and set the frontend'sVITE_API_BASE_URL(§15.2) to this machine's LAN address, notlocalhost. There is no authentication in front of the API yet (see §19) — only do this on a trusted network.
Open a second PowerShell terminal:
cd frontendInstall dependencies:
npm installCreate frontend/.env.
Local-only example:
VITE_API_BASE_URL=http://localhost:8000/apiRemote-access example (backend bound to 0.0.0.0, see §14.4) — use the server's actual LAN IP or hostname, never localhost, since a remote browser's localhost refers to itself, not the server:
VITE_API_BASE_URL=http://192.168.1.50:8000/apiRun:
npm run devVite will provide a local development URL, normally similar to:
http://localhost:5173For remote access (§19), expose Vite on the network too:
npm run dev -- --host 0.0.0.0Open the displayed URL in a browser (substitute the server's LAN IP for localhost when connecting from another machine).
For an experienced developer, the minimum workflow is:
# 1. Start PostgreSQL
# 2. Start Qdrant
# 3. Verify KiCad 10 CLI
& "C:\Program Files\KiCad\10.0\bin\kicad-cli.exe" --version
# 4. Backend
cd backend
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
uvicorn app.main:app --host 127.0.0.1 --port 8000 --reload
# 5. Frontend - separate terminal
cd frontend
npm install
npm run dev# 1. Start PostgreSQL
# 2. Start Qdrant
# 3. Verify KiCad 10 CLI
kicad-cli --version
# 4. Backend
cd backend
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --host 127.0.0.1 --port 8000 --reload
# 5. Frontend - separate terminal
cd frontend
npm install
npm run devThen open the Vite URL shown by the frontend development server.
Changed: earlier builds had a Mock Mode / Live Mode toggle in Settings that showed hardcoded demo data when no project was open. That toggle, and all of the mock/demo data behind it, has been removed — every view is backed by real data from whichever project is currently open.
CircuitMind now has a single mode: a project is either open or it isn't.
The workstation shows an explicit "No project open" empty state instead of placeholder content. The Design/Inspection mode tabs and the Sync Graph indicator are hidden until a project is opened. Settings remains reachable regardless, so the AI Gateway, KCAA, and KiCad paths can be configured before opening anything.
Opening, importing, or uploading a project (File menu: New / Open / Import / Upload ZIP — see §19) connects the workstation to:
There is currently only one active project at a time, shared by every client connected to the backend — opening a different project on one machine changes what every other connected browser sees.
A typical CircuitMind workflow is:
1. Select KiCad Project
│
▼
2. Discover Project Files
│
▼
3. Calculate Project Hash
│
▼
4. Parse Deterministic Design Data
│
├── Schematic
├── PCB
├── BOM
└── Netlist
│
▼
5. Build / Update EDM Graph
│
▼
6. Run Verification
│
├── DRC
└── ERCCircuitMind was originally built as a local desktop-style tool: File > Open/Import and the filesystem browse dialog all read paths on the server's own disk — the same machine the browser was assumed to be running on. That assumption breaks the moment the browser is on a different machine, since a remote client has no way to hand the server a path to its own local files.
File > Upload Project (ZIP)... lets a remote (or local) engineer get a KiCad project onto the server over HTTP instead of typing a path:
.kicad_pro/.kicad_sch/.kicad_pcb file, and stores it under backend/project_uploads/<name>_<id>/..cmi save file — so an uploaded project behaves identically to a locally-opened one from that point on.File > Download Project (ZIP) zips the active project's live server folder on demand and streams it back. Because KCAA merges and approved ChangeSets already write directly into that same folder (§10), whatever is downloaded reflects exactly what has been approved — no separate export step is needed after a merge.
0.0.0.0).backend/project_uploads/ until manually removed; there is currently no "delete project" action for uploaded or locally-opened projects.See §20 (Troubleshooting) for common upload/download and remote-access connectivity issues.
Windows:
Test-Path "C:\Program Files\KiCad\10.0\bin\kicad-cli.exe"
& "C:\Program Files\KiCad\10.0\bin\kicad-cli.exe" --versionUbuntu:
which kicad-cli
kicad-cli --versionIf KiCad is installed elsewhere, update KICAD_CLI_PATH (§14.2).
Verify that PostgreSQL is running and listening on port 5432.
Check:
Test-NetConnection localhost -Port 5432Then verify the configured connection string:
DATABASE_URL=postgresql://circuitmind:circuitmind@localhost:5432/circuitmindCheck:
Test-NetConnection localhost -Port 6333Then verify:
QDRANT_URL=http://localhost:6333Confirm the kicad_ai_assistant plugin's setup script (§13.4 step 3) actually ran and created a .venv with kcaa installed in it before checking the path.
Windows:
Test-Path "$env:USERPROFILE\Documents\KiCad\10.0\scripting\plugins\kicad_ai_assistant\.venv\Scripts\kcaa.exe"
& "$env:USERPROFILE\Documents\KiCad\10.0\scripting\plugins\kicad_ai_assistant\.venv\Scripts\kcaa.exe" --helpUbuntu:
test -f ~/.local/share/kicad/10.0/scripting/plugins/kicad_ai_assistant/.venv/bin/kcaa && echo found
~/.local/share/kicad/10.0/scripting/plugins/kicad_ai_assistant/.venv/bin/kcaa --helpIf the path doesn't exist, re-run setup_plugin.sh / setup_plugin.ps1 / setup_plugin.bat from inside the plugin directory (§13.4). If the executable is installed elsewhere, update KCAA_PATH (§14.2).
Verify that the backend is running:
http://127.0.0.1:8000/docsThen verify:
VITE_API_BASE_URL=http://localhost:8000/apiRestart the Vite development server after modifying .env.
This almost always means the frontend is still calling an API base URL the remote browser can't reach. A remote browser's localhost is itself, not the server.
0.0.0.0, not 127.0.0.1 (§14.4).frontend/.env's VITE_API_BASE_URL (§15.2) points at the server's LAN IP or hostname, not localhost..env — it only reads it at startup.localhost, the .env change wasn't picked up.The uploaded ZIP must contain at least one .kicad_pro, .kicad_sch, or .kicad_pcb file, either at the ZIP's root or inside a single top-level folder (CircuitMind flattens one level automatically — see §19). Re-zip the project folder's contents directly if the structure is deeper than that.
Open, import, or upload a project first (§19) — there is nothing to zip until a project is active.
CircuitMind/
│
├── frontend/ # React + Vite + Tailwind workstation
│ └── src/
│
├── backend/
│ ├── app/
│ │ ├── main.py
│ │ ├── routers/ # REST API (visual_ir.py, knowledge.py, design.py, ...)
│ │ ├── services/
│ │ │ ├── visual_extraction.py # Visual IR pipeline orchestrator
│ │ │ ├── colqwen_service.py # the one ColQwen implementation (/pooling -> Qdrant)
│ │ │ ├── kicad_locator.py # toolchain path resolution (no hard-coded paths)
│ │ │ └── visual_ir/ # detection, association, wires, symbols, connectivity,
│ │ │ # decisions, reconcile, exporter, retrieval
│ │ └── models/ # SQLModel tables (incl. visual_ir.py)
│ ├── alembic/versions/ # schema migrations (production path)
│ ├── tests/ # hermetic unit tests + marked integration tests
│ ├── requirements.txt
│ ├── requirements-visual-ir.txt # Visual IR extras (numpy, opencv, tesseract bindings, ...)
│ ├── uploads/ # Knowledge PDF uploads (§7.1, §7.2)
│ └── project_uploads/ # ZIP-uploaded KiCad projects (§19)
│
├── benchmarks/ # reproducible technology-selection benchmarks (Part III)The exact structure may evolve as the platform develops.
pytest # from the repository root: the hermetic suiteThe hermetic suite uses an in-memory SQLite database, an injected fake detector and an in-memory KCAA stand-in, so torch, ultralytics, KiCad and KCAA are not needed (347 tests collected). Tests that need live services are marked and skipped unless their environment is configured:
| Marker | Needs | Enable with |
|---|---|---|
integration | any live external service (KCAA, KiCad, Qdrant, Ollama, ColQwen) | combined with the markers below |
kcaa | a KCAA executable and KiCad | KCAA_PATH and KICAD_APP_PATH (or KICAD_INSTALL_PATH) |
postgres | a real PostgreSQL database | CIRCUITMIND_TEST_POSTGRES_URL |
gpu | torch / ultralytics and YOLO weights | YOLO_MODEL_PATH |
colqwen | the vLLM ColQwen server and Qdrant | COLQWEN_API_BASE_URL, QDRANT_URL |
Notable suites:
backend/tests/unit/ — pipeline, geometry/placement, resolver/extractor, reconciliation, review actions, snapshots, export gate (ERC-lite, routing replay, verification), Alembic migrations, no-hardcoded-paths, ColQwen contract.backend/tests/integration/test_w4_real_services.py — the multichannel fixture through the real YOLO weights, KCAA and kicad-cli.backend/tests/integration/test_colqwen_real.py — the deployed ColQwen contract (/pooling → Qdrant).Benchmarks (Part III) are separate from the test suite: they live in benchmarks/ and are run by hand (§11.7).
Long-term objective:
Requirement
│
▼
Engineering Analysis
│
▼
Design Proposal
│
▼
Implementation
│
▼
Verification
│
▼
Evidence
│
└──────────────► Engineering DecisionCircuitMind is not intended to:
CircuitMind is intended to augment engineering judgment with deterministic tooling, structured evidence, and AI reasoning.
Add the project's applicable license here.
For example:
Copyright © 2026 CircuitMind / UST
All rights reserved.Replace this section with the actual repository license before public distribution.
| Qty | Value | Footprint | References |
|---|---|---|---|
| 4 | MountingHole | MountingHole:MountingHole_3.2mm_M3 | H1, H2, H3, H4 |
| 3 | 10000ohm | Resistor_SMD:R_0603_1608Metric | R3, R6, R7 |
| 3 | 2.2uF | Capacitor_SMD:C_1210_3225Metric | C1, C2, C3 |
| 2 | 22uF | Capacitor_SMD:C_1210_3225Metric | C6, C7 |
| 2 | B340A-13-F | Diode_SMD:D_SMA | D2, D3 |
| 1 | 100nF | Capacitor_SMD:C_0805_2012Metric | C5 |
| 1 | 162000ohm | Resistor_SMD:R_0603_1608Metric | R1 |
| 1 | 17400ohm | Resistor_SMD:R_0603_1608Metric | R4 |
| 1 | 3.3nF | Capacitor_SMD:C_0805_2012Metric | C8 |
| 1 | 33pF | Capacitor_SMD:C_0603_1608Metric | C9 |
| 1 | 47uF | Capacitor_SMD:CP_Elec_8x10 | C4 |
| 1 | 5015 | TestPoint:TestPoint_Keystone_5015_Micro_Mini | TP1 |
| 1 | 52300ohm | Resistor_SMD:R_0603_1608Metric | R2 |
| 1 | 5A | Fuse:Fuse_1206_3216Metric | F1 |
| 1 | 6.8uH | Inductor_SMD:L_Bourns_SRP1245A | L1 |
| 1 | 604ohm | Resistor_SMD:R_0603_1608Metric | R5 |
| 1 | 61300211121 | Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical | J1 |
| 1 | SMAJ36A | Diode_SMD:D_SMA | D1 |
| 1 | TPS54360DDAR | Package_SO:TI_SO-PowerPAD-8_ThermalVias | U1 |
| 1 | USB4125-GF-A | Connector_USB:USB_C_Receptacle_GCT_USB4125-xx-x-0190_6P_TopMnt_Horizontal | J2 |
| 1 | Vf=2.0V | LED_SMD:LED_0603_1608Metric | D4 |
It measures 50 × 40 mm and has 2 copper layers, 1.6 mm thick.
30 parts, built around the B340A-13-F-MS, SMAJ36A and USB4125-GF-A. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.
From the pginfinitylab-ai/forge repository on GitHub, at the commit this page was made from: the board is board/test3/circuitmind/stages/routing/ca7cdd0f-cd93-418d-bcc9-f9f555646b82/opt_ws/baseline/test3.kicad_pcb. PCBKit opens the board in the browser, with nothing to install.
Its repository states no licence, so its author keeps all rights: ask pginfinitylab-ai before reusing the design.

pginfinitylab-ai/forge
It combines deterministic EDA analysis, native KiCad tooling, structured engineering knowledge, multi-plane design graphs, vector retrieval, and controlled execution to provide an AI-assisted hardware engineering…

pginfinitylab-ai/forge
It combines deterministic EDA analysis, native KiCad tooling, structured engineering knowledge, multi-plane design graphs, vector retrieval, and controlled execution to provide an AI-assisted hardware engineering…

dridri/bcflight
Raspberry Pi based drone flight controller and remote control

jyg9/microduck-community-kit
补充microduck缺少的硬件电路和固件,另外配套一些测试工具。imu_to_dxl;banana_pcb;microduck复刻


Brendanofawesome/SimpleFOC-DualESC
High-performance dual BLDC motor driver with FOC (Field-Oriented Control) algorithm support, designed for applications requiring precise motor control with simultaneous dual-motor operation.

buttonbugs/Stuffed-Crust
This repository contains the code for Wildcat Robotics' team Stuffed Crust. This is a fork of the open source melty brain code, openmelt, with our custom motor control and translational drift algorithm and changes…


enkhbold470/iron-node
ESP32-S3 industrial gateway: W5500 Ethernet, isolated RS-485 (ADM2587E), CAN, 9-36 V input. 4-layer KiCad 10 board.

jacky4566/ZN6_UAEFI_Adapter
A DIY adapter board that plugs the FOME-tech ATLAS ECU into the factory ECU connector of the ZN6 platform (Toyota 86 / Scion FR-S / Subaru BRZ, 2012–2020).