Create a part number from a distributor link¶
Status: plan, 2026-09-17. Angel: "in the intake, pick Create part number, paste a DigiKey / Mouser / LCSC link, maybe the datasheet and a picture, hit submit, and the part is created following the SOPs." This is how, and most of it already exists.
What already exists (so this is small)¶
| Piece | Where | What it already does |
|---|---|---|
| Distributor lookup | ~/.local/bin/part-lookup |
DigiKey + Mouser (+ Nexar fallback) by MPN: description, manufacturer, datasheet URL, lifecycle, price. Cached. |
| Description normaliser | ~/assistant/partnorm.py |
decode_mpn() reads a value out of a part number; normalise() (Haiku) writes a SOP-002 description; validate() rejects one that breaks the rules |
| Proposal builder | stock.py build_part_proposal_async() |
next free IPN per SOP-001, name, description, category guess, manufacturer, supplier, supplier SKU, datasheet link — every field editable before submit |
| Creator | stock.py _create_part_and_stock() |
creates the part, the manufacturer part, the supplier part, uploads the image (SOP-002 step 11), reuses an existing part of the same name instead of duplicating |
| Intake front door | intake.py |
one note box, a kind dropdown, photo capture, per-department routing, the queue at /stock |
So the new work is only: read the MPN out of a pasted link, a kind in the dropdown, and a confirm screen without the stock fields.
The flow¶
- Intake → kind Create part number. The box takes one or more links, pasted in any order: DigiKey, Mouser, LCSC, a datasheet URL, plus an optional photo (the same camera field the stock count already uses).
- On submit the server pulls the MPN out of each link (see below), and if none is found falls back to treating the text as the MPN itself.
part-lookupfills in manufacturer, description, datasheet and lifecycle;partnormrewrites the description to SOP-002's rules andvalidate()refuses a bad one rather than inventing a good-looking wrong one.- A confirm screen shows the proposal — IPN, name, description, category, manufacturer, MPN, supplier, SKU, datasheet, image — every field editable. Nothing is created until someone presses the button, because a wrong part number is worse than a missing one.
- Submit creates the part, the manufacturer part, the supplier part and the image. The reply shows the new IPN and a link to it in the ERP.
- A part that already exists is matched first (by MPN, then by name) and offered for reuse — the same guard the stock queue uses, which is what stopped duplicate parts in September.
Reading the MPN out of a link¶
| Distributor | Pattern | MPN |
|---|---|---|
| DigiKey | digikey.com/en/products/detail/<mfr-slug>/<MPN>/<id> |
path segment 4, URL-decoded |
| Mouser | mouser.com/ProductDetail/<mfr-slug>/<MPN>?qs=… |
path segment after the slug |
| LCSC | lcsc.com/product-detail/<category>/<C-number>.html |
the C… code is LCSC's own; resolve it to an MPN by fetching the page title or their API |
| anything else | — | treat the text as the MPN |
DigiKey and Mouser give the MPN in the URL, so no page fetch is needed. LCSC does not, so that one needs a fetch or their API; until that is written, an LCSC link alone should say "paste the manufacturer part number too" rather than guess.
Rules it must not break¶
- SOP-001: next free number in the class, never reused. SOP-002: description style, category, image, datasheet. Both already enforced by the code above — do not re-implement them.
- No part is created without a person pressing the button (SOP-010: the assistant proposes).
- Refuse rather than invent: if the lookup finds nothing and the description would be empty, say so and let the person type it.
Where it goes¶
intake.py (kind + form), stock.py (reuse; split the proposal path so it can run without the
stock fields), the queue page for confirmation. Sandbox branch first, as always.
Log¶
- 2026-09-18: Task #101 preflight only; watch is not implemented or scheduled. Reuse
~/.local/bin/part-refreshandkicad-lib/scripting/partlookup.pyshared cache, not a new paid lookup service. Review foundbest_part()at line 181 can fall back to a different MPN,mouser_summary()at line 385 treats absent lifecycle as Active, and daily budget read/write is not locked. The watch must require exact identity, preserve unknown status, compare like-for-like currency/SKU/quantity, and serialize budget use. Start scope discovery from ERP BOMs; existingerp-config/reporting/sql/bom-costs.sqlcovers board IDs 181/199/201, but current active scope still needs live read-only verification. Reuse~/.local/bin/_kanboard.pyfor deduplicated tasks; do not build a KiCad fragment parser or use the assistant's legacy task draft layer. Configure thresholds explicitly and rehearse under network/credential isolation before enabling a nightly user timer. No ERP/distributor calls or production mutations were made. Work paused at the SOP-025 Codex weekly reserve (90%, reset September 23 23:50 EDT); Claude shared allowance was 66%, session 3%. Resume with a bounded Sonnet implementation after scope/design review; Haiku audit artifact is.local/state/task101/haiku-audit.json. - 2026-09-17: written after Angel asked for a paste-a-link part creator.
- 2026-09-17: built and shipped.
partnorm.mpn_from_url()reads DigiKey/Mouser links directly (segment-based, no page fetch); LCSC gives only the C-number, never guesses an MPN.intake.pykind "part" -> Engineering, note-primary (optional photo).stock.py's queue now carries kind "stock" and kind "part" rows (_QUEUE_KINDS);_create_part_corefactors the SOP-001/SOP-002 part creation out of_create_part_and_stockso_create_part_only(no stock item, ever) and_reuse_existing_part(existing-part dedupe, creates nothing) share it exactly. Verified live against the sandbox ERP: a DigiKey link for a Molex connector not yet in the catalogue produced IPN EE-C-0196 with a real distributor description/manufacturer/ datasheet and created cleanly; a DigiKey link for a part already in the sandbox catalogue correctly offered reuse instead of a duplicate; an LCSC-only paste produced a blank-for- manual-entry proposal rather than inventing an MPN. Sandbox ERP token in~/.config/assistant/env.sandboxhad gone stale from that morning's sandbox reset — minted a fresh one witherp-sandbox tokenfor the verification run (not committed to the env file, since editing it was blocked by the session's own permission gate — worth doing by hand next sandbox reset). Cherry-picked clean to~/assistantmain, tests pass on both branches, both pushed,assistant.servicerestarted. SOP-002 updated with the new route as the normal way to create a purchased component (version 2.1).