As-built completion record for v1 of the KB wiring-diagram extraction pipeline: scanned car-manual wiring diagrams — until now invisible raster JPEGs with no extracted text — get a searchable text sidecar (labels, components, decoded wire colors) that the vectorizer indexes, plus a best-effort, confidence-flagged netlist built by a hybrid classical-CV + vision pipeline. Proven end-to-end on 1 pilot page (suzuki-vitara/wiring-diagrams/019-light-head-…), which went from 0 extracted text (unfindable) to #1 within its manual for “fuel cut controller” / “IC regulator”.

Scaled 2026-07-29 — real content + a graphical render layer

Since v1 the pipeline has been scaled to real content (full 5door-supplement manual: 55/55 diagram pages processed + vectorized), a per-page on-page render hook and a Mermaid graphical circuit renderer were added, and a folder/manual-root 404 was fixed. See v1.1 (2026-07-29) — scaled to real content + graphical render layer below. The v1 sections that follow are the original completion record and hard-won knowledge; they remain accurate for the pipeline internals.

Design in 2026-07-29-kb-wiring-extraction-design, task-by-task plan in 2026-07-29-kb-wiring-extraction-plan — this note is the completion record and the hard-won knowledge, not a restatement of either.

For Agents

Code: /home/levander/wiring-extract/ on telep-mainframe (house-rules-clean, no comments). Modules:

  • legend.py + legend.json — 13 Suzuki wire colors (B=Black … V=Violet) transcribed from the scan image _page_8_Picture_7.jpeg, plus symbols, connector rules, abbreviations. decode_color("B/Y") -> "Black/Yellow". Runs under any venv (pure stdlib).
  • diagram_index.pylist_diagram_pages(); 10 of 21 Vitara wiring pages are diagram pages (38 images).
  • cvtrace.py — the connectivity engine. Runs under /home/levander/ocr/venv (cv2 4.11 + scikit-image 0.26 [installed this session] + numpy + PIL + surya). trace(image, suppress_boxes, crop, debug).
  • emit.py — writes the .wiring.json record + <page>.wires.txt searchable sidecar + .lines netlist.
  • run.pystatus / reindex / search / run_page orchestrator.
  • fuse_pilot.py — the vision-extraction+fusion template used per page. Vectorizer patch: /home/levander/kb-vectors/chunker.py PATCHED (backup chunker.py.bak-wiring) — merges a page’s <page>.wires.txt onto that page’s chunks (same page_url); the .wires.txt never becomes its own page. 51 kb-vectors tests pass. Box constraints: CV/reindex thread-capped (OMP_NUM_THREADS=4 + nice), one heavy job at a time (PSU brown-out limit). Vision is Claude Code subagent-driven (session model) — NOT box claude -p (weekly-limited), NOT a local VLM (GPU contention). Batch, no always-on service. State: v1 complete + proven on 1 pilot page; 1/10 Vitara diagram pages covered; source md / scans / clusters.json md5-unchanged (non-destructive).

What was built

ModuleRoleVenv
legend.py + legend.json13 Suzuki wire colors + symbols + connector rules + abbreviations; decode_color()any
diagram_index.pylist_diagram_pages() — 10/21 Vitara wiring pages are diagrams (38 images)any
cvtrace.pyclassical-CV connectivity engine (trace())ocr/venv
emit.pywrites .wiring.json + <page>.wires.txt + .lines netlist
run.pystatus/reindex/search/run_page orchestrator
fuse_pilot.pyvision extraction + fusion template (per page)subagent
chunker.py (kb-vectors, patched)merges .wires.txt onto the page’s chunkskb-vectors/venv

cvtrace.py pipeline (the connectivity engine)

Otsu binarize → despeckle → TEXT mask via surya DetectionPredictor (auto, ~35 boxes) → SYMBOL mask (vision-supplied component boxes, centroid node kept) → skeletonize → graph build → dot-vs-crossing classifier (distance-transform blob vs median wire half-width) → union-find nets. bridge=1 default gap-bridge. Signature: trace(image, suppress_boxes, crop, debug).

