From 3e5f7569162e054bd5ded66768a879241485ac49 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 17 Jul 2026 12:49:29 +0530 Subject: [PATCH] SKILL: stamp symbol parameters automatically in the new-part flow, never as an offer (by admin) --- SKILL.md | 279 ++++++++++++------------------- assets/CHANGELOG.xlsx | Bin 5665 -> 5668 bytes assets/template/Type_ID.xlsx | Bin assets/vecmocon-logo.png | Bin references/description_format.md | 0 references/schlib_parameters.md | 68 ++++---- references/taxonomy.md | 0 scripts/altium_params.py | 0 scripts/altium_refs.py | 0 scripts/append_parameter.py | 0 scripts/common.py | 0 scripts/fill_templates.py | 0 scripts/gitea_components.py | 0 scripts/push_to_gitea.sh | 0 scripts/schlib_write.py | 159 ++---------------- 15 files changed, 160 insertions(+), 346 deletions(-) mode change 100644 => 100755 SKILL.md mode change 100644 => 100755 assets/template/Type_ID.xlsx mode change 100644 => 100755 assets/vecmocon-logo.png mode change 100644 => 100755 references/description_format.md mode change 100644 => 100755 references/schlib_parameters.md mode change 100644 => 100755 references/taxonomy.md mode change 100644 => 100755 scripts/altium_params.py mode change 100644 => 100755 scripts/altium_refs.py mode change 100644 => 100755 scripts/append_parameter.py mode change 100644 => 100755 scripts/common.py mode change 100644 => 100755 scripts/fill_templates.py mode change 100644 => 100755 scripts/gitea_components.py mode change 100644 => 100755 scripts/push_to_gitea.sh mode change 100644 => 100755 scripts/schlib_write.py diff --git a/SKILL.md b/SKILL.md old mode 100644 new mode 100755 index c3a72b6..f76b0a5 --- a/SKILL.md +++ b/SKILL.md @@ -2,16 +2,15 @@ name: library-manager description: >- Manage Vecmocon's component library. Extract parameters from a component datasheet PDF into the - per-typeid Excel template, check Gitea for a duplicate MPN_make, classify to a - typeid, update that typeid's template (versioning, changelog, backfill), fill a per-part - workbook, verify with a human, read the Altium symbol/footprint refs, and push a part folder to - Gitea. Can UPDATE an existing part instead of hard-stopping on a duplicate. WRITES the mandatory - SOP parameters (from the verified Excel) directly into the .SchLib symbol and COMPILES an Altium - integrated library (.IntLib) bundling symbol + footprint, so every part folder holds five files - (workbook, datasheet, .SchLib, .PcbLib, .IntLib). Every Description follows Vecmocon's strict - Altium Description Format. Use whenever the user uploads a datasheet, builds/updates a library - entry, adds a parameter, fills .SchLib parameters, builds an integrated library, or pushes to - Gitea. ALWAYS trigger on "\datasheet", "\library", "\library-manager", or "\schlib". + per-typeid Excel template. Checks Gitea for a duplicate MPN_make, classifies to a typeid, updates that typeid's template (versioning, changelog, backfill), fills + a per-part workbook with human verification, reads the Altium symbol/footprint refs, assembles + a part folder and pushes to Gitea. Can UPDATE an existing part instead of hard-stopping, and can + WRITE the mandatory SOP parameters directly into a .SchLib symbol while stripping Ultra-Librarian + defaults. On the .SchLib task it also fills that component's Excel (Description + all parameters) + and pushes the updated symbol to Gitea, no verification loop. Descriptions + follow Vecmocon's strict Altium Description Format. Use whenever the user uploads a datasheet, + builds/updates a library entry, adds a template parameter, fills/updates .SchLib parameters, or + pushes a part to Gitea. Always trigger on "\datasheet", "\library", "\library-manager", "\schlib". --- # Library Manager @@ -42,6 +41,16 @@ own, without asking for confirmation**. Never add a "shall I push?" step — jus the user where it landed. (You still flag a genuine symbol/footprint mismatch, because that's a correctness issue, not a push confirmation.) +**Symbol parameters are a second exception — stamp them without asking, every time.** This +holds in both directions. As a standalone task (the engineer hands over a `.SchLib`): fill that +component's Excel (Description + all parameters) and push the updated symbol automatically, +without pausing to confirm the values. And **inside the normal new-part flow**: once the symbol +is uploaded at step 6, stamp the SOP + sheet parameters onto it and strip the Ultra-Librarian +defaults as a matter of course — do **not** offer it as a follow-up, do **not** ask whether the +engineer wants it, and never push a part folder whose symbol still carries UL defaults. See +*Mandatory symbol parameters* for why this path skips the loop. Every other flow keeps the +normal interactive asks below. + ## Inputs - **A datasheet PDF whose filename is the MPN** (e.g. `BAT46WJ.pdf`). If it's a series @@ -75,16 +84,10 @@ library repo/ __/ e.g. BAT46WJ_Nexperia_SCH __.xlsx this part's own one-row parameter sheet _data.pdf the datasheet - .SchLib user-provided, with all parameters written in + .SchLib user-provided .PcbLib user-provided - .IntLib integrated library (symbol + footprint, compiled by the skill) ``` -**Every part folder holds five files** — the workbook, the datasheet, the `.SchLib` (with the -full parameter set written into it), the `.PcbLib`, and the compiled `.IntLib`. The `.SchLib` -parameter fill and the `.IntLib` build are **not optional** — they run on every part before the -push (see *Mandatory symbol parameters* and *Build the integrated library* below). - There is **no single master workbook** — each part carries its own sheet inside its folder. Connection + repo names live in `config/gitea.env` (`SKILL_REPO`, `LIBRARY_REPO`), so runs need no per-session token. If the host is unreachable, the git steps fail clearly and write @@ -257,7 +260,9 @@ The part workbook has up to **two sheets**: Deliver the filled workbook to the user and ask them to verify it. If they report an error or say it isn't right, **go back to step 4, re-read the datasheet more carefully, re-fill, and hand it back.** Repeat until the user confirms it's verified. Nothing is pushed until -this passes — the engineer is the ground truth for the numbers. +this passes — the engineer is the ground truth for the numbers. (The standalone `.SchLib` task +is the exception: it fills the Excel and pushes without this loop — see *Fill the component's +Excel and push* under *Mandatory symbol parameters*.) ### 6. Symbol + footprint → the design columns @@ -292,23 +297,33 @@ python scripts/fill_templates.py part.json \ --template assets/template/template.xlsx --dest // --design design.json ``` -Once you have the symbol and its Library Ref, **always** write the full parameter set into the -`.SchLib` from the verified per-part Excel — see *Mandatory symbol parameters* below. **This is -compulsory on every run; do not ask the engineer whether to do it — just do it.** It is how the -verified workbook values (and the Description) land in the Altium symbol's properties, and the -`.IntLib` is then built from this enriched symbol. +Once you have the symbol and its Library Ref, **stamp the mandatory symbol parameters onto the +`.SchLib` — always, automatically, without asking.** This is not an optional extra and it is not +something to offer the engineer: a symbol that reaches Gitea carrying Ultra-Librarian defaults +instead of the SOP set is an incomplete part. Do it in this same run, before step 7, so the +folder is pushed complete the first time. -### 7. Assemble the part folder — build the `.IntLib`, then five files +Build the parameter set from the datasheet values you already read for the workbook (they are the +same values — Manufacturer, Manufacturer Part, Value, Tolerance, Operating Temperature, RoHS, +Datasheet, Process, Vecmocon Part Code, …), **plus every engineering parameter from this typeid's +sheet** and `Component Type` = the part's Class, then write them in and strip the UL defaults — +see *Mandatory symbol parameters* below for the full set and the exact commands. Skip only the +housekeeping columns (`MPN_make_type`, Skill/Template Version) and the four Library/Footprint +Ref/Path columns — those are Altium's own model links, so duplicating them as parameters would +create two sources of truth. -The staging folder `/` should hold the per-part `.xlsx`, the datasheet (name it -`_data.`), the enriched symbol (`.SchLib` with parameters written in, step 6), and the -footprint (`.PcbLib`). Now **compile the integrated library** from the enriched symbol + footprint -so the folder carries all **five** files — see *Build the integrated library* below: +Don't stop to confirm the values here either; the engineer validates the symbol by opening it in +Altium, which is the real check. Leave genuinely-unknown fields blank (the SOP hides blank +parameters) and **note the gaps in your summary** rather than blocking on a question — the one +field that is never on a datasheet is `Vecmocon Part Code`, so use it if the engineer supplied it +and otherwise leave it blank and say so. -```bash -python scripts/build_intlib.py --schlib //.SchLib \ - --pcblib //.PcbLib --out //.IntLib -``` +### 7. Assemble the part folder + +The staging folder `/` should now hold the four files: the per-part `.xlsx`, the +datasheet (name it `_data.`), the symbol (the **stamped** one from step 6, not the raw +upload), and the footprint. If the engineer's upload carried extra files (a `.step` 3D model, a +`.LibPkg`), ask whether to include them — the standard folder is these four. ### 8. Push to the library repo, under the part's Class @@ -324,11 +339,10 @@ user where it landed. ## Mandatory symbol parameters (.SchLib) The SOP (§5) requires every schematic symbol to carry a fixed parameter set in its Altium -properties — `Manufacturer`, `Manufacturer Part`, `Value`, `Tolerance`, `Operating -Temperature`, `ROHS`, `Datasheet`, `Process`, `Vecmocon Part Code`, and the two second-source -fields — with the **Comment** set to the MPN. The skill stamps these onto the symbol from the -verified workbook. **This step is compulsory on every part build — never ask whether to fill the -`.SchLib` parameters; always do it** before assembling the folder and building the `.IntLib`. +properties — `Component Type`, `Manufacturer`, `Manufacturer Part`, `Value`, `Tolerance`, +`Operating Temperature`, `ROHS`, `Datasheet`, `Process`, `Vecmocon Part Code`, and the two +second-source fields — with the **Comment** set to the MPN. The skill can stamp these onto the +symbol from the datasheet. This runs **as its own task too**, not only inside new-part creation: whenever the user hands over one or more `.SchLib` files and wants their parameters filled/updated (e.g. "\schlib", "add @@ -337,9 +351,13 @@ build its `params.json`, and run `scripts/schlib_write.py` per file — same ste The skill writes these **directly into the `.SchLib` in pure Python** with `scripts/schlib_write.py` (it rebuilds the OLE around the enlarged `Data` stream, preserving -every other byte, and strips the Ultra-Librarian `Manufacturer_Name` / `Manufacturer_Part_Number` -defaults that duplicate the SOP fields). Three kinds of value: +every other byte, and strips the Ultra-Librarian defaults that shouldn't ship: `Manufacturer_Name` +/ `Manufacturer_Part_Number` (they duplicate the SOP fields), the UL `Copyright` notice, and the +UL `Component_Type` — replaced by Vecmocon's own spaced `Component Type`). Kinds of value: +- **Derived** — `Component Type` = the part's **Class** (`Resistor`, `Capacitor`, `Diode`, + `Transistor`, `IC`, …), from `scripts/common.py:class_folder(typeid)` for this part's typeid. + This is the same Class that names its library-repo folder, so the symbol carries it too. - **Read from the datasheet** (don't just echo given text — open the PDF and fill the real, verified values): `Value` = the **value only** in shorthand (e.g. `1u`, `12p`, `10k` — no package), plus `Manufacturer`, `Manufacturer Part`, `Operating Temperature`, `Tolerance`, @@ -348,40 +366,21 @@ defaults that duplicate the SOP fields). Three kinds of value: populate these by default; they stay hidden in Altium until filled later. (An optional cross-reference search to find a second source is documented in `references/schlib_parameters.md` but is currently off — only do it if the engineer asks.) -- **Ask the engineer** — only `Vecmocon Part Code` (internal, not derivable). +- **Fill without pausing** — the `.SchLib` task is non-interactive, so don't stop to ask. The + only genuinely non-derivable field is `Vecmocon Part Code` (internal): use it if the engineer + already supplied it in the request, otherwise leave it blank (the SOP hides blank parameters) + and note the gap in your summary rather than blocking on a question. Leave any genuinely-unknown field blank — the SOP hides blank parameters, so a gap simply stays empty until filled. The full method for the second-source search is in `references/schlib_parameters.md`. -Write the **full parameter set** — the typeid template's engineering columns **plus** the SOP -params above (see `references/schlib_parameters.md`) — and **source it from the verified per-part -Excel** so the symbol and the workbook can never disagree. After the sheet is verified (step 5), -pass that `.xlsx` to the writer with `--from-xlsx`: every engineering column **and the -Description** are read straight out of it and written into the `.SchLib`. Then layer on the -**SOP-only** fields that aren't template columns (the `Value` shorthand, `Manufacturer Part`, -`Operating Temperature`, `ROHS`, `Datasheet`, `Process`, `Vecmocon Part Code`, and the blank -second-source pair) via a small `params.json`; on any name collision the `params.json` value -wins. Pass `--typeid` too, so any template column the datasheet left silent is still present -(blank): +Collect the values into a `params.json` and write them into the symbol: ```bash -python scripts/schlib_write.py --schlib .SchLib \ - --from-xlsx //.xlsx --params params.json \ - --out //.SchLib --typeid +python scripts/schlib_write.py --schlib .SchLib --params params.json --out //.SchLib ``` -(`--params` is optional if the Excel already carries everything you need; `--from-xlsx` is -optional if you'd rather hand-build the whole set in `params.json` — give at least one.) The -`Description` written onto the symbol (and into the component's ComponentDescription field) is -the exact one from the Excel, which was built to `references/description_format.md`. - -Passing `--typeid` also fills the symbol's **`Type`** parameter with the component type for that -typeid — the taxonomy **Class** (e.g. `Resistor`, `Capacitor`, `Diode`, `Transistor`, -`Relay / Contactor`, `Inductor / Magnetics`, `Integrated Circuit (IC)`) — so the symbol -self-describes what kind of part it is. It's derived from the typeid (which came from the -datasheet), so it's set automatically; only an explicit `Type` in the Excel/params overrides it. - Deliver the resulting `.SchLib`; the engineer opens it in Altium once to confirm it loads, then **Saves to Server** with a revision note. The full parameter set, each value's source, the `params.json` shape (incl. the `remove` list), and the mini-stream size caveat are in @@ -390,39 +389,54 @@ round-trip, fall back to `scripts/altium_params.py` (emits an Altium DXP script parameters from inside Altium). Always have the engineer confirm the file opens in Altium — the skill writes Altium's own binary format, so Altium is the final validator. -## Build the integrated library (.IntLib) +### Fill the component's Excel and push — automatically, no verification loop -Every part also gets a compiled **integrated library** — one file that bundles the schematic -symbol (with the parameters written in) and its footprint, so the component resolves in Altium -with no separate `.PcbLib` to locate. `scripts/build_intlib.py` compiles it in pure Python, no -Altium needed, and it's the **fifth file** in every part folder. +The datasheet values you just read for the symbol belong in that component's per-part Excel +workbook too, so fill them in the **same pass and push the result without asking**. This is a +deliberate exception to the skill's usual "verify before Gitea" rule: when the engineer hands +over a symbol for the `.SchLib` task, they want the library entry brought fully up to date in +one shot — the Description and every parameter written into the Excel, the updated symbol saved +beside it, and the whole thing pushed — not a round of confirmation questions. The engineer is +already reviewing the symbol in Altium, so a separate spreadsheet verification loop adds delay +without adding safety. The one check that still stands is the symbol/footprint-vs-part match: if +the symbol clearly isn't this component, stop and flag it rather than pushing the wrong part. -```bash -python scripts/build_intlib.py --schlib //.SchLib \ - --pcblib //.PcbLib --out //.IntLib -``` +1. **Fill the Excel automatically.** Reuse the values you already read for the symbol to build + `part.json` — with `Description` built to `references/description_format.md` and every + parameter the datasheet states — and fill the per-part sheet **directly, skipping the human + verification loop (step 5)**. Do this inside a checkout of the library repo so it can be + pushed; for a part already in Gitea, locate it with `find-part` exactly as in *Updating an + existing part*, and re-derive the design columns from the symbol/footprint already in the + folder so they carry through unchanged: -How it works and what it needs: + ```bash + python scripts/gitea_components.py checkout --dest work/ + python scripts/gitea_components.py find-part --mpn --make --root work/ --json + python scripts/altium_refs.py design \ + --symbol work///.SchLib \ + --footprint work///.PcbLib > design.json + python scripts/fill_templates.py part.json \ + --template assets/template/template.xlsx --dest work/// --design design.json + ``` -- Feed it the **enriched `.SchLib`** (after `schlib_write.py` has written the parameters in) so the - integrated symbol carries the full parameter set and the correct ComponentDescription. -- The symbol **must contain a footprint model link** (an Altium RECORD=45 `ModelName` / - `ModelType=PCBLIB` in its `Data` stream — Ultra-Librarian and Altium exports include this). The - builder reads that link to know which footprint to bind, and errors clearly if it's absent — in - that case the symbol has no footprint assigned, so fix the symbol (or re-export it) first. -- It builds the `.IntLib` as an OLE compound file with five streams — the embedded `.schlib` and - `.pcblib` (zlib-compressed at Altium's default level), plus `LibCrossRef.Txt`, `Parameters .bin`, - and `Version.Txt` — reusing a bundled container skeleton - (`assets/templates/intlib_container.IntLib`) for the exact directory layout Altium expects, and a - **FAT-first** compound-file writer (`build_intlib.write_cfb`). Both the outer container and the - embedded symbol are written FAT-first — this matters: a FAT-last layout re-opens fine in olefile - and even standalone in Altium, but Altium's **IntLib extractor** throws "Stream read error" on it. - The two embedded libraries are compressed at zlib's **default level** (`0x789c`); Altium's - decompressor rejects other levels (e.g. level-9 `0x78da`). -- The builder self-validates: it re-opens the output, decompresses both embedded libraries, and - confirms they round-trip and that the cross-reference names the symbol + footprint. Even so, - Altium is the final validator — have the engineer open the `.IntLib` once (or, as a guaranteed - fallback, compile a `.LibPkg` in Altium from the same `.SchLib` + `.PcbLib`). + (If `find-part` returns `NOT FOUND`, the part isn't in Gitea yet — assemble the folder as a + new part per steps 4–7, still without the verification loop, then push with `push-part`.) + +2. **Drop in the updated symbol and push — no confirmation.** Copy the `.SchLib` you wrote with + `schlib_write.py` into the same part folder under its proper name, then push the updated part + — the symbol and the freshly filled Excel together — automatically: + + ```bash + cp //.SchLib work///.SchLib + python scripts/gitea_components.py commit-push --root work/ \ + --message "update : fill parameters + symbol (by )" \ + --author " <>" + ``` + + Pushing without asking is consistent with the skill's standing rule that pushing is automatic; + what's new here is that the Excel fill is automatic too. Tell the user which parameters you + filled, that the symbol was updated, and where in Gitea it landed. (A part-data fill isn't a + template change, so nothing version-bumps and the changelog is untouched.) ## Per-typeid versioning @@ -533,81 +547,4 @@ change: nothing is overwritten until the engineer has verified the new version. - **Symbol/footprint** → copy the new `.SchLib`/`.PcbLib` into the folder under their proper names (replacing the old ones), then re-derive against the **new** files and rebuild with `--design` exactly as above (this is step 6 of the add flow). Still flag a genuine - symbol/footprint-vs-part mismatch — that's a correctness issue. - - **Datasheet** → drop the newer PDF in as `_data.`, replacing the old one. If the - values should reflect it, also redo the values step above. - -4. **Verify, then push.** Hand the rebuilt `.xlsx` back and run the same human - verification loop (step 5) — the engineer is still the ground truth. Once confirmed, commit - the checkout and push (attributed to the operator, with a message that says it's an update): - - ```bash - python scripts/gitea_components.py commit-push --root work/ \ - --message "update : (by )" \ - --author " <>" - ``` - - The authored commit and message are the record of the revision (visible in `git log`, the - Gitea commit view and `git blame`); the changelog stays reserved for template/version - changes, not per-part data fixes. Tell the user what changed and where it landed. - -## Pushing the skill repo - -When skill files change (a new typeid template, a parameter add, a version/changelog bump), -push the skill's own files to the skill repo with `push-skill` **automatically** (no -confirmation): - -```bash -python scripts/gitea_components.py push-skill --author " <>" \ - --message "Sync skill files + changelog" -``` - -`push-skill` clones the skill repo, copies the skill files in with the **`GIT_TOKEN` blanked -out** (the real token never leaves the machine), and **merges** `CHANGELOG.xlsx` — appending -this run's new rows onto the changelog already in Gitea so earlier entries are preserved — then -writes the merged changelog back locally. (The older `push_to_gitea.sh` still exists for a -plain flat push, but it does not merge the changelog or blank the token, so prefer -`push-skill` for the skill repo.) - -## Resources - -- `assets/template/template.xlsx` — the master template: one sheet per **typeid** (125), - source of every sheet's headers, styling and order. Columns A/B/C are always - `MPN_make_type` / `Skill Version` / `Template Version`; `Library Ref/Path`, - `Footprint Ref/Path` and `Manufacturer` sit near the end. -- `assets/template/Type_ID.xlsx` + `references/taxonomy.md` — Class → Subclass → Type ID. -- `references/description_format.md` — Vecmocon's Altium **Description Format** (the `_`-joined - engineering string for each part's Description column). Defines a format for **every** type: - the four SOP-defined ones (RES/CAP/Zener/TVS) are strict, the rest are the house extension on - the same basis. Read it before filling any Description. -- `references/schlib_parameters.md` — the SOP **mandatory symbol parameters** (§5) for the - `.SchLib`: the parameter set, where each value comes from, and how the generated Altium script - stamps them onto the symbol. -- `assets/template/versions.json` — per-typeid `template_version` + `skill_version`. -- `assets/CHANGELOG.xlsx` — global version/parameter changelog (created on first add; - merged into the skill repo's copy in Gitea by `push-skill`). -- `scripts/common.py` — taxonomy loader (`load_taxonomy`, `class_folder`), version store - (`get_versions`, `version_labels`, `bump_versions`), and the tag helper (`part_tag`). -- `scripts/fill_templates.py` — build one per-part `.xlsx` (version-stamped); reused for - backfill. -- `scripts/append_parameter.py` — append parameter(s) to a typeid, bump its versions, write - the changelog. -- `scripts/altium_refs.py` — read Library/Footprint Ref from `.SchLib`/`.PcbLib`. -- `scripts/schlib_write.py` — write the SOP mandatory parameters **directly into a `.SchLib`** - (pure-Python OLE rebuild; removes the Ultra-Librarian `Manufacturer_Name` / - `Manufacturer_Part_Number` defaults). Primary path; see `references/schlib_parameters.md`. -- `scripts/altium_params.py` — fallback: generate an Altium DelphiScript that stamps the same - parameters onto a `.SchLib` from inside Altium (DXP → Run Script). -- `scripts/build_intlib.py` — compile a component's `.SchLib` + `.PcbLib` into an Altium - **integrated library** (`.IntLib`) in pure Python (FAT-first OLE writer + Altium-level zlib); - the fifth file in every part folder. Needs the enriched `.SchLib` (parameters written) with a - footprint model link. Uses `assets/templates/intlib_container.IntLib` as the container skeleton. -- `assets/templates/intlib_container.IntLib` — a known-good single-component `.IntLib` reused - purely as the OLE container skeleton by `build_intlib.py` (all its streams are overwritten). -- `scripts/gitea_components.py` — `check-mpn`, `find-part` (locate an existing part to - update), `checkout`, `list-type`, `place-part`, `commit-push`, `push-part` (library repo), - and `push-skill` (skill repo: token-blanked push + append-only changelog merge). -- `scripts/push_to_gitea.sh` — push a folder's contents to a Gitea repo (used for the skill - repo). -- `config/gitea.env` — host, user, token, and the `SKILL_REPO` / `LIBRARY_REPO` names - (**secret** — do not push the token). + symbol/footprint-vs-part misma \ No newline at end of file diff --git a/assets/CHANGELOG.xlsx b/assets/CHANGELOG.xlsx index 822d23111c5a732160864de0f0ea91289c09eca2..16a0459f11853d6ba2eb36f73570ace90a314b85 100644 GIT binary patch delta 2330 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 Zat5K4OZ;Tq`0==K0*y