Footprint source Package for PE-BOM-010
SnapEDA / Ultra Librarian footprints stamp ``Package`` (e.g. ``TDFN-8 Maxim``) and ``SnapEDA_Link`` on the ``.kicad_mod`` or embedded ``.kicad_pcb`` footprint. BoardLab now parses that source document metadata into ``cad_footprints`` and prefers the vendor Package label when classifying land family for PE-BOM-010 — not only the IPC-style Lib:Name (``SON50P…``).
- package_label / source_url on CadFootprintRef.
- parse_pcb_footprint_meta for embedded PCB Package properties.
- PE-BOM-010 / _cad_family prefer Package over bare Lib:Name.
- Regression: TDFN-8 Maxim ≡ datasheet TDFN-EP; SOIC Package vs TDFN datasheet still ERROR.
Library registers CAD footprints
After graph build, each IC’s KiCad ``Lib:Name`` is stored on the extract as ``cad_footprints`` (project + shared library). When the ``.kicad_mod`` resolves, ``descr`` / ``tags`` are parsed from that footprint source file so PE-BOM-010 classifies land family from real CAD metadata, not only the name string.
- CadFootprintRef + persist_cad_footprints on graph_build.
- PE-BOM-010 uses .kicad_mod descr/tags; exact registered Lib:Name waives datasheet-vs-CAD family ERROR for known footprints.
- Tests for meta parse, descr→family, persist/dedupe.
TDFN-EP ≡ SON for PE-BOM-010
Emmaforo U5 MAX17048: datasheet ``TDFN-EP`` vs KiCad ``SON50P200X200X80-9N`` was a false PE-BOM-010 ERROR. Same no-lead EP land family (Maxim TDFN / IPC SON).
- Canon TDFN / UDFN / XDFN → SON; bare DFN stays distinct so KiCad Package_DFN_QFN:VQFN-… still maps to QFN.
- Regression MAX17048 TDFN vs SON CAD; negative TDFN vs QFN still ERROR.
Resume extracts newly added ICs
``mode=failed`` / resume skipped the whole ``ic_extraction`` stage when *any* ``extracted/*.json`` already existed. Adding MAX17048 (U5) after U1–U4 were extracted left schema reviewable from PDF but no pintable JSON — PCB AI then skipped U5 as “no library extraction”.
- Resume reuses per-MPN workspace/library extracts; missing ICs still run extract_pintable (same for simple models).
- Regression: resume with U4 extract + U5 PDF → extract only MAX17048.
Overview run bar tracks PCB stages
The bottom RunStatusBar on Overview treated PCB SSE stage ``checks`` as unknown (``pcb_checks``), so the bar stayed near 0% through deterministic checks / AF and only jumped late in AI review.
- RunStatusBar PCB ladder matches pcb_pipeline stage ids (checks, extract); protocol/timing map to write_report; progress is monotonic.
- run-status-progress unit tests (alias, unknown stage, mid-run fraction).
Find Maxim datasheets (MAX17048G_T10)
Auto-download tries harder before asking for an upload: schematic ``MAX17048G_T10`` (underscore tape-reel) now also searches ``MAX17048G+T10`` / ``MAX17048G`` / ``MAX17048``, and Analog/Maxim PDF URLs are candidates. Misses still surface as not-reviewed + upload.
- mpn_query_variants: _↔+, strip +T10 packing, MAX family
- manufacturer PDF table includes Maxim MAX* on analog.com
Extract die bonds + pull-up/down
Pintable extraction must scan the block diagram and pin notes for ``internal_features``: ``connected_pin_groups`` (die shorts), ``pullup_pins`` / ``pulldown_pins`` (external pulls required). Empty lists when the PDF is silent — never invent. PE-INT-002 warns when a listed pull-down has no resistor to ground.
- extract-pintable skill/prompt: internal_features is a required scan
- pulldown_pins + PE-INT-002
- PDF page select includes block diagram / pull-up|down keywords
Internal pin bonds (VIN↔VINA)
Datasheet die shorts belong in ``internal_features.connected_pin_groups``. ``shortest_path`` reports them; PE-REV no-path rows that contradict a bond are dropped. Emmaforo TPS63031: VINA/EN/PS on a bypass star with VIN on VSYS matches the typical application — VIN↔VINA is on-die, not a board error.
- connected_pin_groups on InternalFeatures + extract skill/schema
- review path tools + PCB library context expose die bonds
- PE-REV filter drops false “no path” across bonded pins
SON/WSON ≡ TI DSK (not QFN)
PE-BOM-010 treated TI ``SON (DSK)`` extracts that mention ``QFN-10 (… R-PWSON-N10)`` as QFN, so a correct KiCad ``Package_SON:WSON-*`` looked like a family mismatch (Emmaforo U4 TPS63031DSKR). WSON/USON/PWSON/VSON canonize to SON; when both QFN and SON codes appear, SON wins. Bare QFN vs WSON still ERROR.
- package family: SON land codes preferred over QFN in mixed blobs
- regression: TPS63031 DSK/PWSON ≡ WSON CAD; QFN≠WSON negative case
KiCad ``(mirror x/y)`` axis semantics
KiCad ``(mirror x)`` reflects across the X axis (negates Y); ``(mirror y)`` reflects across the Y axis (negates X). BoardLab had the axes swapped, so Emmaforo J3 (``mirror x``) pin tips landed on the wrong side of the symbol: pin 1 missed the BAT wire/label and pin 2 missed the power:GND flag.
- sch pin tips honor KiCad mirror-plane axis names
- regression: mirrored connector pin↔wire+label and pin↔power (no label)
NetIndex: first-class net identity
Introduce ``NetCluster`` / ``NetIndex``: electrical identity is the ``{(ref, pin)}`` member set; KiCad strings are aliases. PE-LAY and PE-NC consume the index instead of comparing display names ad hoc.
- engine/net_identity.py — NetCluster, NetIndex, builders
- PE-LAY-001 / PE-NC-001 route through clusters_equivalent
- tests for index members/aliases and sch↔PCB equivalence
PE-LAY by pad-set membership (not name shape)
Replace the anonymous-name PE-LAY shortcut with electrical identity: sch and PCB nets match when their ``{(ref, pin)}`` sets are equal, even if KiCad display strings differ. PE-NC-001 warns only when other parts share the net (floating NC is correct). Keeps 2.87.119 KiCad same-text merge for power + local labels.
- PE-LAY-001 uses pad/pin membership equivalence
- PE-NC-001 only when NC pin shares a net with other components
- anonymous Net-(…) / unconnected-(…) string heuristic
- regression: same members different names OK; different members ERROR
Sch net merge: power + local same text
KiCad joins a power symbol and a local label with the same text into one net; BoardLab bucketed them by label kind and invented a second name via collision rename (``VSYS`` → ``VSYS_``).
- sch same-name merge: local + global/power/hier share one cluster
- no spurious second net when power Value and local label match
KiCad sch pin Y flip (symbol editor → sheet)
``.kicad_sch`` ``lib_symbols`` pin coordinates use symbol-editor axes (Y up). Schematic sheets use Y down. Pin placement now negates Y before rotate/mirror so tips land on the wires KiCad draws. Without this, power pins on modules such as ESP32-C6 looked swapped (GND↔3V3) and schema/PCB conformity falsely failed on a correct board (Emmaforo).
- apply_sym_xy: _rotate(px, -py, rot) for lib→sheet
- regression: U2 pin1=GND / pin2=3V3_DIGITAL (not swapped)
- sch parser fixtures use sheet-space pin1 at y=-3.81
Library rescan first; real admin names
Idle library realignment runs on the asyncio lifespan loop (not a daemon thread that can stall under uvicorn), ticks every 15s while a stale/missing-power backlog remains, and keeps popular ingest paused until rescan clears. Admin Users / Projects / Usage show the local account display name (e.g. Michele Bigi), not the raw ``usr_…`` id.
- idle refresh starts via start_idle_refresh_async (+ thread fallback)
- backlog interval BOARDLAB_LIBRARY_IDLE_REFRESH_BACKLOG_S (default 15)
- admin owner enrich from local_users under local auth
- Usage / search use the same name enrich path
- admin tables: name first, email secondary (hide raw id)
Charger IIN is not a VBUS load
BQ25896-class path ICs were counted as VBUS peak loads when the KiCad symbol pin numbers disagreed with the datasheet pintable (pin 1 = VBUS in the PDF but GND on the board). IIN / adapter current limit (3.25 A) must not become ``i_peak_a`` next to I(VBUS) IQ — it is path capacity.
- _charger_io_nets prefers VBUS/VAC *net leaves*; rejects GND
- path charger skipped as connector-rail load
- sanitize + budget drop charger IIN/adapter peaks from loads
- regressions: renumbered BQ pins; IIN scrub; real MCU still FAILs
iPadOS navigation chrome
Menus and project tabs move to an iPad-style split view: persistent sidebar (264px, frosted, 44px rows, blue selected tint), content-pane top bar for utilities only, segmented control for sibling panels (Parts|Derating, Rails|Domains|Impedance, Project|Fabrication), and a compact bottom tab bar with a left sheet. Admin Library is a flat sidebar group (no accordion).
- AppShell → sidebar + content pane + MobileTabBar
- TopBar drops primary nav; opens sidebar on compact
- ProjectSettingsBar → segmented siblings only
- Admin page content-only; destinations in AppSidebar
- segmented-control, app-sidebar, nav-items, chrome context
- compose attaches app services to external boardlab_edge so Caddy keeps resolving after compose up --build
Hierarchical .kicad_sch care
BOM-from-schematic and netlist walk every KiCad ``Sheetfile`` child, including nested subdirectories (``power/rails.kicad_sch`` → ``ldo.kicad_sch``). Companion sheets stored under ``uploads/`` keep relative paths. Clearer error when a Sheetfile child is missing; bom_parse logs sheet count.
- _safe_sheetfile docs (path relative to the parent *sheet file*)
- hierarchical_sheet_names + bom_parse sheet logging
- regressions: nested Sheetfile BOM/netlist; storage round-trip; PCB-only U99 still ignored
Netlist from schematic (not PCB copper)
Schematic connectivity is the design IR. A present ``.kicad_pcb`` no longer replaces nets or invents PCB-only footprints (e.g. U3 on copper but missing from the sheet). Pad nets are a fallback only when the schematic yields zero nets. Layout checks still use ``layout_graph`` separately.
- build_graph keeps kicad_sch/EDIF nets when PCB is uploaded
- PCB-only refs stay out of the schematic design graph
- regressions: sch wins; PCB fill only if sch nets empty
BOM from schematic (KiCad)
For KiCad projects the schematic is the BOM source of truth. Each schema run regenerates ``bom.csv`` from symbol fields (hierarchical sheets included). IC ``Value`` fills MPN when the manufacturer field is empty. PCB-only footprints are not invented into the BOM.
- bom_parse regenerates BOM from .kicad_sch before extract
- upload/netlist uses bom_csv_from_kicad_sch (Value→MPN for U*)
- regressions: sch extract; stale CSV replaced; no PCB-only U3
Schema progress bar after prepare
Starting schematic analysis right after prepare no longer loses the bottom status bar. Heal used the stale ``pipeline_prepared`` event / graph-without- report to force ``ready`` while the schema worker was still queued — worker logged ``booted into non-queued`` and the UI showed no progress.
- heal_if_pipeline_finished never demotes phase=schema to ready
- prepare finish uses CAS so it cannot overwrite a queued schema start
- regressions: queued schema survives heal; prepare-stuck still heals
TVS IPP is not a rail load peak
Extract/library must not put Peak pulse current (IPP, 8/20 µs) into `power_requirements.i_peak_a`. That is abs-max clamp capability; rail budget uses leakage IR for ESD/TVS. Fixes EmmaForo2 VBUS false FAIL (4.5 A IPP vs 3.25 A adapter).
- extract-pintable skill/schema/validate reject surge IPP as load peak
- sanitize_power_requirements on pintable save; parser **1.21.0**
- rail budget ignores surge peaks already stored in library PR
- regressions: validate IPP reject; VBUS TVS load peak ≪ 1 A
Story Bench polish
Legacy `?section=protocols|bom_coverage` URLs redirect to Interfaces / Coverage. Chapter empty states distinguish **—** from **Pass**. Bench nav keeps story order (1…8) for accessibility.
- resolveBoardMapSectionId legacy aliases + chapterEmptyCopy
- Story bench list is ordered (ol) with chapter index
- regressions for alias map and empty ≠ pass copy
Story Bench lens Schema | PCB | Both
One project report with a lens toggle. **Both** merges schema + PCB findings (PCB wins on the same finding_id), prefers PCB rail budget and protocol cert, and recounts severity from the deduped list. Bare `/report` opens Both when both runs exist.
- report-lens.ts merge + ?kind=both in useReport
- Report header tabs Both / Schema / PCB; nav Report → Both
- regressions: PCB prefer, no false collapse, no ERROR double-count
Buses chapter from bus-only reviews
PE-REV findings that are bus-only (I2C/SPI/I2S/…) route to the Buses chapter so it is no longer empty when pull-up/concordance issues exist. Mixed power+bus reviews stay in IC dossiers.
- sectionForFinding: bus-only PE-REV → buses
- regression: I2C pull-up → buses; VSYS+SPI → ic_review
Power rail dossiers
Power / PDN opens one dossier per rail: budget (I_ord/I_peak/I_cap) plus linked findings grouped as decoupling, filter, copper/geometry, other.
- power-rail-dossier.ts attach findings → rail by net
- PowerRailsSection renders rail dossiers with theme buckets
- regressions for net match and theme attach
IC dossiers structured by theme
IC dossiers chapter opens one panel per designator with themed buckets (Power, Buses, Interfaces, Thermal, Placement, …) instead of a flat list.
- ic-dossier.ts + IcDossiersSection
- Story Bench IC chapter uses dossier panels
- regressions for thermal/power/bus bucket routing
Story Bench panel metrics + headline
Each Story Bench panel shows 2–3 bench metrics (rails/MARGIN, IC count, fixtures certified). A one-line board headline sits above the map.
- BoardMapSection.metrics + buildStoryBenchHeadline
- Story bench home renders headline + per-panel metrics
- regression: Power MARGIN metrics and IC dossier count
Story Bench report (A+B hybrid)
Report home is a **Story Bench**: board story order as test-bench panels. Power / PDN → Buses → Interfaces → SI/HF → Thermal → Protection → **IC dossiers** (IC-by-IC, PE-REV no longer dumped in Coverage) → Coverage. Markdown export follows the same chapters (severity is a filter, not the spine).
- board-map: eight Story Bench chapters + smarter sectionForFinding
- Report UI copy “Story bench”; IC dossiers use designator tree
- Markdown export chapter-ordered; rail budget under Power
- regressions for mapping, export order, IC-review routing
Module MPN must not bind SoC datasheet
HubAudio USB on ESP32-S31-WROOM-3 pads 40/41 was correct; the false warning came from binding the bare **ESP32-S31 Series** (QFN80) PDF to the WROOM module MPN. Module tokens (WROOM/WROVER/MODULE/SIP) must match on both sides of datasheet lookup and pintable `base_family`.
- find_local_pdf / mpn_catalog_match: no die↔module steal
- pintable extract + skill validate reject die-only family on module MPN
- regression: ESP32-S31.pdf ≠ ESP32-S31-WROOM-3; QFN80 base_family reject
Serial buses match net name + IC pinout
I2C/SPI/I2S recognition requires net-name and pinout concordance (or one side only when the other is silent). SPI_* nets with dual-function SCL/SDA pins are not I2C; power/GND nets never carry serial roles.
- protocol_serial_bus: family conflict + power/ground exclusion
- regression: SPI_CLK+SCL pin ≠ I2C; GND+SCLK ≠ SPI CLK; concordance
Mandatory per-rail V + I extract (1.20.0)
Pintable extract must fill every IC supply domain: voltage, ordinary load, max load. Datasheets always state these in ROC / Electrical Characteristics — validation rejects omissions. Parser bump **1.20.0** drives library idle reparse.
- extract-pintable schema + validate.py: required power_requirements.rails[] with rail / voltage_v / i_ordinary_a / i_peak_a; source_capacity required for LDO/buck/PMIC/charger
- PINTABLE_TOOL loads schema.json (tool previously omitted power)
- skills_manifest default_model_version **1.19.0 → 1.20.0**
- rail_power_budget matches per-rail extract to the net (pin / exact token / unique voltage) — **VDD ≠ VDDIO ≠ AVDD** (no substring steal)
- SKILL.md: mandatory rails; DeepSeek must emit one row per named domain; page trim keeps IDD/ICC/power tables
- DeepSeek pintable prompt + PDF page keywords + validate retry text push ROC/EC supply tables (IDD/ICC/IQ) — datasheets always have them
- tests: validate rails, LDO source_capacity, per-rail budget, VDD/VDDIO non-collision
Recognize SPI / I2C / I2S buses
Physical-bus recognition now builds `i2c-pcb` / `spi-pcb` / `i2s-pcb` instances from pin↔net connectivity (pin names and net names), then runs them through L0–L3 like USB/ETH. No invented ohms or MHz.
- protocol_serial_bus.py: I2C (SCL+SDA), classic SPI (CLK+data/CS), I2S (BCLK+LRCLK/SDATA) from shared IC nets
- catalog logical spi / i2s + physical spi-pcb / i2s-pcb skeletons (CONTROLLER_DEPENDENT — distinct from spi-nor / qspi)
- recognize_physical_buses wires serial-bus recognition
- pytest tests/pcb/test_protocol_serial_bus.py (net, pin_name, SPI≠I2C alias, incomplete negative)
Finding quality: less WARNING spam
HubAudio PCB triage: identical PE-PLC-004 ×5, WARNING on missing evidence, RMII "not certified" + "certified" on the same PHY, phantom BUD rails.
- PE-PLC-004 one card per (ref, foreign net); skip keepout on unconnected-(…) NC pads
- complete_finding: INSUFFICIENT never stays WARNING (BUD/AF/REV soft)
- PE-AF-003 missing CLKIN/TX length → one INFO "timing skipped" (not dual WARNING "not certified")
- PE-BUD-001 missing Icap → INFO; skip BUD cards on NC / empty rails
- PE-ESD-002 INSUFFICIENT aggregated per (TVS, connector)
- Demo fixture golden: one PE-I2C-001 per bus (SDA+SCL collapse)
DDR JEDEC VDD cites (no invented PCB Z)
Wire on-file JESD79 DDR1–5 into the protocol catalog for **DRAM VDD** only. PCB geometry (Z0 / skew-mm / length) stays CONTROLLER_DEPENDENT / PHY_DEPENDENT — JEDEC does not state a universal board impedance.
- ddr2/ddr3/ddr4/ddr5 NUMERIC VDD typ (1.8 / 1.5 / 1.2 / 1.1 V) with table/page cites from JESD79-2F / -3F / -4C / -5
- ddr1 / ddr3l voltage stays CONTROLLER_DEPENDENT (bin / 1.35 V addendum) with needed_document pointing at the on-file PDF
- DDR PCB geometry rows carry needed_document for controller/PHY layout guide (JESD79 on file for DRAM electrical, not board Z)
IC review BrokenPipe under concurrent PDF ingest
HubAudio 2.87.92/93 lost all 25 IC LLM reviews (`BrokenPipeError` turn 0) while a single `review_ic_async` still succeeded. Root cause: pipeline worker reused uvicorn→Docker stderr; concurrent datasheet ingest flooded that pipe with fontTools/pypdf warnings until writes raised EPIPE inside the review.
- Worker stdout/stderr → data/workers/<id>.log (not Docker log pipe)
- Soft session.close() / DeepSeek client.close() ignore BrokenPipe
- PDF ingest mutes fontTools warnings; retries once with stderr muted
Stop PE-PRT multiplying on incremental report writes
HubAudio re-run showed PE-PRT-L1-002 still ×52 INFO: `_write_report` re-extended `protocol_exam` findings after every IC. Strip prior `protocol_l*` before extend; lock concurrent writes. Collapse-per-instance (2.87.91) was fine.
Same-cause WARNING aggregation
Warnings with the same root cause collapse to one card that lists every IC:
- **PE-I2C-001/002**: one per bus (SDA+SCL sharing the same IC set)
- **PE-DEC-001/002**: one per rail (all ICs on that supply/VOUT net)
Schematic software FP cleanup
HubAudio schematic report noise (engine, not hardware):
- **PE-PRT-L1-002**: one INFO per bus instance (was WARNING ×N skew skips)
- **PE-I2C-001**: SPI aliases like SCL_M/SCLK_M / SDA_M/MISO_M are not I2C
- **PE-DEC / PE-RST / PE-I2C / PE-INT**: skip KiCad unconnected-(…) nets
- **PE-REV-001**: LLM review findings get a stable rule_id for filter/triage
Project tab BOM → Parts
Michele: flat tab row should say **Parts**, same as the table heading — not BOM. URL stays `?tab=bom` for compatibility.
SPI / QuadSPI protocol PDFs on file
Michele: Motorola SPI Block Guide + SPI family / QuadSPI sources. On-disk archive (gitignored):
- motorola_freescale_nxp_spi_manual_2000.pdf — SPI Block Guide V04.01 (de-facto SPI)
- Analog Devices SPI helpers (ad_spi_*, ad_an-1248_*, ad_introduction_*)
- national_semi_an_452_microwire_1992_ti_snoa743.pdf — Microwire
- open_alliance_10base_t1x_mac_phy_spi_interface.pdf — OPEN Alliance MAC–PHY SPI
- AN13563_S32G_QuadSPI_Deep_Dive.pdf, MBOOTQSPIUG_*, KBLQSPIUG_* — NXP/Freescale QuadSPI
JEDEC DDR1–5 specs on file
Michele pointed at [TheRamGuide-WIP-](https://github.com/RAMGuide/TheRamGuide-WIP-) for DDR protocol PDFs. On-disk archive (gitignored PDFs):
- JESD79F_DDR.pdf (DDR1)
- JESD79-2F_DDR2.pdf
- JESD79-3F1_DDR3.pdf
- JESD79-4C_DDR4.pdf
- JESD79-5_DDR5.pdf
Fix IC review BrokenPipe / Event loop closed
HubAudio (and any exam) lost every IC LLM review with `BrokenPipeError` / `RuntimeError: Event loop is closed`. Root cause: `get_provider_by_name` is `lru_cache`d and `DeepSeekProvider` held one process-lifetime `AsyncOpenAI` httpx client. Idle library refresh / popular ingest call `asyncio.run()` in a background thread — each run opens sockets on a temporary loop, then closes that loop. The next review reused the cached client and died while closing sockets bound to the dead loop (turn 0, all ICs). Fix: provider stores credentials only; each session gets its own `AsyncOpenAI` and `session.close()` awaits `client.close()` on the live loop.
CI seed fix + optical S/PDIF ERROR stays
Michele: CI mail spam from Backend pytest; after PE-BUD cleanup S/PDIF must remain an **ERROR** (true positive), not vanish with the rail FPs.
- ensure_seeded_if_empty skip threshold parser >= 1.1.0 (was 1.1.1, re-seeded 89 packs and failed CI).
- PE-SPDIF-001 deterministic RULE: optical TX on SPDIFIN / RX on SPDIFOUT → ERROR (survives complete_finding).
- Signal-bus pin tokens (I2S_OUT, …) never invent VIN/VOUT roles.
Rail budget: pin roles + supply abs-max only
Michele: HubAudio PE-BUD ERROR were FP — fix root cause, not a suppress patch. ``I2S_IN_*`` / ``I2S_OUT_*`` net names no longer invent regulators (VIN/VOUT match is pin-token only). Load abs-max ignores sense/VIN/bus measurable current (INA228 85 A class); peak keys drop bare ``imax``/``i_abs``.
- _pin_net_by_role(..., match_net=False) for regulator discovery.
- _absmax_current(role=load|capacity) separates IQ vs Iout.
- Regression: I2S nets not rails; sense abs-max not rail load.
Project tabs: flat single row
Michele: not a multi-level sheet menu — **all tabs on one line**. Overview · BOM · Derating · Rails · Domains · Impedance · Project · Fabrication. No second-row schede. ``?tab=parts&panel=…`` from 2.87.83 still resolves.
- ProjectSettingsBar: one underline row of flat destinations.
- project-workspace: hrefForTab / flat WorkspaceTab.
Project workspace: section + panel (superseded)
Two-level chrome (section row + panel schede). Superseded by 2.87.84 flat row; resolver kept for old URLs.
- project-workspace URL resolver (+ node:test).
- Project page content keyed off section + panel.
Global pin-sort fix (KiCad sch is enough)
Michele: create-project 500 on mixed pin numbers (USB-C ``1`` + ``A5`` / ``EP``). **Global** fix in ``natural_sort_key`` — not a HubAudio-only patch. KiCad ``.kicad_sch`` (+ ``.kicad_pcb``) remains the required input; no separate PADS netlist. The API route is still named ``upload/netlist`` (legacy).
- natural_sort_key tags digit vs text tokens and coerces int pins (any board / any connector with mixed pin labels).
- _pins_by_ref normalizes pin numbers to str before sort/JSON.
- Wizard / API error copy says «KiCad project» when .kicad_* files are uploaded (was misleading «Failed to upload netlist»).
HDMI TMDS from TMDS1204 + Admin Mail compose/redirect
Michele: missing HDMI Adopter TMDS Z → take 90 Ω from the TI TMDS1204 component seed (VENDOR, UNOFFICIAL). Admin Mail: compose/send + contact redirect address override.
- hdmi-1.4-tmds-type-a / -type-c-mini, hdmi-2.0-tmds-*, hdmi-frl-pcb: differential_impedance **90 Ω** from TMDS1204 Table 8-6 (source_type VENDOR, mandatory RECOMMENDED); pack-level needed_document still names the missing Adopter PDF.
- Provenance for those packs stays UNOFFICIAL (CEC-only must not elevate to OFFICIAL).
- Admin → Mail: Compose tab + POST /api/admin/mail/send (stores outbound; SMTP best-effort when configured).
- Admin → Mail Redirect: contact_redirect_email override (wins over CONTACT_RECIPIENT env) via GET/PUT /api/admin/mail/settings.
USB3/4 NUMERIC Z from on-file PDFs
Michele: pack board Zdiff numerics from the PDFs already on disk — never invent ohms.
- usb3-over-usb-c / usb4-over-usb-c: pcb_trace_nominal_zdiff **85 Ω** (Type-C R2.5 §3.2.2.5 Informative should), cable_differential_impedance **90 Ω** (Type-C §3.7.1 Informative), impedance_tolerance **±15%** (SuperSpeed Equalizer Design Guidelines §2.1 channel models).
- usb4-over-usb-c compliance_fixture_se_impedance **42.5 Ω** shall (USB4 §3.3.6 fixture; not a PCB Zdiff shall).
- Pack-level differential_impedance stays UNKNOWN — USB 3.2 / USB4 have no PCB Zdiff shall; L2 uses pcb_trace_nominal_zdiff.
Protocol PDF archive + missing-pack cites
Michele: where official PDFs are public, put them on disk; otherwise keep a usable vendor/helper PDF and mark the cert pack ``UNOFFICIAL``.
- Restored INDEX archive under standards/protocol-specs/ (PDFs gitignored).
- USB-IF: USB_3.2_Revision_1.1.pdf, USB4_Version_1.0.pdf, USB_PD_R3_0_V1.2.pdf (+ R2.0 V1.3).
- Helpers: UM10204_I2C.pdf (NXP), CAN_Specification_2.0_Bosch.pdf.
- Catalog packs usb3-over-usb-c, usb4-over-usb-c, usb-c-pd-cc, i2c-pcb, can-pcb cite those files (no invented Z0).
Integrate cert provenance (no false OFFICIAL)
Michele: continue Admin Certs / Library — integrate packs that already have cites on file; stop marking HDMI TMDS as OFFICIAL from CEC-only; helper bus packs (SGMII Cisco, RMII Micrel) show as UNOFFICIAL until a chip MPN links.
- typical: false no longer counts as a numeric cite (false OFFICIAL).
- Official requires a primary board cite (Z / rate / …) when the pack needed_document still says the primary PDF is not on file.
- Chip-dependent buses with vendor helpers → UNOFFICIAL; with linked MPN → COMPONENT_BUS.
- Cert seed 1.1.1 keeps VENDOR helpers; clears misleading pack-level needed_document when official numerics exist.
- Admin Certs shows official/unofficial document titles; auto-refresh provenance when the field is unset.
Admin Certs provenance + chip bus versions
Michele: separate packs missing an official document; one Admin list with a provenance field (official → unofficial → component bus). IC-dependent buses (RMII+) annotate versions by chip MPN so they are looked up again without re-downloading datasheets every time.
- document_provenance on cert packs: OFFICIAL / UNOFFICIAL / COMPONENT_BUS / MISSING_OFFICIAL.
- Admin Certs filters: All · Missing official · With source.
- Chip-dependent bus versions index (RMII+) annotated by MPN; sync from component library + builtin seeds (LAN8720A).
- POST /api/library/cert/refresh-provenance, GET /api/library/cert/chip-bus-versions.
Admin menu reorg
Logical Admin IA: nested Library submenu, one Users surface, MySQL credentials visible to admins, batch processor queue list.
- Admin → Library expands to Archive / MySQL, Components, Batch queue, Autoscan.
- Ops Components + Credits removed (aliases → Library Components / Users).
- Components: MySQL library cards vs file extraction cache clearly labelled.
- Batch queue lists popular-ingest MPNs and rescan backlog (stale / missing power).
- GET /api/admin/library/mysql-credentials — admin-only Adminer login fields.
- Users: create local account, free-service flag, search, and credit adjust in one panel.
Test deploy
- Frontend Docker build: inline proxy config (no re-export); restore PostCSS Tailwind object form.
- npm start listens on all interfaces again (Docker/Caddy); was bound to 127.0.0.1.
Quiet Studio UI primitives
UI controls use 8px radius, panels 11px, 150ms transitions.
- components/ui/* restyled to Quiet Studio (button, badge, card, dialog, sheet, tabs, …).
Admin Mail inbox
Michele: Admin page to read contact mail; reuse the existing contact HTML template.
- Contact form messages persist to admin/mail/inbox/ (even if SMTP is down).
- Admin → Mail: inbox list/detail + Templates catalog of built-in email kinds.
- Contact HTML template shared for outbound notify and admin preview.
Completion alert in Settings
Michele: if the user opts in via Settings, email them when analysis finishes. Toggle lives in the top-bar Settings sheet (same pref as Account).
- Notifications section in Settings: **Email me when analysis completes**.
- Account page reuses the same notification control.
Email auth + completion alerts
Michele: while Stripe is set up, wire local email auth (forgot / reset / change password) and optional alerts when schema or PCB analysis finishes.
- Password reset via email (/forgot-password, /reset-password); change password on /account.
- Account preference **Email me when analysis completes** (email_on_complete); default off.
- Schema and PCB pipelines send report-ready mail only when the user opted in.
Stripe as free-to-start payments
Michele: use a free payment platform for credits. **Stripe** (Italy) has no monthly/setup fee — only per successful charge (~1.5% + €0.25 EEA cards). Already wired; `/credits` copy and `.env.example` point to signup. Without keys, manual pack requests + Admin Grant still work.
- Credits page documents Stripe pricing / signup.
Credits metering + free-service flag
Michele: enable credits even before selling; Admin → Software → Users can turn **Free service** on per user; `/credits` page for balance and packs. Stripe optional (no monthly fee — only transaction fees); without keys, pack buttons create a manual payment request that admin fulfills with **Grant**.
- Credits ledger/packs/gate (billing_enabled default on); trial grant; PCB exam charge.
- /credits wallet UI; top-bar balance link.
- Admin Users: Free service toggle, balance column, Grant credits.
- Billing: Stripe Checkout when STRIPE_SECRET_KEY set; else /api/billing/request-pack + admin fulfill.
Cert engine reads cert_library JSON packs
Michele: every certification's component/requirement specs live as JSON in `cert_library`; the certifying engine must grade from that SoT. `load_certifying_catalog()` overlays pack constraints onto the recognition skeleton; L0–L3 / CertifyingEngine / AF impedance cites use it.
- engine/cert_specs.py — overlay + load_certifying_catalog / get_pack_constraints.
- protocol_exam / engine / L1–L3 defaults consume cert_library packs (catalog.json remains seed/bootstrap only).
- Admin Certs copy states packs are the engine criteria SoT.
Cert system: family labels + Admin Certs UX
Michele: certification is first-class. Fix broken pack bus labels (`1000base` from pack-id split), seed with USB/ETHERNET/HDMI families + human titles, dedupe document requests, Admin Certs grouped by family. Auto-refresh packs still on parser `< 1.1.0` at boot. Seed endpoint allowed for admins.
- Cert pack bus / title from logical protocol family/name (parser 1.1.0).
- Document requests deduped per pack+document (was 477 clones → unique work items).
- Admin → Software → Certs groups packs by family; shows criteria + needed document.
- POST /api/library/cert/seed — admin or library writer.
Cert packs auto-seed
Empty certification library was a config gap — BoardLab seeds protocol-catalog packs (USB / Ethernet / HDMI / …) on boot when `cert_library.sqlite` has zero rows. Admin → Software → Certs also has **Seed from protocol catalog**.
- Admin Certs showed 0 packs / 0 document requests because seed was never run.
- CertLibraryAccess.ensure_seeded_if_empty() called from FastAPI lifespan.
- Admin UI button + seedCertPacks() client for manual re-seed (writer).
Menu decisions · PCBWay fab default
Michele menu decisions: Library only in Admin side nav (not top bar); Contact only inside a project; keep Overview|Schema|PCB; kill Exam; Parts tabular; Settings + PCBWay default from PCB layers; boardlab-* downloads; BoardLabMark.
- Top bar: no Library, no global Feedback; project Contact only (admin email).
- Parts table Drafting density; Setup→Settings; kill Exam user copy; useProjectAnalysisOps.
- PCBWay fab auto-default from .kicad_pcb copper layer count (2/4/6L); fab form on Settings.
- Downloads boardlab-eco.csv / boardlab.kicad_dru; BoardLabMark.
Quiet Studio A+1 (chosen)
Michele chose IA **Studio** + look **Quiet Studio**. Guidelines locked in `docs/development/QUIET_STUDIO_UI.md`. Dark accent stays `#007AFF`; dashboard/report/admin chrome aligned.
- Official UI direction A+1 Quiet Studio.
- Page titles 22px; projects table Quiet surface; findings labels.
- Dark theme primary remains system blue.
Library tab restore · Apple Studio shell
Library is again an Admin Software tab (Overview | Jobs | Library | Certs | Users). Project default is **Overview** (run pulse), not the BOM table. BOM lives under Parts. Ops for exam runs extracted to `useProjectExamOps`.
- Library → /admin?tab=library (Config/library redirects; no Config duplicate).
- Project default tab overview; top nav Exam → Overview.
- Workspace secondary bar grouped Parts / Board / Setup.
- ProjectOverviewPanel + useProjectExamOps (UI vs ops split).
Library archive · MySQL admin · JSON
Config · Library Archive tab: no giant in-app tables. Opens Adminer (`/library-db/`) for library MySQL; ZIP backup/download; JSON component archive export/import.
- /config/library Archive | Autoscan (dropped Catalog/Review tables).
- Adminer service library-adminer + Caddy /library-db/ → MySQL admin.
- GET/POST /api/library/archive.json component JSON export/import.
Config · Library (unified)
Top-nav Library opens **`/config/library`** with Catalog | Review | Autoscan. Admin Software no longer duplicates Library (legacy `?tab=library` redirects). Old `/library` redirects here.
- Unified library UI at /config/library (Catalog / Review / Autoscan).
- Top bar Library → /config/library; /library redirects.
- Admin Software nav: Overview | Jobs | Certs | Users (Library removed).
Admin Software Library/Certs polish
Software Overview shows processes in progress; Library lists (components / scan / obsolete) stay behind expand buttons with export/import and autoscan start/stop; Certs adds document requests + explicit source.
- Admin Software Overview: status strip + live processes in progress.
- Library tab: Components / Scan backlog / Obsolete / Autoscan queue on-demand; Start/Stop autoscan; Export/Import labels.
- GET /api/library/cert/requests — open needed_document requests across cert packs.
Admin Software IA + remove onboarding survey
Admin left nav matches Software config instructions: **Overview | Jobs | Library | Certs | Users** as first-class Software items (not a nested bolted-on tab). Ops tools (Projects, Components, Credits, …) sit in a separate group. Onboarding survey no longer appears on first login / dashboard.
- Admin Software IA: Overview / Jobs / Library / Certs / Users are primary nav; removed duplicate Pipeline Runs and Users & Credits peers.
- OnboardingSurvey from dashboard (backend /api/survey left unused).
- Legacy ?tab=software / ?tab=runs deep links map to Overview / Jobs.
Admin Software config · idle autoscan · SMTP seam
Admin **Software** tab: overview (jobs, library, certs, users, autoscan, email) plus Jobs | Library | Certs | Users. Library autoscan is toggleable (persisted, default ON); idle datasheet RESCAN still has priority over popular ingest. SMTP env stubs (`SMTP_*` / `ADMIN_EMAIL`) with no-op `send_email` until credentials arrive.
- Admin Software configuration panel (overview + Jobs / Library / Certs / Users).
- Persisted library_autoscan_enabled (Admin Library tab + PUT /api/admin/settings/library-autoscan).
- SMTP email seam (send_email, invite/reset/admin stubs) + .env.example vars; overview shows configured yes/no (no secrets).
- Local user create/remove + no-pay flag via /api/admin/users.
- Idle refresh / popular ingest respect autoscan toggle without process restart.
Show Zdiff for differential PCB nets
ImpedenceFinder inventory table showed "—" on USB_D± because pair results live in ``zdiff_*``, not ``z0_*``. UI now shows Zdiff (avg / min–max) for differential nets and keeps Z0 for single-ended.
- Impedance panel PCB net table uses Zdiff when is_differential / zdiff_avg_ohms present.
Scrub library MySQL secrets from compose
Remove default MySQL passwords / DSN from `docker-compose.yml`. Library MySQL credentials live only in `.env` (required vars). Healthcheck uses the image helper (no password in compose).
- No LIBRARY_MYSQL_* / DSN defaults in git.
- compose requires LIBRARY_MYSQL_PASSWORD, LIBRARY_MYSQL_ROOT_PASSWORD, BOARDLAB_LIBRARY_MYSQL_URL.
Library rescan priority · MySQL · bundle
Idle **datasheet rescan** (stale parser / missing power) runs before popular new-IC ingest. Component library runtime is **MySQL only** (compose `library-mysql`; SQLite only via explicit test constructor / `BOARDLAB_LIBRARY_ALLOW_SQLITE=1`). Admin Management tab: **download / upload** BoardLab library ZIP.
- Idle refresh does real PDF reparse (1/tick); popular ingest pauses with rescan_backlog.
- LibraryDB.from_env requires BOARDLAB_LIBRARY_MYSQL_URL.
- compose library-mysql + scripts/migrate_library_sqlite_to_mysql.py.
- GET|POST /api/library/bundle + Management download/upload UI.
- Library page Management / Catalog tabs (2.87.41).
Library Management / Catalog tabs
Admin Library page splits the two jobs into top-level tabs: **Management** (LibraryAccess triage — parser/scan/power/review) and **Catalog** (browse ICs/passives/discrete/datasheets + PDF import). Same store; less stacked UI.
- (app)/library/page.tsx — Management vs Catalog tabs; catalog keeps nested type tabs.
Library scan column + skills_manifest fix
BoardLab admin Library table shows **scan** version and a **review** checkbox when a row is stale, missing power, or idle-refresh pending. Restores valid `skills_manifest.json` (missing `{` broke default parser + LibraryAccess queries and idle ingest).
- boardlab/src/backend/skills_manifest.json — valid JSON again (default_model_version 1.19.0).
- scan_version / needs_review on LibraryAccess row summaries + table columns.
- [Note] Popular datasheet ingest loop still runs when idle (pauses during exams); it was failing open on the broken manifest.
BoardLab landing rewrite
Public home is a new **BoardLab** page (navy / copper / teal, loupe mark, Fraunces + Source Sans). Instructions: create project → upload → exam; file needs + link to File guide. Logo pack from 2.87.37 kept.
- (marketing)/page.tsx rewritten end-to-end (hero + how-to + what you need + CTA).
- Landing CTAs use BoardLab teal; signed-in CTA → Projects.
Library tab admin-only
**BoardLab** Library management (catalog + LibraryAccess table) is visible and callable only for admins. Project exam still uses `/api/library/check` for all authenticated users. Logo/casing from 2.87.37 untouched.
- Top-bar / mobile menu: Library link only when isAdmin.
- /library page redirects non-admins to dashboard.
- Backend GET /library, /library/access*, idle-refresh/popular-ingest status (+ ticks) require admin.
BoardLab casing + loupe mark
User-facing product name is **BoardLab** (capital L in Lab). New mark: PCB board edge + lab loupe (navy / copper / teal). Domain stays `boardlab.it`.
- SITE_NAME, metadata, PWA name, nav wordmark, legal/email copy → BoardLab.
- Brand pack public/brand/boardlab-mark.svg (+ wordmark, favicon_io sizes, OG static).
- Favicon / apple-touch / android-chrome regenerated from the loupe mark.
CertifyingEngine E2–E5 (Grade · UI · class · tiers)
**BoardLab** cert engine completes S1: Detect→Instantiate (E1) plus Grade full, Present UI, class-check unify, and evidence-only tiers. Present-only cards; no board-name hardcoding.
- CertifyingEngine.grade consumes pack L0–L2 + AF measured Z (certified / not certified / skip) with non-empty remediation on NO/SKIP.
- protocol_exam grades after L0–L2 so measured facts and interface-class findings fold into one headline per instance (E4 — no contradictory PE-ETH/USBC double FAIL).
- Protocols UI cards read cert_outcome / headline / remediation; class cards suppressed when an engine instance covers that family (E3/E4).
- Speed tiers remain evidence-only (10/100 vs GbE, HDMI 1.4 vs 2.0+ nested); A5/A9/A10 + E2–E5 fixtures in tests/pcb/test_certifying_engine_e2e5.py.
BoardLab brand + boardlab.it dual-host
User-facing product brand is **BoardLab**; primary public URL **https://boardlab.it**.
- SITE_NAME / metadata / UI titles / email + contact copy → BoardLab; default SITE_URL → https://boardlab.it (NEXT_PUBLIC_SITE_URL override still works).
- Caddy dual-host snippet + DNS cutover notes (docs/deploy/caddy-boardlab.it.caddyfile, docs/deploy/boardlab-dns.md).
- Frontend tsconfig excludes scripts/ so Next build does not typecheck selftest .mts under scripts/.
Stack tip: rail capacity + board-map + metaphor + naming + E1
Board-map is the single report shell; metaphor A+B empty/bench copy folded in (no duplicate map). CertifyingEngine **E1** Detect→Instantiate for ETH/HDMI. No ETH recognize patch.
- Board-map report shell + FACT/REQ/INF finding rows (?section= drill-down).
- Report metaphor EMPTY_COPY / bench summary language (dashboard, library, exam, report).
- CertifyingEngine E1 — Detect→Instantiate→Grade stub→Present for ETH/HDMI; catalog recognition hints.
- [Note] Includes live rail source capacity from 2.87.31. ETH recognize evidence branch not merged.
Board-agnostic rail source capacity
Michele: rail power budget must grade PASS/MARGIN/FAIL whenever capacity can be deduced from **library / datasheet / part class / net topology** — never leave USB / charger / LDO rails UNKNOWN for lack of a contact rating, and **never** special-case a project or board name.
- SourceCapacity on library ComponentConstraints (Iout_max / IINLIM / ISYS) — separate from load power_requirements.
- Extract skill + schema: fill source_capacity for regulators/chargers; Wi-Fi/RF SoC TX peak in power_requirements.
- rail_power_budget capacity order: library source_capacity → specs → abs-max → charger on net (IINLIM/ISYS) → USB-IF class estimate (Type-C 1.5/3 A, USB2 500 mA).
- Battery charger / power-path ICs (VBUS→SYS) are rail **sources**; SYS not attributed to a bare header when a charger produces it.
- Taxonomy: ic.power.charger, ic.rf.wifi, connector.usb.type_c; Wi-Fi SoC family estimate for TX peak.
- skills_manifest **1.19.0** — stale library rows auto-reparse on examine.
- Tests: USB-IF class, charger IINLIM/SYS, library source_capacity, Wi-Fi family estimate, no project-name hardcoding.
- [Note] Board-agnostic only. Version **2.87.31** (2.87.30 claimed by undeployed ETH recognize patch).
Cert cards + HDMI/ETH under Certifications
Michele: HDMI and Ethernet must be visible under **Certifications**; each protocol gets a **card** (`USB-3.0 — certified` / `not certified`, blue/red) with requirements **OK** (green) / **NO** (red) and remediation. Hard rule: only protocols **present** on the board; nested lower rates (100BASE-TX under GbE, HDMI 1.4 under 2.0, USB2 under USB3) may PASS when the higher rate FAILs — never invent Gigabit on a 10/100-only PHY.
- Findings tree — PE-ETH / PE-USBC / PE-POE / AF cert headlines join PE-PRT under Certifications → Protocols (no longer buried under Info → Ethernet).
- Protocol UI — cert cards with blue certified / red not-certified headers; hide pure NOT_APPLICABLE instances.
- Recognition — GbE emits nested 100BASE-TX; HDMI 2.0/FRL emits nested HDMI 1.4; RJ45-only for PE-ETH (SFP/midplane MDI nets no longer fake Ethernet jacks).
- [Note] Project c84a208638c3: Ethernet + 1000BASE-T present (no HDMI on board — correctly omitted).
Auto-reparse stale component on examine
Michele: when examining a component whose library row is behind the current parser version, re-parse from the datasheet and KiCad footprint/symbol, upsert into the component library (writer-gated remotely), then continue the exam with fresh data. One MPN only — idle batch stays paused.
- library_reparse.ensure_fresh_for_examine — stale parser_version → sync datasheet extract + KiCad align → LibraryAccess upsert when allowed.
- review_ic_async — runs ensure before pintable/context tools (schema + PCB examine).
- [Note] Local/self-host always writes; remote respects BOARDLAB_LIBRARY_WRITER_IDS (exam still gets workspace-fresh JSON).
- Tests: stale → reparse → aligned; non-writer skips shared upsert.
Rail power budget report card
Michele: dedicated **Rail power budget** card on the report (not only PE-BUD in the findings tree). Per-rail ordinary/peak vs source with PASS/MARGIN/FAIL/UNKNOWN colors, remediation when failing. PE-BUD now also runs in the **PCB** pipeline (was schematic-only for findings; PCB already used rail peak for PE-PWR/VIA).
- build_rail_power_section → ValidationReport.rail_power_budget on schema + PCB reports.
- Report UI PowerRailsSection card + Markdown export section.
- run_pcb_checks includes check_rail_power_budget (PE-BUD-001/002/003/010).
- [Note] Card shows only rails/domains present on the design. Re-run exam to populate on existing projects.
Certifications together in findings tree
Michele: certification findings must live in **one** place in the report tree — not scattered under Error / Warning / Info. The tree keeps severity folders for non-protocol findings, then a single top-level **Certifications → Protocols** branch with every protocol instance and PE-PRT finding. Existing severity and PASS-green colors are unchanged. Rebased onto live 2.87.25 (`cd69f1d`).
- FindingsTree — one Certifications sibling after Error/Warning/Info (not nested per status).
- hasCertifications in findings-forest for the single cert branch gate.
Continuous top-25 IC idle ingest
Michele loop: periodically fetch ~25 most-used ICs → idle-process one-by-one into the **component library** → when empty, refetch, drop MPNs already present, top-up to **25 NEW**, repeat. Not a one-shot seed. Same exam pause as library scan.
- library_popular_sources — DigiKey → LCSC → curated ranking; rate-limited.
- library_popular_ingest — persistent queue, idle one-by-one extract→LibraryAccess upsert, continuous refill.
- /api/library/popular-ingest/{status,tick,refill} (writer-gated remotely).
- [Note] Cert library untouched. No deploy while exam c84a208638c3 runs. Handoff: internal/top25-ic-ingest.md.
Two libraries + writer gate + idle refresh
**Hard rule:** all chip-specific data lives in the **component library** (SoT). **Certification library** = standards/cert packs only. AF joins chip row + cert pack. Remote component-library writes restricted to `BOARDLAB_LIBRARY_WRITER_IDS`. Idle refresh pauses when any exam runs.
- CertLibraryAccess / cert_library.sqlite — cert packs; rejects pintable/power/bus chip fields; **seed skips chip-SoT packs** (e.g. lan8720a-rmii).
- component_chip_seeds — LAN8720A RMII timing/power migrated into component library; lifespan + POST /api/library/seed-chips.
- LibraryAccess writer allowlist (library_auth) on PUT/POST library mutations.
- Idle library refresh loop — stale/missing power|bus; pause on schema/PCB/placement jobs.
- /api/library/cert, /api/library/writers/me, /api/library/idle-refresh/*.
- AF RMII certifies from component bus_requirements only — **no catalog/cert chip-number fallback**.
- Chip bus_requirements / power remain on component extracts (parser_version 1.18.0).
- [Note] Deploy after exam: 2.87.22 then this branch. Protocol catalog may retain archival pack rows; they are **not** SoT.
Deep rail power budget + library access
Deep per-rail **ordinary / peak** current budget (PE-BUD-001/002/003/010) with cited estimates — no silent skip. Library stores **power_requirements** (startup/ordinary/peak) and **parser_version**; **MySQL** is the management store (SQLite local fallback). Callers use **LibraryAccess** only. PCB PE-PWR/VIA prefer rail peak. Z inventory remains 2.87.22.
- rail_power_budget.py — Σ load ordinary/peak vs regulator/connector capacity (PASS/MARGIN/FAIL).
- LibraryAccess + library_db (MySQL/SQLite), /api/library/access pagination (stale / missing power).
- PowerRequirements + parser_version on ComponentConstraints; skills_manifest **1.17.0**.
- Schema capacity leave power_margin_check (series-R IR stays PE-PWR-001); PCB traces use rail budget currents.
- Library UI management strip: parser version + I start/ord/peak filters.
- [Note] JSON library/extracted is write-through cache / import-export, not a parallel read API.
Inventory Z rewrite (AF-only + plane index)
**Michele product rule:** inventory Z computes only on **AF/SI protocol nets** plus nets the project **explicitly requests** (`ProjectFabSettings.impedance_nets`). Walking every routed non-POWER/GROUND net is enormous and useless — no silent full-board fallback. **Hot path rewrite:** `coverage_at` no longer rebuilds Shapely polygons per sample. Zone outlines become a per-board `_BoardPlaneIndex` (layer lists, prepared geometries, STRtree) once; samples only query. `exclude_net` (CPWG) filters the index without re-parsing outlines. Complexity drops from roughly \(O(S \cdot Z_\ell \cdot P)\) polygon rebuilds to \(O(Z \cdot P)\) build + \(O(S \cdot Q)\) queries (\(Q \ll Z_\ell\) candidates from the tree). Mid-run SSE reports `Z i/N (%) net (samples)`.
- nets_needed / analyze_where_needed — AF (+ extra_nets) only; skip reason no AF or requested nets.
- vendor/impedancefinder/planes.py — board plane index + STRtree contains; clear_zone_polygon_cache().
- ProjectFabSettings.impedance_nets — explicit extra Z nets for inventory.
- Inventory progress SSE during Z walk.
Fix `.kicad_pro` open-file accept
Chromium `showOpenFilePicker` rejects extensions with `_` («Extension '.kicad_pro' contains invalid characters»). Create-project no longer puts `.kicad_pro` in the `accept` list — it opens an untyped picker and filters by filename, then still loads sch/PCB via the folder `startIn` step. Invalid-accept errors fall back to folder pick.
- pickKicadProject — omit underscore extensions from File System Access accept; name-filter .kicad_pro; folder fallback.
Create from `.kicad_pro` (sch + PCB)
Create-project now starts from the **`.kicad_pro`** file: after you pick it, BoardLab requests the containing folder (Chrome `startIn`) and loads companion `.kicad_sch` / `.kicad_pcb` (plus hierarchical sheets). Dropping a project folder still works. The raw browser `TypeMismatchError` («path … not an entry of requested type») stays humanized; directory walks only call `getFile` on `kind === "file"` entries. Multi-unit symbols that share one reference across hierarchical sheets (e.g. COM Express `J12` units on `com_express_7_interfaces` + `com_express_7_mgmt`) no longer fail upload with «Duplicate reference». True clashes (same ref, different symbol/footprint) still error with both sheet names and a rename hint.
- Create upload — select .kicad_pro then ingest schematic + PCB siblings (not folder-only / not lone-pro dead end).
- file-upload-zone — getAsFileSystemHandle drop path; skip non-file directory entries to avoid TypeMismatch; DirEntry typing so production next build accepts kind === "file".
- KiCad sch parser — merge multi-unit refs across sheets; actionable error for real duplicates.
- Create-project label/helper: “Select .kicad_pro”.
Clear KiCad folder pick (path type)
Michele saw the browser File System Access error «The path supplied exists, but was not an entry of requested type.» when opening a project after the `.kicad_pro` file-guide highlight. That string is `TypeMismatchError` (file vs directory), not KiCad CLI. Create-project now maps it to a clear hint: select the **folder** that contains `.kicad_pro` (not the file alone, not only `.kicad_pcb`). Lone file drops and folder picks without a `.kicad_pro` get the same guidance.
- file-upload-zone — catch/humanize TypeMismatchError; refuse lone .kicad_pro/.kicad_pcb drops; require .kicad_pro after folder pick.
- Create-project helper + missing-pro/sch copy aligned with “highlight .kicad_pro” intent (folder that *contains* it).
RMII data−clock timing (not skew-as-hold)
LAN8720A Table 5-11 `tsu`/`tihold` are TXD/TXEN setup/hold **to/after rising CLKIN** at the PHY pin. PE-AF-003 no longer treats inter-line skew (`Lmax−Lmin`) as the hold/setup budget. PCB measurable is **data−clock Δt**; full PASS still needs cited MAC clock-to-out (otherwise not certified / INSUFFICIENT). `toval`/`tohold` are PHY output characteristics — INFO cite only, not PCB flight PASS/FAIL. Inter-line skew is INFO FACT when measurable; Table 5-11 has no skew limit. REF_CLK 50 MHz (Table 5-12) unchanged.
- _lan8720a_phy_cert_findings — data−clock model for setup/hold; chip-spec note for toval/tohold; skew FACT only.
- Tests: large inter-line skew must not fail hold; data−clock without MAC tco is not certified.
Highlight `.kicad_pro` in downloads
On the File Upload Guide download / example list, the KiCad **`.kicad_pro`** project file is the primary callout — not buried among `.kicad_pcb`, sheets, or zip companions. Zip archives should contain / start from the `.kicad_pro`. Create-project upload UI unchanged.
- File guide intro, Example files, KiCad export, and PCB sections lead with .kicad_pro.
- Guide markdown styles .kicad_pro code / list rows with quiet emphasis (no badge cluster).
100 MB project uploads
KiCad archives, PCB, stackup/fab docs, and datasheet PDFs may be up to **100 MB** (was 30 MB). The exact client message was `File too large (max 30 MB)` from the project upload router.
- MAX_UPLOAD_BYTES / KiCad MAX_BUNDLE_BYTES / library MAX_DATASHEET_BYTES → **100 MB**.
- [Note] Live Caddy has no request_body cap; the FastAPI check is the gate. No nginx client_max_body_size on this host.
English UI menus
Chrome and project settings labels are English everywhere. No change to finding report content.
- Project settings bar: **Project settings** · Impedance · Domains · Project (was Impostazioni progetto / Impedenza / Domini / Progetto).
- Exam actions and status bar: **Schematic analysis** / **PCB analysis** / Preparation / Cancel (was Analisi schema / Analisi PCB / Preparazione / Annulla).
- Gear sheet + mobile menu link **Project settings**; Stackup / fab section copy and buttons in English.
- Dashboard empty state, upload folder messages, and related chrome strings.
Analytic tpd from stackup ε_r
When fab `tpd_ps_per_mm` is empty but stackup/project cites ε_r (and W/H/T for outer microstrip), length→delay derives tpd analytically — **IPC-2141/Wheeler** microstrip ε_eff, stripline ε_eff=ε_r, `tpd=√ε_eff/c` with `c=0.299792 mm/ps`. Provenance **DERIVED** with formula + inputs. Manual/fab tpd still overrides. Missing geometry → clear FAIL (no invented ε_r). Sanity WARNING outside ~5–7.5 ps/mm FR-4. UI shows calculated tpd when computed from ε_r. Not BEM/FEM.
- tpd_analytic.py — microstrip/stripline formulas + resolve priority (fab > per-layer > project ε_r).
- PE-AF-003 RMII length→delay uses derived tpd with cite_source=DERIVED when fab tpd is empty.
- Fab settings API derived_tpd preview; Stackup/fab UI shows calculated tpd.
- Unit tests for known microstrip/stripline numeric cases + priority/sanity.
Impostazioni progetto (barra interna)
Project settings live in a segmented bar **inside** `/project/:id` (below the TopBar): BOM · Impedenza · Rail · Domini · Stackup / fab · Derating · Progetto. TopBar no longer has a duplicate Settings tab — the gear sheet is **Appearance only**, with a link to Impostazioni progetto.
- ProjectSettingsBar on the project page (?tab= segments).
- TopBar secondary **Settings** tab removed; Exam · Schema · PCB stay.
- Gear Settings sheet = theme/Appearance; link to Impostazioni progetto (?tab=settings / Progetto).
- Stackup / fab has its own segment (?tab=fab); Progetto keeps PCB upload, collaborators, skipped, report version (no Appearance).
Project Stackup & fab settings (tpd)
Project **Impostazioni stackup / fab**: cited tpd (ps/mm, with ps/inch helper), optional εr / note / fab PDF·MD. Empty = unknown — **no** silent 150 ps/in. Curated **JLCPCB** / **PCBWay** import profiles (VENDOR cite; tpd only when the maker page states it). RMII LAN8720A setup/hold uses project tpd for mm×tpd→ns (IMPLEMENTATION/FAB); missing tpd Action points at Stackup & fab settings.
- ProjectFabSettings on project.json; GET/PUT fab-settings, import profile, upload fab doc.
- Curated fab profiles (fab_profiles_data.json) — JLCPCB impedance Dk/stackups + blog ~6/7 ps/mm; PCBWay 4L/6L εr=4.29.
- PE-AF-003 LAN8720A length→delay reads project tpd; Action: “Set tpd in project Stackup & fab settings…”.
- Settings tab UI: Impostazioni stackup / fab + Importa da PCBWay / JLCPCB.
RMII=PHY DS, MII=Clause 22, GMII=Clause 35
Michele model: RMII has no consortium/IEEE PCB cert — certify from the PHY datasheet in use (HubAudio → LAN8720A). MII from IEEE 802.3 Clause 22 (Section Two). GMII from Clause 35 (Section Three). AN-133 is optional helper only.
- HubAudio RMII PE-AF-003 lines are LAN8720A PASS/FAIL (RMII certified|not certified — according to LAN8720A §…); no more INFO “timing cite”; AN-133 Z0 UNKNOWN is no longer the headline.
- mii-pcb packed from Clause 22 (setup/hold 10 ns, TX clock-to-out 0–25 ns, levels).
- gmii-pcb packed from Clause 35 Table 35–8 (125 MHz, setup/hold).
- lan8720a-rmii expands Tables 5-11/5-12 (refclk 50 MHz, toval/tohold, tclkp).
Schema USB Z N/A + ESD TVS + report default
HubAudio audit fix: Schema must not look like a failed PCB USB cert, and PESD TVS on USB_D± must suppress PE-ESD-001.
- Schematic-only USB Zdiff (layout is None) → **NOT_APPLICABLE / silent** — never ERROR “Z cannot be calculated” spam. PCB exam with layout still ERRORs when Zdiff is uncalculable.
- PE-ESD-001 recognizes discrete.diode.tvs, value/MPN TVS / PESD*, and common ESD diode keywords (USBLC, TPD…).
- Bare /report redirects to ?kind=pcb when a PCB report exists (else schema) — Schema/PCB tabs already in 2.87.8 stay the explicit pick.
UI flow: no Docs limbo, login→dashboard, attach + severity colors
- Marketing top-bar **Docs** (/file-guide) — BoardLab/proprietary leftover (“Documentazione”). Product top bar stays Exam · Schema · PCB · Settings.
- Sign-in / sign-up land on **/dashboard** (honor ?next=); signed-in visits to / redirect to projects — no marketing limbo.
- Open project while Schema/PCB runs → **Exam** with bottom status bar (%/ETA); dashboard shows PCB/Schema running (not stale complete); legacy /progress and /pcb always redirect to Exam; start buttons lock with “In corso…”.
- Report tree severity colors: **ERROR** red, **WARNING** yellow, **INFO** blue; Certifications **PASS / certified** green. English labels unchanged.
Exam flow: Schema vs PCB + status bar
Michele grey-zone fix: a schematic reprocess must never look like a fresh PCB report. Exam now has exactly two primary actions — **Analisi schema** and **Analisi PCB** — with separate reports and a bottom status bar (label + % + ETA) instead of the scrolling log wall. New projects auto-run **preparation** only (BOM / datasheets / graph), then wait for an explicit analysis choice.
- Create → MODE=prepare → status ready; buttons start schema (run) or PCB.
- Report ?kind=schema|pcb never mixes findings; header shows label, timestamp, BoardLab version; PCB shows stale if schema re-ran later.
- Bottom RunStatusBar; kill Log tab + progress stepper wall from the main path.
- Top bar: Exam · Schema · PCB · Settings (iPad light shell).
Pair Zdiff calc + LAN8720A timing (HubAudio 46 Ω)
**FATTO for Michele.** HubAudio Ethernet **46.0053 Ω** was **single-ended Z0** (`w=0.2`/`h=0.1`), not Zdiff — ImpedanceFinder did not pair `RJ45_RXP`/`RXN`. Now: pair → always **differential Z** via IPC-2141A `coupled_diff_z` (=2·Zodd); SE → Z0 only; report `zdiff_*` vs `z0_*` split; actionable Action cites the target. Pack **LAN8720A** Table 5-11 timing (`lan8720a-rmii`) and emit **both** bus + PHY PE-AF-003 lines when both apply. Builds on 2.87.6 USB ±15% Zdiff work.
USB Zdiff ±15% tolerance (88≠ERROR)
USB D+/D− compare **Zdiff** to packed 90 Ω ±15% (USB 2.0 §7.1.1.1/§7.1.1.3 cable Z0; PCB `should` RECOMMENDED). Inside band → PASS; outside → WARNING; uncalculable → ERROR. Never SE Z0. Bus cite + datasheet VENDOR are two cert lines. IEEE 802.3-2012 §25.4.9.2.2 Ethernet is nominal 100 Ω with **no** ± band — outside stays ERROR; MDI pairs also use Zdiff only.
UI reorder: one shell, one report tree, KiCad-only ingress
Full frontend cleanup after iPad top-bar landed beside leftover sidebar-era report chrome. Report shows **only** the findings tree (+ Save as Markdown). Project create/replace is **KiCad folder only** — no BOM csv/xlsx or PADS/EDIF entry UI. Soft-bump on the 2.87 line (FPGA 2.88.x stays separate). No deploy until Michele says so.
- Dual report look: old FindingCard dashboard chrome inside the tree; dead ComponentGroup / PcbExamSection; top-bar Protocols tab (duplicate of Report).
- Sidebar stub; BOM/non-KiCad upload options at project ingress.
- Uniform iPad language: top bar chips, tree leaf rows, filters, Exam/Settings surfaces, English Error/Warning/Info labels.
- Ingress copy: Open / Replace KiCad project; BOM built from schematic fields on the server.
U18 PE-PLC-002 thermal vias cite gate
ADAU1467WBCPZ300R (U18) was ERROR for `9 thermal vias … (min_via_count 49)`. The `49` came from datasheet p.203 Figure 87 — an **example** 7×7 via grid (“shows 49 vias”), not a mandatory minimum. Same EP/ESD cite rule: ERROR only for a MANDATORY count with cite; figure/example counts are REVIEW + quote, or skip with no cite.
- placement_check.py PE-PLC-002: mandatory-language gate; EXAMPLE → WARNING/REVIEW with source_quote.
- extract-pintable skill: min_via_count only for “at least N”; figure grids stay note-only. Skills manifest **1.16.0**.
- pytest: ADAU Fig.87 REVIEW; “at least 4” ERROR; uncited skip.
HubAudio AF verify + re-run cue
Live already carried the 2.87.2 PE-AF-002 cleanup, but HubAudio's last PCB exam was still stamped **2.87.0** — so Michele still saw duplicate RD/TD “speed not identified”, ETH_MDIO 100 Ω, Italian RMII/USB tr/f. Replay of the saved HubAudio design/layout graphs confirms the fix. Soft-bump so the report version moves; **re-run HubAudio PCB exam** on live.
- Regression: USB_D+ skips PE-AF-002; protocol owns the English 90 Ω cert line.
- Changelog cue: HubAudio must be re-examined after AF noise fixes.
HubAudio AF noise: MDIO≠MDI, RD≡RX, USB/RMII cert
HubAudio PE-AF-002 cleanup on live 2.87.x. Ethernet MDI aliases (`RD+/−` ≡ `RXP/RXN`, `TD+/−` ≡ `TXP/TXN`) are one cert per pair; LAN8720A supplies 10/100 when the magnetics-side nets lack the PHY MPN. `ETH_MDIO` / `MDC` are management — not IEEE 100BASE-TX 25.4.9. USB D+/D− leave the AF tr/f skip (protocol 90 Ω cert owns them). RMII stays on the AN-133 PE-AF-003 English line.
- si_check.py: is_mdio_mgmt; MDI(?!O) so MDIO is not eth_mdi.
- af_trace_check.py: MDI channel dedupe; graph-wide PHY speed; USB skips PE-AF-002.
- pytest test_hubaudio_af_noise.py; AF geometry tests use HDMI (USB is cert-owned).
Remove coffee/email progress notice
Drop the running-pipeline “grab a coffee / we’ll email you” banner. Progress shows a neutral “Pipeline running…” status instead.
- Coffee icon + email promise on project progress while a run is active.
Live integrate: iPad top-bar, MAC–PHY pack, USB/RMII cert
Combines the iPad top-bar shell (light default, Save as Markdown), the MAC–PHY bus catalog pack (Micrel AN-133, OPEN Alliance RGMII EPL V2.2, Cisco SGMII ENG-46158), USB 2.0 D+/D− cert lines versus the packed 90 Ω cite, RMII not-certified-on-recognition, and the `not_reviewed` count fix. One coherent live version above 2.86.0.
- App shell: TopBar instead of sidebar; theme default light; Markdown report export.
- protocol_catalog.py / catalog.json: rmii-pcb, mii-pcb, rgmii-pcb, sgmii-pcb.
- PE-AF-003 / USB / Ethernet cert lines; J2 silence on missing L1/L2/L3 fields.
- not_reviewed one designator per row; AI failures → review_errors.
- pytest mac-phy / USB / RMII / not_reviewed; frontend shell + export tests.
- Root README.md (+ boardlab/README.md): product copy for 2.87.0 — PCB analysis, protocol cert, findings tree, top-bar UI, Markdown export; Gitea as source of truth.
MAC–PHY bus pack (RMII/MII/RGMII/SGMII)
Packed Micrel AN-133, OPEN Alliance RGMII EPL V2.2, and Cisco SGMII ENG-46158 into the protocol catalog. Only explicitly stated NUMERIC fields with provenance. PCB Z0 / length stay UNKNOWN where the PDF does not state them. PE-AF-003 no longer certifies on recognition alone. HubAudio-style RMII (LAN8720A ETH_RXD/TXD/TXEN) reports `RMII not certified — because …` with the AN-133 cite when PCB impedance is UNKNOWN. SGMII uses the Cisco 100 Ω Rload / Rin 80–120 Ω cite when geometry allows a pair Z. RJ45/MDI 100 Ω stays separate.
- protocol_catalog.py / catalog.json: rmii-pcb, mii-pcb, rgmii-pcb, sgmii-pcb.
- af_trace_check.py PE-AF-003 cert lines.
- standards/protocol-specs/INDEX.md (+ on-disk PDFs, gitignored).
- pytest test_mac_phy_pack.py, test_rmii_cert_line.py.
USB / RMII cert lines; not_reviewed count
USB 2.0 D+/D− is one line: calculated pair Z versus the packed 90 Ω board cite. Certified names the measured ohm and the cite. Not certified says why — Z is outside that cite, or Z cannot be calculated from the PCB stackup/geometry. Missing length, stub, via, termination, rise/fall, and channel fields do not each warn. L3 is omitted when no channel fact is on file. LAN8720A ETH_RXD / ETH_TXD / ETH_TXEN are RMII (single-ended PHY–MAC), not RJ45/MDI 100 Ω. Recognition alone is not certification (fixed in 2.86.0). RJ45 pairs keep `Ethernet certified / not certified`. `not_reviewed` count matches the list (one designator per row). Real PCB AI failures go to `review_errors` with the exception message — not a blanket `pcb_review error` on every skip. ICs already in coverage are not listed as not-reviewed.
- protocol_l1.py, protocol_l2.py, protocol_l3.py, protocol_report.py, af_trace_check.py, si_check.py.
- pcb_validation.py, pcb_pipeline.py, pcb_checks.py merge; report UI copy.
- Protocol tree shows the certification sentence.
- pytest tests/pcb/test_j2_usb_cert_line.py, test_rmii_cert_line.py, test_not_reviewed_count.py.
Exposed-pad names, ESD cites, CC-via-net, Ethernet pair Z
PE-BOM-011 treats EP, EPAD, THERMAL PAD, exposed pad, and the footprint's exposed land (including pin_count+1) as the same pad when both sides have one. A datasheet exposed pad with no land on the footprint stays ERROR. PE-ESD-001 says "datasheet-specified" only with a part and section/page. A 3V3 jack rail and VBUS are not warned without that cite. Optical S/PDIF (TOSLINK/optocoupler) is not an ESD warning; an unverified S/PDIF path stays a visible warning. PE-PRT-L0-001 follows Type-C CC pins (A5/B5) to the net and looks for the packed CabCon Rd 5.1 kΩ. The net does not have to be named CC1/CC2. PE-AF-002 on an Ethernet MDI pair compares one calculated pair Z to the packed cite for the speed on the board (LAN8720A 10/100 → 100 Ω, IEEE 100BASE-TX 25.4.9; Clause 40 only if gigabit). Inside is PASS, outside is FAIL, and no calculation is ERROR. No tr/f request.
- bom_pcb_check.py, esd_return_check.py, protocol_l0.py, af_trace_check.py.
- pytest tests/pcb/test_bom_esd_ep_cc_eth.py.
- Findings tree labels are English: domains, Certifications, Protocols, actions, and empty states. Error / Warning / Info stay.
Apple-like shell; USB-C vSafe still UNKNOWN
Shell Finder/Settings: sidebar, large title, system font, hairline. Findings tree keeps Error / Warning / Info folders (FAIL first) with + disclosure, then domains/rules, then Certificazioni/Protocolli. HDMI 1.4 TMDS electrical displays UNKNOWN — no guessed Zdiff. Alta Frequenza zip and `af-allegati` were searched for vSafe5V / vSafe0V. Contents are TI SI (`snla027b` AN-807, `sdaa499` EMI) — they do not state vSafe voltages. USB Type-C CabCon R2.5 §1.6 p.35 points at USB PD without a volt range. Charger Figure 4-39 4.75–5.5 V stays not-vSafe.
- App chrome (sidebar, globals.css, dashboard list, report large title).
- Findings tree grouping + protocol section inside the tree.
- Protocol UI: HDMI 1.4 TMDS Zdiff/limit/measured show UNKNOWN.
- Frontend tests findings-forest, protocol-report.
- vsafe5v / vsafe0v needed_document; catalog.json.
- INDEX archives AF TI SI as not packed for vSafe.
HDMI 2.0+ TI fields use source_type VENDOR
Michele exception: HDMI 2.0 and FRL electrical/PCB numbers from TMDS1204 / SLLA633 use `source_type=VENDOR` (not HDMI Forum STANDARD). TMDS1204-specific limits (skew, vias, FRL data_rate, SIGDET) are PHY_DEPENDENT with a document/rev/section cite and no number. HDMI 1.4 TMDS stays UNKNOWN. pcbsync.com not packed.
- SourceType includes VENDOR; protocol_catalog.py + catalog.json.
- pytest tests/pcb/test_protocol_pdf_pack.py.
Protocol PDF archive + HDMI 2.0/FRL TI VENDOR overlay
On-disk archive `standards/protocol-specs/` (PDFs gitignored). INDEX.md lists cites. HDMI 2.0 and FRL take TMDS1204 Table 8-6 layout as VENDOR/PHY RECOMMENDED — not HDMI Forum STANDARD. HDMI 1.4 TMDS Zdiff stays UNKNOWN. pcbsync.com not packed.
- standards/protocol-specs/INDEX.md; .gitignore standards/protocol-specs/*.pdf.
- HDMI 2.0 / FRL constraints; protocol_catalog.py + catalog.json.
- pytest pdf-pack / m6.
TI TMDS1204 HDMI layout as VENDOR/PHY only
TMDS1204 datasheet SLLSF57A fills sink/source PCB layout (ZPCB 90 Ω / 75 Ω ranges, skew, length, vias, XTALK −24 dB, 5W spacing, RINT, AC cap) on physical ids `tmds1204-hdmi-sink` / `tmds1204-hdmi-source`. source_type PHY, source_class VENDOR, RECOMMENDED. Generic `hdmi-*` TMDS/FRL Zdiff stays UNKNOWN. SLLA633 is SIGDET wakeup (PHY_DEPENDENT), no layout ohms. pcbsync.com blog not packed.
- protocol_catalog.py + catalog.json.
- pytest tests/pcb/test_protocol_pdf_pack.py, test_protocol_catalog_m0.py.
HDMI CEC electrical from HDMI Specification 1.3
`CEC_HDMI_Specification.pdf` is High-Definition Multimedia Interface Specification Version 1.3 (22 June 2006), not a CEC-only pamphlet. CEC pull-up 27 kΩ, VOL 0.6 V, VOH 2.5 V, device 200 pF / cable 700 pF, interconnect 5 Ω are packed. TMDS/FRL Zdiff/skew stay UNKNOWN (Adopter spec still required). DDC/HPD not packed.
- protocol_catalog.py + catalog.json.
- pytest tests/pcb/test_protocol_pdf_pack.py, test_protocol_m6_m7.py.
1000BASE-T from IEEE 802.3-2012 Section Three
IEEE Std 802.3-2012 Section Three Clause 40 fills 1000BASE-T: 1000 Mb/s, 100 m Class D link, cable 100 Ω, MDI return loss 16 dB (1–40 MHz), pair skew 50 ns, isolation 2 MΩ / 1500 V rms, MDI peak 0.67 V, link delay 570 ns. Cable 100 Ω is **CABLE**; L2 PCB Zdiff stays UNKNOWN. Clause 36 fills 1000BASE-X 1000 Mb/s (optical Zdiff N/A). Insertion-loss formula not packed as a single dB.
- protocol_catalog.py + catalog.json.
- pytest tests/pcb/test_protocol_pdf_pack.py, test_protocol_m6_m7.py.
10BASE-T from IEEE 802.3-2015 Section One
IEEE Std 802.3-2015 Section One Clause 14 fills 10BASE-T: 10 Mb/s, 100 m design-objective link, cable 100 Ω ± 15 Ω, 11.5 dB link insertion loss, MAU TD return loss 15 dB, isolation 2 MΩ / 1500 V rms, TD peak 2.2 V. Cable 100 Ω is **CABLE**, not L2 PCB Zdiff. 1000BASE-T (Clause 40) is not in this volume.
- protocol_catalog.py + catalog.json.
- pytest tests/pcb/test_protocol_pdf_pack.py, test_protocol_m6_m7.py.
Type-C CabCon R2.0 hole-fill (CC shall-detect; VBUS from R2.5)
USB Type-C Cable and Connector Specification Release 2.0 (August 2019) fills Source CC shall-detect voltages that R2.5 only illustrates as variance examples (Table 4-32 p.240: vRd 1.50 V, vOPEN 1.65 V, vRa 0.15 V). Type-C Current charger VBUS 4.75–5.5 V no-load / 4.0–5.5 V at 3 A, zOPEN 126 kΩ, tCCDebounce 100–200 ms, and tVBUSON 275 ms stay on newer CabCon R2.5 (Figure 4-39 / Tables 4-30, 4-32, 4-34). Rp/Rd/Ra unchanged. vSafe5V volt range still needs USB PD.
- protocol_catalog.py + catalog.json.
- pytest tests/pcb/test_protocol_pdf_pack.py.
Protocol packs from USB 2.0 Rev 2.0 and Type-C CabCon R2.5
USB 2.0 Base Specification (April 27, 2000) fills LS 1.50 Mb/s, FS 12 Mb/s, HS 480 Mb/s, pull-up/down, VBUS 4.75–5.25 V, LS/FS edges. Cable Z0 90 Ω is `source_type=CABLE`; HS PCB traces *should* 90 Ω is RECOMMENDED PCB — L2 `differential_impedance` stays UNKNOWN so 45/50 Ω fixtures are not treated as Zdiff. Type-C Cable and Connector Specification Release 2.5 fills Rp/Rd/Ra from Tables 4-27/4-28/4-29. VBUS volts still need USB PD vSafe5V. IEEE 802.3 Section One/Three and HDMI Adopter remain missing.
- protocol_catalog.py + catalog.json.
- pytest tests/pcb/test_protocol_pdf_pack.py.
Protocol packs from recovered PDFs (USB CTS, Type-C FTS, DVI 1.0, IEEE 802.3-2012 §2)
NUMERIC constraints now carry title, revision, section/table, and page from the recovered PDFs. USB 2.0 HS fills data rate / rise-fall / 1.5 kΩ pull-up / DCR from Electrical CTS v1.08; Zdiff stays UNKNOWN (CTS does not state 90 Ω). Type-C Rp/Rd/VBUS volts stay UNKNOWN (Functional Test Spec points at Cable and Connector tables). DVI 1.0 is a separate protocol from HDMI. IEEE 802.3-2012 Section Two fills 100BASE-TX cabling 100 Ω / 100 m / isolation and 100 Mb/s; 10BASE-T Clause 14 and 1000BASE-T Clause 40 are not in Section Two.
- protocol_catalog.py + catalog.json; DVI recognition.
- pytest tests/pcb/test_protocol_pdf_pack.py.
Protocol certification M6/M7 (USB-C / Ethernet / HDMI packs)
USB 2.0 (LS/FS/HS) is the protocol; USB Type-C is a connector/interface system; USB 2.0 over Type-C is the physical implementation. Type-C does not imply USB3 or USB4. Mini HDMI is a connector, not a protocol. Ethernet variants (10/100/1000/2.5G/5G/10G, SGMII, 1000BASE-X, 100BASE-FX, 10GBASE-R) are incomplete skeletons. All listed numeric fields stay **UNKNOWN** with `needed_document` (USB-IF / IEEE 802.3 / HDMI Adopter). No invented ohms, no typical-as-standard, no OpenEMS.
- protocol_catalog.py + catalog.json; needed_document on UNKNOWN/MISSING_SOURCE.
- USB/Ethernet/HDMI recognition: connector alone is not the protocol.
- pytest tests/pcb/test_protocol_m6_m7.py.
Protocol certification M10 (catalog skeletons)
Catalog skeletons and `required_checks` for **PCIe / CXL** (logical vs physical), **I2C**, **CAN**, **RGMII**, plus missing high-speed processor ids (PCI, PCI-X, CCIX, OpenCAPI, UPI, DMI, Infinity Fabric, HyperTransport, UCIe). Numbers stay **UNKNOWN / MISSING_SOURCE / PHY_DEPENDENT** until an official cite is in the pack. `pcb_relevant` is YES or CONDITIONAL as appropriate. No invented ohms, no USB-IF fill, no OpenEMS.
- protocol_catalog.py + committed catalog.json.
- pytest tests/pcb/test_protocol_catalog_m10.py.
Protocol certification M9 (L3 channel + visible skip)
L3 **CHANNEL** certifier: structure for timing budget (TOTAL−USED=MARGIN), insertion loss, return loss, crosstalk, and complete channel. Numerical PASS/FAIL only when **existing channel FACT** (dB/ps) is already present — never invented S-parameters, never OpenEMS/FEM. Otherwise a visible skip: «Interfaccia X non certificata a L3 per mancanza di …». Internals (AXI/HBM) stay NOT_APPLICABLE. L0/L1 are not electrical. No M6 numeric packs.
- protocol_l3.py; PE-PRT-L3-001 / PE-PRT-L3-002.
- pytest tests/pcb/test_protocol_l3_m9.py.
Protocol certification M8 (DDR instance + L1 grouping)
DDR **physical instances** are controller-aware: one instance per memory device, DQ/DQS **byte lanes** separate from ADDRESS / COMMAND / CONTROL / CLOCK. Skew/length stay **CONTROLLER_DEPENDENT / PHY_DEPENDENT / UNKNOWN** — no universal mm/ps/ohm. HBM is package/interposer (`pcb_relevant: NO`); classic DDR PCB DQ rules are not applied. M6 numeric packs and L3 solver are not in this release.
- protocol_ddr.py; L1 grouping reconstruction without PCB geometry.
- pytest tests/pcb/test_protocol_ddr_m8.py.
Protocol certification M5 (Protocolli report)
Report section **Protocolli**: recognised instances, max level L0–L3 (L3 is a visible not-run skip, no solver), each check with measured / limit / margin / source / method, traceability chain net → group → physical interface → protocol → constraint → document → section. FAIL is listed first and styled as Error — not hidden under Warning (HubAudio). Visible skips stay on the page. No pack numbers, no invented Z, no OpenEMS.
- protocol_report.py; Protocolli table UI.
- pytest tests/pcb/test_protocol_report_m5.py; protocol-report.test.ts.
Protocol certification M4 (L2 electrical)
L2 ELECTRICAL certifier: impedance, termination, voltage/levels, rise/fall when sources exist. Z only from datasheet / stackup / fabricator / **cited pack** — never invented 90 Ω USB. USB-IF/IEEE only if the pack has a cite. Silicon cross is max PCB × PHY subset when datasheet windows exist. Length is not delay. No L3/OpenEMS. L0/L1 are **not** electrical. RECOMMENDED never FAIL. MISSING_SOURCE / UNKNOWN when cite is absent. Visible skip when Z cannot be obtained.
- protocol_l2.py; PE-PRT-L2-001 / PE-PRT-L2-002.
- pytest tests/pcb/test_protocol_l2_m4.py.
Protocol certification M3 (L1 geometric)
L1 GEOMETRIC certifier: length, topology, vias, stubs, layer, grouping. Geometric skew only vs **NUMERIC** millimetre or an explicit **DESIGN LIMIT**. Otherwise UNKNOWN / VENDOR_DEPENDENT / CONTROLLER_DEPENDENT / PHY_DEPENDENT — never invent mm/ps. L1 is **not** electrical. No L2/L3. RECOMMENDED never FAIL. Internal interfaces stay NOT_APPLICABLE. Missing DQ/DQS grouping is a visible skip, not PASS.
- protocol_l1.py; PE-PRT-L1-001 / PE-PRT-L1-002 / PE-PRT-L1-003.
- pytest tests/pcb/test_protocol_l1_m3.py.
Protocol certification M2 (L0 structural)
L0 STRUCTURAL certifier: nets must exist and connect at least two components. FAIL only for missing **MANDATORY** structure. AXI4 / internal interconnects are **NOT_APPLICABLE** (not FAIL). RECOMMENDED never FAIL. No L1–L3, no invented ohms/mm/ps.
- protocol_l0.py; PE-PRT-L0-001 / PE-PRT-L0-002.
- pytest tests/pcb/test_protocol_l0_m2.py.
Protocol certification M1 (instance recognition)
`physical_bus_instance` from declaration / silicon / part / pin / net / pairs. Output: instances, signal groups (DIFF pair, byte lane, clock/address), confidence. Ambiguity is **REVIEW**, never FAIL. AXI4 internal is recognised with `pcb_relevant: NO` and **no PCB nets**. No L0–L3 certifier. No invented ohms.
- protocol_recognize.py; report Protocolli lists recognised instances or stays empty.
- pytest tests/pcb/test_protocol_recognize_m1.py (USB/DDR fixtures; AXI has no nets).
Protocol certification M0 (catalog, no numbers)
Versioned JSON protocol pack + Python loader/validator. Catalog skeletons for USB2 / USB-C-USB2 / 100BASE-TX / HDMI (connector distinct) / DDR / LPDDR / HBM / HyperBus / QSPI-octal / eMMC / UFS / AMBA / Wishbone / Avalon / TileLink / PCIe-CXL. Constraints without an official cite are UNKNOWN / CONTROLLER_DEPENDENT / PHY_DEPENDENT / MISSING_SOURCE — **no invented 90/100/50 Ω**. `logical_protocol_id` ≠ `physical_interface_id`. `pcb_relevant` is YES|NO|CONDITIONAL. RECOMMENDED is not FAIL-mandatory. Typical-as-standard is rejected by the parser. Report section **Protocolli** is empty (`nessun protocollo riconosciuto`). Recognition and L0–L3 certifiers are not in this release.
- protocol_catalog.py, protocol_data/catalog.json, schema 1.0.0.
- protocol_certification on ValidationReport; UI section Protocolli.
- pytest tests/pcb/test_protocol_catalog_m0.py.
AF trace analysis (trigger, cascade, visible skips)
PCB exam adds **AF trace analysis** after unchanged `run_pcb_checks` (SI/HF stay). Trigger is λ/10 or tr/(6 tpd) from FACT only. Differential pairs are one unit. Z0/Zdiff only vs a **datasheet** window. Missing tr/f/εr/stackup/Z target/via span is a visible INSUFFICIENT finding («Pista ad alta frequenza non controllata per mancanza di …»), not invented 50/90 Ω. Trigger false → no AF finding. Numerical cascade is lossless RLGC sections (ImpedenceFinder Z0); not OpenEMS/FEM. Python. PE-AF-001 AI path unchanged.
- check_af_traces / af_trigger / af_rlgc; pipeline stage af_trace.
- PE-AF-002 visible skip; PE-AF-020…061 on triggered nets.
- Via (layers …) parsed on LayoutVia (span; size is not a track).
- Changelog / PCB progress lists AF trace before AF+AI.
Report Error group, J2, U18 max severity
The findings tree always has an **Error** folder when ERROR findings are visible. Group badge is the worst child (U18 PE-PLC-002 ERROR is not labeled WARNING). Connectors (J2) sit in the same sidebar as U*. PE-PLC stays in the PCB exam bucket. No auth change.
- Status folders Error / Warning / Info; severity = max of children.
- J2 and other non-U refs listed with ICs.
- isPcbExamFinding matches layout findings (PE-PLC with PE-BOM).
Module die, truncated QFN pintable, QFN=VQFN
HubAudio software leftovers on PE-BOM-010/011: a WROOM module is not the ESP32 die pintable; a QFN-48 land is not wrong because extraction stopped at pins 1–24; VQFN-24 4×4 is the same QFN family as datasheet QFN-24. Extra signal pads above the declared QFN-n still ERROR. Pad ≠ via. Kelvin unchanged.
- PE-BOM-011 skips die-pintable extras on WROOM/WROVER module footprints (U11).
- PE-BOM-011 skips QFN-n pads the pintable never listed (U12 25–48).
- PE-BOM-010 treats VQFN/WQFN/HVQFN as QFN (U19).
PE-BOM-011 joins pins by name, not pin_count
Datasheet `pin_count` and PCB pad cardinality are incommensurable (USB-C 16P vs SH/mount/EP). PE-BOM-011 joins the pintable × KiCad `.kicad_mod` × embedded PCB pads by **pin name**. USB-C A5/B5 are CC pins, not EP. Missing pintable is a skip, never ERROR on `pin_count=16`. The library footprint is checked against the datasheet; lib vs embedded PCB is a separate mismatch. AI still extracts the table and runs review/class/SI — it does not count pads. Pad ≠ via.
- PE-BOM-011 no longer treats [A-Z]\\d+ as EP (HubAudio J2 16 vs 1 SH).
- Scalar package_info.pin_count is not an ERROR authority.
- Named join: datasheet pintable vs lib vs PCB; SH/EP/mount classified first.
Library component from PDF; AF+AI beside PCB checks
Library import registers a **component** (not only a PDF blob) with no exam. AF Board+AI is a pipeline section **after** unchanged `run_pcb_checks` (PE-SI / HF line stay in 2.62.1).
- POST /api/library/datasheets writes an IC inbox card or a passive/discrete model. Empty pintable never lands in library/extracted/.
- PCB pipeline stage af_ai calls append_investigated beside run_pcb_checks. Duplicate SI clones are not double-counted. PE-AF-001 stays additive.
- Report tree root “AF Board + AI”. Progress step between deterministic checks and PCB AI exam.
Library as its own product door
Standalone `/library` + `/api/library`: browse, add a datasheet PDF without an exam, update a component card. Datasheet prelim stays on the analysis path. Tests grouped datasheet → library → schematic → PCB → AF+AI (extra HF flags, then deterministic investigate). Rust criteria documented; no rustup. FEM out. DRC remains KiCad.
- POST /api/library/datasheets stores a PDF by MPN with no project id.
- GET/PUT /api/library/components/{type}/{mpn} for extracted cards.
- Library page import form; empty library no longer says “run a review”.
- investigate_hf_hypotheses (PE-AF-001) — AI Z0 flag without stackup is INSUFFICIENT, never 50/90 Ω. Existing run_pcb_checks unchanged.
PCB analysis remainder (Phase B)
Stackup vs fab spec, via current with datasheet I (no IPC via chart), ESD pad distance TVS–connector, USB-PD contract, PoE isolation voltage, antenna keepout/matching, PDN Z(f). Same pipeline and finding quality as 2.61.2 / 2.62.0. Skip if no evidence. Not DRC. Not FEM.
- PE-STK-001 board copper thickness vs sourced fab stackup (never 1 oz).
- PE-VIA-002 I + via count/drill FACT; IPC via ampacity is not applied.
- PE-ESD-002 TVS–connector **pad** distance (via ≠ pad); INFO if no mm.
- PE-PD-001 USB-PD only with a PD controller (not USB2 Rd).
- PE-POE-004 isolation voltage only from a sourced number.
- PE-ANT-001/002 antenna keepout and matching if an antenna exists.
- PE-PDN-001 Z(f) only with frequency-domain FACT; SI Z0 is not PDN.
HF lines/buses in PCB checks (Phase A)
USB / Eth / DDR / MIPI / PCIe / HDMI / LVDS / HF clock in `run_pcb_checks` at the same finding quality as 2.61.2. Pair integrity: stub (track only; via ≠ pad ≠ zone), reference-split under the line, BOM termination. CPU data/address/control and FPGA only if that device is on the graph. Skip if no evidence. Not DRC. Not FEM. Datasheet millimetres only; no invented Z/I.
- PE-SI-007 dangling track stub vs datasheet mm (INFO if no mm).
- PE-SI-011 GND-pour gap under an HF pair (REVIEW; skip without stackup/zone).
- PE-SI-012 BOM series/parallel terminator FACT (skip if none).
- PE-DDR-001…003 DRAM class / CK-DQS-DQ / VTT only if DRAM present.
- PE-CPU-001…004 parallel CPU bus only if D/A/control exist.
- PE-FPGA-001…003 FPGA class / config flash / VCCINT·VCCIO only if FPGA present.
- bus_class adds MIPI and leftover HF clocks; XTAL/OSCIN stay out.
Certifiers only for connectors on this board
USB-C / RJ45 / PoE / DDR3 are the method, not a catalog. If this graph has a USB-C receptacle, run USB-C questions. If it has RJ45, run Ethernet. PoE only with PoE evidence — never because RJ45 exists. No connector → no that certifier (not N/A). HubAudio has USB-C USB2 + PoE 10/100 MagJack and no DDR: no DDR findings. Skip-I (2.61.1) unchanged. No FEM, no via IPC, no invented I/Z/PoE/SuperSpeed.
- Absent USB-C / RJ45 / PoE no longer emit INFO N/A.
- Bare RJ45 is Ethernet only; PoE is skipped without PoE evidence.
Skip trace-width vs current without datasheet I
PE-PWR-001 (and PE-VIA-001) run only when the datasheet reports I_load / Imax / I_abs. Missing that number is a skip — not INFO INSUFFICIENT, not a typical USB 500 mA, not Iout_max-as-load. USB-C / Ethernet / PoE class certifiers still have no current/ampacity check. No via IPC table. No FEM.
- PE-PWR-001 / PE-VIA-001 skip when datasheet current is absent.
- Imax / I_abs accepted for width; Iout_max is still not load.
Interface class certifiers (USB-C, Ethernet, PoE)
Dedicated per-interface certifiers (deterministic + AI explanation). Not generic SI. USB-C: CC1/CC2, Rp/Rd 5.1 kΩ, VBUS/GND, SuperSpeed only if the class is USB3. Ethernet: 10/100 vs GbE; magnetics required for GbE. PoE only with PoE evidence — a bare RJ45 is N/A, never invented PoE. Missing generation/class/ohms → INSUFFICIENT. DDR3 and via IPC ampacity are out of this slice. No FEM.
- PE-USBC-001…005 USB-C class + CC + Rd/Rp + VBUS/GND + SuperSpeed gate.
- PE-ETH-001…004 Ethernet 10/100 vs GbE, TX/RX pairs, GbE magnetics, Bob Smith REVIEW.
- PE-POE-001…003 PoE evidence / class / isolation; bare RJ45 is N/A.
- Taxonomy connector.rj45. AI prompts must not invent PoE or SuperSpeed.
PCB software false positives
KiCad 9/10 boards have no numeric net table (`(net "GND")` on pads/tracks). The net index is filled from those names. PE-LAY-004 counts copper on **tracks, vias, and pours**, skips `unconnected-(…)` NC nets, and matches `/sheet/` prefixes — it must not report ~139 “unrouted” nets when KiCad DRC `unconnected_items` is ~30 (islands on already-named nets). Missing `I_load` is INSUFFICIENT on PE-PWR-001 / PE-VIA-001. Thermal/EP extra pads are not PE-BOM-011 ERROR. Voltage abs-max binds to the named pin. Kelvin ignores CRS/IN±. EMI/PI skip NC nets. LQW18AN `murata_lqw_inductance` decodes nH. FB/BLM beads are ferrite; DCR is not Z. Thinking `reasoning_content` echo unchanged.
- Empty LayoutGraph.nets on KiCad 9/10 name-only nets.
- PE-LAY-004 zone/via/NC join vs DRC unconnected order of magnitude.
- PE-PWR-001 / PE-VIA-001 emit INSUFFICIENT without I_load.
- PE-BOM-011 EP/thermal extras; PE-BOM-012 pin-bound abs-max.
- PE-KEL-001 / PE-EMI-001 / PE-PI-001 NC and IN± / CRS false positives.
- LQW decoder; BLM/FB not typed as a DCR resistor.
Hierarchical sheets + DeepSeek thinking echo
Apri progetto still skips `.history` / `.git` / backups so the folder pick does not hang. It now keeps **every** linked schematic: all `*.kicad_sch` next to the `.kicad_pro`, plus `Sheetfile` / `(sheet` / `file=` children in a subfolder. Not only `HubAudio.kicad_sch`. DeepSeek thinking-only review turns (HubAudio U11/U12/U38) 400'd because the next request dropped `reasoning_content`. The native client echoes it on the follow-up. Retry failed IC reviews after this deploy.
- USB / Codec / POWER (and other hierarchical modules) are ingested with the root sheet.
- Nested sheet paths are followed; .history copies are ignored.
- Thinking-mode reasoning_content is passed back on subsequent turns.
Apri progetto does not walk forever
After folder permission, 2.60.8 kept `Next` disabled because `showDirectoryPicker` then recursed `dirHandle.entries()` / `getFile()` through the whole tree (HubAudio `.history` is ~2000 files). We only read the project directory: `.kicad_pro`, `.kicad_sch`, `.kicad_pcb`. Timeout shows an error instead of a spinner forever. Next enables when a `.kicad_pro` is in.
- Folder pick no longer hangs on nested dirs / permission / giant trees.
- Next on step 1 when .kicad_pro is present.
Folder picker, not Carica file dialog
Apri progetto opened the Mac file list (**Carica**, no row selected) because the control was a React `<input type="file" accept="">` without a working `webkitdirectory`. Empty Carica then looked like “no .kicad_pro”. It now uses `showDirectoryPicker` (Chrome) or a hidden input created with `webkitdirectory=true` and **no accept**. Cancel/empty pick is ignored.
- One folder gesture; .kicad_pro from name or webkitRelativePath.
Detect HubAudio.kicad_pro after folder pick
Apri progetto was rejecting the folder that actually contains `HubAudio.kicad_pro`. Safari/macOS folder pick often puts the real name in `webkitRelativePath` while `File.name` is the stem without `.kicad_pro`, and React was not setting `webkitdirectory` on the input. We match name **or** relative path, sniff JSON if the suffix is missing, and set the folder attribute on the DOM.
- Folder that contains HubAudio.kicad_pro is accepted.
- webkitdirectory set via DOM setAttribute so Safari/Chrome recurse.
Apri progetto KiCad (cartella)
Un controllo: **Apri progetto KiCad**. Si sceglie la cartella dove sta il `.kicad_pro`. Carichiamo root + fogli hierarchical `.kicad_sch` e il `.kicad_pcb` fratello. Niente netlist, niente CSV. MPN solo da proprietà schematico (PNM/MPN/Value). Uno `.asc` PADS vecchio nella stessa cartella è ignorato.
- Folder picker / drop della cartella progetto.
- .kicad_pro da solo non finge di avere lo schematico.
- Connectivity dallo schematico, non da un netlist stantio (niente U13 fantasma).
Hierarchical schematic MPN when BOM omits a ref
graph_build reads `PNM` on `.kicad_sch` symbols (all nested sheets). If the CSV has no row for a designator, the child-sheet `PNM`/`MPN`/IC `Value` still fills `mpn`. Empty child-sheet fields stay empty — no invented part numbers. A sibling `.kicad_sch` next to a PADS netlist is consulted the same way.
- KiCad property PNM is an MPN alias on symbols, not only on BOM CSV.
- Hierarchical fixture: U13 on a child sheet with PNM, omitted from CSV, still gets TPD2E007DCKR.
KiCad PNM and honest missing-MPN skips
KiCad BOM column `PNM` is an MPN alias. Grouped `Reference` lists still split. IC `Value` fills MPN when PNM is blank (PCA9534ARGTR). Netlist-only U* with no BOM row are `INSUFFICIENT_EVIDENCE` (quoted Value/Footprint), not a silent drop and not an invented part number.
- Progress skip text no longer says “no MPN in BOM” when the designator is absent from the CSV.
- HubAudio KiCad BOM fixture: PNM header, U2/U3 Value fallback, L4 bead PNM, U13…U39 not invented.
Ferrite Z from library MPN datasheet
graph_build looks up the bead MPN PDF in project `uploads/datasheets` and `library/datasheets` (named files and blob refs). Z is filled only when the PDF states impedance (e.g. 120 Ω @ 100 MHz). Missing Z stays omitted; graph_build does not abort.
- HubAudio-style beads recover impedance_ohm from the manufacturer PDF, not from inventing Z or stuffing ohms into value_henries.
- Tests: MPN + datasheet PDF fills Z; MPN + PDF without Z does not invent and still loads.
Ferrite Z from datasheet, no graph_build abort
Beads with an MPN pull `impedance_ohm` from the datasheet or a quoted `N ohm @ freq` string. Missing Z after that is INSUFFICIENT, not a crash. Ohms are never written to `value_henries`.
- graph_build loads DCR-only beads; leftover ferrite bead requires impedance_ohm removed.
- Ferrite Z recovered from datasheet PDF / quoted value; extract-specs skill 1.15.0.
Ferrite bead without Z still loads
Ferrite beads that only have current/DCR validate. Missing `impedance_ohm` is not invented and does not crash `graph_build`. Other inductors still need `value_henries`.
- InductorSpecs no longer requires Z on passive.ferrite_bead.
- Bad models/*.json are skipped with a log, not a pipeline abort.
Incomplete PCB exam and owner project delete
Unfinished placing/routing still uses MODE=pcb. Parser and checks run on partial layouts; missing copper is INSUFFICIENT or skip, never invented millimetres. Via≠Pad unchanged. Dashboard owners can delete a project after a confirm dialog.
- Decoupling proximity uses routed copper only (PE-PLC-005 when unrouted).
- PE-LAY-004 INFO when pad nets have no tracks.
- Owner-only DELETE /api/projects/{id} with confirm dialog; collaborators get 403.
Analysis pipeline: project owner + LCSC lookup
- Project meta writes follow owner_user_id (Emmaforo JWT prefix).
- Exact-MPN LCSC reverse lookup; prefix hits rejected.
BoardLab mark
New BoardLab mark (boardlab/lens) at the existing favicon and PWA pixel sizes.
- Brand mark: public/brand/boardlab-mark.svg, favicon_io 16/32/180/192/512, header BoardLabMark.
PCB not_reviewed is board ICs, not the shared library
Emmaforo showed 20 “components not reviewed” that were other projects’ 1.5–1.7 extracts (LAN8720A, W25Q, …). BOM ICs were already 1.13.0 and *were* reviewed.
- si_extract_needed_skips only flags ICs on this graph. Shared library/extracted cache is not not_reviewed.
SI bus gate (EN RC ≠ USB series R)
Datasheet SI facts apply only to the bus in the quote. ESP32 EN RC 10 kΩ / 1 µF is not a USB D+/D− series resistor. USB without a library Z number stays measurement-only (88 Ω), not a 90 Ω FAIL. Coverage: PHY+RJ45 MDI, MAC–PHY RGMII/SGMII, DDR3, USB-C SuperSpeed.
- PE-SI-009 (and other SI kinds) require net_class / quote bus match; strap/EN RC is skipped.
- bus_class: usb2 / usb3 / eth_mdi / rgmii / sgmii / ddr3_*.
- PE-SI-010 is per bus with no impedance FACT (not silenced by an unrelated SI kind).
- Pintable skill 1.13.0: SI net_class required; EN/CHIP_PU RC is not series_resistor.
BOM↔PCB↔datasheet, SPOF, EMI FACT, gated Tj
Remaining Fase B slices except auto-place and thermal FEM / OpenEMS.
- PE-BOM-010…014 package / pinout / voltage / temp / current when BOM, PCB, and datasheet all have numbers.
- PE-SPOF-001 single regulator or shared crystal — REVIEW, never ERROR.
- PE-EMI-001 only if layout_rules quotes EMI/common-mode/ferrite/shield (not IEC 61000).
- PE-THM-002 Tj = Ta + P·θJA when P, θJA, copper area, and via count exist; else INSUFFICIENT.
- Pintable skill 1.12.0: SI+EMI kinds; empty SI on older extracts triggers schematic re-extract (si_extract_needed.json on PCB).
ImpedenceFinder SI checks (not a dump table)
USB/HDMI/PCIe/ETH/LVDS/DDR layout_rules (diff/SE Z, skew, max length, spacing, ref plane, vias, layer, return, series R) are compared to ImpedenceFinder + copper. One PASS/FAIL/MARGIN finding per requirement. I2C/GPIO/CC/REGN are not 50 Ω.
- PE-SI-002…PE-SI-010; ImpedenceFinder z0_avg/min/max in run_pcb_checks.
- PE-SI-001 only on impedance-controlled buses matching the rule net_class.
- extract-pintable layout_rules kinds for SI; default_model_version 1.11.0.
PCB complete UI, not a raw SSE error
A finished PCB exam no longer shows the raw watcher string `pcb_status=complete (terminal)`. The SSE hatch emits `pcb_complete`; the PCB page then opens `/report?domain=layout` (expand-on-click tree).
- Synthetic PCB SSE for pcb_status=complete is pcb_complete, not pcb_error.
- /project/[id]/pcb navigates to the layout report when the run finishes.
- Restored changelog heading for 2.33.0 (version history).
ESD/PI filters, ERROR-first sort, findings tree
PE-ESD-001 only on J* ∩ IC nets (no NC / unconnected / VSYS/GND/3V3 spam). PE-PI-001 treats any capacitor on the rail, including KiCad `/` names, as decoupling. Report API and UI sort ERROR then WARNING then INFO, then RULE > RISK > REVIEW > INFO. The report left list is an expand-on-click tree (IC → pin/net → cards), not a dump of open groups.
- Connector GPIO / onboard rails no longer flood PE-ESD-001.
- Bulk or slash-prefixed caps (C68 /VBUS, C71–C75 VSYS) suppress “no local cap”.
- GET /report and complete_findings order by severity then class.
- Findings sidebar is a collapsed tree; PCB exam cards live in the same tree.
Fase B: hierarchy, derating bands, timing, PI, ESD/return
Usable slices (not a platform): component→pin→net→block inventory, capacitor PASS/MARGIN/RISK from Vop/Vrated, RC/strap timing only with numbers, local vs bulk PI, ESD and HS return as REVIEW.
- complete_finding clamps ERROR unless finding_class=RULE and provenance=MANDATORY (LLM REVIEW cannot stay ERROR).
- hierarchy.json + PE-HIER-001 dangling IC pins.
- Derating table stress PASS/MARGIN/RISK; findings PE-DRT-001/002/003.
- PE-TIM-001/002 skip without R/C and t_reset or Vih/Vil.
- PE-PI-001/002 local vs bulk; distance only with layout_rules mm.
- PE-ESD-001, PE-RET-001 are REVIEW, never RULE ERROR.
PCB Action on card + parsed geometry in AI context
Finding cards always show an **Action.** sentence (`action`, else `recommendation`, else a default). PCB AI context now includes vias under the footprint, copper thickness, nearby trace widths, courtyard, and keepout polygons from `.kicad_pcb`.
- Layout/AI findings no longer show an empty Action chip; the sentence is on the card.
- PCB exam injects parsed via counts/drills, stackup thickness, pin-net widths, courtyard/keepout. Insufficient evidence only when those extracts are empty.
- submit_review recommendation is required for INFO as well as ERROR/WARNING.
Finding engine (schema + PCB)
Same finding object on schematic analysis and PCB exam: FACT / REQUIREMENT / INFERENCE, datasheet provenance in the rule DB, RULE vs REVIEW, designer decisions, independent severity/confidence/evidence.
- docs/motore-finding.md — engine spec. finding_engine.py + decisions.json.
- Report cards show Fact / Requirement / Inference, class, provenance, insufficient evidence.
- Recommended datasheet notes cannot stay ERROR. LLM findings are REVIEW, never RULE.
- Schematic and PCB share library/extracted; PCB does not re-read the PDF.
Shared library PCB exam (no second PDF pass)
Schematic and PCB share `library/extracted`. PCB AI consumes that cache (pintable required; no PDF attach). Report **PCB exam** section plus power-trace/thermal checks.
- Report section **PCB exam** (source=pcb_review and PE-PWR/THM/VIA/KEL/STCH).
- PE-PWR-001 width × copper vs I_load (IPC-2221 only with stackup + Tjmax).
- PE-THM-001 dissipation vs courtyard vias/pour; PE-VIA-001 via share of I_load (INFO).
- PE-KEL-001 Kelvin/sense on a shared load net; PE-STCH-001 GND stitch in a signal bbox over a pour.
- PCB review skips ICs without library pintable (“run schematic review first”).
PCB review pipeline (exam, not auto-place)
Parallel `MODE=pcb` job examines an uploaded `.kicad_pcb`: domains/groups, trace inventory, deterministic layout checks, and per-IC AI datasheet review. Findings merge into the report with a Layout filter. No packing or pcbnew write-back.
- POST /api/pipeline/{id}/pcb/start — PCB exam job (pcb_status).
- Deterministic PE-LAY-*, PE-PLC-*, PE-SI-001, PE-DRT-001 with required recommendations.
- AI PCB review (same loop as schematic) with layout/domain context.
- Schematic review no longer seeds placement/SI checks.
Fix sign-in /login 404 after rebrand
Landing CTAs pointed at `/login`, which did not exist (page is `/sign-in`). Added redirects, accepted legacy JWT issuer `boardlab-local`, and CORS for both BoardLab and BoardLab hosts.
- /login → /sign-in, /register → /sign-up; marketing links updated.
- JWT decode accepts boardlab-local issuer from pre-rebrand sessions.
- Deploy script CORS includes both public hostnames.
BoardLab name and finding IDs
Docker containers, deploy script and default site URL use the **BoardLab** name. Deterministic finding IDs use the `PE-*` prefix. Existing project files and browser storage keys are still read.
- Deterministic rule IDs use the PE-* prefix.
- Default host / update script → boardlab.michelebigi.it / update-boardlab.sh.
Antenna templates: IFA / meander / stub + .kicad_mod
Progetta antenna now returns a full parametric drawing: polyline segments (mm), SVG preview, and a downloadable KiCad footprint. Choose IFA, meander, or stub; geometry scales to the `antenna` zone when present. Still a routing template — no EM/VSWR invented.
- antenna_geometry.py (IFA / meander / stub → segments + SVG + .kicad_mod).
- POST /antenna/design accepts template; recipe includes geometry.
- RF / Impedance Progetta UI: template select, SVG preview, download footprint.
RF / Impedance: verify antenna + design recipe
Project tab renamed **RF / Impedance**. Verify matching from IC ANT/RF pins toward an `ANT*` / `ANT_FEED` marker; Progetta returns microstrip `w` for 50 Ω (stackup or UI h/εr), optional λ/4 length from `f0`, and antenna-zone bbox. Auto-draw copper comes later — no EM/VSWR invented.
- antenna_rf.py + GET/POST /api/projects/{id}/antenna.
- Verify + Progetta cards on the impedance panel.
- Sidebar label **RF / Impedance**.
Classify strap/bootstrap satellites; hide other
Caps on EN/BOOT/REGN/PMID and bootstrap caps between IC pins get real roles. Unclassified `other` parts are dropped from satellites and assemble_order.
- _role_hint classifies GND bypass on bias/strap nets and flying bootstrap caps.
- Assemble / IC groups omit role_hint=other.
Cleaner Domains / Power rails membership
Power rails follow primary domain only. IC satellites for decoupling/bulk/filter/pullup stay on the IC’s primary supply rail (LDO 3V3 no longer inherits VSYS input caps). Connectors/switches drop out of noisy “other” satellites.
- Rails view membership = domain primary rail (not transitive satellite nets).
- _group_for_ic primary-rail filter for power-role satellites; skip connector/switch “other”.
Stronger layout_rules extraction skill
Pintable skill now treats PCB / typical-application layout guidance as a first-class extract. Page trim keeps layout keywords; IC cache re-extracts once when `layout_rules` are empty under an older `model_version` (1.10.0+).
- skills/extract-pintable — dedicated layout_rules step, examples, hard negatives; tighter validate.py.
- Datasheet page trim includes PCB layout / decoupling / typical-application keywords.
- Analysis IC extraction refreshes empties when model_version < default (1.10.0).
- default_model_version → 1.10.0.
Placement F2 pack skeleton (gated)
Placement pipeline can propose satellite xy only when a `.kicad_pcb` layout exists and a `decoupling_proximity` rule has numeric `max_distance_mm`. No millimetres invented; otherwise `placement_pack.json` is skipped with an explicit reason.
- placement_pack.py + placement_pack.json after F1 plan.
- GET /api/pipeline/{id}/placement/pack and Pack section on the placement page.
- Placement stepper adds a **Pack satellites** stage.
Split Domains by primary supply rail
Domains no longer merge the whole board when a charger/LDO bridges VBUS→VSYS→3V3. Each IC is clustered by its primary supply rail (output for power ICs, regulated digital rail for consumers).
- _build_domains groups by primary rail instead of transitive POWER union-find.
- Domain ids use the rail name (e.g. domain_3V3_DIGITAL).
Domains & power-rail topology views
Browse routing-first functional groups from the project sidebar: Domains (power-net islands) and Power rails, each highlighting IC groups and satellite roles.
- Sidebar tabs **Domains** and **Power rails** (from placement_plan / functional_groups).
- Analysis graph_build also writes placement_plan.json.
- Heal stuck Placement queued/running when the worker is dead.
Unstick zombie running pipelines
A finished run whose worker died before flipping meta stayed `running`, so Progress showed Review spinning forever. Heal those projects when the event log already ends with `pipeline_complete`.
- heal_if_pipeline_finished on project get / pipeline status.
- Progress stepper marks all stages complete on pipeline_complete.
- Admin sweeper also covers local zombies without execution_name.
Stop progress→report bounce on finished projects
Opening a finished project no longer flashes Processing and dumps you on the report. Progress only auto-opens the report after a live run on that visit.
- /progress checks project status before SSE; finished projects go to the hub.
- Analysis SSE ignores placement events and does not treat already-terminal status as a crash.
Fix project sidebar navigation
Opening a finished project no longer dumps you on the report with a stuck left menu. Hub first; Report stays in the sidebar. Leaving `/report` for BOM/tabs uses a full navigation so the page actually changes.
- Dashboard project cards/table open /project/{id} (not /report).
- Sidebar Dashboard / Library / BOM tabs escape nested report/progress/placement routes.
Placement pipeline (parallel)
Dedicated Placement job builds the routing-first topology plan without touching the analysis pipeline status or spending credits. No millimetres — domains, IC groups, satellites only.
- POST /api/pipeline/{id}/placement/start (+ cancel, events SSE, get plan).
- Project page **Build placement plan** → /project/{id}/placement progress + topology viewer.
- Worker MODE=placement writes placement_plan.json (and refreshes functional_groups.json).
Layout F1 topology + crystal/NC checks
Routing-first floorplan foundation without inventing millimetres: domains and satellite role hints after graph build, plus deterministic crystal CL and NC-pin checks.
- functional_groups.json from graph_build (domains, IC groups, role_hint, attached layout_rules).
- crystal_cl_check (PS-XTAL-*) and nc_pin_check (PE-NC-001) in the deterministic suite.
- [Docs] Piano §0c Placement F1/F2; packing mm stays Layout F2.
DeepSeek roadmap integrations
Close the open P0/P2 items from the growth plan: offline smoke on the demo fixture, reviewer `shortest_path`, library promotion gate, PDF drop logging, per-stage cache hit-rate helper. IC placement stays Layout-product backlog.
- Offline demo-fixture smoke script + smoke_baseline.json.
- Graph tool shortest_path; library_gate before shared extracted/.
- cache_stats_by_stage(); PDF truncate/omit page logs.
- [Test] tests/test_roadmap_integrations.py.
Local multi-user auth
Self-host BoardLab accounts (email + password) so several people can share a project. Invite collaborators by email from the project page.
- AUTH_JWT_SECRET enables register/login; first user is admin and inherits users/local projects.
- /sign-in and /sign-up; sidebar account menu. Set NEXT_PUBLIC_AUTH_MODE=local on the frontend build.
- Collaborator lookup works for local users.
Prefer PCB nets for connectivity
When a `.kicad_pcb` is present, pad nets from the board drive the design graph. Schematic geometry alone was inventing swapped rails (GND↔3V3) and floating pins on good KiCad 10 designs.
- KiCad 10 (net "Name") pad form.
- Graph build overrides sch nets with board connectivity; /NET → NET.
- [Test] PCB nets beat a misleading schematic.
KiCad power nets merge
`.kicad_sch` connectivity treated every `GND` power flag as its own island (`GND_`, `GND__`…), so the reviewer invented swapped rails and floating pins on good designs.
- Same-name power symbols and global/local labels merge within a sheet (KiCad semantics).
- Pins sitting mid-wire attach to that net; mirror x/y handled correctly.
- [Test] Disconnected GND flags, local labels, mid-segment wire.
KiCad project zip upload
One zip (or every `.kicad_sch`) covers hierarchical sheets. If the zip has `bom.csv` and `.kicad_pcb`, those are taken too. Pipeline re-reads companions from storage.
- Zip → sheets + optional BOM/PCB. Nested Sheetfile paths kept.
- [Test] Upload → workspace re-parse finds child sheet parts.
Three file boxes
New project: BOM, schematic, and PCB on the first screen. PCB is optional.
- No extra wizard step for the board.
Ask for the KiCad board
The create-project wizard has a dedicated PCB step (skip still allowed). Report load errors show the API detail instead of a generic fetch failure.
- Wizard step for .kicad_pcb. Upload later from Settings or Impedance.
- Report page retries briefly and links back to pipeline progress when report.json is missing.
- Drop the KiCad project folder (or a zip). Extra library junk is ignored; the board is picked up if present.
ImpedenceFinder Z0 on PCB nets
A pipeline run with `.kicad_pcb` + stackup samples routed **signal** nets (power/ground skipped). Extra net names can be analyzed from the Impedance tab. Z0 is ImpedenceFinder net_walk/zsolver; no invented εr.
- impedance_nets.json after graph build. GET/POST /api/projects/{id}/impedance/nets.
Keepout courtyard
`layout_rules` `keepout` flags a foreign net whose track endpoint is inside the KiCad courtyard. Own net and missing courtyard skip. No invented analog/digital classes.
- PE-PLC-004 WARNING. Tests use the demo fixture's crystal, its input net and GND.
Crystal load caps and track path
Load caps on XIN/XOUT use the same `max_distance_mm` as decoupling. If the PCB has segments on the net, the limit is shortest-path length, not a guessed “loop is too big” ratio.
- Crystals (and their load caps on the demo fixture) run PE-PLC-001 when layout_rules has millimetres.
- Detour tracks: path along segments vs the same max_distance_mm. No segments → euclidean pad distance.
same_layer decoupling
If `layout_rules` sets `same_layer: true`, a decoupling cap on the opposite copper from the IC is a WARNING. A via inside the courtyard (calculated) is enough. Unset flag → skip.
- PE-PLC-003 WARNING when every placed cap on the net is on F vs B opposite the IC.
Thermal vias vs min_via_count
Via count is calculated inside the KiCad courtyard. The limit is the `min_via_count` parameter from `layout_rules`. No courtyard or no count → skip. No pad radius default.
- PE-PLC-002 when vias in courtyard < min_via_count. The demo fixture has no PCB so it stays silent.
Layout SI skew (datasheet mm only)
Intra-pair skew is measured on the PCB only when `layout_rules` `length_match` has a number. 3W, creepage, and CPWG are not guessed.
- PE-SI-001 ERROR when a named pair (_DP/_DM, _P/_N) exceeds that millimetre. No mm in the datasheet → skip.
Placement vs datasheet (PCB)
Decoupling distance is measured on the `.kicad_pcb` against `layout_rules`. No board → no `PE-PLC-001`. A null millimetre skips — no 3 mm default.
- PE-PLC-001 when a decoupling cap is farther than datasheet max_distance_mm. Empty layout_rules and missing caps skip.
Finding review and ECO
Findings can be accepted, marked false-positive, or wontfix with a required reason. Accepted rows export as ECO; OpenEMS-style layout SI is still not this.
- Review state on the report (open / accepted / false_positive / wontfix). Empty reason is rejected except when returning to open.
- ECO CSV/JSON of accepted findings only. False-positive and wontfix stay off the ECO.
- Release signature: SHA-256 of findings + user + timestamp. Needs-review filter ?review=open.
ImpedenceFinder calculator
The Impedance tab uses the closed-form engine from ImpedenceFinder (Hammerstad–Jensen / Cohn), not a second formula set and not OpenEMS.
- Project tab Impedance: microstrip, stripline, coupled-diff Z0, stackup → 50/90/100 Ω widths, download .kicad_dru advice.
- Vendored ImpedenceFinder core under vendor/impedancefinder/ (no gerber2ems, no pcbnew).
- CPWG stays unimplemented — no invented number.
Lifecycle and datasheet extras
Distributor lifecycle is a cached check, not a review scrape. Errata and layout_rules stay structured and skip when the catalog or the PDF has no number.
- PE-LF-001 EOL, PE-LF-002 NRND, PE-LF-003 explicit RoHS fail. Replacement only if the distributor lists it. Active / RoHS N/A / missing cache row are silent.
- PE-ERRATA-001 when a catalogued workaround pull-up is missing. No URL → skip.
- PE-INT-001 when internal_features.pullup_pins has no rail resistor.
- layout_rules closed kinds; non-numeric max_distance_mm is stored as null.
KiCad cad-bridge
BoardLab writes `boardlab-findings.json` next to the report so a KiCad 9/10 action plugin can pan to the symbol uuid on the right sheet.
- E2 cad-bridge JSON (version, ref, pins, sheet, uuid, severity). Layout rule ids target pcbnew; others target eeschema.
- .kicad_sch symbols keep cad_uuid + cad_sheet in cad_index (child sheet, not the empty root).
- Action plugin in plugins/kicad/ reads the JSON beside .kicad_pro / .kicad_pcb.
Power margin, sequencing, DNP enable
Schema checks now compare regulator load to Iout_max, look at PG→EN when a sequence is declared, and treat DNP as a fitted-variant graph.
- PE-PWR-001 when specified IQ+I_load exceeds Iout_max, or an explicit series R/ferrite DCR drops >5% of the rail. Missing IQ and PCB traces are not guessed.
- PE-SEQ-001 WARNING if power_sequence is in IC specs and upstream PG does not net to downstream EN.
- BOM DNP/Fitted/Variant on bom_fields. Fitted enable with only a DNP pull is PE-DNP-001 ERROR; no DNP column skips the check.
Filtri e termico schema
Deterministic checks now match RC/LC/π/T filters and estimate LDO/resistor dissipation without inventing missing numbers.
- Filter topology PE-FLT-001 (fc INFO) / PE-FLT-002 vs adc_sample_rate only when that spec exists. Ferrite DCR PE-FLT-003 only with a datasheet limit.
- LDO P = I_load×(Vin−Vout) and Tj = 25 + P·θJA. Missing θJA is PE-TH-001 INFO. Iout_max is not treated as load.
- Resistor I²R vs power_rating_w on LED paths and shunts with known ΔV (PE-TH-003).
DC-bias C_eff stima
The derating table now shows an effective capacitance under DC bias for C0G/X7R/X5R ceramics. It is labelled *stima* — not a vendor lot curve.
- C_eff column from an empirical V/Vrated table. Tantalum/electrolytic and unknown dielectrics are left blank.
- C0G/NP0 stays at nominal C; X7R at 50% of rated V is about 70% of C.
- Bulk C without a ~100 nF ceramic is PE-ESR-001 INFO (no invented Z(f) target).
Pull-up sizing and LDO Cout
Deterministic schema checks now size I2C pull-ups, flag NRST pull-downs, and look at LDO VOUT capacitance — still WARNING, never a invented datasheet µF ERROR.
- I2C pull-up value vs a wide NXP UM10204 band (PE-I2C-002). 4.7 kΩ is in-band; missing values are not sized.
- Active-low reset with a resistor to ground is PE-RST-002.
- Regulator VOUT needs Cout (PE-DEC-001); 100 nF-only on VOUT is PE-DEC-002. MCU VDD 100 nF is not flagged.
- Pin-mux UART on the demo fixture's MCU nets is covered in tests (SPI PICO/POCI already was).
DeepSeek V4.1 and re-analyze
BoardLab now defaults to DeepSeek-V4.1-Flash (`deepseek-flash`) for every LLM stage, shows API cost in dollars, and lets you replace the BOM and netlist on an existing project without deleting it.
- Default model is deepseek-flash (native vision). Legacy deepseek-v4-flash / deepseek-v4-flash-vision-exp names still work; they route to V4.1.
- Replace BOM & netlist on a finished project and re-run the analysis. History, library cache, and prior spend stay on the same project.
- API cost uses V4.1 Flash peak rates and is shown on the report, logs tab, and run estimate (not only credits).
Deeper datasheet review
Each IC review now sees more of the datasheet and starts from a structured abs-max table, so voltage, decoupling, and interface checks are less likely to stop at "Unverified".
- Extraction pulls Absolute Maximum Ratings (supplies, pin voltages, current, temperature) alongside the pin table, and the reviewer gets those numbers in context.
- DeepSeek ingest uses PyMuPDF text (tables survive better than pypdf) and, on vision models, renders pin / abs-max / electrical / application pages instead of always the first 24.
- Review checklist now includes recommended operating conditions, VIH/VIL, crystal load caps, and datasheet-named external parts. Turn budget 16, more excerpt pages, three follow-ups per concern.
Component library
Chips and passives stay in a shared library after the first look, so the next board does not re-download datasheets or re-extract pin tables.
- Library page in the sidebar: ICs (pin tables), passive series, discrete specs, and saved PDFs.
- Datasheets are written to the library as soon as they are fetched or uploaded, not only after a full review.
- The create-project wizard already skipped parts that were extracted once; that reuse now covers the PDF itself as well.
Automatic datasheets
BoardLab now finds datasheet PDFs on its own. You can still upload a file, but you no longer need DigiKey keys for the common case.
- Auto-fetch from LCSC (no API key) by manufacturer part number or LCSC code, with exact-MPN matching so a CH340E never silently becomes a CH340G.
- Direct Texas Instruments datasheet URLs (ti.com/lit/ds/symlink/…) as a second source for TI parts.
- Pipeline fallback: if a datasheet was not uploaded in the wizard, extraction and review try the same lookup before skipping the IC.
- DigiKey remains an optional third source when DIGIKEY_CLIENT_ID / DIGIKEY_CLIENT_SECRET are set.
DeepSeek API
BoardLab now talks to DeepSeek by default. Extraction skills run locally; datasheet PDFs are converted to text (and page images on the vision model) because DeepSeek does not accept native PDF documents.
- DeepSeek provider (deepseek-v4-flash, deepseek-v4-pro, deepseek-v4-flash-vision-exp) via the OpenAI-compatible Chat Completions API.
- Local skill runner: skills/*/SKILL.md is inlined and validate.py runs in-process — no Anthropic Console upload required.
- PDF ingest for DeepSeek: pypdf text extraction plus optional PyMuPDF page renders on vision models.
- Anthropic and Gemini remain optional fallbacks via PROVIDER_* / FALLBACK_PROVIDER_*.