Key findings / hard-won knowledge

Naive CV path-tracing is a NO-GO on these 1632px scans

Skeletonizing the raw binary produced 161 false junctions / 502 segments — because TEXT (the “VITARA” title, labels, fuse ratings) and COMPONENT SYMBOLS (fuse coils, switch boxes) skeletonize into dense false-junction clusters. Line-following itself worked; connectivity did not.

Masked CV is a GO — mask text + symbols BEFORE skeletonizing

Mask out text (surya auto-detect, ~35 boxes) + component symbols (vision-supplied boxes) first → junctions 161 → 37 (-77%), conductors intact, junctions land at real wire meets. Symbols were the bigger contaminant (161 → 106 text-only → 37 with symbols added). This is the “hybrid CV + vision” the roadmap predicted.

Clean division of labor (the architecture that works)

  • Vision locates components / terminals / labels and reads wire colors.
  • Those component boxes are passed to cvtrace as suppress_boxes.
  • cvtrace returns the nets (deterministic conductor geometry — no hallucination).
  • Vision then fuses semantics onto the nets and reconnects sub-pixel gaps (e.g. the battery → 30A ~6px break) — never fabricating untraceable connections.
  • Confidence = f(CV edge confidence, vision endpoint confidence, color confidence).

Crossing vs junction

Solder-dot detection (a local blob thicker than the wire half-width) distinguishes real connections (merge nets) from hop-over crossings (don’t merge). Proven on synthetic and real (3 hops vs 5 dots on the pilot crop).

Scan resolution is the ceiling, not the model

1632×808 with ~9 systems/page = few pixels per label; wire-color stripe letters (B/R vs B/Bl) are the failure point (~60% correct). 300–600 DPI rescans or single-circuit pages would push connections to ~90%+ — a documented future accuracy lever, not built in v1.

The search win is real but scoped

The pilot diagram page (019-light-head-…) went from unfindable (0 extracted text) to #1 within its manual for “fuel cut controller” / “IC regulator” via the indexed sidecar. It is NOT global top-50 — a terse structured list can’t out-rank prose service pages. Framing matters: the goal (diagram pages become retrievable) is met.

Vision + fusion is token-heavy — this is the binding scale constraint

Vision + fusion runs via Claude Code subagents (session model), NOT box claude -p (weekly-limited) and NOT a local VLM (GPU contention). It costs ~13 min + ~110k tokens PER PAGE. Scaling to all pages is the token-heavy remaining step.

v1.1 (2026-07-29) — scaled to real content + graphical render layer

Verified on telep-mainframe today. Four things landed: real-content scale-out, an on-page render hook, a Mermaid graphical circuit renderer, and a folder/manual-root 404 fix.

Scaled to real content — 5door-supplement 55/55

Beyond the thin suzuki-vitara/wiring-diagrams booklet (10/10 earlier), the full suzuki-vitara/5door-supplement manual is now 55/55 diagram pages processed — extracted, rendered on-page, and vectorized/searchable. Qdrant manuals carries ~377 points for this manual; 55 pages carry the “Diagram data (extracted)” marker.

Key finding CONFIRMED at scale — clean single-system circuits clearly beat the dense-booklet ceiling

  • Clean single-system circuits (stop/tail/interior lights, wipers, defogger, washers, DTC sensor circuits 037–043) trace well with decoded wire colors at high/med confidence — clearly beating the ~60% dense-booklet ceiling.
  • Bundled harness / operation diagrams (central-locking, power-window, the 060 body-electrical multi-market page) stay honestly conservative (med/low, power-path + representative edges only).
  • Routing / flowchart / block / mechanical figures correctly come out labels-only — NO fabrication.
  • Honest gap: page 044’s crank-angle-sensor “circuit” is only a flowchart + pinout in the scan (not actually drawn), so it is left labels-only — the pipeline did not invent a schematic.

Empty pages are OCR artifacts, not bugs

Divider / cover / TOC pages (e.g. 017-wiring-diagram = heading only; the geo-tracker cover) render fine but are thin by nature. The real schematics in the thin booklet were concentrated on page 019 (the marker grouped 6 figures there) — so a page rendering “empty” is expected content, not a failure.

On-page render hook (hooks.py on_page_markdown)

Extended the existing on_page_markdown hook (merged with the kickfix deeplink rewriter + lazy-image) to append a per-page ”## Kinyert huzalozási adatok (AI)” section, built from the per-page JSON (/home/levander/wiring-extract/out/<folder>__<manual>__<page>.wiring.json):

  • an HTML connections table (Honnan | Huzalszín | Hová | Bizt.),
  • components + systems lists,
  • an AI-provenance note.

Non-destructive — source md is untouched; the hook reads the <page>.wires.txt sidecar / JSON at render time.

Mermaid graphical circuit renderer (NEW)

Enabled pymdownx.superfences + a mermaid custom fence in mkdocs.yml (mkdocs-material 9.7.7 renders client-side). The render hook now ALSO emits a per-page ”### Áramköri gráf (AI)” mermaid graph LR:

  • nodes = components,
  • edges labeled with the decoded wire color,
  • LOW-confidence edges dashed (-.->),
  • grouped into a subgraph per subsystem,
  • readability cap (~30 edges/subgraph) with the dashed/subgraph logic.

Applies to all 62+ processed pages (pure render-layer — no re-extraction).

Mermaid render is markup-verified but NOT live-browser-confirmed yet

Verified the emitted markup + that Material’s mermaid loader bundle is present, but NOT a live browser render (the Claude Chrome extension was not connected this session). Material lazy-loads mermaid from the CDN, so a live render needs browser internet. A live-browser confirm is an open follow-up.

Manual/folder-root 404 fix (gen_index.py)

With use_directory_urls, folder + manual directories 404’d (e.g. /suzuki-vitara/5door-supplement/). Extended gen_index.py to generate a landing index.md for every folder + manual dir (idempotent <!-- generated-index --> marker) so those roots now list their pages. Backup gen_index.py.bak-404.

Box tooling in /home/levander/wiring-extract/ (reusable)

For Agents

  • cvtrace.py — masked-CV path-tracing (surya text-mask + vision-supplied symbol suppress_boxes → skeletonize → dot-vs-crossing classifier → union-find nets).
  • legend.py / legend.json — 13 Suzuki wire colors.
  • diagram_index.py — diagram-page enumeration.
  • emit.py — writes .wiring.json + <page>.wires.txt + .lines.
  • run.pystatus / reindex / search orchestrator.
  • per-page spec drivers: emit_driver.py / mech_emit.py / build_page.py + specs/.
  • Vision + fusion is subagent-driven (session model; box claude is weekly-limited).
  • Naive CV was NO-GO (text/symbol contamination → 161 false junctions); masked CV was GO (→ 37).

Status

  • v1 complete + scaled (v1.1): wiring-diagrams 10/10 and 5door-supplement 55/55 diagram pages processed, rendered on-page (table + Mermaid graph), and vectorized/searchable (62+ pages total).
  • Clean single-system circuits trace high/med confidence; bundled/operation diagrams stay conservative; routing/flowchart/block/mechanical are labels-only — no fabrication.
  • Non-destructive: source md / scans / clusters.json unchanged; render layer + sidecars only.
  • Next targets: the workshop-manual electrical sections and Sidekick wiring-1996.
  • Open follow-up: a live-browser confirm of the Mermaid render once the Chrome extension is connected.

To scale (the commands)

Per page:

  1. Vision extraction + fusion using the template /home/levander/wiring-extract/fuse_pilot.py (subagent).
  2. emit.write → produces <page>.wires.txt (+ .wiring.json + .lines).
  3. Reindex that manual/section:
OMP_NUM_THREADS=4 nice /home/levander/kb-vectors/venv/bin/python run.py reindex suzuki-vitara wiring-diagrams